pairsのBlue-Green-Deployment事情とゴールデンイメージの生成高速化(失敗)について

こんにちは。エウレカでエンジニアをしている恩田と申します。
主に弊社サービス”pairs”のインフラ周りを担当しています。

今回は、弊社で実践しているDeploy手法についてお話したいと思います。

Blue Green Deploymentとは

エウレカではステージング/本番環境へのDeploy方法として、
Blue Green Deploymentを採用しています。Blue Green DeploymentとはImmutable Infrastructure(不変なインフラストラクチャ)思想の延長上にあるDeploy手法です。ミドルウェアの内容やアプリケーションを更新したい場合に、稼働しているサーバ群に変更を加えるのではなく、サーバ群ごと作りなおして切り替えるという手法です。

サーバ群が2系統必要なので2系統をそれぞれBlueとGreenと呼び、それを更新の度にBlueとGreenを切り替えるのでBlue Green Deploymentと呼ばれています。DNSやロードバランサーを切り替えるだけで変更を反映を出来るため、ダウンタイム無しでのリリースができたり、リリース後に問題があった場合に旧環境に戻すといったことも可能になります。

blue-green1

Blue Green Deploymentの実践によってImmutable Infrastructureの恩恵を
受ける事ができると考えています。具体的には

  • 全てのサーバが同じ状態な事が保証できる(自動化が必須になる為)
  • テストとの親和性が高く、インフラ構築 & 修正もTDD(テスト駆動開発)が行える

immutable1

Blue Green Deploymentの実現の為には環境構築の自動化が必須になります。すなわちインフラ構成をコード化する必要がある、と同義とも言えます。
また、サーバ自体を使い捨ててDeploy = 一度立ち上げたサーバに対して二度と追加修正を行わない事になるので、サービスインしてるサーバ設定が常に同じである事が保証できます(コード上で管理されている最新のミドルウェアやサーバ設定が常に全台へ反映される)

また、サーバ状態がコード管理されているという事は、同じくテストが書ける、と言うことです。例えば、下記のようなAPIサーバの要件もテストとして記述できます。

  • nginxが立っており、外向けにport:80/443が開いている
  • supervisor経由で、アプリケーションがport:9000で起動している
  • Mackarel Agentが稼働している.
  • Natインスタンスを通じた外部への通信が出来る

ちなみに、弊社ではVPC等ネットワーク関連のものはTerraform、サーバプロビジョニングとアプリケーション配布はAnsible、テストはserverspecを用いて上記を実現しています。

# Ansible Roles for provisioning pairs servers
├── roles
│   ├── ami
│   ├── autoscaling_blue_green_deploy
│   ├── bigquery_key
│   ├── common
│   ├── deploy_app
│   ├── deploy_conf
│   ├── deploy_crond
│   ├── deregister_elb
│   ├── destory_ec2
│   ├── embluk
│   ├── git
│   ├── go
│   ├── gvm
│   ├── hostname
│   ├── lock
│   ├── mackerel
│   ├── maintenance
│   ├── mha
│   ├── mount
│   ├── mysql
│   ├── nginx
│   ├── nodejs
│   ├── ntp
│   ├── nvm
│   ├── openssl
│   ├── pairs-apps
│   ├── python-pip
│   ├── redis
│   ├── register_elb
│   ├── restart_app
│   ├── revel
│   ├── route53
│   ├── rpm
│   ├── service
│   ├── snmp
│   ├── supervisor
│   ├── td-agent
│   ├── tripwire
│   ├── unlock
│   ├── update_state
│   └── update_version
# tasks,test,configuration files for nginx
.
├── defaults
│   └── main.yml
├── files
│   └── robots.txt
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── spec
│   └── nginx_spec.rb
├── tasks
│   ├── conf.yml
│   ├── install.yml
│   ├── main.yml
│   └── vhosts.yml
├── templates
│   ├── nginx.conf.j2
│   └── vhost.conf.j2
└── vars
└── main.yml
# serverspec`s test for nginx
require ¥'spec_helper¥'

describe package('nginx') do
it {should be_installed}
end

describe service('nginx') do
it {should be_enabled}
it {should be_running}
end

describe process("nginx") do
its(:args) { should match /-c \/etc\/nginx\/nginx.conf\b/ }
end

