読者です 読者をやめる 読者になる 読者になる

時と場合によりけり

日々のアップデートとイノベーションに翻弄され彷徨える IT エンジニアの覚書

Ansible のベストプラクティス

概要

Ansible を使うとなるとディレクトリ構成をどうするか?という悩みが出てきます。設定の仕方は、人それぞれかとは思いますが、本家サイトを手本に構築してみます。今回は、Vagrant仮想マシンを使って ping の playbook を例として挙げておきます。Ansible のインストールについては以下のエントリーをご参照ください。

stangler.hatenablog.com

参考サイト

Best Practices — Ansible Documentation
Inventory — Ansible Documentation
Ansibleを使い出す前に押さえておきたかったディレクトリ構成のベストプラクティス - 双六工場日誌

ハードウェア

  • マシン: Macbook Pro Early 2011
  • メモリ: 16 GB
  • ストレージ: SSD 512 GB

ホスト

ゲスト

構造

最終的には以下のような形にしたいと思います。Vagrant VM の使用を想定しております。

% tree
.
├── Vagrantfile
├── dbservers.yml
├── development
├── group_vars
├── roles
│   ├── ping
│   │   ├── README.md
│   │   ├── defaults
│   │   │   └── main.yml
│   │   ├── files
│   │   ├── handlers
│   │   │   └── main.yml
│   │   ├── meta
│   │   │   └── main.yml
│   │   ├── tasks
│   │   │   └── main.yml
│   │   ├── templates
│   │   ├── tests
│   │   │   ├── inventory
│   │   │   └── test.yml
│   │   └── vars
│   │       └── main.yml
├── site.yml
└── webservers.yml

Vagrantfile

web と db 2台の仮想マシンを立ち上げます。それぞれにプライベート ip を付与します。

% vim Vagrantfile
Vagrant.configure(2) do |config|

  config.vm.box = "centos68"
  config.ssh.insert_key = false
  config.vm.provider "virtualbox" do |vb|
    vb.memory = "2048"
  end

  config.vm.define "web" do |node|
    node.vm.hostname = "web.com"
    node.vm.network :private_network, ip: "192.168.33.10"
  end

  config.vm.define "db" do |node|
    node.vm.hostname = "db.com"
    node.vm.network :private_network, ip: "192.168.33.11"
  end

end

SSH 接続確認

仮想マシンを起動して、SSH で接続できるかどうか、一応、確認しておきます。

% vagrant up
% ssh -i ~/.vagrant.d/insecure_private_key -l vagrant 192.168.33.10
% ssh -i ~/.vagrant.d/insecure_private_key -l vagrant 192.168.33.11

インベントリ

今回は、開発環境として立てた Vagrant仮想マシンのみなので、development というインベントリファイルひとつです。本番やステージング環境を作成したときに、staging や production といった名前でインベントリファイルを追加すればわかりやすと思います。

% vim development
[all:vars]
ansible_ssh_port=22
ansible_ssh_user=vagrant
ansible_ssh_private_key_file=~/.vagrant.d/insecure_private_key

[vagrant-webservers]
web ansible_host=192.168.33.10

[vagrant-dbservers]
db ansible_host=192.168.33.11

[webservers:children]
vagrant-webservers

[dbservers:children]
vagrant-dbservers

playbook

ここでは、playbook を3つ作成します。web 用(サブ)、db 用(サブ)、そして、それらを統括する site.yml (メイン)です。

まずは、メイン。

% vim site.yml
---
- include: webservers.yml
- include: dbservers.yml

次に web 用のサブ。

% vim webservers.yml
---
- hosts: webservers
  become: yes
  roles:
    - ping

最後に db 用のサブ。

% vim dbservers.yml
---
- hosts: dbservers
  become: yes
  roles:
    - ping

グループ変数

変数をまとめて記述し、管理できる場所を作成しておきます。

% mkdir group_vars

変数の設定は YAML で行います。

% vim group_vars/xxxx.yml

ロール

「Nginx をインストールする」、「 php.ini を書き換える」といった実際の作業は、それぞれのロール(役割)に分けて設定を記述していくと、サーバー構成の全体像が把握しやすくなります。

ディレクトリ作成

まずは、ディレクトリを作成します。

% mkdir roles

ロール作成

ansible-galaxy init コマンドを使うと、ロールで必要となるディレクトリやファイルの雛形を自動で作成してくれて便利です。ここでは、ping というロール名で作成します。

% ansible-galaxy init roles/ping

以下のような雛形がすぐにできてしまいます。

% tree
.
├── README.md
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

8 directories, 8 files

ファイル

今回は使いませんが、ファイルをアップロードするときは以下のディレクトリにファイルを置きます。

roles/ping/files/xxxx

テンプレート

今回は使いませんが、テンプレートをアップロードするときは以下のディレクトリにテンプレートファイルを置きます。

roles/ping/templates/xxxx.j2

Jinja2 という Python テンプレートエンジンを使用します。

