NHN Cloud NHN Cloud Meetup!

Ansible&AWX(インストールから基本的活用とTCD連動まで)

はじめに

サーバーの増設や、既存の設定を変更するなどして1台あるいは複数台のサーバー作業が必要になった場合、どのように処理しますか?
たとえば、20台のサーバーを設定するとしましょう。

  • 基本ディレクトリを作成
  • jdkインストール
  • scouterファイルのコピーおよび設定変更
  • managerAgent.warファイルのコピー
  • scriptファイルのコピー
  • crontabの登録
  • などなど…

これらの作業を数回のクリックで可能にしてくれるものがあります。

Ansibleとは?

複数台のサーバーを効率的に管理するために考案された環境構成の自動化ツールの1つで、エージェントレス(SSH)、冪等性など、さまざまなモジュールに対応し、アクセシビリティが高い(Pythonに対応)などの特徴があります。

ライセンス

GPL 3.0(Ansible)/ Apache 2.0(AWX)に従い、環境設定の目的に社内でも利用できます。

AnsibleはGPL3.0ライセンスに従い、GPLが適用されたオープンソースを含めて配布する場合、関連するソースは共に公開しなければならない義務があります。
ただし、サーバーでのみインストールし、ウェブネットワークを通じてサービスを行う形態であれば(当該オープンソースが他に配布されなければ)、使用可能です。
AWXはApache2.0ライセンスに従い、修正して使用する場合は修正目的について簡単にソース上部に作成する必要があります。 上記のAnsibleと同様に、ウェブネットワークを通じてサービスを行う場合は、該当の義務はありません。
ライセンスの義務条項さえ遵守すれば、オープンソースとして使用できます。

環境設定の自動化ツールは、どのようなものがあるか?

区分 Chef Ansible Puppet Salt
実装言語 Ruby / Erlang Python Ruby Python
定義ファイル DSL(Ruby) YAML DSL(Ruby) YAML、DSL、(Python)
エージェントのインストール Pull(Agent) Push(AgentLess) Pull(Agent) 選択可能
GitHub Star 6.4k 45.5k 5.9k 11.3k
メリット ・プログラミング可能
・Communityおよびドキュメント
・大規模配布に安定的
・インストールと設定が簡単
・エージェントレス
・様々なモジュールに対応
・冪等性
・SSH/winRM接続方式は、他のモデルと比較して安全
・初期設定と構成が簡単
・ウェブUIコンソールでノード管理タスクが容易
・Shellレベルの構造を操作できる機能あり
・大規模インフラ管理に安定的
・Communityおよびドキュメント
・拡張性と復元力に優れている
・より多くのオプションと柔軟性を提供
デメリット ・学習曲線が高い
・初期設定が複雑
・Pull-based方式
・SSH通信速度 ・DSLやRubyの学習曲線
・CLIのサブタイトル
・Pull-based方式
・No GUI

Googleトレンド

AWXとは?

Ansible Towerのオープンソース版で、AnsibleをGUIで管理し、APIで制御できるようにするシステムです。
上図のように、LAUNCHを1回クリックすると、サーバーの設定が完了します。

Ansible&AWXインストール

AWXがDocker基盤でインストールされるため、CentOS 6では容易にインストールできません。

用意するもの

CentOS 7
Python 3.6
Docker

Pythonインストール

Python 3を基準に説明します。

sudo yum install gcc zlib-devel openssl openssl-devel libffi-devel -y
wget https://www.python.org/ftp/python/3.6.8/Python-3.6.8.tar.xz
tar -xvf Python-3.6.8.tar.xz
cd Python-3.6.8
mkdir -p /home/ansible/package
./configure --prefix=/home/ansible/package/Python-3.6.8
make && make install

pipインストール

wget https://bootstrap.pypa.io/get-pip.py
/home/ansible/package/Python-3.6.8/bin/python3 ./get-pip.py

Virtualenvインストール(オプション)

この手順は必須ではなく、Pythonパッケージを別途管理したい場合にのみ行います。(進行を推奨)

cd /home/ansible/package/Python-3.6.8/bin
./pip3 install virtualenv
./pip3 install virtualenvwrapper
# vi ~/.bashrc

