軽いネタを少し。

問題

CentOS 6.6 における Python の標準バージョンは 2.6、mod_wsgi は 3.2 です。アプリケーションやライブラリの都合から Python 2.7 が必要になる場合には、非標準のリポジトリから Python 2.7 を落としてくる必要があります。もしくは自前でビルドする必要があります。私は ius のリポジトリを好んで使っています。

ius リポジトリには Python 2.7 が含まれているのと同時に、mod_wsgi 4.4 が含まれています。となれば、Python 2.7 でアプリケーションを実行するなら mod_wsgi 4.4 を!となりそうなもんなのですが、これがうまくいかない。mod_wsgi 4.4 はなんとも不安定で、アプリケーションに何回かアクセスしていると、httpd が突然の死を迎えるのです 1

仕方ないので mod_wsgi 3 系を使おうとなるのが人情ってもんです。おもむろに

$ sudo yum install mod_wsgi

とやるわけですが、そう簡単にはいきません。この mod_wsgi は標準リポジトリで配布されているバイナリであり、あくまでも同じく標準リポジトリで配布される Python 2.6 と一緒に使うことを前提にビルドされているものです (libpython2.6 にリンクしてあります)。つまり非標準リポジトリで配布される Python 2.7 とは一緒に使えないのです。

解決策

mod_wsgi 3 系をソースからビルドします。なんとも原始的な解決策ですが、結局単純な方法を採ったほうが、変なことで悩まずに済みます。今回は 3.5 をビルドすることにします。

$ sudo yum install @development httpd-devel
$ wget -q https://github.com/GrahamDumpleton/mod_wsgi/archive/3.5.tar.gz -O /tmp/3.5.tar.gz
$ tar xf /tmp/3.5.tar.gz -C /tmp
$ cd /tmp/mod_wsgi-3.5
$ ./configure --with-python=/usr/bin/python2.7
$ make
$ sudo make install
$ sudo sh -c 'echo LoadModule wsgi_module modules/mod_wsgi.so >/etc/httpd/conf.d/wsgi.conf'
$ sudo service httpd restart

configure で --with-python=/usr/bin/python-2.7 としているのがミソですね。

環境よっては @developmenthttpd-devel のインストールあたりで kernel-devel がないと怒られるかもしれません。おそらく /etc/yum.conf に

exclude=kernel*

という行があるので、一時的にコメントアウトすれば、エラーなく通るようになると思います。

Ansible Playbook 化

せっかくなので Ansible の Playbook 化しておきます。

Ansible?

Ansible って何よ?って方のために簡単に紹介しておきます。

Ansible は構成管理ツールの一種です。類似のツールとして Chef, Puppet などがあります。主に、クラウド環境のように複数のサーバーのセットアップを一気に実行することを目的としますが、開発環境やテスト環境のセットアップにも役立てることができます。

他のツールと大きく異なる点は、エージェントレスであるという点です。Chef や Puppet では、セットアップ対象となるマシンにエージェント(制御ホストからの命令を受け付ける役)が必要になり、それ自体のセットアップが必要になります。

一方 Ansible は、SSH でスクリプトを送って SSH でそれを実行するという、極めてシンプルなアーキテクチャになっているので、エージェントのような特別な仕組みが必要ありません。

mod_wsgi をビルドする Playbook

mod_wsgi のバージョンや checksum, url などを変数化しておきます。

modWsgiVersion: 3.5
modWsgiDownloadUrl: https://github.com/GrahamDumpleton/mod_wsgi/archive/{{ modWsgiVersion }}.tar.gz
modWsgiChecksum: f0674c38f0f568ece55610bcc6a775c179835c4cba23aa7f876d2a2a8520bf93

アーカイブの解凍、ビルド、モジュールロードの設定追加あたりは Ansible の Playbook で表現しようとするとかえって面倒なので、シェルスクリプト化しておきます。

#!/bin/sh

tar xf /tmp/{{ modWsgiVersion }}.tar.gz -C /tmp
cd /tmp/mod_wsgi-{{ modWsgiVersion }}

./configure --with-python=/usr/bin/python2.7
make
make install

echo 'LoadModule wsgi_module modules/mod_wsgi.so' >/etc/httpd/conf.d/wsgi.conf

最後に、この辺の設定、スクリプトを駆動する Playbook を書きます。

---
- name: ensure build packages have installed
  yum: name={{ item }} state=installed
  with_items:
    - "@development"
    - httpd-devel

- name: ensure mod_wsgi has downloaded
  get_url: url={{ modWsgiDownloadUrl }}
           dest=/tmp/{{ modWsgiVersion }}.tar.gz
           sha256sum={{ modWsgiChecksum }}

- name: ensure build script has copied
  template: src=wsgi_build.sh.j2 dest=/tmp/wsgi_build.sh mode=755

- name: build mod_wsgi
  shell: /tmp/wsgi_build.sh creates=/usr/lib64/httpd/modules/mod_wsgi.so
  notify: restart httpd

大体こんな流れです:

  1. @development, httpd-devel を yum でインストール
  2. mod_wsgi のアーカイブをダウンロード
  3. ビルド用のシェルスクリプトをリモートホストにコピー
  4. リモートホスト上で、3 のシェルスクリプトを実行
  5. シェルスクリプト後は httpd を再起動

2 のダウンロードや 4 のビルドは、Ansible のべき等性の制御により、多重に動作することはありません。この辺も Ansible を使う旨味のひとつです。

終わりに

結局書きたかったのは最後の Ansible に関するところだったりします。

サーバーの構築作業は何度もやるのが面倒で、スクリプト化しようと思ったことは何度もあるのですが、エラー処理やべき等性(的なこと)を考えると億劫で、手がついていませんでした。Chef や Puppet などのツールはツールそれ自体のセットアップが面倒で、手をつけるには至っていませんでした。

その点 Ansible は、導入が簡単なうえ、エラー処理やべき等性といった自前で書くと面倒な部分を受け持ってくれます。現時点では理想的な構成管理ツールのひとつだと思っています。

「サーバー構築手順」をちまちま書くぐらいだったら、Playbook 化してしまったほうがいいんじゃないかと思っています 2。Playbook をバージョン管理下に置いておけば、履歴も残りますしね。結構おすすめです。


  1. 以前そういう現象が起こっていたことは間違いないのですが、手元の環境で再現させようと思っても再現しないので、もはや解決した問題かもしれません。もしくは私のオペミスってだけだったかもしれません。 

  2. もちろん、そういうツールを使って云々というドキュメントの必要性は分かっているつもりです。