ハンドラ

service nginx restart みたいな、デーモンに登録されたサービスの再起動など、頻繁に使用するものをここに設定しておいて、notify で呼び出すと便利です。

roles/ping/handlers/main.yml

タスク

実際の設定タスクを記述します。例として、ping を飛ばすのみの設定。

% vim roles/ping/tasks/main.yml
---
- name: ping
  action: ping

おまけ ansible-playbook コマンドについて

タスク一覧表示

どんなタスクが稼働するのかを確認するときに便利です。

% ansible-playbook -i development site.yml --list-task

今回の ping ロールの場合は、以下のように表示されます。

playbook: site.yml

  play #1 (webservers): webservers  TAGS: []
    tasks:
      ping : ping   TAGS: []

  play #2 (dbservers): dbservers    TAGS: []
    tasks:
      ping : ping   TAGS: []

シンタックスチェック

読んで字のごとくで、シンタックスをチェックしてくれます。

% ansible-playbook -i development site.yml --syntax-check

何事もなければ、以下のような表示となります。

playbook: site.yml

ドライラン

dry run する癖をつけておくとよいと思います。

% ansible-playbook -i development site.yml --check

結果は以下となります。

PLAY [webservers] **************************************************************

TASK [setup] *******************************************************************
ok: [web]

TASK [ping : ping] *************************************************************
ok: [web]

PLAY [dbservers] ***************************************************************

TASK [setup] *******************************************************************
ok: [db]

TASK [ping : ping] *************************************************************
ok: [db]

PLAY RECAP *********************************************************************
db                         : ok=2    changed=0    unreachable=0    failed=0   
web                        : ok=2    changed=0    unreachable=0    failed=0 

デバッグ

さらに詳細を知るためには、-vvv オプション付きでデバッグしてみましょう。

% ansible-playbook -i development site.yml -check -vvv

結果は以下となります。

No config file found; using defaults

PLAYBOOK: site.yml *************************************************************
2 plays in site.yml

PLAY [webservers] **************************************************************

TASK [setup] *******************************************************************
<192.168.33.10> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.33.10> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.33.10 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1477444104.28-163173032406711 `" && echo ansible-tmp-1477444104.28-163173032406711="` echo $HOME/.ansible/tmp/ansible-tmp-1477444104.28-163173032406711 `" ) && sleep 0'"'"''
<192.168.33.10> PUT /var/folders/h4/nlm7x1td5zxbq8xqgpd89hz80000gn/T/tmp8xGDT_ TO /home/vagrant/.ansible/tmp/ansible-tmp-1477444104.28-163173032406711/setup
<192.168.33.10> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r '[192.168.33.10]'
<192.168.33.10> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.33.10> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.33.10 '/bin/sh -c '"'"'chmod u+x /home/vagrant/.ansible/tmp/ansible-tmp-1477444104.28-163173032406711/ /home/vagrant/.ansible/tmp/ansible-tmp-1477444104.28-163173032406711/setup && sleep 0'"'"''
<192.168.33.10> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.33.10> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r -tt 192.168.33.10 '/bin/sh -c '"'"'sudo -H -S -n -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-nuzhegeywpbnznqdldgdponratiqkqtg; LANG=ja_JP.UTF-8 LC_ALL=ja_JP.UTF-8 LC_MESSAGES=ja_JP.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1477444104.28-163173032406711/setup; rm -rf "/home/vagrant/.ansible/tmp/ansible-tmp-1477444104.28-163173032406711/" > /dev/null 2>&1'"'"'"'"'"'"'"'"' && sleep 0'"'"''
ok: [web]

TASK [ping : ping] *************************************************************
task path: /Users/xxxx/Ansible/mysql/roles/ping/tasks/main.yml:2
<192.168.33.10> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.33.10> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.33.10 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1477444105.79-167924733132151 `" && echo ansible-tmp-1477444105.79-167924733132151="` echo $HOME/.ansible/tmp/ansible-tmp-1477444105.79-167924733132151 `" ) && sleep 0'"'"''
<192.168.33.10> PUT /var/folders/h4/nlm7x1td5zxbq8xqgpd89hz80000gn/T/tmpTtHI9q TO /home/vagrant/.ansible/tmp/ansible-tmp-1477444105.79-167924733132151/ping
<192.168.33.10> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r '[192.168.33.10]'
<192.168.33.10> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.33.10> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.33.10 '/bin/sh -c '"'"'chmod u+x /home/vagrant/.ansible/tmp/ansible-tmp-1477444105.79-167924733132151/ /home/vagrant/.ansible/tmp/ansible-tmp-1477444105.79-167924733132151/ping && sleep 0'"'"''
<192.168.33.10> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.33.10> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r -tt 192.168.33.10 '/bin/sh -c '"'"'sudo -H -S -n -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-jrkezesztxjprcikonwapeyuehroemyq; LANG=ja_JP.UTF-8 LC_ALL=ja_JP.UTF-8 LC_MESSAGES=ja_JP.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1477444105.79-167924733132151/ping; rm -rf "/home/vagrant/.ansible/tmp/ansible-tmp-1477444105.79-167924733132151/" > /dev/null 2>&1'"'"'"'"'"'"'"'"' && sleep 0'"'"''
ok: [web] => {"changed": false, "invocation": {"module_args": {"data": null}, "module_name": "ping"}, "ping": "pong"}