export VIRTUALENVWRAPPER_PYTHON=/home/ansible/package/Python-3.6.8/bin/python3
source /home/ansible/package/Python-3.6.8/bin/virtualenvwrapper.sh
# vi ~/.bash_profile

PATH=$HOME/package/Python-3.6.8/bin:$PATH:$HOME/bin
export WORKON_HOME=$HOME/.virtualenvs
export PATH
source ~/.bash_profile
mkvirtualenv -p /home/ansible/package/Python-3.6.8/bin/python3 ansible

Ansibleインストール

順番通りに進行すると、

上図のようにworkon ansibleを実行しなくても、前に(ansible)のように表記されるでしょう。
(Virtualenvをインストールしない場合は無視し、下のpipを通じたパッケージインストールが、すべての仮想環境の状態で進行されると考えましょう。)

pip install ansible

workon

# 仮想環境 ansible接続
workon ansible

# 仮想環境 ansible終了
deactivate

AWXをインストールして実行

# docker

sudo yum install -y yum-utils device-mapper-persistent-data lvm2
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io
# docker compose

sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
docker-compose --version

# Docker駆動および登録

sudo systemctl start docker
sudo systemctl enable docker
# rootの他にアカウントもDockerが利用できるようにusermod変更
sudo usermod -a -G docker ansible

# Ansibleアカウントに再接続し(必ず再接続)テスト
docker ps
# python docker / docker-compose

pip install docker
pip install docker-compose
# git

sudo yum install -y curl-devel expat-devel gettext-devel perl-ExtUtils-MakeMaker
cd ~/downloads
wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.22.0.tar.xz
tar -xvf git-2.22.0.tar.xz
cd git-2.22.0
make prefix=/home/ansible/package/git all
make prefix=/home/ansible/package/git install
# vi ~/.bash_profile

PATH=$HOME/package/git/bin:$PATH
export PATH
# node.js (LTS 10)

curl -sL https://rpm.nodesource.com/setup_10.x | sudo bash -
sudo yum install -y nodejs
# https://github.com/ansible/awx/releases

cd ~/downloads
wget https://github.com/ansible/awx/archive/15.0.0.tar.gz
tar -xvf 15.0.0.tar.gz
ln -s awx-15.0.0 awx

# または
# use github
cd ~/downloads
git clone https://github.com/ansible/awx.git awx_git
ln -s awx_git awx
# vi /home/ansible/downloads/awx/installer/inventory
# 以下の項目を検索し注釈を削除、または値を変更
# use_docker_compose=trueの場合、追加

localhost ansible_connection=local ansible_python_interpreter="/home/ansible/.virtualenvs/ansible/bin/python"
postgres_data_dir=/home/ansible/package/awx/pgdocker
host_port=10080
host_port_ssl=10081
use_docker_compose=true
docker_compose_dir=/home/ansible/package/awx/awxcompose
project_data_dir=/home/ansible/package/awx/projects
# AWX初回実行(docker-compose.ymlファイル作成)
# このとき、"Create Docker Compose Configuration"でlibselinux-pythonエラーが発生した場合、selinux設定をdisabledにする

cd /home/ansible/downloads/awx/installer
/home/ansible/.virtualenvs/ansible/bin/ansible-playbook -i inventory install.yml
# AWX停止/開始

cd /home/ansible/package/awx/awxcompose
docker-compose stop
docker-compose start
# AWXログ確認
docker logs -f awx_task

# docker process確認
# awx_task, awx_web, awx_postgres, awx_redisが動作していなければならない
docker ps
# AWX接続
# 初期パスワード : admin / password

http://{サーバーIP}:10080

AWX接続

上図が表示され、初期パスワードでログインできたら、無事インストールされました。

初回駆動時、ローディング画面で少し時間がかかることがあります。
docker logs -f awx_taskで確認時にエラーが発生する場合は、上の停止/開始段階で再起動させてください。

社内アカウント連動(LDAP)

社内アカウントでログインしたい場合は、Settings -> Authentication -> LDAPから登録できます。

Ansible基本使用(コマンド)

JDKファイルを配布する方法で基本的な使い方を説明します。

用意するもの

