systemd下でphpenvを使って複数バージョンのphp-fpmを動かす

# 目的と背景
　岡島は目を覚ました。中秋から晩秋になろうという季節だ。布団に入っていてもひんやりとした外気を感じる。元来寒がりの彼は、目覚めたばかりの低い体温ではこの寒さには耐えられぬ、もう少し体をあたためてから起きよう、と掛け布団を体にきつく巻き付ける。ふたたびまどろみ始めた彼は今日の予定はなんだったか、とふと考えた。そしてすぐに思い出す。

　「そうだ！今日は客先に行って、自社アプリの構成説明をするんだった！」

　彼は飛び起き、そして自分で考えたベストなプラットフォーム構成を説明し、顧客にもきっと満足してもらえるだろうと考え、胸を高鳴らせたのだった。

<br>

　「Apacheですか？うーん…」

　まだ始業してから幾時間も経たない頭も冴えわたる時間、そして外はまさに秋晴れという快晴にも関わらず、株式会社大村田商会の会議室内に情報システム課の花島係長の落胆とも取れる、浮かない声が響いた。

　「はい、Apacheですが…。何か問題があるでしょうか？」

　岡島はまさかこんなところで説明を遮られるとは思ってもいなかったので、内心困惑していた。何だってんだ？LinuxでWebアプリケーションを動かすのにApache使って何が問題なんだ？そう思ってしまう。

　「Apacheというのはもう古いでしょう。今はどこもnginxですよ？」

　古い？何を言ってるんだ？まだバリバリ開発中で数か月前に新バージョンもリリースされてるぞ？そりゃそんな頻繁には更新はないけど、それは安定しているということであって決して悪いことじゃない。それにどこもnginxだって？お前は生え抜きで経理から配置換えさせられた社内SEだろ？短くて1か月、長くて2年で様々なプロジェクトに放り投げられてきた俺より多くの現場を知っているとでもいうのか？仮に知っていたとしても「みんなが使ってるから」というのはまったく論理的じゃない。何の根拠にもならない。

　岡島は心の中で一気にそうまくし立てたが、さすがにこんなことで客にかみつくほどエンジニア歴は短くない。努めて冷静に返す。

　「なるほど。たしかにnginxも大分利用が増えてきていますね。ですが、運用のことも考えるとApacheがいいかと思います。OSはRHEL6ですから、商用サポートの下、公式リポジトリのパッケージを使用すれば重要なセキュリティ上の問題等があっても素早く修正版がインストールできると思います。」

　岡島も運用フェーズは担当したことがある。有名どころのライブラリなどに脆弱性が見つかると、Red Hat Customer Portalを見て、その脆弱性への対応版がリリースされていないかよく確認していたものだ。

　「ああ、それなんですがね。実はOSはRHEL7にすることになったんですよ。」

　「はっ？」

　岡島は思わず、口からそう漏らしてしまった。RHEL7だって？聞いていないぞ。

　「6から7へのアップグレードができるということでしたのでね、この際そうしてしまおうかと。」

　「いえいえいえ、そうはいってもですね。既存のアプリケーションなどの互換性もありますし、それにこちらとしてもですね、RHEL6という前提でしたので検証などもそれに合わせた環境をですね」

　今更OSの変更など考えられない。それにこのアプリは別のカスタマイズ版が他の顧客のRHEL6と、自社のCentOS6いう環境では動作実績があるが、それ以外では実績はないのだ。

　「既存の他のアプリに関しては別のサーバーに移動するので問題ありません。7にする方はそちらのアプリ専用になります。nginxはEPELのモノでいいでしょう。」

　「ですが…」

　「7にすることは決定事項ですので。そうですね、事前にお伝えしてなかったのは申し訳ありませんでした。ですので、この打ち合わせはまた来週にしましょう。それまでにRHEL7+nginxの構成の検証をお願いします。」

　食い下がろうと言葉を発しようとしたとき、同席していた岡島の上長である田村課長と目が合う。その目は、鋭くにらみを利かせ、「口答えをするな」と言っていた。

　「…わかりました。ではお時間をいただけるということで、その間に検証をいたします。」

　たったの１０分で終わる会議のために俺は動作検証をし、資料を作り、客先まで出向いてきたのか。なんで事前に、もっと早く変更について伝えてくれなかったのか。岡島は怒りを覚えそうになったが「こんなこといつものことじゃないか。これまでにもっと理不尽な扱いを受けたことだってあったじゃないか。」と固く拳を握り、感情を抑え資料を片付けて挨拶をし、退出しようとした。

　「では、本日はどうもありがとうございました。また来週よろしくお願いします。」