PLAY [dbservers] ***************************************************************

TASK [setup] *******************************************************************
<192.168.33.11> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.33.11> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.33.11 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1477444106.43-105804328418573 `" && echo ansible-tmp-1477444106.43-105804328418573="` echo $HOME/.ansible/tmp/ansible-tmp-1477444106.43-105804328418573 `" ) && sleep 0'"'"''
<192.168.33.11> PUT /var/folders/h4/nlm7x1td5zxbq8xqgpd89hz80000gn/T/tmpPAkMXk TO /home/vagrant/.ansible/tmp/ansible-tmp-1477444106.43-105804328418573/setup
<192.168.33.11> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r '[192.168.33.11]'
<192.168.33.11> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.33.11> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.33.11 '/bin/sh -c '"'"'chmod u+x /home/vagrant/.ansible/tmp/ansible-tmp-1477444106.43-105804328418573/ /home/vagrant/.ansible/tmp/ansible-tmp-1477444106.43-105804328418573/setup && sleep 0'"'"''
<192.168.33.11> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.33.11> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r -tt 192.168.33.11 '/bin/sh -c '"'"'sudo -H -S -n -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-dlutylzltxwajiynpvuoggyjxwuurhxi; LANG=ja_JP.UTF-8 LC_ALL=ja_JP.UTF-8 LC_MESSAGES=ja_JP.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1477444106.43-105804328418573/setup; rm -rf "/home/vagrant/.ansible/tmp/ansible-tmp-1477444106.43-105804328418573/" > /dev/null 2>&1'"'"'"'"'"'"'"'"' && sleep 0'"'"''
ok: [db]

TASK [ping : ping] *************************************************************
task path: /Users/xxxx/Ansible/mysql/roles/ping/tasks/main.yml:2
<192.168.33.11> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.33.11> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.33.11 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1477444107.89-62030979407524 `" && echo ansible-tmp-1477444107.89-62030979407524="` echo $HOME/.ansible/tmp/ansible-tmp-1477444107.89-62030979407524 `" ) && sleep 0'"'"''
<192.168.33.11> PUT /var/folders/h4/nlm7x1td5zxbq8xqgpd89hz80000gn/T/tmpocuv2A TO /home/vagrant/.ansible/tmp/ansible-tmp-1477444107.89-62030979407524/ping
<192.168.33.11> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r '[192.168.33.11]'
<192.168.33.11> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.33.11> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r 192.168.33.11 '/bin/sh -c '"'"'chmod u+x /home/vagrant/.ansible/tmp/ansible-tmp-1477444107.89-62030979407524/ /home/vagrant/.ansible/tmp/ansible-tmp-1477444107.89-62030979407524/ping && sleep 0'"'"''
<192.168.33.11> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.33.11> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o Port=22 -o 'IdentityFile="/Users/xxxx/.vagrant.d/insecure_private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=vagrant -o ConnectTimeout=10 -o ControlPath=/Users/xxxx/.ansible/cp/ansible-ssh-%h-%p-%r -tt 192.168.33.11 '/bin/sh -c '"'"'sudo -H -S -n -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-nlsiznzvxbmoviappsaontgjiucsscmg; LANG=ja_JP.UTF-8 LC_ALL=ja_JP.UTF-8 LC_MESSAGES=ja_JP.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1477444107.89-62030979407524/ping; rm -rf "/home/vagrant/.ansible/tmp/ansible-tmp-1477444107.89-62030979407524/" > /dev/null 2>&1'"'"'"'"'"'"'"'"' && sleep 0'"'"''
ok: [db] => {"changed": false, "invocation": {"module_args": {"data": null}, "module_name": "ping"}, "ping": "pong"}

PLAY RECAP *********************************************************************
db                         : ok=2    changed=0    unreachable=0    failed=0   
web                        : ok=2    changed=0    unreachable=0    failed=0   

実行

上記のチェックが終了したら、実際のデプロイです。

% ansible-playbook -i development site.yml

デバッグのときのように、pong は返ってきませんが、以下のように ok になれば成功です。

PLAY [webservers] **************************************************************

TASK [setup] *******************************************************************
ok: [web]

TASK [ping : ping] *************************************************************
ok: [web]

PLAY [dbservers] ***************************************************************

TASK [setup] *******************************************************************
ok: [db]

TASK [ping : ping] *************************************************************
ok: [db]

PLAY RECAP *********************************************************************
db                         : ok=2    changed=0    unreachable=0    failed=0   
web                        : ok=2    changed=0    unreachable=0    failed=0   

以上です。