Virtualboxを使ってCentOS 7を作成し、そこにAnsible/AWXをインストールして進行します。
(ネットワーク:ホスト専用など、SSHアクセスが可能な形態)

ディレクトリ構造とファイルの詳細

/home/ansible/package/awx/projects/ansible_firstに以下のファイルを作成します。
(AWXインストールが完了すると、projectsパスがroot権限になっています。super userからansible_firstを作成し、所有者を変更してください。)

sudo mkdir -p /home/ansible/package/awx/projects/ansible_first
sudo chown ansible:ansible /home/ansible/package/awx/projects/ansible_first
ansible.cfg
group_vars/
    common.yml

hosts/
    local

playbooks/
    commons/
        openjdk-role.yml

roles/
    commons/
        openjdk/
            tasks/
                main.yml
            vars/
                main.yml

ansible.cfg

基本設定でrolesのパスを指定します。
指定しない場合は、Playbookファイルがある場所のroles(~/playbooks/commons/roles)がデフォルトになります。

[defaults]
roles_path = ./roles

group_vars/common.yml

共通で利用する値を設定します。

# project
project_home: /home/ansible
apps_home: /home/ansible/apps
download_home: /home/ansible/downloads

hosts/local

Ansibleを使って展開するサーバー情報を設定します。
local:childrenのように、複数のサーバーをグループ化できます。

[local:children]
centos7

[centos7]
{{vm_ip}}             ansible_user=ansible

playbooks/commons/openjdk-role.yml

Ansible&AWXで実行する1つの単位だと考えましょう。
(1つまたは複数rolesを指定)

---
- name: install openjdk
  vars_files:
    - ../../group_vars/common.yml
  hosts: "{{ host_vars }}"
  roles:
    - commons/openjdk

roles/commons/openjdk

1つの実行シナリオ

# tasks/main.yml

---
- name: Directory exists (downloads)
  file:
    path: "{{ download_home }}"
    state: directory

- name: Download JDK
  get_url:
    url: "https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u265-b01/{{ file_jdk }}.tar.gz"
    dest: "{{ download_home }}/{{ file_jdk }}.tar.gz"
    mode: 0755

- name: Directory exists (path jdk)
  file:
    path: "{{ path_jdk }}"
    state: directory

- name: Extract archive (jdk)
  unarchive:
    src: "{{ download_home }}/{{ file_jdk }}.tar.gz"
    dest: "{{ path_jdk }}"
    extra_opts: [--strip-components=1]
    remote_src: "yes"

- name: Create a symbolic link
  file:
    src: "{{ path_jdk }}"
    dest: "{{ java_home }}"
    state: link

- name: Remove file (jdk)
  file:
    path: "{{ download_home }}/{{ file_jdk }}.tar.gz"
    state: absent

- name: profile
  lineinfile:
    path: "{{ project_home }}/.bashrc"
    line: "{{ item }}"
    insertafter: EOF
    state: present
  loop:
    - "export LC_ALL=ko_KR.UTF-8"
    - "export LANG=ko_KR.UTF-8"
    - "export JAVA_HOME={{ java_home }}"
    - "export PATH=${JAVA_HOME}/bin:$PATH"
# vars/main.yml

# file
file_jdk: OpenJDK8U-jdk_x64_linux_hotspot_8u265b01

# path
path_jdk: "{{ apps_home }}/{{ file_jdk }}"

# java
java_home: /home/ansible/apps/jdk

Ansible実行

コマンドラインでJDKインストールを進めましょう。

# -vvvのオプションはデバッグモード
# openjdk-role.ymlというPlaybookをhosts/localにある"local"グループに実行させなさい
workon ansible
ansible-playbook -i hosts/local playbooks/commons/openjdk-role.yml --extra-vars '{"host_vars":"local"}' -vvv

上図のようにpermission deniedが発生します。
基本的にAnsibleの場合はSSH通信をするため、known_hostと公開鍵の登録作業が必要です。
AWX credentialsに登録する秘密鍵のペアとなる公開鍵を各サーバーに登録

「手動ですべて登録しなければならないのか」と思われるかもしれませんが、後ほどTCD(TOAST CLOUD DEPLOY)を利用する方法を紹介します。

# 秘密鍵と公開鍵の作成
ssh-keygen -t rsa

