Docker + Mesos + Marathon + Graphite + Fluentd + Sensuを組み合わせたデプロイ管理ツールの話

開発合宿でDevOps界隈やモニタリング界隈で流行りのツールを組み合わせてBlue Green Deploymentできる何かを作りました。

同じチームで開発したid:shiba_yu36 先生やid:wtatsuru 先生が既にブログを書いてますが、自分の視点で書いてみます。(13/12/24追記: より詳細な内容が新規に書かれたのでリンク先を入れ替えました)

僕は主に、各ツールから得られる情報をまとめて管理し、デプロイを実行するデプロイ管理ツールを作成していましたので、それについて書きます。 普段は運用の修行をしていたり、サーバ管理ツールなどを作っていたりします。

背景

参照すべき資料はたくさんありますが、以下のエントリがよくまとまっていると思います。

2014年のウェブシステムアーキテクチャ - stanaka's blog

上記エントリに書かれている開発フローを引用します。

またDockerを利用することで、開発、テスト、プロダクション環境を全て同一のDockerイメージを使うことが可能となります。具体的なフローとしては、次のようになります。

  • 手元で開発しつつ、Dockerイメージを作成
  • Dockerイメージをイメージリポジトリに登録
  • JenkinsでCIテストする
    • テストが通るとデプロイ可能な状態に
  • Dockerイメージを本番サーバにデプロイ
  • 本番サーバで起動イメージを差し替えてDocker再起動
    • ロードバランサーへの組込み

本番サーバへのデプロイや、Docker再起動、ロードバランサーへの組込みは、複数のホストを効率的に管理するためのクラスタ管理ツールの役割りとなります。

Statelessサーバに限定すると、Dockerやクラスタ管理ツールをうまく使えば、Blue Green DeploymentやAuto Scaleを比較的容易に実現できそうだなということがわかってきました。 ただ、実現するためのパーツは揃っていそうだけど、各ツールを具体的にどうやって組み合わせたらいいのかよくわからない、というのが現状なのかなと思っています。 そこで、今回はStateless Server環境におけるBlue Green Deploymentの実現に的をしぼって、DockerやMesosの組み合わせ方を考えてみました。

実現したこと

安全に簡単に高速にデプロイ
  • [安全] デプロイのたびに環境を作り直す。デプロイ前に本番環境と同等の環境を用意する。さらに、以前の環境に切り戻せる。
  • [簡単] ボタン1つでデプロイできる。どの物理ホストに何個のコンテナを配置するかを考えなくて良い。
  • [高速] 環境の作成からデプロイまでが速い (およそ2分程度)

全体フローとアーキテクチャ

f:id:y_uuki:20131222173449p:plain

  • ① 人間がGit Repositoryにpush
  • ② pushを検知したJenkinsがdocker imageを作成し、そのimageをコンテナとして起動しコンテナ内でアプリケーションのテストを実行
  • ③ Jenkinsがhttps://github.com/dotcloud/docker-registry:docker private registryにdockerのimageを保存
  • ④ Jenkinsがデプロイ管理ツールに新しい環境の作成依頼
  • ⑤ デプロイ管理ツールがMarathonに依頼 Marathon -> Mesos -> Mesos-Slave -> mesos-dockerを経由してリソースの空いたホストにコンテナを作成
  • ⑥ Marathonから作成されたコンテナの情報(ホストとport番号など)を取得
  • ⑦ ユーザが本番環境として使いたい環境を選択してデプロイボタンを押す
  • ⑧ デプロイ管理ツールはnginx.confを書き換えて、本番環境の切り替えを行う。

図では表現しきれていませんが、新環境を本番環境に切り替える前にバージョン番号を付与したURLで動作の確認ができたりします。 また今回はmasterブランチが変更されたときのみ環境が作成されますが、branchごとに環境を作成するといったことも出来そうです。

Marathon と Mesosについては、このコンテナを何個作ってくださいといい感じにリソースの空いたホストを選んでコンテナを作ってくれる便利な何かという認識でいます。 Mesosの具体的なリソース管理方法などはまだまだ勉強不足です。

これ以外に環境の切り戻しの指標とするために、FluentdとSensuによりアクセスログと各コンテナのメトリクスをGraphiteに送信し、GraphiteのAPIを叩いてグラフ画像をデプロイ管理ツールに表示させています。

