リリースとデプロイ

この記事は Asakusa Framework Advent Calendar 2013 の 17 日目の記事として書いています.

Hadoop クラスタの選定と全体構成の検討と来て, 今回は実際にどんな構成で動かしていたか, バッチアプリケーションのリリースやデプロイはどう行っていたか, について (ぼやかしながら) 書きます. 関連した保守の話題なども含めてあります.

全体構成

リリースやデプロイの話をする前に, その対象となるシステムの構成について書きます. 構成については, 前回, 前々回の記事に基本的な考え方を書きましたので, 必要に応じて参照してください. → Asakusa バッチ実行環境の構成 (前編), Asakusa バッチ実行環境の構成 (後編)

クラスタは EMR を使っています. 当初 EMR が無かった頃は EC2 クラスタを使っていたのですが, なにぶんクラスタの操作を行うプログラムの保守工数がバカにならないので EMR に切り替えました.

データの流れという視点で, システムの構成を記述すると以下のようになります.

「世代」というのは, システム構成が大きく変わったところを区切りにして, 名前を付けています. Asakusa Framework のバージョンとは全く関係 ありません . ちなみに「第零世代」と呼ぶべき構成もあるのですが, 当時の Asakusa Framework は今のものと大きく違い過ぎますし, 自分がシステム構成設計に関わっていない時期の話なので割愛します.

表1. データ経路
世代 入力データソース コンポーネント 入力ファイル クラスタ 出力ファイル コンポーネント 出力データソース
第一世代 RDBMS WindGate → (SSH) → EC2 → (SSH) → WindGate RDBMS
第二世代 RDBMS WindGate → (SSH) → EMR → (SSH) → WindGate RDBMS
第三世代 RDBMS 独自ツール SequenceFile on S3 EMR SequenceFile on S3 独自ツール RDBMS

第一世代と第二世代は構成はほとんど変わりません. クラスタの起動処理で, stopped 状態にしてあった EC2 クラスタを起動するのか, EMR クラスタを作成するかの違いです. EMR はクラスタを作って, bootstrap action で内部の環境を準備するためだけに使い, Hadoop ジョブの実行は外部から SSH 接続を通してコマンドを実行していました.

第一世代の構成, 第二世代の構成ともに WindGate でデータ入出力を行っています. ここが障害の原因になることも多く課題となっていました. また EMR のマスターノードの IP アドレスを, やや無理矢理割り出していたので, その部分のコードの保守性も気になっていました.

それを受けて第三世代の構成では, EMR のデータの出入りは S3 に統一しました. EMR の内部で行う処理は step script に集約し, EMR 外部の他のマシンから SSH 接続する必要を無くしました. こうすることで EMR の IP アドレスを誰も知らなくても良くなり, マスターノードの IP アドレスを割り出すコードが不要になりました.

テスト・CI・リリース

上記の構成で AWS 環境のテスト, バッチ全体のテストやそれらの CI を回す方法を考えてみます.

AWS 環境テスト

テスト上で大きな違いは WindGate の有無です. WindGate は Hadoop クラスタとのやり取りにファイルを作成せず, SSH 通信でデータを転送します. これはファイルの配置場所やサイズを考えなくて済むのですが, 逆に言うと処理の区切りができません. さらに Asakusa バッチアプリケーションが Hadoop ジョブの間で出力する中間データは, Asakusa framework の内部的なものなので, 前回実行分を取っておいて再利用するような使い方はできません. (ファイル名がランダムな UUID になっていますし) なので本番と同じようにバッチレベルのテストを動かす場合は, WindGate も含めてテストを実行しなければなりません.

さすがにこれでは不便なので第三世代の構成では, バッチの前後に S3 上のファイルを挟み, それを処理の境界としました. こうすることでバッチ部分だけの通しのテストが可能になります. さらに, 入出力ファイルの場所も外部から与えられるようにパラメータ化したおかげで, 本番とそっくり同じコピーの環境を作りバッチを実行することが簡単になりました. パラメータとして S3 の bucket 名と key prefix を渡すようになっているので, この基点となる S3 のパスを変更することでクラウド側の環境の切り替えが簡単に行えます.

バッチ全体のテスト