# 公開鍵登録
ssh-copy-id -i ~/.ssh/id_rsa.pub ansible@{{vm_ip}}

上図のようにfailed=0が出力されます。このように表示されればJDKが正常にインストールされました。

基本用語

Playbooks

設定管理など、Ansible実行の基本単位
1つあるいは複数のrolesから構成されます。

roles

Playbookのさまざまなサーバー設定に対するグループを区分する単位
ディレクトリ作成、ファイルダウンロードなど、1つあるいは複数のtaskで構成されます。

tasks

Ansibleの実際の作業単位
(例:ディレクトリを作成してファイルをダウンロード)

inventory

管理対象のノードリスト
(サーバーリスト。上記サンプルでは、CentOS 7をlocalにグループ化します。)

vars

roleで使用する変数を定義
varsの種類はさまざまで優先順位も存在します。

下図では番号が大きいほど優先順位が高くなります。
https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable

AWX登録および実行

上章では基本的なコマンドラインと基本的な構文を紹介しましたが、AWXではそれらをGUIで実行する方法について説明します。

なお、AWX GUIメニューではコードの変更はできません。
したがって、そのような場合は、AWXにプロジェクトをGitHub連動させておき、ローカルでコードを修正してから簡単にテストします。そして、その後commitしてAWXでテストを実施します。

プロジェクト登録

これから私たちが実行するPlaybookなどのファイル位置を決めます。
(GitHubのrepository選択段階と考えると分かりやすいでしょう。)

10.png

SCM TYPEをManualにすると、最初にAWXをインストールしたときのinventoryファイルを修正したproject_data_dirが固定されます。
(つまりAWXサーバーから直接ソースコードを修正し、すぐに実行するときはこの方式を選択します。)

inventory

Playbookを実行させるサーバーを登録します。
inventory/group/host/groupの追加順に進行します。(Git連動時にはより簡単な方法で処理可能)

credential登録

配布先のサーバーにアクセスする際に利用する秘密鍵の情報を登録します。
(ここではAWXサーバーで作成されたキー情報を利用します。)

cat ~/.ssh/id_rs

 

template登録

実際に実行するPlaybookを登録するメニューです。

prompt on launchをチェックすると、その項目を実行するときに再び照会し当該画面で変更することができます。

template実行

実行履歴とステータス確認(Jobs)

jobsメニューで現在実行中のジョブと履歴などを確認できます。
選択すると、詳細結果画面を照会できます。

付録(GitHub連動編)

AWX GitHubプロジェクト登録

credential

GitHub repositoryにアクセスするアカウント情報を記録します。
(上記で作成されたcredentialとは他の用途です。)

project

Playbookが登録されたrepositoryアドレスを登録します。

プロジェクト登録をしてリストを照会すると、既存のansible firstプロジェクトとは異なるボタンができたことを確認できます。
右矢印ボタンはGitHubから最新の情報を更新するボタンで、左側の赤い感嘆符は、現在のVMでは会社のGitHub接続をしていないため、更新ステータスにおいて警告表示されたものです。
(VMのネットワークをホスト専用にし、ホストコンピュータからVPN接続すれば接続可能です。)

inventory

GitHub連動プロジェクトの場合、hostを1つ1つ登録しなくても、inventory作成後にsourceを登録して、シンクさせるとhostが自動的に登録されます。
(上記サンプルをGitHubにアップロードすると、hosts/localファイルに登録されたhostが登録できます。)

template登録/実行

サンプルで紹介したものと同じです。

TCDから公開鍵を登録

TCDはアプリケーション配布サービスで、NHNではTCDを用いてサービスの配布を行っています。
(TCDを活用して20台のサーバーに配布すると仮定した場合、4台ずつ、または1台ずつ順次配布が可能です。)

AWXサーバーのStrictHostKeyCheckingオプション変更

TCDから公開鍵を登録する前に、known_host設定を変更する必要があります。

# vi ~/.ssh/config

Host *
    StrictHostKeyChecking no
chmod 400 ~/.ssh/config

上記のように設定すると、known_hostの登録有無が問われません。

公開鍵の登録

