GeekFactory

int128.hatenablog.com

クックパッドにおけるScalable Deploymentsのスライドが興味深い

クックパッドにおけるアプリのデプロイの資料が非常に興味深いので紹介します.これは @sora_hさんがRubyKaigi 2014で発表 された資料で,100台以上のサーバに短時間でアプリをデプロイする仕組みをどうやって作り上げたのかが説明されています.

以前,スライドの内容を箇条書きにまとめていたのでシェアします.最近では,Jenkins User Coferenceの発表(How We Use Jenkins? // Speaker Deck)でほんの少し引用されていたりします.

内容のまとめ

スライドは90枚あります.ざっくりまとめた内容を以下に示します.

  • 概況
    • 140サーバに1日10回のデプロイを実施している(ピーク時)
    • コードベースが大きい
      • モデルだけで約 69K LOC
      • プロダクトコードとテストコードを合わせると約 319K LOC
  • デプロイのルール
    1. CIのビルドが成功したリビジョンをデプロイする
    2. 業務時間内のみデプロイできる
    3. デプロイ後は1時間ほどエラーをモニタリングする
    4. エラーレートが増加したり,何らかの問題が発生した場合はロールバックする
  • 元々のデプロイフロー
    1. 開発者がブランチをマージするとCIが走る(10分ぐらい)
    2. 開発者がCIの結果を確認する(1〜5分ぐらい)
    3. 開発者がHubotにデプロイを依頼する(10分ぐらい)
      • チャットで依頼するとデプロイが実行される
      • デプロイサーバからアプリサーバにCapistranoSSHrsync)でアプリをpushする
  • 課題
    • デプロイの所要時間を短くしたい(15〜20分ぐらい)
    • デプロイスクリプトの技術的負債を解消したい
    • Capistrano 2のSSHが時々失敗する
      • SSHはCPU負荷が高い
      • 失敗するとリトライを繰り返すので,所要時間はもっと延びる
  • 開発基盤チームのミッション
    • 開発者の生産性を改善する
    • 開発スピードを速く保つ
    • テスト環境を維持して改善していく
  • 改善計画
    • Capistrano 3へのアップデートを検討したが,SSHは遅いので避けたい
    • そこで,MAMIYAというデプロイツールを開発した
      • Serfでオーケストレーションを行う
      • マスターノードが各エージェントにメッセージを送る
      • 各ノードにアプリをpushするのではなく,各ノードがS3上のアプリをpullする
    • デプロイフローを3つのステップに分ける
      1. Fetch(各ノードがS3からアプリを取得する)
      2. Prepare(各ノードが依存関係の解決などを行う.bundle install とか)
      3. Switch(各ノードがサーバプロセスをリロードする)
  • 改善後のデプロイフロー
    1. 開発者がブランチをマージするとCIが走る
      • CIは,ビルドが成功すると,アプリをパッケージング(tar ball)してS3にアップロードする
    2. 開発者がCIの結果を確認する
    3. バックグラウンドで Prepare が走る
      • マスターノードが,エージェントに Prepare リクエストを送る
      • 各エージェントは,S3からパッケージを取得して配置する
      • マスターは,すべてのエージェントの Prepare が完了するまで待つ
    4. 開発者がデプロイを開始する
      • マスターは,エージェントに Switch リクエストを送る
      • 各エージェントは,シンボリックリンクを切り替えて,サーバプロセスをリロードする
  • 改善効果
    • 所要時間が8.4分から45秒に短縮された(110サーバに対するデプロイの時間)
    • 約11倍の高速化!

後半部分はスライドに書いてある内容をつなぎ合わせて(半分想像で)書いているので,もし間違いがありましたらご指摘ください.

雑感

有益な資料を公開していただき,ありがとうございます.

  • RubyからSerfをコントロールしているのはすごく面白そう.villeinを使ってみたい.
  • SSHのファイル転送は確かに重いので,今後はS3やDocker Registryなどからpullするモデルが主流になりそうです.
  • デプロイを管理するダッシュボードと連携したら便利そうです.(すでに連携しているかもしれませんが)
  • 異常系のワークフローが気になりました.例えば,一部のエージェントでエラーが発生した場合とか.
  • 開発フローにどのように組み込まれているのか気になりました.Pull Request Reviewやステージング環境での確認などからプロダクションのデプロイにつながるまでの流れです.
  • 資料では明示されていませんでしたが,リロードはUnicornのGraceful Restartを利用しているのではないかと思います.他言語でJVMで同じことをするにはDockerが役に立ちそうです.(追記:他言語だと広すぎて誤解を招くので訂正します)

(1/14 1:15追記)Markdownのリストがずれていたので修正しました.また,雑感の文体が雑すぎたので訂正しました.