平常運転

アニソンが好き

過去記事とかは記事一覧で見れます

Herokuとwerckerによる継続的インテグレーション・自動デプロイでperlのwebアプリケーションを開発する

この記事ははてなエンジニアアドベントカレンダー2014の17日目です。昨日は id:cockscomb による
Swiftでenumとジェネリクスを活用したかっこいいAPIクライアントを書く でした。
このエントリでは、CIツールのwerckerとアプリケーションプラットフォームのherokuを組み合わせることでperlのwebアプリケーションのCIを行い、自動デプロイする環境を実現する方法について述べます。

動機

今回のエントリで述べる自動デプロイの流れについては、はてなで11月に行った開発合宿のために筆者、および筆者の開発チームで必要になったために構築されました。
id:onishi によるサービス開発合宿のエントリにもあったように、今回の合宿では合宿先から社内へのVPNが整備されていないことなどから、社内の開発用サーバに依存しないしない各種PaaSを用いた環境を構築することになりました。
また、自動デプロイ環境を整えることで、短い開発合宿期間中にサービスの開発に集中できるようにしたい、という狙いもありました。

これらの条件より、今回はプラットフォームとしてHerokuを選択し、GitHubのプライベートリポジトリからWebhookでデプロイするためのCI環境としてwerckerを使うことで自動デプロイが行われる環境を構築することにしました。
また、CIとしてテストを実行することは今回の開発合宿では必須の要件ではなかったのですが、自動デプロイのためにCIを使うなら折角、ということでwercker上でテストも実行することにしました。

今回目標としたのは以下の構成です。
f:id:astj:20141217123806p:plain

Heroku

今回、アプリケーションプラットフォームとしてはHerokuを選択しました。開発メンバー間で比較的馴染みがあり、実際にperlのwebアプリケーションを動作させたこともあったことから、ここはスムーズにHerokuに決まりました。
Herokuもデフォルトではperlをサポートしていませんが、perlによるアプリケーションを動作させるための各種buildpackが公開されているため、これらを用いることで、
.perl-versionによるperlのバージョン指定、cartonによるモジュールのバージョン固定などを行ったモダンなperl webアプリケーションを動作させることが可能になります。
buildpackについてはHerokuのドキュメントにも記載がありますが、今回はvdvu3k6k氏のQiitaのエントリを参考に、pnu氏によるbuildpackを使うことにしました。

Perl5のPSGIアプリ用buildpackのまとめ - Qiita

pnu/heroku-buildpack-perl · GitHub

wercker

werckerは"The open devlicery platform"と銘打っているCIサービスのひとつで、現在Betaとして公開されています。
werckerは標準ではRuby, Node.js, Pythonなど主要な言語でのビルドが可能となっており、(近年のプラットフォーム系サービスではよくあることですが...)perlは標準では非対応となっています。
しかしながら、werckerはビルドやデプロイの環境・各ステップをboxおよびstepという形で公開し、ユーザー間で共有することができることから、これらを上手く組み合わせることで、perlのwebアプリケーションのCIも行うことが可能となります。また、Herokuへのデプロイが標準でサポートされていることから、上記の目標フローを容易に実現できそうだったことも好材料でした。

CIサービスには他にもtravis-ciCodeshipなどを検討しましたが、werckerの選定に至ったのは主に以下の2点でした。

  • プライベートリポジトリからも無料で使うことができる
  • ビルド回数に制限が無い

werckerではCIのフェーズがbuildとdeployに分かれていて、werckerのコンソールから特定のリビジョンをデプロイすることができる他、特定のブランチに関してはビルド成功後に自動的にデプロイすることができます。
f:id:astj:20141217140004p:plain

今回はbuildフェーズでコードのテストを行い、deployフェーズで実際にherokuへのデプロイを行うことにしました。

werckerでperlのテストを行う

werckerでは、リポジトリごとのCI設定はリポジトリルート直下のwercker.ymlファイルに記述します。このファイル内に、ビルドを実行する環境であるboxの指定や、具体的なビルドの各処理であるstepの指定を記述していきます。

wercker devcenter

werckerに標準でサポートされていないperlの場合、perl実行環境のセットアップや実際のテストコマンドなどをwercker.ymlに記載していく必要があります。
werckerの各stepはwercker組み込みのstepの他に任意のコマンドをstepとして実行させることができるため、以下のように必要なコマンドを記述していくことになります。

box: wercker/default

build:
    steps:
        - someauthor/somestep
        - script:
            name: prove
            code: carton exec -- prove -lvr t/
...

また、前述したようにwerckerのboxやstepはコミュニティベースで様々なものが公開されているので、werckerのサイトから検索を行うことができます。

"perl"で検索するといくつかのboxがヒットします。perl 5.18.2や5.20.1などの一部のバージョンについては、該当バージョンのperlがセットアップされたboxが公開されているので、それらのバージョンのperlを利用する場合は簡単にperl + cartonの揃った環境を構築することができます。