TCDのシナリオを活用して、AWXサーバーの公開鍵(上記で登録した秘密鍵と対になる)stringをPython Scriptを使い各サーバーに登録する方法です。
(リソースにPython Scriptを登録し、配布するサーバーグループを選択して公開鍵を登録)

write_authorized_keys.py

# -*- coding: utf-8 -*-

import errno
import os
import stat
import sys

ANSIBLE_CLIENT = "ansible@{{ awx hostname }}"
ANSIBLE_PUBLIC_KEY = "{{ 公開鍵 }}"

# ディレクトリ作成
def make_dir(dir_path):
    try:
        if not(os.path.isdir(dir_path)):
            os.makedirs(os.path.join(dir_path))
    except OSError as e:
        if e.errno != errno.EEXIST:
            print("Failed to create directory!!!!!")
            raise


# 公開鍵登録
def write_authorized_keys(file_path):
    fp = os.path.join(file_path, "authorized_keys")

    lines = []
    if os.path.isfile(fp):
        with open(fp, "r") as f:
            lines = f.readlines()

    with open(fp, "w") as f:
        for line in lines:
            if line.find(ANSIBLE_CLIENT) == -1:
                f.write(line)
        f.write(ANSIBLE_PUBLIC_KEY)

    os.chmod(fp, stat.S_IREAD | stat.S_IWRITE)


if __name__ == '__main__':
    if len(sys.argv) == 2:
        ssh_path = sys.argv[2]
    else:
        ssh_path = os.path.expanduser('~') + "/.ssh"

    make_dir(ssh_path)
    write_authorized_keys(ssh_path)

AWX APIの活用

特定のタスクを進行する際、サービスをシャットダウンして入れ替え作業が必要な場合に活用できます。
(scouter/jdkのバージョンアップなど)

AWXトークン発行

AWX APIを利用するには、トークンの発行過程が必要です。
(ここではtcdというアカウントも新規に作成します。)

application作成

awx -> Applications -> +

このとき、作成されたsecret情報は、以下の作業では必要ありません。

user作成

ユーザー作成後、権限の追加が必要です。

トークン作成

tcdアカウントに再ログインし、トークンを発行します。

awx -> Users -> tcd -> TOKENS -> +

SCOPEをWriteで設定しないと、templateが実行できません。

APIテスト

API呼び出し時、JSON Stringで応答します。

https://docs.ansible.com/ansible-tower/latest/html/towerapi/api_ref.html
上記サイトからAPI確認可能

job_templates照会

# 要請
curl --location --request GET 'http://192.168.1.205:10080/api/v2/job_templates' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer TMMeHBAmFHBKrGlktjS6XO3TWvQjF4'
# 応答
{
   "count":1,
   "next":null,
   "previous":null,
   "results":[
      {
         "id":9,
         "type":"job_template",
         "url":"/api/v2/job_templates/9/",
         "created":"2020-10-14T09:01:21.536040Z",
         "modified":"2020-10-14T09:01:21.536066Z",
         "name":"template first",
         "playbook":"playbooks/commons/openjdk-role.yml",
      }
   ]
}

template実行

job_template照会で確認したid値をpath_variableに配信します。(ここでは9)

# 要請
curl --location --request POST 'http://192.168.1.205:10080/api/v2/job_templates/9/launch/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer TMMeHBAmFHBKrGlktjS6XO3TWvQjF4' \
--data '{
    "extra_vars":{
        "host_vars":"local"
    }
}'
# 応答
{
   "job":4,
   "ignored_fields":{

   },
   "id":4,
   "type":"job",
   "url":"/api/v2/jobs/4/",
}

jobステータス確認

template実行後、受信した応答値のid値でjobステータスを確認できます。(ここでは4)

# 要請
curl --location --request GET 'http://192.168.1.205:10080/api/v2/jobs/4/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer TMMeHBAmFHBKrGlktjS6XO3TWvQjF4'
# 応答
{
   "id":4,
   "type":"job",
   "url":"/api/v2/jobs/4/",
   "created":"2020-10-14T14:34:31.647515Z",
   "modified":"2020-10-14T14:34:32.005339Z",
   "name":"template first",
   "job_type":"run",
   "playbook":"playbooks/commons/openjdk-role.yml",
   "extra_vars":"{\"host_vars\": \"local\"}",
   "status":"successful",
   "failed":false,
   "started":"2020-10-14T14:34:32.048388Z",
   "finished":"2020-10-14T14:35:07.141185Z",
}

