ファイル更新もchef実行もstretcherで!Lobiをデプロイするときにやっていること

Lobiチームの長田です。

今回は現在運用中のLobiというサービスのデプロイについて紹介します。

Lobi Chat & Game Community

Lobiについての紹介は以前のエントリを参照ください。

サーバーサイドエンジニア視点でLobiというサービスを紹介します | tech.kayac.com - KAYAC engineers' blog

TL;DR

  • デプロイ=各種ファイル更新とサービスへの反映、chefの実行
  • 毎日十数回のデプロイを行っている
  • デプロイ対象は数十台単位
  • 十余名のサーバーサイドエンジニア全員にデプロイ権限がある
  • Auto Scalingを考慮したデプロイ手段を採用している
  • ファイル更新もchef実行もstretcherでOK
  • まだまだ改善の余地あり

おおまかな手順

本番環境にデプロイするぞ!という段になると、こんな操作が行われます。

  1. GitHub上でコードレビューを通過したPull Requestをmasterブランチにmerge
  2. GitHubから最新の変更内容をデプロイサーバーにpull
  3. デプロイの前処理実行
  4. stretcherで各本番サーバーのファイル更新
  5. 各種アプリケーションの再起動

それぞれについて詳しく見ていきます。

デプロイサーバーへの変更反映

手順の1.〜2.にあたります。

LobiチームではメインのリモートリポジトリとしてGitHubを利用しています。

GitHub上のリポジトリとは別に、デプロイ用のリモートリポジトリ(Production Repository)を用意しています。 これはGitHubがダウンした際にもデプロイ操作を行うためです。


(GitHub) -> local -> Production -> DeployServer

デプロイの前処理

手順の3.にあたります。

自動生成のたぐいはすべてデプロイ前にデプロイサーバー上で行っています。 具体的には以下のような処理があります。

  • Perlモジュールの更新(carton install)
  • npmパッケージの更新(npm install)
  • JSファイルの圧縮(grunt build)
  • go製アプリのビルド(go build)

これら自動生成のファイルはリポジトリには含めず、リポジトリの軽量化を図っています。

インストールされるPerlモジュールはcpanfileで、npmパッケージはpackages.jsonでバージョン指定を行っているため、 開発環境などと差異が発生することはありません。

各本番サーバーのファイル更新

手順の4.にあたります。

各種ソースコードや画像などのファイル更新には弊社藤原が開発したstretcherを利用しています。

stretcherについて詳しくはこちらをご覧ください。

各種アプリケーションの再起動

手順の5.にあたります。

アプリケーションのリロードをしてはじめてLobiというサービスへの変更が適用されることになります。 リロード操作はstretcherの後処理に登録しているため、 ファイルの更新が完了し次第各サーバーで順次リロードが実行されます。

ファイルの更新が発生した際には必ずリロードが実行されることになりますが、 Lobi内で稼働しているアプリケーションは、一部を除いてgracefulな再起動が行えるようになっています。

chefの実行もstretcherで

サーバーのセットアップにはchefを利用しています。

以前はchef-serverとchef-clientを使用していたのですが、 今ではstretcherとchef-soloを組み合わせて運用しています。 これには以下のような利点があります。

  • chef-serverがSPOFにならない
  • chef-serverがデプロイ処理のボトルネックにならない
  • 開発環境などでも同じ方法でchef実行ができる

stretcherで必要なファイル(cookbookやnode設定)を取得し、後処理としてchef-soloを実行しています。

歴史

はじめは全サーバー(といっても数台)でgit pullしていました。 リポジトリも小さいですし、台数も少ないので特に問題はありませんでした。


repository --[git pull]--> servers

運用期間が長くなりリポジトリが大きくなってくると、.gitディレクトリのサイズが気になってきます。 また、新しいサーバーを追加する際にはgit pullに時間がかかってしまいます。 これらの問題を解決するために、デプロイサーバーからrsycnコマンドで 各サーバーにファイルを配布する方式に変更されました。 これにより必要なファイルだけを配布することができるようになりました。


deploy server --[rsync]--> servers

サービスの規模が大きくなりデプロイ対象が数十台規模になってくると、 1台しかないデプロイサーバーの通信量がボトルネックになってきます。 同時にrsyncする台数に制限を加え、ファイル配布を複数回に分けて行っていました。 このためファイルを配るだけで3〜4分の時間がかかっていました。

アプリケーションサーバーをAuto Scalingする際の問題もあります。 Auto Scalingによって追加されるサーバーは実際にリクエストを受け付け始める前に 最新の状態に更新する(=デプロイ処理が実行される)必要があります。 しかし、デプロイサーバーからrsyncする方式(push型)のデプロイ方法で これを実現しようとすると煩雑な仕組みになってしまいます。 追加されたサーバー自身が最新のファイルを取得しに行く方式(pull型)のデプロイ方法が必要でした。

これらの問題を解消するためにstretcherが導入されました。 通信量の問題はデプロイ元をAWS S3に設定することで解決しています(*1)。 デプロイの同時実行数を制限する必要もなくなり、 30秒程度ですべてのサーバーに対してデプロイ処理を完了できるようになっています。 streatchrはpull型のデプロイツールであるため、Auto Scaling時の問題も解決しました。


// 最新のファイルとマニフェストをupload
deploy server --[upload]--> S3

// 最新のファイルが有ることをconsul eventで通知
deploy server --[consul event]--> servers

// consul eventを受けて各サーバーが最新のファイルを取得、マニフェストに従って反映
// Auto Scalingで起動した際はこの部分だけが実行される
servers <--[aws s3 cp]-- S3

chefについてもchef-server & chef-clientからstretcher & chef-soloに変更したため、 chef-serverの負荷を気にすることなくサーバーの設定変更が行えるようになりました。

  • *1: いくらS3とはいえ帯域が無限に確保されているわけではありません。 stretcher v0.4.0から追加された-random-delayオプションを指定することで 一斉デプロイ時のファイル取得タイミングをずらすことができます。 詳しくはStretcherで起動時にrandom sleepするを参照してください。

課題

デプロイサーバーにsshログインし、デプロイ用のコマンドを手動で実行する必要があります。 Lobiチームではデプロイ担当者は設けず、サーバーサイドエンジニアであれば誰でも (レビュー通過済みの)変更を本番環境に反映できることにしています。

この方法には以下のような問題があります。

  • 単純に手順が多い
  • sshログインしなければいけない
    • 本番サーバーにアカウントが必要
    • サービスに直接関係しないデプロイサーバーとはいえ、本番環境での手作業が発生

はじめは小さなサービスだったのでこれでも問題なかったのですが、 チームメンバーが増え、そういうわけには行かなくなってきました。 コマンド自体は一つのシェルスクリプトにまとまっているため、 デプロイ処理は毎回同じ方法で実行されるのですが、 手作業が発生する以上なにかしら人的ミスによる事故が発生する可能性があります。 これを防ぐため、Slack botまたは管理画面でデプロイ操作を完結できるよう検討しています。

次回

メインのDBとして使用しているMySQLのフェイルオーバーについて紹介します。


カヤックでは毎日行う作業を効率化したいエンジニアを募集しています!