uzullaがブログ

uzullaがブログです。

俺がやってみたかったh2oとphpの構成について

やっと暇ができたのでやってみました。

ところで、これをやりながらMicrosoftの発表見ていたんですが、マジですごい。Surface bookとかLumiaとか実際にモノを見てみないとなんともいえないけど、プレゼンだけでいえば完全にApple越えたプレゼンだった…。なんていうか、久々に発表で盛り上がった!

さておき

PHPのサーバー構築をシュッとやるのが個人的には結構面倒*1。理由はいくつかあるけれど

  • 「他言語の感覚で」ウェブアプリを複数個、同一のサーバー(というより、バーチャルドメイン)で動かす時、キメラみたいなhtdocsを作るハメになるか魔法みたいなconfを書くハメになる。
  • nginx+N個のApachePHPをたてれば解決する場合もあるが 、apacheは複数飼うのは面倒。
  • phpのバージョンを上げたり下げたり共存させたりが面倒(わかりきった事だが)、Apacheだとさらに面倒(apxsめんどい)
  • やっぱりhttpdが欲しい、fastcgiだけではURLのbaseが変わるようなrewriteは設定面倒
  • とにかくすっきり切り分けしたい!!

という感じ。

他の言語の世界では大体supervisordとかで起動したらhttpdがあがってきて上にnginxをかぶせる感じであり、PHPもそういう風にしたい!!!と思いつつ幾数年。

そこで気になっていたのがh2o+php-cgi、h2oなら軽量に、簡単にphpを管理できるのでは…?

PHPカンファレンスもおわったし(まだブログ書いて無い)やっと試した。以下はその手順である。

こういう構成はどうか?ということである

プロセスの親子関係的には

(systemd) -> supervisord -> ( H2O -> PHP(php-cgi) )

外から見たときはphpをh2oでwrapすることで、php自体がhttpdをもってるような見え方にしたい。

ファイルは、基本的に/optの下に全部がはいっている。

/opt
  /php PHPの一切合切
  /h2o h2oの一切合切
  /logs
  /sites/_/htdocs (まあどこでもいい)

手順

※ これはUbuntu 15での例です。

apt-get -y install libcurl4-openssl-dev libmcrypt-dev libreadline-dev libtidy-dev cmake git build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev openssl libssl-dev libxml2-dev libgd-dev libxslt-dev libxml2-dev libgd-dev 

mkdir -p /opt/src
mkdir -p /opt/logs
chmod 777 /opt/logs
mkdir -p /opt/site/_/htdocs
echo "<?php phpinfo();" > /opt/site/_/htdocs/index.php

# h2o のビルド
cd /opt/src/
wget https://github.com/h2o/h2o/archive/v1.5.0.tar.gz
tar xvzf v1.5.0.tar.gz
cd h2o-1.5.0/
cmake -DWITH_BUNDLED_SSL=on -DCMAKE_INSTALL_PREFIX=/opt/h2o .
make
make install

# phpのビルド
cd /opt/src
wget https://downloads.php.net/~ab/php-7.0.0RC4.tar.gz
tar xvzf php-7.0.0RC4.tar.gz
cd php-7.0.0RC4/
./configure --prefix=/opt/php/ --with-gd --enable-sockets --with-jpeg-dir=/usr --with-png-dir=/usr --enable-exif --enable-zip --with-zlib --with-zlib-dir=/usr --with-kerberos --with-openssl --with-mcrypt=/usr --enable-soap --enable-xmlreader --with-xsl --enable-ftp --enable-cgi --with-curl=/usr --with-tidy --with-xmlrpc --enable-sysvsem --enable-sysvshm --enable-shmop --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-pdo-sqlite --enable-pcntl --with-readline --enable-mbstring --disable-debug --enable-fpm --enable-bcmath
make
make install

vi /opt/h2o/h2o.conf

pid-file: /opt/logs/h2o.pid
access-log: /opt/logs/access.log
error-log: /opt/logs/error.log
user: nobody

listen: 80

file.custom-handler:
  extension: .php
  fastcgi.spawn: "PHP_FCGI_CHILDREN=32 exec /opt/php/bin/php-cgi"

hosts:
  "localhost:80":
    paths:
     /:
        file.dir: /opt/site/_/htdocs
        redirect:
          url: /index.php
          internal: YES
          status: 307

ここで、とりあえず起動できるかテスト。以下コマンドでh2oを起こして、ブラウザで開いてphpinfoがでてくればOK。

/opt/h2o/bin/h2o -c /opt/h2o/h2o.conf

問題なければsupervisordでデーモン化。

apt-get -y install supervisor

vi /etc/supervisor/conf.d/h2o.conf

[program:h2o]
command=/opt/h2o/bin/h2o -c /opt/h2o/h2o.conf
autorestart=true
service supervisor restart

完了。

個人的にはスーパーシンプルである。

プロセスはこんな感じになる

        ├─supervisord───h2o─┬─perl───php-cgi───10*[php-cgi]
        │                   ├─perl
        │                   └─2*[{h2o}]

まとめ

  • アプリの数だけh2o.confとsupervisordの設定を書けば、複数のPHPアプリのサーバー*2を他の言語っぽく管理できそう
  • php/opt/phpに全部固まっているので見通し安いし、差し替えやすい*3php-buildとかの相性良いと思う。
  • つまり、タレ化(自動化w)しやすい
  • H2Oをつかうと、php-fpmをつかわないでもphpプロセスが管理できるので、実質h2oだけ相手すればいい
  • supervisordでh2oを起動しているので、initやupstartやsystemdの混乱に入らずに済む

実運用では、さらにこの上にnginxをかぶせる感じを想定している。

「h2oで配信すればHTTP/2でいいじゃん?持ち腐れでは?」というのはそうなのだが、実運用ではやはりもうちょっと各部に柔軟性がほしい*4

課題と展望

  • ログをどうやって管理すっかなー…?
  • 環境変数とかでPHPバイナリやコンフィグをシュッっと切り換えたりしてみたい。もっとphpを単なるプロセスとしてあつかいたい。
  • まだ本番ではこの構成で運用してみてない、今度やってみる予定。
  • 今気付いているのが、h2oのfastcgi.spawnはbacklogが少ない(?)ので、PHP_FCGI_CHILDREN(PHPプロセスの数)を少なくすると、100とか200とか同時接続を増やしたときに、ぼちぼちエラーがでる。雑にPHP_FCGI_CHILDRENをふやしたら解決したが、どこのところをいじったら待ち行列が伸ばせるのか?
  • (とはいえ、私がやるようなメールフォーム案件とかで同時接続数100とか滅多に無いし、並べればいいので問題にはならない)

こちらからは以上です、ご意見お待ちしております。

*1:そらまあ、yumでいれたらシュッですけど

*2:ウケる、PHPには無い概念だ

*3:勿論このディレクトリ構成は例なんだけど、一個にはいってるってのが重要でしょ?

*4:私のh2oの慣れ不足でもある…mrubyに期待したさもある