応答値のstatusの値で完了かどうかを判断します。

TCD活用

TCDで利用するために、上記のようにcURLを呼び出して結果のパージ(parsing)をShell Scriptで作成することが難しかったので、Python Scriptを利用しました。

上記のように、template nameとhost_varsなどを引数で受け取って実行します。
(AWX APIを使ってtemplateを実行させ、完了するまで待機する過程は、Python Script内部で処理します。)

awx_run_template.py

#!/usr/bin/python
# -*- coding:utf-8 -*-
import datetime
import sys
import time
import urllib2
import json

AWX_API_URL = "http://192.168.1.205:10080"
AWX_KEY = "TMMeHBAmFHBKrGlktjS6XO3TWvQjF4"


def get_templates():
    url = AWX_API_URL + '/api/v2/job_templates'
    headers = {'Authorization': 'Bearer ' + AWX_KEY}

    req = urllib2.Request(url, None, headers)
    response = urllib2.urlopen(req)
    json_data = json.loads(response.read())

    return json_data.get('results')


def get_template_id(template_name):
    templates = get_templates()
    for ii in templates:
        if ii.get('name').encode('utf-8') == template_name:
            return ii.get('id')
    return None


def run_template(template_id, parameters):
    url = "{url}/api/v2/job_templates/{id}/launch/".format(url=AWX_API_URL, id=template_id)
    headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + AWX_KEY}

    req = urllib2.Request(url, parameters, headers)
    response = urllib2.urlopen(req)
    return json.loads(response.read()).get('id')


def check_job_status(job_id):
    url = AWX_API_URL + '/api/v2/jobs/{id}/'.format(id=job_id)
    headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + AWX_KEY}

    req = urllib2.Request(url, None, headers)

    while True:
        response = urllib2.urlopen(req)
        json_data = json.loads(response.read())

        print("{dt} job status : {status}".format(
            dt=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), status=json_data.get('status')))

        if json_data.get('status') == 'successful':
            return True
        else:
            time.sleep(3)


if __name__ == "__main__":
    template_name = None
    parameters = ""
    if len(sys.argv) == 2:
        template_name = sys.argv[1]
        parameters = '{"extra_vars":{"host_vars":"alpha","dist":"alpha","target":"all"}}'
    elif len(sys.argv) == 3:
        template_name = sys.argv[1]
        parameters = sys.argv[2]
    else:
        print("usage : awx_run_template.py "
              "\"playbook-first\" "
              "'{\"extra_vars\":{\"host_vars\":\"alpha\",\"dist\":\"alpha\",\"target\":\"all\"}}'")
        exit(-1)

    try:
        template_id = get_template_id(template_name)
        print("{dt} template name : {name}, id : {id}".format(
            dt=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), name=template_name, id=template_id))

        job_id = run_template(template_id, parameters)
        print("{dt} job id : {id}".format(
            dt=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), id=job_id))

        check_job_status(job_id)
    except Exception as e:
        print("awx run template error : {error}".format(error=e))
        exit(-1)

付録1:バックアップ&リストア

AWXの場合は、AWX内でのジョブは、ほとんどpostgresqlに保存されるので、これだけバックアップしておけばOKです。
(PlaybookなどのソースはGitHubで管理)

# backup
docker exec -t awx_postgres pg_dump -c -d awx -U awx > awx_backup_db.dmp

# restore
cat awx_backup_db.dmp | docker exec -i awx_postgres psql -U awx

付録2:AWXバージョンアップ

https://github.com/ansible/awx/blob/devel/INSTALL.md#upgrading-from-previous-versions

  1. postgresql backup(バージョンアップ失敗に備えて)
  2. 既存の駆動中のDockerコンテナ停止
  3. 新バージョンをダウンロードし、インストールansible-playbook -i inventory install.yml前まで進行
    1. バージョンアップファイルをダウンロード後、解凍
    2. inventory内部情報の修正
  4. ansible-playbook -i inventory install.yml
  5. AWXサイトに接続し、バージョンアップされたことを確認
    1. 正常に動作しない場合は、docker logs -f awx_taskを確認し、状況を見て既存バージョンに戻す