AWS で動かすテストはこれでできるようになりましたが, 毎晩 CI で動かすと EMR 使用料がバカにならないので, なんとか社内で動かしたいものです. 「EMR がローカルで動けばいいのだが……」と考えた末ふと思い付いたのは, 「bootstrap action と step script は shell script だからローカルでも実行できるのでは?」というアイディアでした.

もうこの時点で, 基点となる S3 パスのパラメータ化は完了していたので, ローカル実行用の S3 のディレクトリとローカルで動く Hadoop があれば良いはずです. 色々試行錯誤した結果, Hadoop を疑似分散モードで動かし, バッチの起動には bootstrap action と step script を S3 からダウンロードして, 普通の shell script として bash で実行しました. Hadoop をローカルモードで動かしたときは不可解に消えてしまっているファイルがあったので, 疑似分散モードにしています. なぜファイルが消えたかまでは原因は追っていません. 最後に, バッチ実行後の後片付けをする shell script を書いて完成です.

数日手こずりましたが, 意外と上手く行ったので満足です.

デプロイ

前回の記事で触れた通り, 同一の Asakusa バッチアプリケーションを必要な箇所にデプロイする必要があります. 第一世代の構成では「WindGate のいるマシン」と「EC2 クラスタ」に同一の batchapps.jar を配置する必要がありました. EC2 クラスタは全てのノードを同一の AMI から作成するため, まずはリリースする batchapps.jar を含んだインスタンスを作り, その AMI を作成する必要がありました. やったことある人は分かると思いますがすごく面倒です. いちいちリリースに時間と手間が掛かります.

「こりゃやってられん」ということで第二世代の構成では, クラスタを EMR で作った後にデプロイモジュールを S3 から取ってくるようにしました.

第一世代がいわゆる「ゴールドマスター方式」で, 完成したマシンイメージをコピーして立ち上げる方式です. この方式は, 一度動いた環境をそのまま保存し再度実行するので, 内部の構成や設定の齟齬が無いことが保証されています. しかし, これでは OS のバージョンアップやマシン内部の一部のモジュールだけを差し替えたいときに激しく面倒です. また, 変更の世代管理も行う必要があります.

第二世代はそれと対照的に, EMR の bootstrap action をプロビジョニングツールとして使う方式です. この方式では EMR の起動時に bootstrap action に指定した shell script が, S3 に上がっているモジュールをダウンロード, 展開し環境の初期化を行います. なのでクラスタにデプロイする batchapps.jar を更新するときは, S3 に新しい .jar ファイルをアップロードするだけで済むようになりました.

第三世代はさらにリリースとデプロイの手間が簡素化されています. 一番のポイントは, 第一, 第二世代ではローカル側にあった YAESS モジュールが, EMR のマスターノードへ移動したことです. YAESS を起動する処理はローカルのスクリプトから EMR の step script へ移動しました. その結果, batchapps.jar は (S3 からダウンロードされたものが) EMR 内にだけ存在することになり, ジョブネットの不整合が絶対に起きない構成になりました.

またこうすることで, バッチ実行中のクラスタがローカル側のマシンと通信することはほぼ無くなり, 処理がクラスタ内部で自己完結しています. ローカル側のモジュールは EMR の完了をポーリングしながら待っているだけになりました.

YAESS がクラスタごとに存在し, クラスタで自己完結した構成になったおかげで, クラスタの同時起動ができるようにもなっています. クラスタを都度作成し, 終わったら破棄する方式ではなく, 常時起動させておきたければ, EMR の起動の代わりにステップの追加を行えば良いはずです. (今のところ, 常時起動するクラスタの監視のコストとの折り合いで, 都度クラスタを作成する方式を採っています)

終わりに

ここ 2 年くらい, テスト環境整備やらリリースやら障害対応やらやってきて感じたことは, 「運用を考えて作れ, シンプルに作れ」ということです. 今回この Advent Calendar を書いたきた内容も, それを軸に話しています. 話の流れを運用から逆算する形で構成しているのもそういう意図です. 分かってる人には当たり前な戦略方針だとは思いますが, 自分が経験したことを Asakusa バッチアプリケーションを例にまとめてみました. Asakusa バッチアプリケーションを使ったシステムの構成を考えるヒントになれば嬉しいです.

次回はログの話を書く予定です.