一方、用いるperlのバージョンをリポジトリ直下の.perl-versionファイルで管理する場合には、これらのboxをそのまま使うのではなく、plenvを用いることになります。
その場合のplenvおよびcartonのセットアップにはmoltar氏によるboxを使うこともできますが、今回は他のstepとの兼ね合いもあり、同じmoltar氏が提供しているplenvのstepを用いることにしました。

同氏が提供しているcarton installのstepと組み合わせて以下のようなwercker.ymlファイルをリポジトリに追加することで、t/以下に配置したテストスクリプトを任意のバージョンのperlとcartonを用いて実行できるようになります。

box: wercker/default

build:
    steps:
        - moltar/plenv
        - moltar/carton-install@0.3.0:
            without: develop
            cache_local: false # ./local/をキャッシュしない
        - script:
            name: prove
            code: carton exec -- prove -lvr t/

この構成の初回ビルドではperl-versionで指定したバージョンのperlがビルドされますが、ビルドされたperlはwerckerのキャッシュの中に保存されるため、2度目以降のビルドで再びperlのビルドが走ることはないため、perlのビルドによって処理時間が延びるということは特にありませんでした。

carton-install stepを用いたcarton Install

上述のcarton-installのオプションの中ではcache_local: falseを指定しています。
carton-installでは、一度carton installした際にモジュールがインストールされた./local/ディレクトリをキャッシュし、次回以降のビルドではそのキャッシュを利用することでcarton installを高速化するようになっています。これは一見大変便利なように思えますが、開発上の都合で使用しなくなったモジュールをcpanfileから削除してもキャッシュ内には残り続けるため、cpanfile.snapshotとwerckerでの実行環境に差異が生じることになります。実のところ今回の開発合宿ではこのオプションは有効にしたままで特に問題なかったのですが、無用なトラブルを回避するためにこのオプションは無効にしておいたほうがよいように思います。

werckerからHerokuへのデプロイ

前述したようにwerckerではHerokuへのデプロイが標準でサポートされているので、単にwerckerからHerokuへのデプロイするだけならば、werckerで提供されているstepの指示に従うだけで行うことができます。
あとはHerokuで適切なbuildpackを選択するだけですが、前述したpnu氏によるbuildpackであれば、.perl-versionさえ指定しておけば適切な環境がセットアップされます。今回はあくまで開発用の中央ホストとしての位置づけだったので、こちらはあまり細かいパラメータチューニングなどは行いませんでした。

carton-installperl buildpackの組み合わせ

基本的には上記で書いたとおり指示に従うだけでよいのですが、今回一点ハマったことがありました。
werckerによるbuildフェーズはあくまでbuildフェーズなので、その後のdeployフェーズではbuildフェーズでの成果物をデプロイする形になっています。前述の処理フローではcarton installを行った結果./local/が生成されていますが、そのままこれをgit pushするとHeroku上での環境セットアップと衝突して起動に失敗することがありました。
そのため、今回はworkaroundとしてビルドの最後に./local/を削除するstepを追加しました。

box: wercker/default

build:
    steps:
        - moltar/plenv
        - moltar/carton-install@0.3.0:
            without: develop
            cache_local: false # ./local/をキャッシュしない
        - script:
            name: prove
            code: carton exec -- prove -lvr t/
	- script:
            name: local_cleanup
            code: rm -rf $WERCKER_ROOT/local # localは消しておく

deploy:
    steps:
        # Execute the heroku-deploy, heroku details can be edited
        # online at http://app.wercker.com/
        - heroku-deploy:
            key-name: HEROKU_DEPLOY_KEY

構築された環境

以上のプロセスで、本稿の冒頭のこの図のような自動デプロイ構成を構築することができました。
f:id:astj:20141217123806p:plain
筆者のチームは実際にほぼこの構成*1で開発合宿に臨みましたが、今実際にどのリビジョンがheroku上で動作しているかを意識せずに済み、常に最新のmasterの動作を確認しながら開発することができたのは大きなメリットでした。

もっとも、何一つ文句が無かったという訳では無く、運用して以下の点が少し気になるところでした。

  • (トピックブランチで)希に同一リビジョンのビルドが連続して2回走る
  • masterブランチで複数のリビジョンのビルドが同時に走って古いビルドに時間がかかった際に、ビルドの終了するタイミングの前後によって古いリビジョンがデプロイされてしまうことが希にあった

前者はあまり実害がなかったのですが、後者に関してはwerckerがまだbetaということで、今後の機能強化などで何らかの改善がなされればよいな、というように思いました。

まとめ

本稿では、werckerを用いてperlのwebアプリケーションのテストとherokuへの自動デプロイを行う方法について述べました。

以上をもって本日のはてなエンジニアアドベントカレンダー2014としたく思います。明日の担当はid:hakobe932です。

*1:実際にはビルド失敗時やデプロイ成功時にSlackに通知するスクリプトを実行する、などのおもてなしを加えました。