注意事項

https://github.com/ansible/awx/releases

AWXの場合、バージョンアップ速度がかなり速いので、たとえば9.3.0バージョン利用中に、15.0.0バージョンへ直接アップデートするのではなく、すぐ上の段階のバージョンに更新する方向で順次進行することを推奨します。

付録3:モジュールと文法

https://docs.ansible.com/ansible/2.5/modules/list_of_all_modules.html#all-modules
Ansibleはさまざまなモジュールを提供しており、便利に利用することができます。
(yum / get_url / copy / unarchive / fileなど)

become/become_method

super userとして実行します。

---
- hosts: all
  become: yes
  become_method: sudo
  tasks:
  - name: install the latest version of Apache
    yum:
      name: httpd
      state: latest
become/become_methodを利用しない場合

ansible_userに切り替えて、コマンドラインを使って利用する方法があります。

- name: change user (ansiblesu)
  set_fact:
    ansible_user: ansiblesu

- name: CAP_NET_BIND_SERVICE
  command: "sudo /usr/sbin/setcap CAP_NET_BIND_SERVICE=+eip {{ path_nginx_home }}/sbin/nginx"

- name: change user (ansible)
  set_fact:
    ansible_user: ansible

ただし、コマンドの場合、冪等性を提供していません。

loop/with_items

ループと同様な機能で、同じジョブを何度も書く必要がありません。

- name: Copy template file (script)
  template:
    src: "{{ item.src }}"
    dest: "{{ item.dest }}"
    mode: 0755
  loop:
    - { id: 1, src: "scripts/{{ file_rotatelog_sh }}.j2", dest: "{{ path_crontab }}/{{ file_rotatelog_sh }}" }
    - { id: 2, src: "scripts/{{ file_compresslog_sh }}.j2", dest: "{{ path_crontab }}/{{ file_compresslog_sh }}" }

- name: Downloading NGINX sources
  get_url:
    url: "{{ item.url }}"
    dest: "{{ item.dest }}"
  with_items:
  - { id: 1, url: "{{ file_server_url }}/{{ file_nginx }}.tar.gz", dest: "{{ download_home }}/{{ file_nginx }}.tar.gz" }
  - { id: 2, url: "{{ file_server_url }}/{{ file_openssl }}.tar.gz", dest: "{{ download_home }}/{{ file_openssl }}.tar.gz" }

register/when

ymlファイルの作成時、特定のタスクに変更があった場合にのみ、特定のジョブを実行することがあると思います。
registerで現在のステータスを登録し、whenを使ってchangeの場合にのみ圧縮を解く、というサンプルです。

- name: Downloading NGINX sources
  get_url:
    url: "{{ item.url }}"
    dest: "{{ item.dest }}"
  with_items:
    - { id: 1, url: "{{ file_server_url }}/{{ file_nginx }}.tar.gz", dest: "{{ download_home }}/{{ file_nginx }}.tar.gz" }
  register: nginx_source

- name: Unpacking NGINX
  unarchive:
    src: "{{ nginx_source.results.0.dest }}"
    dest: "{{ path_nginx_unpack_dir }}"
    extra_opts: [--strip-components=1]
    remote_src: "yes"
  when: nginx_source is changed
  register: nginx_source_unpack

whenの場合は、registerされたステータスだけでなく、特定の値である場合にも処理が可能です。
以下はserviceというvarsの値がhangame/paycoでない場合にのみ、そのジョブを実行せよという意味です。

- name: nginx (source)
  vars:
    nginx_version: 1.17.8
  include: "{{ item.role }}"
  with_items:
  - { id: 1, role: "source.yml", service: "{{ service }}" }
  when: item.service != "hangame" and item.service != "payco"

付録4:サンプル

nginxソースインストール

- name: change user (ansiblesu)
  set_fact:
    ansible_user: ansiblesu

- name: install compile library
  shell: "sudo yum install -y gcc gcc-c++"

- name: change user (ansible)
  set_fact:
    ansible_user: ansible

