VagrantのCentOS6.5にAnsibleでMovable Typeを入れる

以下の続編です。

免責事項

間違いもあると思います。

はじめに

Linuxサーバ入門の一環として、今回はVagrantで立ち上げたCentOSMovable Typeを入れてみたいと思います。

他人に見せるつもりではないローカルブログを構築したい時など、役立つかもしれません。

環境づくり&MTパッケージ取得

Vagrant boxは、以下と同じchef/centos-6.5を使います。
2015年のCGI入門 - the code to rock

Movable Typeの導入については、「さくらのナレッジ」に書かれた以下が大変詳しいです。
Movable Type をさくらのレンタルサーバにインストールしてみよう - さくらのナレッジ
Movable Type をさくらのVPSにインストールしてみよう - さくらのナレッジ

前者では最新MTの取得方法、後者ではサーバー環境に必要なモジュール等を入れていく方法が細かく解説されています。

ここからは、それらの情報などを元に、すでにVagrantは立ち上げ済み、MTも取得済み、という前提で話を進めます。

Ansibleの準備1: rootに鍵認証でssh接続する

Ansibleを使うには、手元のホストマシンからMTを入れたいリモートマシンにssh接続が出来なければいけないので、まずはそれを設定していきます。

初めに、ホストマシンとリモートマシンのrootを鍵認証で繋げます。
ホストマシンに鍵のペアがまだ無い場合は、ドットインストールの以下が参考になります。
#06 鍵認証を設定しよう (1) | さくらのVPS入門 - プログラミングならドットインストール
#07 鍵認証を設定しよう (2) | さくらのVPS入門 - プログラミングならドットインストール

今回はIPアドレスが「192.168.33.12」にセットされているとして、以下のコマンドで繋げます。

$ ssh-copy-id root@192.168.33.12

「Are you sure you want to continue connecting (yes/no)? yes」と聞かれたら、「yes」でエンター。

その後、パスワードを聞かれるので、「vagrant」とします。(Vagrantのrootパスワードの初期設定)

そこで一旦流れが切れるので、以下のコマンドを打って、あらためてrootにsshログインします。

$ ssh root@192.168.33.12

入れました。

[root@localhost ~]#

Ansibleの準備2: 最小限の構成ファイルを作る

言い忘れていましたが、Ansibleの操作についてもドットインストールが大変参考になりました。

Ansibleでは、「playbook」という、いわば設定ファイルを書いたり実行したりするのですが、それとは別に最低限必要なファイルとして Inventoryファイルというのがあります。

また、デフォルトだとplaybookの実行時にInventoryファイルの場所を指定しなければならないのですが、「ansible.cfg」というのを作っておくと、その指定コマンドを省略できてラクなので、それも作っておきます。

その辺についてもドットインストールの以下で詳しく解説されています。
#06 ansibleを使ってみよう | Ansible入門 - プログラミングならドットインストール

ということで、それらをこんな感じで作ったとします。
まず「hosts」ことInventoryファイル。

[MT]
192.168.33.12

次に「ansible.cfg」。

[defaults]
hostfile = ./hosts

勢いでplaybookも作ってみましょう。テストがてら、こんな感じで。

---
- hosts: all
  remote_user: root
  sudo: yes
  tasks:
    - name: ping check
      ping:

深く考えずに「mt.yml」の名前で保存して、実行。

$ ansible-playbook mt.yml

結果。

PLAY [all] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [192.168.33.12]

TASK: [ping check] ************************************************************
ok: [192.168.33.12]

PLAY RECAP ********************************************************************
192.168.33.12              : ok=2    changed=0    unreachable=0    failed=0

うまく通りました。

ここからの作業は、主にこのplaybook(mt.yml)にどんどん手を入れていく感じです。

playbookを作る

playbookの作成においては、MTをインストールする手順をyaml形式で記述していくわけですが、この際には初めの方で挙げた以下の記事がとても参考になりました。
Movable Type をさくらのVPSにインストールしてみよう - さくらのナレッジ

というか、これがなかったら自分的にはMT入れるのは不可能でしたね……。MTを動かすために最低限どのような要素が必要なのか、過不足なく書かれたむちゃくちゃ良記事なのですが、あまりブクマが付いていないので不思議です。