　だがしかし最後にかけられたのは「こちらこそよろしく」的な社交辞令ではなく、

　「ああ、それとPHPは5.6にしますので。今更5.3を使うなんてねえ。」

　時代遅れの技術者を小馬鹿にするような、構成変更を伝える言葉だった。
* * *
※この物語はフィクションであり実在の人物・団体とは一切関係ありません。また、技術的に正しくない表現が存在します。
Red Hat® Enterprise Linux®は Red Hat, Inc. の製品です。製品またはサポートの正確な内容については Red Hat, Inc.にお問い合わせください。

## 補足
上の文章を読んで、何が問題で最終的な目的が何かよくわからなかった方のために(そんな方はいないとは思いますが)補足します。

**CentOS 7 + nginx + phpenvでphp-fpmのバージョンを5.3と5.6で切り替えて動作するようにします。**

CentOS 6 + Apache2.2 + PHP5.3(Apacheモジュール版)の環境とそこで動作するPHPアプリがあったのですが、新たにCentOS 7 + nginx + PHP5.6(FastCGI)の環境で動作させる必要が出てきたので、開発環境はせめてPHPのバージョン間での互換性を確認できるようにしとこう、つまりphp-fpmのバージョンを切り替えられるようにしてみよう、という感じです。

もちろん現行環境にてApacheモジュールを切り替えてもよかったのですが、それは過去にやったこともあるし、そろそろCentOS 7とnginxの時代だろうということで新たに構築してみました。

