Ansibleで自社Wordpressを移行を自動化
先日弊社Webサイト、http://www.cflat-inc.com のサーバーをAWSからさくらVPSに移行しました。元々t1.microを使っていたのですが、さすがに非力すぎて時折アクセスできないこともありました。かといってsmallを使うとスペックの割にコストがかかると考えさくらVPSを採用しました。
サーバーが決まったら次は環境の移行を行いますが、失敗することなく同一の環境を構築するためにまずはVirtualBoxの仮想マシンで動作を検証し、問題がなければさくらVPSでセットアップを行うという流れを取ることにしました。つまり2度セットアップを行うことになります。
2度同じセットアップを行うのはミスが入り込む余地だらけですし、そもそも面倒なのでいわゆるプロビジョニングツールを使うことにしました。PuppetとかChefに代表されるアレですね。今回はその中でも(機能的に)軽量であるという噂のAnsibleをトライアルするいい機会だと考え、Ansibleを使って移行を行うことにしました。
ちなみにAnsibleはhomebrewでインストールすることができます。↓でOK。
$ brew install ansible
Ansible 最終形
Ansibleでは、playbookというYAML形式の設定ファイルのようなものに処理を色々と書いておいて、playbookを指定して実行することで対象のサーバーを操作することができます。
今回最終的には次のようなコマンドを実行することになります。
$ ansible-playbook -i hosts all.yml
これはhosts
というファイルに書いてあるサーバーに対してall.yml
に書いてある処理をしてね、という意味になります。
まずhosts
ですが、次のようになっています。
# hosts [server] # 192.168.33.11 cfalt2
コメントアウトしている一つ目のIPはVagrantで立ちあげたVirtualBoxのIPアドレスです。テスト時はこのコメントを外します。
2つ目のcflat2
とあるのは、~/.ssh/config
に書いてある設定の中のホスト名です。本番サーバーを意味します。
次にplaybookのall.yml
はこうなっています。
# all.yml - include: ssh-iptables.yml - include: yum.yml - include: lamp.yml - include: mysql-secure-installation.yml - include: mysql-initialize.yml - include: prepare-contents.yml
playbookでは他のplaybookをinclude
することができるようになっています。all.yml
ではもう少し細かい単位に分けたplaybookをincludeするだけにしてあります。書かれている内容は上から順番に実行されます。
ざっくり言うと、
- iptablesの設定して
- yumの設定して
- Apache、PHP、MySQL入れて
- MySQLのインストールして
- MySQLの初期設定して(ここは↑とまとめてもよかった)
- 元サイトのコンテンツを入れる
という流れになっています。
各playbook解説
playbookを載せると長くなるかもしれませんが、せっかくの事例紹介なので全部お見せします。
ssh-iptables.yml
まずはSSHとiptablesの設定をするplaybook。
- name: change ssh settings and iptables hosts: server sudo: yes tasks: - name: create iptables template: src=files/iptables.j2 dest=/etc/sysconfig/iptables - name: disallow root SSH access lineinfile: dest=/etc/ssh/sshd_config regexp="^#PermitRootLogin " line="PermitRootLogin no" state=present - name: disallow password authentication lineinfile: dest=/etc/ssh/sshd_config regexp="^#PasswordAuthentication " line="PasswordAuthentication no" state=present # NOTE: Combine two tasks to keep ansible access with port 22 - name: restart sshd and restart iptables shell: service sshd restart && /etc/init.d/iptables restart
いきなりAnsible固有の色々がでてきたのでここで多少解説をいれておきます。
項目 | 意味 |
---|---|
name | その部分の名前、確かログに出るぐらいの意味 |
hosts | 対象ホスト、hostsの[server]セクションにある全てのhostを対象にする |
sudo | rootで実行するかどうか |
tasks | 処理を書くところ |
template | テンプレートファイルをdestにコピーする。変数展開も使える。 |
lineinfile | destファイルに指定した行があることを確約したり、置換したりする |
shell | shellコマンドを実行する |
このplaybookの中でtemplate
が参照しているfiles/iptables.j2
にiptablesの設定を書いておきます。
yum.yml
続いてyumのリポジトリを追加します。OSに最初から設定されているリポジトリだけでは足りなかったので。書くのが遅れましたがOSはCentOS 6.3です。
command
とyum
とかありますが、なんとなく読めると思います。
- name: yum setting hosts: server sudo: yes tasks: - name: update yum command: yum -y update # repository - name: add repository 'rpmforge-repo' yum: name=http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm state=present - name: add repository 'remi-repo' yum: name=http://rpms.famillecollet.com/enterprise/remi-release-6.rpm state=present
lamp.yml
- name: Installing Apache, PHP, MySQL hosts: server sudo: yes vars: httpd_port: 80 mysql_port: 3306 tasks: # Apache - name: install apache yum: name=httpd enablerepo=remi,epel,rpmforge state=present - name: start httpd service: name=httpd state=started enabled=yes - name: add iptables rule for httpd lineinfile: dest=/etc/sysconfig/iptables regexp="{{ httpd_port }}" line="-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport {{ httpd_port }} -j ACCEPT" insertbefore="^# add end" state=present notify: restart iptables # PHP - name: install php yum: name={{ item }} enablerepo=remi,epel,rpmforge state=present with_items: - php - php-mysql # MySQL - name: install mysql yum: name={{ item }} enablerepo=remi,epel,rpmforge state=present with_items: - mysql - mysql-server - MySQL-python - name: add iptables rule for mysql lineinfile: dest=/etc/sysconfig/iptables regexp="{{ mysql_port }}" line="-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport {{ mysql_port }} -j ACCEPT" insertbefore="^# add end" state=present notify: restart iptables handlers: - name: restart iptables service: name=iptables state=restarted
長いですね。ここで見るべきはvars
とhandlers
、notify
、with_items
あたりでしょうか。
項目 | 意味 |
---|---|
vars | 変数を使える。あとから参照するには{{ variable }} などとする |
handlers | notifyとセットで使う。他のタスクから呼び出せる共通の処理。 |
notify | handlersに書いた処理の名前を指定することでその処理を呼び出せる。 |
with_items | 複数の項目に同じコマンドを実行したいときに使う。項目は{{ item }}として参照できる。 |
mysql-secure-installation.yml
これもなんとなく読めると思います。
- rootパスワードの設定
template
で.my.cnf
ファイルをコピー
などを行います。
- name: MySQL setup hosts: server sudo: yes vars: root_db_password: XXXXXXXX mysql_root_password: XXXXXXXXX tasks: - name: ensure mysql is running and starts on boot service: name=mysqld state=started enabled=true # Need to do this for idempotency, see # http://ansible.cc/docs/modules.html#mysql-user - name: update mysql root password for all root accounts mysql_user: name=root host=localhost password={{ root_db_password }} - name: copy .my.cnf file with root password credentials template: src=files/.my.cnf dest=/root/.my.cnf owner=root mode=0600 - name: update mysql root password for all root accounts mysql_user: name=root host={{ item }} password={{ root_db_password }} with_items: - 127.0.0.1 - ::1 - name: ensure anonymous users are not in the database mysql_user: name='' host={{ item }} state=absent with_items: - localhost - 192.168.33.11 - name: remove the test database mysql_db: name=test state=absent
mysql-initialize.yml
ここではサイト用のMySQLユーザーやデータベースを作っています。
- name: Creating DB User hosts: server vars: dbname: dbname dbuser: dbuser loginuser: root loginpassword: XXXXXXXX tasks: - name: create database for wordpress mysql_db: db={{ dbname }} state=present encoding=utf8 login_user={{ loginuser }} login_password={{ loginpassword }} - name: create a user for wordpress mysql_user: name={{ dbuser }} password="YYYYYYYY" priv={{ dbname }}.*:ALL state=present login_user={{ loginuser }} login_password={{ loginpassword }}
prepare-contents.yml
このplaybookではコンテンツをサーバーにセットアップします。
ウェブサイトにはWordpressを用いています。このWordpressのファイル(テーマやプラグイン含む)はgitで管理しているのでgitから取得します。また、データベースに入っているデータ類はあらかじめmysqldumpなどでエクスポートしておきます。
- name: instal Git and clone wordpress from git repo hosts: server sudo: yes tasks: # Git - name: install git yum: name=git enablerepo=remi,epel,rpmforge state=present - name: create contents dir file: path=/var/www/contents state=directory owner=apache group=apache - name: clone from repo git: repo=your_repo dest=/var/www/path/to/dir - name: copy vhost template: src=files/virtual.conf dest=/etc/httpd/conf.d/virtual.conf owner=root mode=0644 notify: restart httpd # initial data - name: copy database.sql to server copy: src=files/database.sql dest=/tmp - name: import database.sql to db mysql_db: name=dbname state=import target=/tmp/database.sql handlers: - name: restart httpd service: name=httpd state=restarted
以上のplaybookを全て実行するとサーバーのセットアップからコンテンツの移行までを一括して行ってくれます。
$ ansible-playbook -i hosts all.yml
この状態でサーバーにアクセスすると元サイトと同じ状態になっていることがわかると思います。あとはドメインの設定を変更して、新サーバーを指すようにしてあげれば完了です。
Ansibleのようなツールを使うことで仮想環境でテストしてから本番に適用というのがとても楽ですし、次に同じようなの移行が必要になっても、最後のコンテンツ部分(gitとかsql)の部分を新しくしておくだけで、あとの手順はコマンド一発になり非常に便利です。サーバーの状態がコードとして書かれているというのもよく言われるメリットですね。
Ansibleを使うことで学習コストがかかりましたが、手作業でやってしまおうという誘惑にまけなくてよかったと感じています。
Ansible、Chefに比べるとシンプルで使いやすいという印象をうけました。今回のような小さめのプロジェクトではAnsible優位な気がします。