SensuとGraphiteによるモニタリングについては、以前に何か書いてます。

モニカジ京都に参加して、SensuとGraphiteの話をしました #monitoringcasual - ゆううきブログ

LXCのメトリクス取得の具体的な方法については、id:wtatsuru 先生が書いてくれると思います。

なぜデプロイ管理ツールが必要か

今回のデプロイ管理ツールには、各ホストおよびコンテナの情報やデプロイ番号を集約させています。 このような中央集権管理が必要な理由はいくつかありますが、現段階ではシステムが何かのアクションをする判断を自動で行うことは難しいからだと考えています。例えば、git pushしてJenkinsが回って新しい環境ができたけど、デプロイ前に一旦人間の手で動作チェックしたいという要求は必ずでてくると思います。

今回のシステムに近いものをデプロイ管理ツールなしで、例えばSerfを使って実現することもできますが、人間にデプロイするタイミングを一旦預けたり、以前の環境に切り戻すなどを実現することがかなり難しいと考えます。

サービスに必要なコンテナ数が爆発的に増加し、World Wide Webのようにとても中央で管理していられないというような状況になったときに、ある程度自律して動作する分散システムを構築する必要があるかもしれません。

デプロイ管理ツールの実装

f:id:y_uuki:20131222173756p:plain

次にデプロイ管理ツールの実装について簡単に説明します。

  • サービス情報、ホスト情報、コンテナ情報、デプロイバージョン情報を管理する4つのテーブルでデータ管理
  • docker registryから対応するサービスの最新のimage情報を取得し、デプロイバージョンテーブルに格納
  • MarathonのAPIを叩いて、デプロイ番号に対応したコンテナ群を生成
  • 生成されたコンテナのステータス(runningなど)をMarathonのSubscription APIを使って取得し、コンテナ情報テーブルを更新
  • 各情報を一覧できるビュー
    • Graphiteからグラフ画像を取得
  • デプロイボタン
    • nginxのupstreamの設定を指定した環境に書き換える
    • nginxとデプロイ管理ツールは同居しているので、単純に”nginx reload”を実行するだけ。(ここはひどい。本来はnginxに専用のデーモンを立てて、そのデーモンが設定の書き換えやreloadを実行するべきだったけど時間がなかった。)
  • 情報更新の際にIRCに通知
  • Perl、オリジナルの軽量フレームワークで書かれている

MarathonのAPI周りについては、先輩が何か書いてくれると思います。(Marathon のAPIはドキュメントとかなくて大変な感じでした)

補足

今回は上述のようなアーキテクチャに収束しましたが、これがベストな解だとは思っていません。 Blue Grenn Deploymentの実現方法の単なる一例にすぎないですし、今後の各ツールの成熟や機能追加により、特にデプロイ管理ツールの機能構成は変化すると思います。

サーバ管理ツールとの統合

YAPC::Asia 2013ではてなのサーバ管理ツールの話のはなしをしました - ゆううきブログ

今回のシステムをサーバ管理ツールに組み込むということを考えられます。 現在、サーバ管理ツールに物理ホスト、仮想ホストの情報などを集約させていますが、ここにコンテナ情報とデプロイ番号の情報も合わせて管理すると、サーバ管理ツールとデプロイ管理ツール間でデータが分散せずに済むと思います。 今後は、Dockerコンテナの管理に対応したサーバ管理ツール/モニタリングツールが必要になってくると思います。

課題

  • 先輩方も書いてるけど、プロダクション環境で使うためには、DockerやMesos、Marathonといった今回実現した仕組みに必要不可欠なツールの成熟が必要。
  • FluentdとSensuの役割がかぶっているので、できればFluentdでメトリクスをとりたい
  • Mesosの仕組みがあまり理解できていないところが多い
  • Stateful Serverどうする?
  • サーバ管理ツールと統合したい

感想

  • 結構安定して動いていて、気軽にデプロイできるので、デプロイボタンを連打して遊んでいました。
  • デプロイ管理ツールの名前はRocket::Launcherという名前になってて、Rocket::Launcherでデプロイすることをランチャーと呼びます。
  • 更なる詳細の説明とかどうしてもOSS化してほしいみたいな要望があればがんばります