# 実行した手順
序盤の方は、Qiitaにもすでに様々なすばらしい記事があるので割愛してもよかったのですが自身の忘備録も兼ねて全部書いておきます。
[CentOS 7のDVD ISO](http://isoredirect.centos.org/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-1511.iso)にて最小構成でインストールした状態からを想定しています。

## 1. yumで必要なものをインストール
### 1.1. EPELリポジトリを追加
後の手順でEPELにあるパッケージが必要になるので、登録しときます。
```
$ sudo yum install epel-release
```
デフォルトでenableになっているので、この手順ではそのままで。

### 1.2. nginxインストール
EPELから普通にインストール。
```
$ sudo yum install nginx
```

### 1.3. gitインストール
最近はこれが無いと何もできない状態ですね。
```
$ sudo yum install git
```
リポジトリにあるgitのバージョンが気に入らない方はソースからビルドしましょう。

## 2. phpenvの導入
### 2.1. phpenv用にユーザー作成
phpenvでバージョンを切り替える用のユーザーを作っときます。
このユーザーのホームディレクトリ下にphpenvをインストールして全ユーザーがphp関連のコマンドを使えるようにするので、ホームディレクトリは他ユーザーがアクセス可能なようにしときます。
```
$ sudo useradd phper
$ sudo chmod 755 /home/phper
```

### 2.2. phpenvをインストール
[phpenvはこちら](https://github.com/CHH/phpenv)。`phpenv-install.sh`を実行すればOKです。ここではcloneしてから実行してみます。

```
$ sudo su - phper
$ git clone https://github.com/CHH/phpenv.git
$ phpenv/bin/phpenv-install.sh
$ cat <<'EOF' >>.bashrc
> export PATH="/home/phper/.phpenv/bin:$PATH"
> eval "$(phpenv init -)"
> EOF
$ source .bashrc
```

### 2.3. php-buildもインストール&設定
[php-buildはこちら](https://github.com/php-build/php-build)。これがないと`pepenv install`出来ません。
```
$ git clone https://github.com/php-build/php-build.git ~/.phpenv/plugins/php-build
```
そして、PHPのビルドオプションを追加しときます。
```
$ echo '--with-fpm-systemd' >> ~/.phpenv/plugins/php-build/share/php-build/default_configure_options
```
正直これが無くても大丈夫なような気がしますし(PHP5.3だとこのビルドオプションは無いような…)、systemdの仕組みもよくわかってない(サービスは起動時に開始したよシグナルをどこかに送らないといけない感じ？)のですが、指定してみて今のところ動いてるのでつけてみます。

php-fpmを有効にする`--enable-fpm`はデフォルトでついてるので大丈夫です。他に必要なものや不要なものがあれば上記ファイル内のオプションを修正してください。

## 3. PHPの導入
### 3.1. 依存関係のインストール
おそらくここまでの流れとPHPのビルドオプションでは、以下のライブラリなどをインストールする必要があります:

|パッケージ|リポジトリ|
|:--|:--|
|autoconf|base|
|bison|base|
|bzip2|base|
|libcurl-devel|base|
|libjpeg-turbo-devel|base|
|libmcrypt-devel|epel|
|libpng-devel|updates|
|libtidy-devel|epel|
|libtool-ltdl-devel|updates|
|libxml2-devel|updates|
|libxslt-devel|base|
|openssl-devel|updates|
|re2c|epel|
|readline-devel|base|
|systemd-devel|updates|

yumでインストールできるユーザーで全部インストールしてください。

※autoconfはエラーメッセージが出なくてちょっとはまりました…。こちらの記事が非常に参考になりました:[phpenv+php-build環境によるphpバージョン管理~Mac（Yosemite）編~](http://qiita.com/omega999/items/c5b1c177331f8d342efd)

それと一番重要なコンパイラ、`gcc`もインストールします。
```
$ sudo yum install gcc
```

### 3.2. PHPをインストール
5.3と5.6の最新のものをビルド&インストールしときます。結構時間がかかります。

```
$ sudo su - phper
$ phpenv install 5.3.29
$ phpenv install 5.6.27
```

### 3.3. php.iniとphp-fpm.confを修正
それぞれのバージョンの設定ファイルを修正しておきます。
`~/.phpenv/versions/<バージョン>/etc/`下にphp.iniとphp-fpm.conf.defaultがあります。

php.iniはデフォルトでもたぶん問題ないですが、必要に応じて修正してください。
php-fpmの方はやや修正します。.defaultをコピーして以下のようにしてみました:

```
$ cd ~/.phpenv/versions/<バージョン>/etc/
$ cp php-fpm.conf.default php-fpm.conf
```

```:php-fpm.conf
[global]
 :
pid = /run/php-fpm.pid
 :
error_log = /var/log/php-fpm.log
 :
[www]
 :
user = nginx
group = nginx
 :
listen = /run/php-fpm.sock
 :
listen.owner = nginx
listen.group = nginx
 :
```
PIDファイルとログの場所を変更し、nginxとソケットファイルでやり取りするようにしてます。
PHPの両バージョンとも同じ設定です。

### 3.4. 各phpコマンドへのリンク作成

まず各バージョンで`.phpenv/versions/<バージョン>/bin/`下に`.phpenv/versions/<バージョン>/sbin/php-fpm`へのシンボリックリンクを作成します。

```
$ cd ~/.phpenv/versions/<バージョン>/bin
$ ln -s ../sbin/php-fpm php-fpm
```

phpenvは`rehash`すると、.phpenv/versions/<バージョン>/bin/以下のファイルと同名の実行スクリプトを`.phpenv/shims/`に作成してくれます。このスクリプトの中で現在選択されているバージョンのbin/ディレクトリにあるコマンドを実行しているという仕組みです。なので、`php-fpm`もbin/以下に配置してバージョンごとに切り替わるようにしているわけです。そして、

```
$ phpenv rehash
```
とすれば、以下のように`.phpenv/shims/`に`php-fpm`が作成されているはずです。

```
$ ls -l ~/.phpenv/shims/
total 28
-rwxrwxr-x. 1 phper phper 399 Nov  7 12:08 phar
-rwxrwxr-x. 1 phper phper 399 Nov  7 12:08 phar.phar
-rwxrwxr-x. 1 phper phper 399 Nov  7 12:08 php
-rwxrwxr-x. 1 phper phper 399 Nov  7 12:18 php-cgi
-rwxrwxr-x. 1 phper phper 399 Nov  7 12:08 php-config
-rwxrwxr-x. 1 phper phper 399 Nov  7 13:45 php-fpm
-rwxrwxr-x. 1 phper phper 399 Nov  7 12:08 phpize

```

さらにこのまま`.phpsenv/shims/`にあるだけだと他のユーザーからはパスが通ってなく、使いづらいので誰でも使えるように各コマンドにシンボリックリンク作成しておきます。ここでは`/usr/local/bin`と`/sbin`以下にシンボリックリンクを作成しました。sudo可能なユーザーで実行してください。

```
$ cd /usr/local/bin
$ sudo ln -s /home/phper/.phpenv/shims/php php
$ sudo ln -s /home/phper/.phpenv/shims/phar phar
$ sudo ln -s /home/phper/.phpenv/shims/php-config php-config
$ sudo ln -s /home/phper/.phpenv/shims/phpize phpize
$ cd /sbin
$ sudo ln -s /home/phper/.phpenv/shims/php-fpm php-fpm
```

## 4. systemdなどの設定
### 4.1. php-fpmサービスの登録
systemdについてあまり詳しくないので、いい加減ですが…。既存のサービスや以下のサンプルを参考にして`.service`ファイルを作成しました。
https://gist.github.com/magnetikonline/c1a214e12cdbc65df7c620bbbc3af6e8

```bash:/etc/systemd/system/php-fpm.service
[Unit]
Description=PHP FastCGI Process Manager
After=network.target nginx.service

[Service]
Type=forking
PIDFile=/run/php-fpm.pid
ExecStartPre=/usr/bin/rm -f /run/php-fpm.pid
ExecStart=/sbin/php-fpm -D
#ExecReload=/bin/kill -s HUP $MAINPID
#KillSignal=SIGQUIT
#TimeoutStopSec=5
#KillMode=control-group
#PrivateTmp=true

[Install]
WantedBy=multi-user.target
```
コメントアウトの部分はもちろんなくて大丈夫です。この辺りでsystemdに対応していないデーモンとかは開始/終了シグナルのやりとりはどうなるんだろうみたいな疑問はあります。
(何かの時に、「サービスstartしたけど、開始シグナルがねえ！killする！」って言われてstart直後に停止してしまうという状態になりました…)

### 4.2. nginx設定
nginxがphp-fpmを使うように設定します。php-fpm側でソケットファイルを使うように設定したので`fastcgi_pass`にはそのファイルを指定します。

```bash:/etc/nginx/conf.d/mysite.conf
server {
  listen 80;
  server_name <アクセスされるときのFQDNまたはIP>;
  root /opt/mysite;
  index index.php;

  location / {
    try_files $uri $uri/ /index.php?$args;
  }

  location ~ \.php$ {
    try_files $uri =404;
    include /etc/nginx/fastcgi_params;
    fastcgi_pass unix:/run/php-fpm.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  }
}
```

そして動作確認用のサンプルサイトとして、ここではindex.phpでphpinfo()でも出力させてみることにします。

```
$ cd /opt
$ sudo mkdir mysite
$ echo '<?php phpinfo();' | sudo tee mysite/index.php
```

## 5. サービス有効化と起動
### 5.1. 必要なサービスを起動
設定は完了したので、`php-fpm`サービスを動作させてみます。
まずはphpenv用ユーザーでバージョンを指定します。

```
$ sudo su - phper
$ phpenv global 5.3.29
```

いろいろめんどくさいのでとりあえずファイアウォールとSELinuxをOFFにしておきます。

```
$ sudo systemctl stop firewalld
$ sudo setenforce 0
```

そして、sudo可能なユーザーにてstartします。

```
$ sudo systemctl enable nginx
$ sudo systemctl start nginx
$ sudo systemctl enable php-fpm
$ sudo systemctl start php-fpm
```

psで確認してみてプロセスが動作していれば大丈夫です。

```
$ ps -ef | grep php-fpm
root     19943     1  0 14:21 ?        00:00:00 php-fpm: master process (/home/phper/.phpenv/versions/5.3.29/etc/php-fpm.conf)
nginx    19944 19943  0 14:21 ?        00:00:00 php-fpm: pool www
nginx    19945 19943  0 14:21 ?        00:00:00 php-fpm: pool www
```

### 5.2. 動作確認
ブラウザでアクセスしてみます。
![phpinfo5.3.png](https://qiita-image-store.s3.amazonaws.com/0/137783/1d1d6082-465a-355d-b8af-91cef7fe0da4.png)

FastCGIで5.3が動作してますね。では切り替えてみましょう。
まず現在のサービスを停止します。

```
$ sudo systemctl stop php-fpm
```

phpenv用ユーザーで切り替えます。

```
$ sudo su - phper
$ phpenv global 5.6.27
```

sudoユーザーで再びサービスを起動します。

```
$ sudo systemctl start php-fpm
```

再びブラウザでアクセスすると…
![phpinfo5.6.png](https://qiita-image-store.s3.amazonaws.com/0/137783/389cb1b7-62c5-b43a-362a-91bab79daa79.png)

見事に指定したバージョンになっていますね！

# 終わりに
手順上では5.3と5.6でしたがPHP 7など、もっといろいろなバージョンを動作させることもできるはずです。

systemdについてはまだ勉強中(というかサラっと調べただけ)なので不適切な設定などがあるかもしれません。ご了承ください。

冒頭の駄文については、ただ手順を書くのもつまらなかったので入れました。フィクションです。実際のところApacheとnginxのシェアは今どうなんでしょうかね？もちろん異なる役割で両方つかっているケースもあると思いますが。またRHEL6からRHEL7(CentOS6からCentOS7)への移行状況も気になります。

正直私は今のところはsystemdの必要性や利便性がまったくわかりません。ですが、CentOS 6でPHP 5.3を使うのはもううんざりです。