CentOS 7.1 (1503)にHHVM 3.9をインストールしてApache 2.4でsuEXECのように動かす

2015年10月14日(水)

HHVMなるものを今更知ったのですが、速いとのことで、おもしろそうなので入れてみることにします。
タイミングのいいことに、HHVM3.9がリリースされたばかりです。
WordPressは動作するっぽいです。
このところCentOS 7漬けです。

Ubuntuはapt-getでインストールできるようですが、残念ながらCentOS 7パッケージの提供はありません。

“CentOS 7 HHVM”でググっても、謎のレポジトリのパッケージからインストールしていたり、そもそもCentOS 6用のパッケージをインストールしていたりする内容が出てくるのですが、後々トラブルが出そうでちょっと同じようには行いたくない感じです。

パッケージがない以上ビルドするしかないわけですが、日本語でまとめてあるのは下記のサイトぐらいでした。
http://y-uti.hatenablog.jp/entry/2014/07/11/060126
2014年の記事というかCentOS 7が出たばかりの頃の記事なので、ビルドまでの道のりが少しと思ってもう少し調べたら本家に書いてありました…。
CentOS 7が出てから1年も経ってますからね。

https://github.com/facebook/hhvm/wiki/Building-and-installing-hhvm-on-CentOS-7.x

この手順をベースに進めますが、実際に試すと書いてあることだけではうまくいきませんでした。
断片的な情報を集めて試行錯誤しましたが、最終的にはうまくいきました。

今回もsuEXECもどきです。Apacheと組み合わせてHHVMをユーザー権限で動作させます。

まずは、ビルドに必要なパッケージをインストールします。
EPELが必要です。

今更ですが、EPELは”yum install epel-release”で、インストールできます。

# yum install cpp gcc-c++ cmake git psmisc {binutils,boost,jemalloc}-devel \
{ImageMagick,sqlite,tbb,bzip2,openldap,readline,elfutils-libelf,gmp,lz4,pcre}-devel \
lib{xslt,event,yaml,vpx,png,zip,icu,mcrypt,memcached,cap,dwarf}-devel \
{unixODBC,expat,mariadb}-devel lib{edit,curl,xml2,xslt}-devel \
glog-devel oniguruma-devel ocaml gperf enca libjpeg-turbo-devel openssl-devel \
make --enablerepo=epel

実際にEPELからインストールされるのは、
enca
glog-devel
jemalloc-devel
libmcrypt-devel
lz4-devel
oniguruma-devel
だけでした。

“–enablerepo=epel”を付け忘れたら、
# yum install jemalloc-devel lz4-devel libmcrypt-devel glog-devel oniguruma-devel enca –enablerepo=epel
します。別に書かなくてもいいですね。

ccacheがインストールされていると自動で使ってくれるようなので、ccacheもインストールします。
ビルドし直すときに効果を発揮します。できればmakeは一度で終わりたいです。
# yum install ccache –enablerepo=epel

ソース用のディレクトリを作成します。
# mkdir ~/dev
# cd ~/dev

gitからソースコードを入手します。最新はこれです。
# git clone https://github.com/facebook/hhvm -b master hhvm –recursive

今回はHHVM-3.9.1を指定してみました。(※2015/10/14 HHVM 3.9.1に変更しました。)
# git clone https://github.com/facebook/hhvm -b HHVM-3.9.1 hhvm –recursive
かなり大きいので時間がかかります。

hhvmディレクトリに移動します。
# cd hhvm

※追記
そのままビルドすると、UNIX domain socketで動作させた場合に、socketのアクセス権が760なってしまいユーザー権限で動かす際に不都合があるのでソースを一カ所修正します。
# vi hphp/runtime/server/fastcgi/fastcgi-server.cpp

if (m_socketConfig.bindAddress.getFamily() == AF_UNIX) {
auto path = m_socketConfig.bindAddress.getPath();
chmod(path.c_str(), 0760);
}
↓113行目あたりにある0760を0766に変更します。
if (m_socketConfig.bindAddress.getFamily() == AF_UNIX) {
auto path = m_socketConfig.bindAddress.getPath();
chmod(path.c_str(), 0766);
}

この部分の変更でリビルドしましたが、2分くらいで終わりました。
入れててよかったccache。
※追記ここまで

自分でビルドしたものは標準のディレクトリに入れるとトラブル時に面倒なので、cmake実行時にprefixを指定します。
/opt/hhvmにインストールします。
こうすれば、問題が出たり要らなくなったときにさくっと消すことができます。
“-march=native”も指定してみました。

cmake \
    -DCMAKE_INSTALL_PREFIX:PATH=/opt/hhvm \
    -DCMAKE_C_FLAGS="-march=native" \
    -DCMAKE_CXX_FLAGS="-march=native" \
    .

「make with CORE_COUNT+1 threads, that would be fast. You may run out of RAM」ということで、
# make -j$(($(nproc)+1))
でmakeします。
さくらのVPS 1Gだとメモリも少ないですし、ただのmakeでもいいと思います。