describe port(80) do
it {should be_listening.with('tcp')}
end

describe port(443) do
it {should_not be_listening.with('tcp')}
end

describe file('/etc/nginx/nginx.conf') do
it { should be_file }
end

<h2>ゴールデンイメージの生成</h2>

pairsのサーバはAWSのauto-scaling-groupで管理しています。
auto-scaling-group作成には紐づく起動設定(launch configuration)が必要があり、AMIの設定が必須です.
( 自動でスケールイン/アウトするサーバのAMIを設定しておく必要がある)

Blue Green Deployment x auto-scalingな環境へ対応するには、
サーバ構築を自動化するだけではなく、常に"サービスイン可能な状態のマシンイメージ"を
保持している必要があります。このイメージの事をよくゴールデンイメージとか言います。

ゴールデンイメージは、サーバのプロビジョニングが行われていると同時に、アプリケーションのDeployも必要です。つまり、Deployする = 新しいアプリバージョンをDeployしたauto-scaling-groupが必要になる = 新しいアプリがDeploy済みのAMIが必要になる、
という事になります。
(他にも方法はあり、例えばスケールアウトするサーバは未プロビジョニングのサーバを立ち上げ、lifecyfle-hookの仕組みを使ってサービスインまでの間にプロビジョニング & アプリケーションのDeploy、みたいな方法もあったりします)

弊社ではリリースフローにgithubフローを用いており、マスターブランチ毎に更新があれば都度launch-configurationを再作成する必要があります。

# launch configurations for auto-scaling-groups
aws autoscaling describe-launch-configurations | jq '.LaunchConfigurations[].LaunchConfigurationName'

app_prod_jp_v1511301751_master_06252361211
app_stage_jp_v1512020106_master_0c1de8aaaa6
app_stage_tw_v1512020106_master_1205d39bda3
.
.
.

課題

Immutableな環境を実現しつつ、auto-scaling前提な環境へのBlue Green Deployを実現するには、以下の手順を踏む必要があります.

スクリーンショット 2015-12-01 8.48.59

弊社ではアプリケーション側言語にGolangを採用しており、コンパイルが必須な事に加え、
AMI作成 + Deploy(=green環境立ち上げ)の為に計2回分のインスタンス立ち上げが
必要になります。このインスタンス立ち上げにかかる時間が馬鹿にならず、Deployに
時間がかかる、という課題がありました。

インスタンス立ち上げの高速化取り組みと失敗

結論から述べると、インスタンス立ち上げ自体の高速化はできなかったです。
( 立ち上がる = sshが通りAnsibleによるプロビジョニング可能になるまでの時間と定義)
以下、試してみた事ですが、結論どうにもならずでした。

  • インスタンスサイズ毎の比較
  • rootボリュームの増減
  • マウントするEBSのサイズ/数
  • region毎の差異
  • インスタンス毎の個体差を調べて見る(インスタンスガチャと言うらしい、、)

他で時間を稼ぐ

インスタンス自体の起動を早く出来ないのであれば、他で時間を稼ぐ必要があります。
具体的には以下のような対応をしました。

ゴールデンイメージの作成自動化
  • マスタ〜ブランチへのmerge->webhookでbuildサーバへ通知
  • プロビジョニング,deploy,テスト,AMI作成までを自動化
ゴールデンイメージ作成用サーバの使い回し
  • ミドルウェアへの変更がない場合は新規に立ち上げずサーバを使い回す
  • Deployに必要なインスタンス起動回数が2=>1回へ
  • 1日一回(朝4時)はゴールデンイメージ作成用サーバを作成し直す

また、将来的にはbuildサーバの並列化等も視野に入れた形になっています。

最後に

Blue Green Deploymentはいざ運用に乗せると、しんどい箇所が他にも色々ありました。そのへんのお話もどこかでいずれできればいいな〜とか思っています。

ではでは!

  • このエントリーをはてなブックマークに追加

エウレカでは、一緒に働いていただける方を絶賛募集中です。募集中の職種はこちらからご確認ください!皆様のエントリーをお待ちしております!

Recommend

Elasticsearch 5.0.0-alpha1 が公開されたので早速使ってみた

Go言語の社内情報共有に関する試み、講演動画のすゝめとテストに関する翻訳を添えて