具体的なplaybookを見る前に、作業全体のファイル構成を確認しておくと、今回必要なのは以下のファイルです。

  • playbook
  • my.cnfの書き換え用ファイル
  • MTのzip

「必要」と言いましたが、あくまで僕のplaybookの場合です。とくに2番目のmy.cnf云々というのは、その「さくらのナレッジ」でも紹介されている以下のわずかな作業を自動化しているだけなので、

[mysqld] セクションに

character-set-server=utf8

[mysql] セクションに

default-character-set=utf8

これを手動でやるなら無くても大丈夫です。(後述のplaybookからもその部分を外す必要がありますが)

ここではそのファイルは「my.cnf.edit」という名前で、以下のように書いています。

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

character_set_server=utf8
default-storage-engine=InnoDB
innodb_file_per_table
[mysql]
default-character-set=utf8
[mysqldump]
default-character-set=utf8

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

最後に挙げたMTのデータですが、これを書いている数日前に最新版(セキュリティアップデート版)の6.1.1というのが出たので、それを使う前提とします。

この程度の少量ファイルなら、playbookと同じディレクトリに置いておいても害はないと思いますが、僕の場合は他のAnsible作業でもそこそこスタイルを応用できるように、playbook, hosts, ansible.cfg以外のデータは「src」というディレクトリに入れています。

playbook実例

という前提で、作ったplaybookが以下です。(mt.yml)

