July Tech Festa にて開催されたハンズオンの資料が公開されていたことに刺激され、Chef の代わりに Ansible を使う資料を作りました。 Ansible を使って WordPress サーバーのセットアップを行い、ServerSpec でテストを行います。 まだ Ansible を試し始めたばかりで自分の勉強がてら書いています。 Puppet にも Chef にも乗り遅れたので Ansible に飛び乗ってみようかと。
Ansible は非常にシンプルなIT構成エンジンです。これはあなたのアプリケーションやシステムのデプロイを容易にします。スクリプトや専用のコードを書くことなくアプリケーションをデプロイし、更新することができます。エージェントをインストールすることなく、SSH を使い、自然な英語に近い感覚で自動化します。 Ansible は最もシンプルな構成管理、自動化ツールです。次のサイトも参考になります (これ、とても良いスライドです。この Tutorial の後にどうぞ..)
何度でも綺麗な環境でやり直せるように VirtualBox + Vagrant を使ってこの Tutorial 用環境を構築します。
あたりを参考に最新版の VirtualBox と Vagrant をインストールしてください。$ mkdir ansible-tutorial
$ cd ansible-tutorial
$ vagrant init centos6 http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box
Ansible サーバーと、Ansible に制御される側の2台のサーバーを立てることにするので Vagrantfile
を編集します
$ vi Vagrantfile
config.vm.box = "centos6"
の行を次のように書き換えます。2台じゃなくて3台でも4台でもOK
config.vm.define :node1 do |node|
node.vm.box = "centos6"
node.vm.network :forwarded_port, guest: 22, host: 2001, id: "ssh"
node.vm.network :private_network, ip: "192.168.33.11"
end
config.vm.define :node2 do |node|
node.vm.box = "centos6"
node.vm.network :forwarded_port, guest: 22, host: 2002, id: "ssh"
node.vm.network :forwarded_port, guest: 80, host: 8000, id: "http"
node.vm.network :private_network, ip: "192.168.33.12"
end
編集が終わったら起動させます
$ vagrant up
起動後、Ansible で node1 から node2 へ ssh するため、Vagrant 用の秘密鍵をコピーする
$ vagrant ssh-config node1 > ssh_config
$ scp -F ssh_config .vagrant/machines/node2/virtualbox/private_key node1:.ssh/id_rsa
vagrant ssh
コマンドでログインできます
$ vagrant ssh node1
$ vagrant ssh node2
やり直したくなったら destroy して up すれば元の状態に戻ります
$ vagrant destroy node2
$ vagrant up node2
EPEL から yum でもインストール可能ですが、ここは pip で最新版をインストールします。CentOS 6 の Python は 2.6 なのでついでに 2.7 の最新を入れます (@tagomoris さんの xbuild を使わせてもらいます)
$ sudo yum install bzip2-devel sqlite-devel git patch gcc openssl-devel
$ cd /var/tmp
$ git clone https://github.com/tagomoris/xbuild.git
$ sudo xbuild/python-install 2.7.10 /opt/python-2.7
$ sudo /opt/python-2.7/bin/pip install ansible
$ echo 'PATH=/opt/python-2.7/bin:$PATH' >> ~/.bashrc
node1 から ping モジュールで疎通確認してみます
$ vagrant ssh node1
$ ansible 192.168.33.12 -m ping
ERROR: Unable to find an inventory file, specify one with -i ?
おっとエラーです。-i
で inventory file を指定せよと言われています。Ansible はインベントリファイルに書かれたホストにしかアクセスしません。デフォルトのインベントリファイルが /etc/ansible/hosts
です(ansible.cfg
で定義されています)が、コマンドラインオプションの -i
で指定できます。
ちなみに ansible.cfg は次の順で探します
ここではとりあえずカレントディレクトリに hosts
ファイルを作成しましょう。
$ echo 192.168.33.12 > hosts
今度は -i
でインベントリファイルを指定して ping モジュールを実行してみましょう
$ ansible -i hosts 192.168.33.12 -m ping
paramiko: The authenticity of host '192.168.33.12' can't be established.
The ssh-rsa key fingerprint is 80c661a0ec2d1f68d5352986ad417f2c.
Are you sure you want to continue connecting (yes/no)?
yes
192.168.33.12 | success >> {
"changed": false,
"ping": "pong"
}
ok ですね (たしか、バージョン 1.3 から host key をチェックするようになりました)
次はリモートホストで任意のコマンドを実行してみましょう
$ ansible -i hosts 192.168.33.12 -a 'uname -r'
192.168.33.12 | success | rc=0 >>
2.6.32-431.el6.x86_64
ついでに yum モジュールでパッケージのインストールも試してみましょう
$ ansible -i hosts 192.168.33.12 -m yum -s -a name=telnet
192.168.33.12 | success >> {
"changed": true,
"msg": "warning: rpmts_HdrFromFdno: Header V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY\nImporting GPG key 0xC105B9DE:\n Userid : CentOS-6 Key (CentOS 6 Official Signing Key) <centos-6-key@centos.org>\n Package: centos-release-6-5.el6.centos.11.1.x86_64 (@anaconda-CentOS-201311272149.x86_64/6.5)\n From : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6\n",
"rc": 0,
"results": [
"Loaded plugins: fastestmirror, security\nLoading mirror speeds from cached hostfile\n * base: www.ftp.ne.jp\n * extras: www.ftp.ne.jp\n * updates: www.ftp.ne.jp\nSetting up Install Process\nResolving Dependencies\n--< Running transaction check\n---< Package telnet.x86_64 1:0.17-47.el6_3.1 will be installed\n--< Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package Arch Version Repository Size\n================================================================================\nInstalling:\n telnet x86_64 1:0.17-47.el6_3.1 base 58 k\n\nTransaction Summary\n================================================================================\nInstall 1 Package(s)\n\nTotal download size: 58 k\nInstalled size: 109 k\nDownloading Packages:\nRetrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6\nRunning rpm_check_debug\nRunning Transaction Test\nTransaction Test Succeeded\nRunning Transaction\n\r Installing : 1:telnet-0.17-47.el6_3.1.x86_64 1/1 \n\r Verifying : 1:telnet-0.17-47.el6_3.1.x86_64 1/1 \n\nInstalled:\n telnet.x86_64 1:0.17-47.el6_3.1 \n\nComplete!\n"
]
}
モジュールのドキュメントが ansible-doc
コマンドで確認できます。これ地味に便利です。バージョン 1.4 から PAGER を使って表示されるようになりました
$ ansible-doc yum
それではいよいよ Playbook を書いてみましょう
今度はインベントリファイルでグループを定義しておきます
$ cat <<_EOD_ > hosts
[test-servers]
192.168.33.12
_EOD_
次に playbook ファイル (YAML) を作成します
$ cat <<_EOD_ > simple-playbook.yml
---
- hosts: test-servers
become: yes
tasks:
- name: be sure httpd is installed
yum: name=httpd state=installed
- name: be sure httpd is running and enabled
service: name=httpd state=started enabled=yes
_EOD_
この playbook の内容は次の通り
yum
, service
がモジュール名で、それに続くのが各モジュールのオプションですplaybook の syntax check を行なってみます。--syntax-check
オプションを使います
$ ansible-playbook -i hosts simple-playbook.yml --syntax-check
playbook: simple-playbook.yml
次に task の一覧を確認してみましょう。--list-tasks
オプションを使います (その他のオプション一覧はこちら)
$ ansible-playbook -i hosts simple-playbook.yml --list-tasks
playbook: simple-playbook.yml
play #1 (test-servers):
be sure httpd is installed
be sure httpd is running and enabled
エラーがなければこのような出力です。エラーがあると赤字で ERROR ... と表示されます
次は dry-run です、--check
オプションを指定することで変更は行わないが、実際に実行するとこうなるという出力がされます
(1.2 の時はエラーになってたけど改善されてますね)
$ ansible-playbook -i hosts simple-playbook.yml --check
PLAY [test-servers] ***********************************************************
GATHERING FACTS ***************************************************************
ok: [192.168.33.12]
TASK: [be sure httpd is installed] ********************************************
changed: [192.168.33.12]
TASK: [be sure httpd is running and enabled] **********************************
ok: [192.168.33.12]
PLAY RECAP ********************************************************************
192.168.33.12 : ok=3 changed=1 unreachable=0 failed=0
ではいよいよ実行してみましょう
$ ansible-playbook -i hosts simple-playbook.yml
PLAY [test-servers] ***********************************************************
GATHERING FACTS ***************************************************************
ok: [192.168.33.12]
TASK: [be sure httpd is installed] ********************************************
changed: [192.168.33.12]
TASK: [be sure httpd is running and enabled] **********************************
changed: [192.168.33.12]
PLAY RECAP ********************************************************************
192.168.33.12 : ok=3 changed=2 unreachable=0 failed=0
うまくいきましたね
node2 で実際に設定されているか確認しましょう
$ sudo chkconfig --list httpd
httpd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
$ sudo service httpd status
httpd (pid 2896) is running...
冪等性があるため再度実行しても対象サーバーの状態は変わらない。 ただし、既に package がインストールされていたり、サービスの設定がされていたりするのでコマンドの出力は変わる。(changed が ok だったり skipping だったりする)
2度目の実行の出力
$ ansible-playbook -i hosts simple-playbook.yml
PLAY [test-servers] ***********************************************************
GATHERING FACTS ***************************************************************
ok: [192.168.33.12]
TASK: [be sure httpd is installed] ********************************************
ok: [192.168.33.12]
TASK: [be sure httpd is running and enabled] **********************************
ok: [192.168.33.12]
PLAY RECAP ********************************************************************
192.168.33.12 : ok=3 changed=0 unreachable=0 failed=0
実行結果に「GATHERING FACTS」と出力されています。このような task は playbook に書いていません。これは何でしょう?名前のとおりですが、これは対象サーバーから情報を収集する処理です。次のようにして内容を確認すことができます。
$ ansible -m setup -i hosts 192.168.33.12
192.168.33.12 | success >> {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"10.0.2.15",
"192.168.33.12"
],
"ansible_all_ipv6_addresses": [
"fe80::a00:27ff:fe4d:b37d",
"fe80::a00:27ff:fed5:b2ee"
],
"ansible_architecture": "x86_64",
"ansible_bios_date": "12/01/2006",
"ansible_bios_version": "VirtualBox",
"ansible_cmdline": {
"KEYBOARDTYPE": "pc",
"KEYTABLE": "us",
"LANG": "en_US.UTF-8",
"SYSFONT": "latarcyrheb-sun16",
"quiet": true,
"rd_LVM_LV": "VolGroup/lv_root",
"rd_NO_DM": true,
"rd_NO_LUKS": true,
"rd_NO_MD": true,
"rhgb": true,
"ro": true,
"root": "/dev/mapper/VolGroup-lv_root"
},
"ansible_date_time": {
"date": "2013-12-16",
"day": "16",
"epoch": "1387201344",
"hour": "13",
"iso8601": "2013-12-16T13:42:24Z",
"iso8601_micro": "2013-12-16T13:42:24.143102Z",
"minute": "42",
"month": "12",
"second": "24",
"time": "13:42:24",
"tz": "UTC",
"tz_offset": "+0000",
"year": "2013"
},
"ansible_default_ipv4": {
"address": "10.0.2.15",
"alias": "eth0",
"gateway": "10.0.2.2",
"interface": "eth0",
"macaddress": "08:00:27:4d:b3:7d",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "10.0.2.0",
"type": "ether"
},
"ansible_default_ipv6": {},
"ansible_devices": {
"sda": {
"holders": [],
"host": "IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)",
"model": "VBOX HARDDISK",
"partitions": {
"sda1": {
"sectors": "1024000",
"sectorsize": 512,
"size": "500.00 MB",
"start": "2048"
},
"sda2": {
"sectors": "82860032",
"sectorsize": 512,
"size": "39.51 GB",
"start": "1026048"
}
},
"removable": "0",
"rotational": "1",
"scheduler_mode": "cfq",
"sectors": "83886080",
"sectorsize": "512",
"size": "40.00 GB",
"support_discard": "0",
"vendor": "ATA"
},
"sr0": {
"holders": [],
"host": "IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)",
"model": "CD-ROM",
"partitions": {},
"removable": "1",
"rotational": "1",
"scheduler_mode": "cfq",
"sectors": "2097151",
"sectorsize": "512",
"size": "1024.00 MB",
"support_discard": "0",
"vendor": "VBOX"
}
},
"ansible_distribution": "CentOS",
"ansible_distribution_release": "Final",
"ansible_distribution_version": "6.5",
"ansible_domain": "localdomain",
"ansible_env": {
"CVS_RSH": "ssh",
"G_BROKEN_FILENAMES": "1",
"HOME": "/home/vagrant",
"LANG": "C",
"LESSOPEN": "|/usr/bin/lesspipe.sh %s",
"LOGNAME": "vagrant",
"MAIL": "/var/mail/vagrant",
"PATH": "/usr/local/bin:/bin:/usr/bin",
"PWD": "/home/vagrant",
"SELINUX_LEVEL_REQUESTED": "",
"SELINUX_ROLE_REQUESTED": "",
"SELINUX_USE_CURRENT_RANGE": "",
"SHELL": "/bin/bash",
"SHLVL": "2",
"SSH_CLIENT": "192.168.33.11 58880 22",
"SSH_CONNECTION": "192.168.33.11 58880 192.168.33.12 22",
"USER": "vagrant",
"_": "/usr/bin/python"
},
"ansible_eth0": {
"active": true,
"device": "eth0",
"ipv4": {
"address": "10.0.2.15",
"netmask": "255.255.255.0",
"network": "10.0.2.0"
},
"ipv4_secondaries": [],
"ipv6": [
{
"address": "fe80::a00:27ff:fe4d:b37d",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "08:00:27:4d:b3:7d",
"module": "e1000",
"mtu": 1500,
"promisc": false,
"type": "ether"
},
"ansible_eth1": {
"active": true,
"device": "eth1",
"ipv4": {
"address": "192.168.33.12",
"netmask": "255.255.255.0",
"network": "192.168.33.0"
},
"ipv4_secondaries": [],
"ipv6": [
{
"address": "fe80::a00:27ff:fed5:b2ee",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "08:00:27:d5:b2:ee",
"module": "e1000",
"mtu": 1500,
"promisc": false,
"type": "ether"
},
"ansible_form_factor": "Other",
"ansible_fqdn": "localhost.localdomain",
"ansible_hostname": "localhost",
"ansible_interfaces": [
"lo",
"eth1",
"eth0"
],
"ansible_kernel": "2.6.32-431.el6.x86_64",
"ansible_lo": {
"active": true,
"device": "lo",
"ipv4": {
"address": "127.0.0.1",
"netmask": "255.0.0.0",
"network": "127.0.0.0"
},
"ipv4_secondaries": [],
"ipv6": [
{
"address": "::1",
"prefix": "128",
"scope": "host"
}
],
"mtu": 16436,
"promisc": false,
"type": "loopback"
},
"ansible_machine": "x86_64",
"ansible_memfree_mb": 169,
"ansible_memtotal_mb": 458,
"ansible_mounts": [
{
"device": "/dev/mapper/VolGroup-lv_root",
"fstype": "ext4",
"mount": "/",
"options": "rw",
"size_available": 37423112192,
"size_total": 40797364224
},
{
"device": "/dev/sda1",
"fstype": "ext4",
"mount": "/boot",
"options": "rw",
"size_available": 446151680,
"size_total": 507744256
},
{
"device": "/vagrant",
"fstype": "vboxsf",
"mount": "/vagrant",
"options": "uid=900,gid=999,rw",
"size_available": 22555742208,
"size_total": 117461032960
}
],
"ansible_os_family": "RedHat",
"ansible_pkg_mgr": "yum",
"ansible_processor": [
"Intel(R) Core(TM) i3-3217U CPU @ 1.80GHz"
],
"ansible_processor_cores": 1,
"ansible_processor_count": 1,
"ansible_processor_threads_per_core": 1,
"ansible_processor_vcpus": 1,
"ansible_product_name": "VirtualBox",
"ansible_product_serial": "NA",
"ansible_product_uuid": "NA",
"ansible_product_version": "1.2",
"ansible_python_version": "2.6.6",
"ansible_selinux": false,
"ansible_ssh_host_key_dsa_public": "AAAAB3NzaC1kc3MAAACBAOQ2z5AKK04+mx8RJoSB8axADs5md7igiVmva7EhmibLkB35vpKVICI78y6l9jt6yq16+PFsEfOCEVXifXblfz122vu+pIeccEan52q5vn+W4xfu7svNDoKKg6VXgAezMCHWk15u9rQ2S5PY49VSut/SaVa2bYarNOpjY88hQv/NAAAAFQDjFghslSnBJfqJiRDgkVW7gR9P/QAAAIBXo7OpZh7kBgqHIbHFY2gSbtr6UUa/n5BmHBAbuJQpcv6EgeW0LJkE/6JjFgqJLMEjgd6JKdotz6p8397S7xXzwJBn/EONWR4g9NSwIgjcK4/6UALpkxcz73lSXhYXvGIMC+GYlZOTNm1ieuy1Oi/K0S4DBJhLNtlXE4Pm32gDCQAAAIB7C97Tt5DPEDl5IekSuZDfV9D33uj5BCZivINM40JxdVzG79RQyUAstiVvRWmvai02C0ff9T2VLbihHidHeaA/cTmAIXOlEda/vE/qrmmCVoalUOmEyOLDR6UXE/OKriKLtlpHzos5RJrfPc9xpme4DBdwuiXkwMyi/lY6164jfA==",
"ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAABIwAAAQEA1MfoV6b96gaG82/Iee8abtYEpyGEuaMKovWBoIKyEiDH4A/iiICRHtyhc/IYFQixM6GQI/j1kuVC4moDUCVMXqqwI1aJcKroNexVsRnoKToLXW/NGZ90bpd7m+YVd+LomaXUAFWq64j3Eo3+hXkf4k8iSQrz06jCLcfllDuf8JuRto1wIi7GJCqxCjgEEelmhCSvwrLbuFM2FD4i34tWj7+PraPNm/kkCqooev0mKPArXSZ65WCsIuZ2VV4F0FQnU6iOqYwphEsiWPvaQZfEwzzD3Tr4RkWDPu7i4VQN++KmzsPIjYp514KMydJy7/3Msqee3xtexkv/l0lRyihpRQ==",
"ansible_swapfree_mb": 927,
"ansible_swaptotal_mb": 927,
"ansible_system": "Linux",
"ansible_system_vendor": "innotek GmbH",
"ansible_user_id": "vagrant",
"ansible_userspace_architecture": "x86_64",
"ansible_userspace_bits": "64",
"ansible_virtualization_role": "guest",
"ansible_virtualization_type": "virtualbox"
},
"changed": false
}
このようにして収集したデータを task のオプションやテンプレートの変数に使うことができます
{{ ansible_eth0["ipv4"]["address"] }}
のようにして使えます。 {{ ansible_eth0.ipv4.address }}
でもいけますねtasks:
- name: gathering data task example
command: echo {{ ansible_eth0.ipv4.address }}
{{ ansible_processor_count * 5 }}
と計算結果を使うこともできます。CPU のコア数を元にプロセス数を設定したりする場合に使えます
tasks:
- name: "shutdown Debian flavored systems"
command: /sbin/shutdown -t now
when: ansible_os_family == "Debian"
そして、これらのデータが不要な場合は gather_facts: no
とすることで収集しないことで playbook の実行時間を短縮できます
- hosts: all
become: yes
gather_facts: no
roles:
- common
Best Practices のディレクトリ構成にならって WordPress サーバーを構築する Playbook を作成します。 ここは長くなりそうだから詳細は別ページにしよう
こんなディレクトリ構成で進めます。playbook branch のファイルで一応動作すると思います。随時改善していきます。 このファイルは GitHub https://github.com/yteraoka/ansible-tutorial.git にあります
$ git clone https://github.com/yteraoka/ansible-tutorial.git
$ cd ansible-tutorial
$ git checkout playbook
$ tree -F
.
|-- common.yml
|-- group_vars/
|-- host_vars/
|-- roles/
| |-- common/
| | |-- defaults/
| | |-- files/
| | |-- handlers/
| | | `-- main.yml
| | |-- meta/
| | |-- tasks/
| | | |-- common-packages.yml
| | | |-- epel.yml
| | | |-- main.yml
| | | |-- ntp.yml
| | | `-- sshd.yml
| | |-- templates/
| | `-- vars/
| `-- wordpress/
| |-- defaults/
| |-- files/
| |-- handlers/
| | `-- main.yml
| |-- meta/
| |-- tasks/
| | |-- httpd.yml
| | |-- main.yml
| | |-- mysql.yml
| | |-- php.yml
| | `-- wordpress.yml
| |-- templates/
| | |-- httpd.conf.j2
| | `-- wp-config.php.j2
| `-- vars/
| `-- main.yml
|-- site.yml
|-- test-servers
`-- wordpress.yml
19 directories, 19 files
タスクの一覧を確認するには次のように --list-tasks
オプションをつけて ansible-playboook
コマンドを実行します
$ ansible-playbook --list-tasks -i test-servers site.yml
playbook: site.yml
play #1 (all):
be sure epel repository is installed
disable epel repository
be sure common packages are installed
configure sshd_config
be sure ntpd is running and enabled
Please enter MySQL wordpress user password [wordpress]:
play #2 (wordpress):
be sure httpd is installed
create document root
be sure httpd is configured
remove httpd welcome.conf
be sure httpd is running and enabled
be sure mysql-server is installed
be sure mysqld is running and enabled
Create database
Create database user
be sure php is installed
set timezone in php.ini
download wordpress package
unzip wordpress zip file
generate secret keys
read secret keys
configure wp-config.php
MySQL ユーザーのパスワードは playbook に書かず、実行時に入力するようにしてあるため、Please enter MySQL wordpress user password:
と、プロンプトが表示されます。ブランクで Enter を押すと playbook に書いてあるデフォルトのパスワードが使われます
実際の実行は次のコマンドですが
$ ansible-playbook -i test-servers site.yml
実行前に各ディレクトリ、ファイルの役割を確認しましょう
数種類のサービスが Web - App - DB 構成(それぞれは Role)で構築されており、それぞれに2ヶ所のロケーションにあるとする(Multi-AZみたいな)
files/
, handlers/
, tasks/
, templates/
, vars/
, defaults/
, meta/
というサブディレクトリを作成します (その role で使うもののみ)include
という定義で複数ファイルをそこから読み込ませることが可能ですroles/A/meta/main.yml
に次のように書きます---
dependencies:
- role: B
foo: "bar"
ansible-playbook
コマンドに渡す大元 (root) の playbook ファイルですroles/common/tasks/common-packages.yml
を見てみましょう
- name: be sure common packages are installed
yum: name={{ item }} state=installed
with_items:
- ntp
- bind-utils
- unzip
tags: common-packages
with_items
にインストールされているべきパッケージ名を並べ、それぞれを yum モジュールで処理します。 tags
を指定しておくと ansible-playbook の --tags / -t
オプションを使うことで指定の tag のついた task だけを実行可能。複数の task に同じ tag を付けられます
roles/common/tasks/sshd.yml
でも sshd_config の複数行の編集をまとめるために with_items
を使っています
- name: configure sshd_config
lineinfile: >
dest=/etc/ssh/sshd_config
owner=root group=root mode=0600 backup=yes
regexp="{{ item.regexp }}"
line="{{ item.line }}"
insertafter="{{ item.insertafter }}"
with_items:
- regexp: '^PasswordAuthentication'
line: 'PasswordAuthentication no'
insertafter: '#PasswordAuthentication'
- regexp: '^PermitRootLogin'
line: 'PermitRootLogin no'
insertafter: '#PermitRootLogin'
- regexp: '^GSSAPIAuthentication'
line: 'GSSAPIAuthentication no'
insertafter: '#GSSAPIAuthentication'
notify: restart sshd
tags: sshd
それぞれ別の task にしてしまうと notify
によって3度も sshd の restart が必要になってしまう...と思ったら、別々にしても notify
は一度しか処理されませんでした。notify
はその role の最後に実行されるようです。よって、こういう場合はわかりやすい書き方を選ぶのが良いでしょう。ただし、task が増えるとその分 SSH の回数が増えます。
lineinfile
モジュールは設定ファイルの置換に便利です。insertafter
や backref
オプションは要チェックです
パッケージのインストールだけでなく、DBの作成、ユーザーの作成も行えます
---
# yum で mysql-server と mysql_* モジュールで必要な MySQL-python パッケージをインストール
- name: be sure mysql-server is installed
yum: name={{ item }} state=installed
with_items:
- mysql-server
- MySQL-python
tags: mysqld
# mysqld の起動、自動起動設定
- name: be sure mysqld is running and enabled
service: name=mysqld state=running enabled=yes
tags: mysqld
# wordpress 用 DB の作成
- name: Create database
mysql_db: db={{ dbname }} state=present encoding=utf8
tags: mysqld
# wordpress 用 DB ユーザーの作成
- name: Create database user
mysql_user: >
name={{ dbuser }}
password="{{ dbpassword }}"
priv={{ dbname }}.*:ALL
state=present
tags: mysqld
dbname
, dbuser
変数は roles/wordpress/vars/main.yml
に書いてあり、dbpassword
は wordpress.yml
にて次のように vars_prompt
設定し、ansible-playbook 実行時に入力させています
- hosts: wordpress
become: yes
roles:
- wordpress
vars_prompt:
- name: "dbpassword"
prompt: "Please enter MySQL wordpress user password"
default: "wordpress"
それでは playbook を実際に実行してみましょう
$ ansible-playbook -i test-servers site.yml
うまくいったでしょうか?
今実行した playbook がうまく動作したことを確認するために ServerSpec を利用してチェックするようにしてみましょう
ServerSpec は Ruby 製ですので、まずは Ruby のインストールですが、 CentOS 6 はいまだに 1.8.7 と古いので最新のバージョンを ruby-build を使って /opt/ruby-2.0.0 にインストールします
Ruby のコンパイルに必要なものをインストール
$ sudo rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
$ sudo yum -y install git gcc gcc-c++ make patch readline-devel zlib-devel libyaml-devel openssl-devel
ruby-build を git clone する
$ git clone https://github.com/sstephenson/ruby-build.git
最新の Ruby (2.0.0-p353) をインストール
$ cd ruby-build
$ sudo bin/ruby-build 2.0.0-p353 /opt/ruby-2.0.0
Ruby や Perl のコンパイルには結構時間がかかりますねぇ。お茶でも飲んで待ちましょう
Ruby のインストールが終わったら ServerSpec を gem でインストールします
$ sudo /opt/ruby-2.0.0/bin/gem install serverspec --no-ri --no-rdoc
あ、bundler 使ったほうが良かったかな?
まずは serverspec-init
コマンドで土台を作ります
$ mkdir ~/serverspec
$ cd ~/serverspec
$ /opt/ruby-2.0.0/bin/serverspec-init
Select OS type:
1) UN*X
2) Windows
Select number: 1
Select a backend type:
1) SSH
2) Exec (local)
Select number: 1
Vagrant instance y/n: n
Input target host name: 192.168.33.12
+ spec/
+ spec/192.168.33.12/
+ spec/192.168.33.12/httpd_spec.rb
+ spec/spec_helper.rb
+ Rakefile
Vagrant 使ってるけど、Vagrant の Guest 同士での SSH だから Vagrant じゃないよと
今回の設定では httpd.conf の ServerName は localhost となっているのでテストをちょっといじる
$ sed -i 's/192.168.33.12/localhost/' spec/192.168.33.12/httpd_spec.rb
それでは rake コマンドでテストを実行してみましょう。今回インストールした ruby のパスが PATH に入ってないので先に入れておく必要があります
$ PATH=/opt/ruby-2.0.0/bin:$PATH
$ rake spec
/opt/ruby-2.0.0/bin/ruby -S rspec spec/192.168.33.12/httpd_spec.rb
......
Finished in 0.99275 seconds
6 examples, 0 failures
成功しました
spec/192.168.33.12/httpd_spec.rb
に書いてあるテストが実行されました。
内容は次の通り
/etc/httpd/conf/httpd.conf
ファイルが存在することhttpd_spec.rb
を参考に mysqld のテストを作成しましょう。
これは JTF のパクリですね とまったく同じ課題です。
$ vi spec/192.168.33.12/mysqld_spec.rb
(例はこちら: https://gist.github.com/yteraoka/6156753)
作成したら再度 rake spec コマンドを実行します
$ rake spec
/opt/ruby-2.0.0/bin/ruby -S rspec spec/192.168.33.12/httpd_spec.rb spec/192.168.33.12/mysqld_spec.rb
..........
Finished in 1.24 seconds
10 examples, 0 failures
Chef 版との違いが出ました。WordPress が日本語版だったので出力されるHTMLが日本語です。。。
http://localhost/wp-admin/install.php にアクセスすると出力されるメッセージ(HTML)に「5分でできる WordPress の有名なインストールプロセスへようこそ」が含まれることをチェックしましょう
$ vi spec/192.168.33.12/wordpress_spec.rb
(例はこちら: https://gist.github.com/yteraoka/6186278)
Ansible Note にモジュールや細かいことを書いていきます。(Ansible in detail はなかなか更新できないので Wiki に移しました)
この tutorial では role
として common
, wordpress
という分け方をしましたが、その後、Ansible AWX のインストーラーなどを見ると nginx
, mysql
などを role
にするのが良さそうです。その上で site.yml を次のようにするのが良いようです
---
- hosts: all
become: yes
roles:
- { role: sshd }
- { role: ntpd }
- { role: epel }
- hosts: webservers
become: yes
roles:
- { role: nginx, name: 'value' }
- ...
- hosts: dbservers
become: yes
roles:
- { role: mysql, name: 'value' }
- ...
「role A は role B に依存しているよ」なんていう書き方もできるようです。 Ansibleのroleを使いこなす [Qiita]
ここで紹介したベストプラクティス構成で多種多様なサーバーを一つにまとめるのは非常に困難です。共有したい role だけを外だしにするのが筆者の今のお勧めです。「Ansible オレ>オレベストプラクティス - Qiita [キータ]」お試しあれ。