この記事は、モバイルファクトリー Advent Calendar 2015 11日目の記事です
※ 投稿内容は私個人の意見であり、所属企業・部門見解ならびに技術戦略を代表するものではありません。
昨日は@rymizukiさんのnpmライブラリの運用と管理についてでした。今日はPerlの話です。
お仕事やプライベートでPerlのwebアプリケーションを書くことが多く、いろいろ知見が溜まってきてるので、ここで少し紹介しようと思います。 今回はPlack/PSGIなwebアプリケーションの実行環境の話です。mod_perlなアプリケーションとはちょっとコンテキストが違います。 少しかっちりコンテキストに近いです。個人で軽くwebアプリケーション立てるならもう少しゆるふわでも問題ないはずです。
OS
UbuntuのLTSを使うことが多いです。Ubuntu前提の内容が後に続きます。
Perl
System Perlは使ってません。OS/ディストリビューションが変わってもなるべくそのまま動くようにしたいためです。
perl-buildで独自ビルドしたPerlを使います。インストール場所としては、 /usr/local/perl/perl-5.${VERSION}
に置きます。
Perlを独自ビルドしたものをDebian package化して実行環境にはインストールします。
他の方法としては、ビルド済みのperlをtarで固めて、配布するというのもあります。
どちらでも構わないのですが、ローカルネットワークにaptサーバ立てている関係で、Debian packageの方が運用しやすいのです。
また、perlのマイナーバージョンアップの際もDebian packageを作り直した上で、 apt-get upgrade
(or aptitude safe-upgrade
)で完結するので、aptの操作に慣れていて楽というのもあります。
モジュール管理
今風にcpanfileでモジュール管理してます。モジュールインストールはCartonを使ってます。
Cartonの後継でCarmelも開発されてます。個人的にはそろそろ触っておきたいところです。
また、cpanfile.snapshotもレポジトリに入れています。
一般的なモジュールは特定の(古い)バージョンに依存せずに動くべきですが、
依存モジュールのバージョン違いによって現在動いているアプリケーションが壊れるのを防ぐために、バージョン固定します。
cpanfile.snapshotがある状態で下記のように carton install
してあげると、どの環境でも同じバージョンの
モジュールがインストールされます。
carton install --deployment --without develop,test
今やってないですが、別方法としては、モジュールがインストール済みの状態で、 carton bundle
すると vendar/
にモジュールのtarが固められるので、それもレポジトリ管理した上で、下記の様にインストールするという手もあります。インストールの際は vendor/bin/carton
にfatpackされたcartonコマンドが入るのでそれを使います。
(アプリ実行環境にcartonを敢えて入れる必要は無い)
# 依存モジュールを固める carton bundle # インストール # env.shは後述 ./script/env.sh vendor/bin/carton install --cached --deployment --without develop,test
さらに別方法としては、ビルドサーバで依存モジュールをビルドした上で、ディレクトリごと実行環境にrsyncしてあげる方法です。 ビルドサーバを運用しているならば、この方法でも良いでしょう。
参照
独自モジュール
なるべく、独自モジュールは使わない方が良いのですが、個人的な事情などで、CPANに公開出来ないモジュールに関しては、OrePAN2 でDarkpanを作ってそこからローカルに配信するようにしてます。 OrePAN2のサーバを簡単に立ち上げられるOrePAN2::Serverがありますが、一時期は使っていましたが、モジュールのアップロード機能は別にいらないなどの理由で今はwebサーバから静的配信してます。
環境変数
プロジェクトのレポジトリに config/env.rc
という名前で、アプリケーションを動かすために必要な環境変数を定義したファイルを作ります。
PERL5_VERSION="22" export PROJECT_BASE="/path/to/project" export PERL_CARTON_MIRROR="http://orepan.local/" export PERL5LIB="${PROJECT_BASE}/local/lib/perl5:${PROJECT_BASE}/lib" export PATH="${PROJECT_BASE}/local/bin:/usr/local/perl/perl-5.${PERL5_VERSION}/bin:${PATH}" export PLACK_PORT=5555
また、 script/env.sh
という名前で config/env.rc
を読み込んだ上で、プログラムを実行するラッパースクリプトを作ります。
スクリプトなどは基本的にこれを通して実行します。
#!/bin/bash -ue # 諸々環境変数を設定した上でコマンドを実行する君 # # env.sh perl hogehoge.pl # source /path/to/project/config/env.rc exec "$@"
開発環境で、いちいちラッパースクリプト通すのが面倒な場合は、config/env.rc
のsymlinkをプロジェクトルートに .envrc
として張った上で、direnv使って済ましてしまう場合もあります。
web サーバ起動スクリプト
psgiファイルを plackup
するのではなく、こんな感じのスクリプトをscript/web
みたいな名前で 用意してアプリケーションサーバを起動するようにしてます。
#!/usr/bin/env perl use strict; use warnings; use lib "$ENV{PROJECT_BASE}/lib"; use Plack::Loader; use SomeApplication::Config; use SomeApplication::Web::Handler; my $config = SomeApplication::Config->load(); my $app = SomeApplication::Web->to_app(); Plack::Loader->load( $config->{psgi}->{server}, %{ $config->{psgi}->{config} }, )->run($app);
また、このスクリプトをstart_serverを経由して起動することで、(graceful restartによる)ホットデプロイをできるようにしてます。
start_server
のプロセスにSIGHUPを送ると子プロセスのアプリケーションサーバを再起動してくれるのですが、 plackup
コマンドで起動してると start_server
に渡した引数をそのまま使ってplackup
を再起動するので、 max_workers
の数を変えたいときなど、 start_server
自体のプロセスを再起動しなくてはならないので不便です。
なので、起動スクリプトを作ってます。そのほかにも理由があるのですが、参照リンクに詳しくあります。
サーバ実装としては、StarletやGazelleを使ってます。
参照
デーモン管理
現在はUpstartでアプリケーションサーバのデーモン管理してます。 以下の理由で、個人的には好きでした(過去形)。最新のUbuntuはSystemdに変わってしまったので、将来的にはSystemdに移行することになるでしょう。
/etc/init/web-some-application.conf
みたいな名前でこんな設定ファイルを作ります
description 'some web application' author 'masasuzu <hogehoge@masasuzu.net>' start on runlevel [2345] stop on starting rc RUNLEVEL=[016] setuid webapp setgid webapp # 異常時に再起動する respawn script . /path/to/project/config/env.rc export PLACK_ENV="production" exec ${PROJECT_BASE}/local/bin/start_server \ --interval 10 \ --port ${PLACK_PORT} \ -- ${PROJECT_BASE}/script/service/web end script
上記のファイルを作ると以下のように操作出来ます。reloadでSIGHUPが送れるので、アプリケーションサーバのstart_server経由のgraceful restartができます。
# 起動 service web-some-application start # 停止 service web-some-application stop # (start_serverのプロセスごと)再起動 service web-some-application restart # Plackサーバを再起動 service web-some-application reload
アプリケーションサーバ以外も、ジョブのワーカーなども、独自に設定ファイルを作って、Upstart経由で起動したりしてます。
Upstart以外の選択肢としては、先に挙げたSystemdの他、以下のものがあるでしょう。 好みと要件に合わせて使えば良いと思います。
参照
- Server::Starterから学ぶhot deployの仕組み
- Server::Starter の --interval オプションは大切
- Upstart を使ってお手軽 daemon 化
- Upstart Intro, Cookbook and Best Practises
おわりに
WAF(Web Application Framework)やログの話など膨らまそうと思えばもっと膨らませられますが、実行環境の話なので、ここまでで抑えておきます。
ざっくりと、Plack/PSGIなアプリケーションの実行環境について説明してきました。 PerlでWebアプリケーションを作る時に何か参考になれば幸いです。 また、もっと良い方法があれば、教えていただけるとありがたいです。
明日は、@nekobato さんです webpackのなにか面白い話があるんじゃないかとわくどきしてます。