---
- hosts: all
  remote_user: root
  sudo: yes
  vars:
    #src
    version_tar: MT-6_1_1.zip
    version: MT-6.1.1
    my_cnf: my.cnf
    my_cnf_edit: my.cnf.edit
    #path
    pathroot: /var/www/html/
    pathmain: /var/www/html/mt
    path_etc: /etc
    #mysql
    dbuser: mtdbuser
    dbpass: mtdbpasswd
    dbname: mtdb
  tasks:
    - name: Install modules before perl-modules
      yum: name={{item}} state=latest
      with_items:
        - libselinux-python
        - gcc
        - httpd
    - name: httpd.conf backup
      command: cp /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.orig creates=/etc/httpd/conf/httpd.conf.orig
    - name: httpd start
      service: name=httpd state=started enabled=yes
    - name: install perl modules
      yum: name={{item}} state=latest
      with_items:
        - perl
        - perl-CPAN
        - perl-core
        - perl-devel
        - perl-CGI
        - perl-Archive-Tar
        - perl-libwww-perl
        - perl-YAML
        - ImageMagick
        - openssl-devel
        - zlib-devel
        - readline-devel
        - perl-ExtUtils-MakeMaker
        - postgresql-devel
        - gd-devel
        - libxml2-devel
        - expat-devel
        - ImageMagick-perl
    - name: cpanm - set locale
      command: localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
    - name: install cpanm
      shell: curl -L http://cpanmin.us | perl - --sudo App::cpanminus
    - name: install cpan-modules with cpanm
      cpanm: name={{item}} notest=True
      with_items:
        - Plack
        - CGI::PSGI
        - IPC::Run
        - Cache::File
        - Archive::Zip
        - XML::SAX
        - Authen::SASL
        - Mozilla::CA
        - Image::Size
        - CGI::Parse::PSGI
        - Imager
        - Crypt::DSA
        - Cache::Memcached
        - Digest::SHA1
        - XML::SAX::Expat
        - YAML::Syck
        - DBD::Pg
        - Net::SSLeay
        - XMLRPC::Transport::HTTP::Plack
        - SOAP::Lite
        - Crypt::SSLeay
        - Net::SMTP::SSL
        - Net::SMTP::TLS
        - IO::Socket::SSL
        - GD
        - XML::Atom
        - XML::LibXML::SAX
        - XML::SAX::ExpatXS
        - DBD::SQLite2
 #php
    - name: install php
      yum: name={{item}} state=latest
      with_items:
        - php
        - php-devel
        - php-mbstring
        - php-mysql
 #mysql
    - name: install mysql
      yum: name={{item}} state=latest
      with_items:
        - python-setuptools
        - python-devel
        - mysql-devel
        - mysql-server
    - name: install pip
      easy_install: name=pip
    - name: install mysql-python
      pip: name=MySQL-python
    - name: my.cnf backup
      command: cp {{ path_etc }}/{{ my_cnf }} {{ path_etc }}/{{ my_cnf }}.orig creates={{ path_etc }}/{{ my_cnf }}.orig
    - name: copy my.cnf.edit
      copy: src=src/{{ my_cnf_edit }} dest={{ path_etc }}/{{ my_cnf_edit }} force=no
    - name: replace my.cnf to my.cnf.edit
      shell: cat {{ my_cnf_edit }} > {{ my_cnf }} chdir={{ path_etc }}
    - name: mysql chkconfig
      command: chkconfig mysqld on
    - name: mysql restart
      command: /etc/init.d/mysqld restart
    - name: mysql user create
      mysql_user: name={{ dbuser }} password={{ dbpass }} priv=*.*:ALL,GRANT state=present
    - name: mysql db create
      mysql_db: name={{ dbname }} state=present
    - name: install unzip
      yum: name=unzip state=latest
    - name: mkdir for blog
      file: path={{ pathroot }} state=directory
    - name: copy & unarchive src
      unarchive: src=src/{{ version_tar }} dest={{ pathroot }} copy=yes
    - name: rename main directory
      command: mv {{ pathroot }}/{{ version }} {{ pathmain }} creates={{ pathmain }}
    - name: mt cgi permission
      shell: chmod 755 {{ pathmain }}/*.cgi
    - name: dir permission
      command: chmod 777 {{item}}
      with_items:
        - "{{ pathmain }}"
        - "{{ pathmain }}/mt-static/support/"
      notify:
        - restart apache
  handlers:
    - name: restart apache
      service: name=httpd state=restarted

いろいろ解説すべき点があると思うのですが、大変なので割愛します。

唯一触れておくべき点として、冒頭の変数宣言の部分で、データベースのためのDB名、DBユーザー名、DBパスワードを設定しています。

    #mysql
    dbuser: mtdbuser
    dbpass: mtdbpasswd
    dbname: mtdb

これはあくまでサンプルとして入れていますので、各自で必要に応じて書き換えてください。
まあ、自宅でローカルブログとして使うならこのままでもいいと思いますが。

dry run

では、実行してみましょう。
……と、その前に、このぐらい長いplaybookの場合は、僕は一旦dry runをします。

dry runについてはドットインストールだと以下で説明されていますが、
http://dotinstall.com/lessons/basic_ansible/29108

ようは本番で実行する前に「ちょっとテスト」的に走らせてみるものです。失敗してもサーバー(リモートマシン)本体に影響が出ないので、心理的な負荷が軽減されます。

やることは単純で、実行コマンドの最後に「--check」と入れるだけです。
ちなみに、playbookのyaml文法を確認したいときには「--syntax-check」とします。

この辺の、念には念を、という感じで事前にテストしまくれるのって、僕的には大変性格に合うというか、好きですね。

ではdry run。

$ ansible-playbook mt.yml --check

ええと、たぶん「httpd start」のあたりでコケると思います。

僕はここでかなりハマったというか、悩んだのですが、これはじつはdry run「だから」コケてるのであって、リモートマシンの方に実際にはインストールされていないパッケージが入っている前提でdry runが走るので、「そのパッケージ入ってないからエラーねww」みたいに出てくるのだと思われます。

逆に言うと、実際に実行した時にはそのパッケージはちゃんと入っているので、そこではコケない(はず)、ということになります。

ではあらためて、実行してみましょう。

$ ansible-playbook mt.yml

結果。

PLAY [all] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [192.168.33.12]

TASK: [Install modules before perl-modules] ***********************************
changed: [192.168.33.12] => (item=libselinux-python,gcc,httpd)

TASK: [httpd.conf backup] *****************************************************
changed: [192.168.33.12]

TASK: [httpd start] ***********************************************************
changed: [192.168.33.12]

TASK: [install perl modules] **************************************************
changed: [192.168.33.12] => (item=perl,perl-CPAN,perl-core,perl-devel,perl-CGI,perl-Archive-Tar,perl-libwww-perl,perl-YAML,ImageMagick,openssl-devel,zlib-devel,readline-devel,perl-ExtUtils-MakeMaker,postgresql-devel,gd-devel,libxml2-devel,expat-devel,ImageMagick-perl)

TASK: [cpanm - set locale] ****************************************************
changed: [192.168.33.12]

TASK: [install cpanm] *********************************************************
changed: [192.168.33.12]

TASK: [install cpan-modules with cpanm] ***************************************
ok: [192.168.33.12] => (item=Plack)
ok: [192.168.33.12] => (item=CGI::PSGI)
ok: [192.168.33.12] => (item=IPC::Run)
ok: [192.168.33.12] => (item=Cache::File)
ok: [192.168.33.12] => (item=Archive::Zip)
ok: [192.168.33.12] => (item=XML::SAX)
ok: [192.168.33.12] => (item=Authen::SASL)
ok: [192.168.33.12] => (item=Mozilla::CA)
ok: [192.168.33.12] => (item=Image::Size)
ok: [192.168.33.12] => (item=CGI::Parse::PSGI)
ok: [192.168.33.12] => (item=Imager)
ok: [192.168.33.12] => (item=Crypt::DSA)
ok: [192.168.33.12] => (item=Cache::Memcached)
ok: [192.168.33.12] => (item=Digest::SHA1)
ok: [192.168.33.12] => (item=XML::SAX::Expat)
ok: [192.168.33.12] => (item=YAML::Syck)
ok: [192.168.33.12] => (item=DBD::Pg)
ok: [192.168.33.12] => (item=Net::SSLeay)
ok: [192.168.33.12] => (item=XMLRPC::Transport::HTTP::Plack)
ok: [192.168.33.12] => (item=SOAP::Lite)
ok: [192.168.33.12] => (item=Crypt::SSLeay)
ok: [192.168.33.12] => (item=Net::SMTP::SSL)
ok: [192.168.33.12] => (item=Net::SMTP::TLS)
ok: [192.168.33.12] => (item=IO::Socket::SSL)
ok: [192.168.33.12] => (item=GD)
ok: [192.168.33.12] => (item=XML::Atom)
ok: [192.168.33.12] => (item=XML::LibXML::SAX)
ok: [192.168.33.12] => (item=XML::SAX::ExpatXS)
ok: [192.168.33.12] => (item=DBD::SQLite2)

TASK: [install php] ***********************************************************
changed: [192.168.33.12] => (item=php,php-devel,php-mbstring,php-mysql)

TASK: [install mysql] *********************************************************
changed: [192.168.33.12] => (item=python-setuptools,python-devel,mysql-devel,mysql-server)

TASK: [install pip] ***********************************************************
changed: [192.168.33.12]

TASK: [install mysql-python] **************************************************
changed: [192.168.33.12]

TASK: [my.cnf backup] *********************************************************
changed: [192.168.33.12]

TASK: [copy my.cnf.edit] ******************************************************
changed: [192.168.33.12]

TASK: [replace my.cnf to my.cnf.edit] *****************************************
changed: [192.168.33.12]

TASK: [mysql chkconfig] *******************************************************
changed: [192.168.33.12]

TASK: [mysql restart] *********************************************************
changed: [192.168.33.12]

TASK: [mysql user create] *****************************************************
changed: [192.168.33.12]

TASK: [mysql db create] *******************************************************
changed: [192.168.33.12]

TASK: [install unzip] *********************************************************
changed: [192.168.33.12]

TASK: [mkdir for blog] ********************************************************
ok: [192.168.33.12]

TASK: [copy & unarchive src] **************************************************
changed: [192.168.33.12]

TASK: [rename main directory] *************************************************
changed: [192.168.33.12]

TASK: [mt cgi permission] *****************************************************
changed: [192.168.33.12]

TASK: [dir permission] ********************************************************
changed: [192.168.33.12] => (item=/var/www/cgi-bin/)
changed: [192.168.33.12] => (item=/var/www/cgi-bin/mt)
changed: [192.168.33.12] => (item=/var/www/cgi-bin/mt/mt-static/support/)

NOTIFIED: [restart apache] ****************************************************
changed: [192.168.33.12]

PLAY RECAP ********************************************************************
192.168.33.12              : ok=26   changed=23   unreachable=0    failed=0

通りました。

httpdの設定

この状態でページにアクセスすると、メインのアドレスはApacheのテストページになっていますが、mtディレクトリに行くと、このように出迎えてくれます。

f:id:note103:20150417004430p:plain

やった〜! と思って「Movable Typeにサインイン」の青いボタンを意気揚々とクリックすると、

f:id:note103:20150417004611p:plain

ソースコードが出迎えてくれます。

これはCGIの設定が不充分だからです。
以前に書いたCGI入門の記事では、「cgi-bin」ディレクトリにCGIのファイルを入れていたので、
http://note103.hateblo.jp/entry/2015/04/06/084031

それによって不問に付されていた幾つかの設定がありましたが、今回はドキュメントルート(/var/www/html)の下にMTを入れたので、その設定が必要になってくる、ということのようです。(詳細未把握。勉強中)

といっても、そんなに大変なことでもなくて、取り急ぎ必要なのは以下の2点です。順番に見ていきます。

AddHandlerの非コメントアウト

設定する対象のファイルは、2点ともhttpd.confというやつです。

とりあえず、同ファイルが突然の死を迎えても大丈夫なように、バックアップをとっておきます。
(rootで作業してしまうのでsudoは付けません)

# cp /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.orig

「/etc/httpd/conf/」配下にあるファイルなので、一応上記のように書いていますが、「cd」でそのディレクトリまで入ってファイル操作しても良いと思います。
ただ、自分の経験では上記のようなフルパスでやったほうが結局はラク、という印象があるのでそうしています。

同様にフルパスでファイルを編集します。この仮想マシンにはVimが入っていないので、Viで操作します。

# vi /etc/httpd/conf/httpd.conf

エディタが起動したら、「AddHandler」を検索します。

#AddHandler cgi-script .cgi

というのが出てくるので、行頭の「#」を外します。

(余談ですが、この元の形のように行頭に「#」を付けて無効化/コメント化するのを「コメントアウト」と呼ぶようですが、そのコメントアウトを外すこのような作業はなんと呼ぶのでしょうね。コメントアウトアウトとは言わないと思いますが、気分的にはそれが近いので一応疑問がてらメモしておきます)

ExecCGIの付与

次に、「ExecCGI」というのを以下の場所にくっつけます。
「/html」で検索をかけて(viエディタ内の検索なので実際には「\/html」ですが)、

<Directory "/var/www/html">

というのを見つけます。その15行ぐらい下に、

    Options Indexes FollowSymLinks

というのがあるので、その最後に、スペースを挟んで「ExecCGI」と追記します。

    Options Indexes FollowSymLinks ExecCGI

こんな感じ。

これは実際には、「/var/www/html」以下の「mt」ディレクトリに効かせたい設定なので、たとえば、

<Directory "/var/www/html/mt">
    Options ExecCGI
</Directory>

というのを足すのでもいいと思います。(個人の見解です)

ここまで出来たら、エディタを保存&閉じて、httpdをリスタートします。

# service httpd restart

では、あらためて「 http://192.168.33.12/mt/ 」に飛んで、青い「サインイン」ボタンを押してみましょう。

こんな感じになれば素敵です。

f:id:note103:20150417005418p:plain

MTの設定

ここからはMT自体の設定なので、上述の以下の記事にお世話になるのが良いでしょう。
Movable Type をさくらのレンタルサーバにインストールしてみよう - さくらのナレッジ

真ん中辺り、「インストールウィザードの起動」というぐらいから見ると良いです。

データベース関連の情報(DB名、ユーザー名、パスワード)は上述のとおり、playbookの冒頭に記載した内容を参照してください。

メール設定については、その記事にあるレンタルサーバーと同様で良さそうですが、何も選択しなくても問題ないようでした。

まとめ&次回予告

ということで、Vagrantで立てたローカル環境としてのCentOS6に、Ansibleを使ってMovable Typeの最新版を入れてみる、という一連の作業をやってみました。

上記のplaybookのサンプルは、Ansibleの最小限のモジュールを組み合わせたものなので、ちゃんと調べたらもっと効率的にあれこれできそうだと思います。

また、今回紹介した「さくらのナレッジ」の記事や、ドットインストールの該当講座、あるいは前回までのこのブログの内容などを応用すれば、さくらのVPSにAnsibleでMTを構築するのもけっこう簡単だと思います。

次回予告として、同様の手法を使ってWordPress、そして個人的な念願だったtDiaryをさくらのVPSVagrantに入れる、というのをやってみたいと思います。

ところで、これを書いている今日、たまたまこのような記事が公開されました。

非常に勉強になりますが、この中の「管理画面に BASIC 認証をかける」というのが、一見シンプルそうでありながらなかなか大変で、何とか解決したもののすぐ忘れそうなので、その辺の知見も時間ができたら書いておきたいと思います。

以上です。