NHN Cloud Meetup 編集部
Ansible&AWX(インストールから基本的活用とTCD連動まで)
2020.12.09
6,023
はじめに
サーバーの増設や、既存の設定を変更するなどして1台あるいは複数台のサーバー作業が必要になった場合、どのように処理しますか?
たとえば、20台のサーバーを設定するとしましょう。
- 基本ディレクトリを作成
- jdkインストール
- scouterファイルのコピーおよび設定変更
- managerAgent.warファイルのコピー
- scriptファイルのコピー
- crontabの登録
- などなど…
これらの作業を数回のクリックで可能にしてくれるものがあります。
Ansibleとは?
複数台のサーバーを効率的に管理するために考案された環境構成の自動化ツールの1つで、エージェントレス(SSH)、冪等性など、さまざまなモジュールに対応し、アクセシビリティが高い(Pythonに対応)などの特徴があります。
ライセンス
GPL 3.0(Ansible)/ Apache 2.0(AWX)に従い、環境設定の目的に社内でも利用できます。
ただし、サーバーでのみインストールし、ウェブネットワークを通じてサービスを行う形態であれば(当該オープンソースが他に配布されなければ)、使用可能です。
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選択段階と考えると分かりやすいでしょう。)
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
- postgresql backup(バージョンアップ失敗に備えて)
- 既存の駆動中のDockerコンテナ停止
- 新バージョンをダウンロードし、インストールansible-playbook -i inventory install.yml前まで進行
1. バージョンアップファイルをダウンロード後、解凍
2. inventory内部情報の修正 - ansible-playbook -i inventory install.yml
- 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
参考サイト
- https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable
- https://docs.ansible.com/ansible-tower/latest/html/towerapi/api_ref.html
- https://docs.ansible.com/ansible/2.5/modules/list_of_all_modules.html#all-modules
- https://www.edureka.co/blog/chef-vs-puppet-vs-ansible-vs-saltstack/