Edited at
qnoteDay 19

VagrantのプロビジョンファイルをシェルスクリプトからAnsibleに書き換えてみました


背景

現在担当しているリプレース案件では、現ソースコードの動作を確認するため別途の開発環境を立ち上げることが必要です。そのため、VagrantでUbuntuのイメージをベースにして環境を作りました。

PHPやデータベースはVagrantのUbuntuのイメージに入っていないので、プロビジョン層でインストールしました。当時は、最もやりやすいシェルスクリプトでプロビジョンをやり、必要なコマンドを一つの.shファイルにまとめて、Vagrant環境を最初起動するときに自動で実行されるようにしました。

しかし、環境を作るにあたりいくつかの問題が見つかりました。冪等性が保証できない上、長時間触らないと書いたコードは徐々に忘れてしまいます。

そこで、シェルスクリプトから簡単に整理できるAnsibleのプレイブックに書き換えてみました。


前提

当たり前ですが、ansiblevagrantを環境に入れることが必要です。macOS上では、すごく便利なhomebrewで簡単にインストールできます。

$ brew install vagrant

$ brew install ansible


目標

本記事では、以下のプロビジョンをやります。


  1. Apache をインストール

  2. PHP 5.6 をインストール

  3. PostgreSQL 9.2 をインストール

  4. PostgreSQL ユーザーとデータベースを生成

  5. DB dumpファイルをインポート


  6. /vagrantから/var/wwwにsymlinkを生成


Before(シェルスクリプト)

上記の目標を達成するための、書き換え前のシェルスクリプトをみてみましょう。

#!/usr/bin/env bash

###########################################################
# Config
###########################################################

# DB設定
DB_ROOT_PASS='root'
DB_USER='user'
DB_PASS='pass'
DB_NAME='db'

# Install Apache
apt-get update
apt-get install -y apache2

# Install PHP 5.6
add-apt-repository ppa:ondrej/php
apt update
apt install -y php5.6 php5.6-pgsql php5.6-mbstring php5.6-gd
apt install -y libapache2-mod-php5.6 libapache2-mpm-itk
a2enmod php5.6

# Install postgreSQL 9.2
wget -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
echo deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main | tee /etc/apt/sources.list.d/postgresql.list
apt-get update
apt-get install -y postgresql-9.2 postgresql-contrib-9.2

# DBとユーザーを生成
sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH SUPERUSER CREATEDB ENCRYPTED PASSWORD '${DB_PASS}'"
sudo -u postgres createdb ${DB_NAME}

# DB dumpファイルをインポート
sudo -u postgres psql -f db.dump -d ${DB_NAME}

# /vagrantから/var/wwwにsymlinkを生成
rm -rf /var/www
ln -fs /vagrant /var/www

# restart apache
service apache2 restart
# restart postgresql
service postgresql restart
# done
echo "Provisioning completed."


書き換えましょう!


1. playbook.ymlファイルの用意

Ansibleでは、全ての設定やコマンドはYAMLファイルにて管理します。基本の構成は以下のようです:

---

- hosts: all
become: yes
become_user: root

tasks:
- name: Sample task 1
- name: Sample task 2
- ...


2. シェルスクリプトを移植

playbook.ymlが出来ていたら、まず既存のコマンドはそのままで移植します。書き方はこんな感じです:

---

- hosts: all
become: yes
become_user: root

tasks:
- name: 'apt-getを更新'
shell: apt-get update

- name: 'apacheをインストール'
shell: apt-get install -y apache2

- ...

(Github Gistはこちら)

各コマンドに説明を付けるとわかりやすくなりますが、

冪等性は前と変わりませんでした。そこで、モジュールを導入しましょう。


3. モジュールの導入

Ansibleのモジュールは、いろんなアプリのAPI経由で定義された結果を達成します。その上モジュールを使うと、定義された結果をすでに達成していた場合には変更はされません。


例1: aptモジュール

aptモジュールは、パッケージのインストールや削除ができます。

  # before

tasks:
- name: 'apt-getを更新'
shell: apt-get update

- name: 'apacheをインストール'
shell: apt-get install -y apache2

# after
tasks:
- name: 'apacheをインストール'
apt:
name: apache2
state: present
update_cache: yes # apt-get updateと同じ

覚えられないフラグはモジュールのパラメーターに切り替えたので、可読性も上がりました。


例2: postgresql_userモジュール

例1に書いてあるapt-get installは基本何回実行しても結果は変わらないですが、DBユーザー作成するときは同じ名前のユーザーが存在するとエラーが出ます。postgresql_userモジュールで、エラーを避けられる上、定義したロールやパスワードがちゃんと反映するか確認できます。

  # before

tasks:
- name: 'DBを生成'
shell: "sudo -u postgres createdb {{ DB_NAME }}"

# after
tasks:
- name: '[postgres] ユーザーを生成'
become: yes
become_user: postgres
postgresql_user:
name: "{{ DB_USER }}"
password: "{{ DB_PASS }}"
encrypted: yes
role_attr_flags: 'CREATEDB,SUPERUSER'
state: present

Ansibleのモジュールはたくさんあるので全部紹介できないため、

他にあるモジュールはAnsibleのdocsを参照してください。


4. Handlerを活用

Handlerはタスクと構成がほぼ一緒ですが、指定したタスクに変更があれば最後に実行します。Handlerは何回起こしても1回しか実行されません。

  # before

tasks:
- name: 'phpを有効'
shell: a2enmod php5.6

- name: 'apacheサービスを再起動'
shell: service apache2 restart

# after
tasks:
- name: '[php] phpを有効'
apache2_module:
name: php5.6
state: present
notify:
- '[apache] サービスを再起動'

handlers:
- name: '[apache] サービスを再起動'
service:
name: apache2
state: restarted


5. Vagrantfileを修正

最後に、VagrantをプロビジョンをするときにAnsibleを適用するためVagrantfileに以下の修正が必要です:

  # before

config.vm.provision "shell", path: "bootstrap.sh"

# after
config.vm.provision "ansible" do |ansible|
ansible.playbook = "playbook.yml"
end


6. 完成!

完成物はこちらのGithub Gistをご参照ください。


まとめ

Ansibleでプロビジョンをやると、可読性を高め、冪等性も保証でき、

よりスッキリなコードが書けます。

Vagrantで環境を作る方はぜひ試してみてください!