- name: delete source dir & file
  file:
    path: "{{ item }}"
    state: absent
  with_items:
  - "{{ path_nginx_source_dir }}"
  - "{{ download_home }}/{{ file_nginx }}.tar.gz"
  - "{{ download_home }}/{{ file_openssl }}.tar.gz"
  - "{{ download_home }}/{{ file_pcre }}.tar.gz"
  - "{{ download_home }}/{{ file_zlib }}.tar.gz"

- name: Directory exists (nginx)
  file:
    path: "{{ item }}"
    state: directory
  with_items:
  - "{{ download_home }}"
  - "{{ path_nginx_source_dir }}"
  - "{{ path_nginx_home }}/conf/conf.d"
  - "{{ path_nginx_home }}/ssl"
  - "{{ path_nginx_log }}"

- name: Downloading NGINX sources
  get_url:
    url: "{{ item.url }}"
    dest: "{{ item.dest }}"
  with_items:
  - { id: 1, url: "{{ file_server_url }}/{{ file_nginx }}.tar.gz", dest: "{{ download_home }}/{{ file_nginx }}.tar.gz" }
  - { id: 2, url: "{{ file_server_url }}/{{ file_openssl }}.tar.gz", dest: "{{ download_home }}/{{ file_openssl }}.tar.gz" }
  - { id: 3, url: "{{ file_server_url }}/{{ file_pcre }}.tar.gz", dest: "{{ download_home }}/{{ file_pcre }}.tar.gz" }
  - { id: 4, url: "{{ file_server_url }}/{{ file_zlib }}.tar.gz", dest: "{{ download_home }}/{{ file_zlib }}.tar.gz" }
  register: nginx_source

- name: Unpacking NGINX
  unarchive:
    src: "{{ nginx_source.results.0.dest }}"
    dest: "{{ path_nginx_source_dir }}"
    extra_opts: [--strip-components=1]
    remote_src: "yes"
  when: nginx_source is changed
  register: nginx_source_unpack

- name: Extract archive (openssl)
  unarchive:
    src: "{{ nginx_source.results.1.dest }}"
    dest: "{{ path_nginx_source_dir }}/"
    remote_src: "yes"

- name: Extract archive (pcre)
  unarchive:
    src: "{{ nginx_source.results.2.dest }}"
    dest: "{{ path_nginx_source_dir }}/"
    remote_src: "yes"

- name: Extract archive (zlib)
  unarchive:
    src: "{{ nginx_source.results.3.dest }}"
    dest: "{{ path_nginx_source_dir }}/"
    remote_src: "yes"

- name: Configuring NGINX source with custom modules
  command: "./configure --with-pcre=./{{ file_pcre }} --with-zlib=./{{ file_zlib }} --with-openssl={{ file_openssl }} --with-http_ssl_module --with-http_realip_module --prefix={{ path_nginx_home }} --user=ansible --group=ansible"
  args:
    chdir: "{{ path_nginx_source_dir }}"
  when: nginx_source_unpack is changed
  register: nginx_configure

- name: Installing NGINX
  shell: make && make install
  args:
    chdir: "{{ path_nginx_source_dir }}"
  when: nginx_configure is changed

- name: change user (ansiblesu)
  set_fact:
    ansible_user: ansiblesu

- name: CAP_NET_BIND_SERVICE
  command: "sudo /usr/sbin/setcap CAP_NET_BIND_SERVICE=+eip {{ path_nginx_home }}/sbin/nginx"

- name: change user (ansible)
  set_fact:
    ansible_user: ansible

- name: delete source dir & file
  file:
    path: "{{ item }}"
    state: absent
  with_items:
  - "{{ path_nginx_source_dir }}"
  - "{{ download_home }}/{{ file_nginx }}.tar.gz"
  - "{{ download_home }}/{{ file_openssl }}.tar.gz"
  - "{{ download_home }}/{{ file_pcre }}.tar.gz"
  - "{{ download_home }}/{{ file_zlib }}.tar.gz"
  - "{{ path_nginx_home }}/logs"

- name: symbolic link (log)
  file:
    src: "{{ path_nginx_log }}"
    dest: "{{ path_nginx_home }}/logs"
    state: link

 

参考サイト

NHN Cloud Meetup 編集部

NHN Cloudの技術ナレッジやお得なイベント情報を発信していきます
pagetop