環境次第ですが1~2時間くらいかかります。

Embedding php in hhvm
Creating symlinks for hhvm
[100%] Built target hhvm

と表示されたら完了です。

テストします。
# ./hphp/hhvm/hhvm –version

HipHop VM 3.9.1 (rel)
Compiler: tags/HHVM-3.9.1-0-g0f72cfc2f0a01fdfeb72fbcfeb247b72998a66db
Repo schema: 41db3511d4f69e5584304f64434cb13aa602e1ee

上記のようにバージョンが表示されます。(※2015/10/14 HHVM 3.9.1に変更しました。)

インストールします。
# make install

ビルドディレクトリを抜けます。
# cd ~

HHVMのサービスを追加します。
# vi /etc/systemd/system/hhvm.service

[Unit]
Description=HHVM HipHop Virtual Machine (FCGI)
After=syslog.target network.target

[Service]
ExecStartPre=-/usr/bin/mkdir -p /var/run/hhvm
ExecStartPre=-/usr/bin/chmod 777 /var/run/hhvm
ExecStart=/opt/hhvm/bin/hhvm --config /etc/hhvm/server.ini --mode daemon

ExecStartPost=-/usr/bin/sleep 2s
ExecStartPost=-/usr/bin/chmod 766 /var/run/hhvm/hhvm.sock

Restart=on-failure

[Install]
WantedBy=multi-user.target
DirectoryIndex

socketが標準だと760のアクセス権で作成されます。
PHP-FPMの時は666なので問題なかったのですが、760だとHHVMをユーザー権限で動かすことができません。
ExecStartPostでHHVMの起動後にアクセス権を変更しています。
すぐにchmodを行うとファイルがないと言われるので、sleepを入れています。
※追記:ExecStartPostでchmodを行う方法は、負荷が高いときや起動時にsleep 2sを入れても安定しない場合があったのでので、ソースを書き換えることにしました。

ExecStartであまりオプションを指定していませんが、ここでは最低限のオプションを指定し、設定はserver.iniファイルに代わりに書きます。

設定ファイル用のディレクトリを作成します。
# mkdir /etc/hhvm

設定ファイルを作成します。
# vi /etc/hhvm/server.ini

hhvm.server.user = apache

hhvm.server.type = fastcgi
hhvm.server.file_socket = /var/run/hhvm/hhvm.sock
hhvm.repo.central.path = /var/run/hhvm/hhvm.hhbc
pid = /var/run/hhvm/pid

hhvm.log.header = true
hhvm.log.use_log_file = true
hhvm.log.file = /var/log/hhvm/error.log

hhvm.server.fix_path_info = true

date.timezone = "Asia/Tokyo"
session.cookie_httponly = 1
session.hash_function = 1

session.save_handler=memcache
session.save_path="tcp://localhost:11211?persistent=1&weight=1&timeout=1&retry_interval=15"

server.iniはhhvmの設定と、php.iniが合体したような感じです。
memcachedも使用するようにしました。設定値はmemcacheです。

試行錯誤した結果、”hhvm.server.fix_path_info = true“を指定するのがポイントでした。
これがあると、Apache側では

<FilesMatch "\.php$">
    SetHandler "proxy:unix:/var/run/hhvm/hhvm.sock|fcgi://localhost/"
</FilesMatch>

でphpをHHVMで動かすことができます。
このオプションがないとphpを動かすとパスが変になって、PHP-FPMのようにFilesMatchとSetHandlerでfcgi proxyを使うことができません。
“hhvm.server.fix_path_info = true”なしの場合はProxyPassMatchで動かすことになりますが、ProxyPassMatchはDirectoryIndexが効かなかったりするのでおすすめできません。

ログのディレクトリとファイルを作成します。
# mkdir /var/log/hhvm
# touch /var/log/hhvm/error.log
# chown apache:apache /var/log/hhvm/error.log

サービスを起動します。
# systemctl start hhvm
# systemctl enable hhvm

例としてapache権限で動かす設定を記載しましたが、apache以外のユーザーで動かすことも可能なので、suEXECもどきとしても使えます。

例えば、
サービスと設定ファイルを
/etc/systemd/system/hhvm-user.service
/etc/hhvm/server-user.ini
みたいにして、

server-user.iniで

hhvm.server.user = user

hhvm.server.file_socket = /var/run/hhvm/hhvm-user.sock
hhvm.repo.central.path = /var/run/hhvm/hhvm-user.hhbc
pid = /var/run/hhvm/pid-user

hhvm.log.file = /var/log/hhvm/user.log

という感じにして、

ApacheではVirturalHostなどで、

<FilesMatch "\.php$">
    SetHandler "proxy:unix:/var/run/hhvm/hhvm-user.sock|fcgi://localhost/"
</FilesMatch>

とすれば、HHVMの実行ユーザーを変えることができます。

PHP-FPMで設定したときはPHP-FPMのサービスは一つでしたが、だいたい同じですね。