AWS ECSでDockerコンテナ管理入門(基本的な使い方、Blue/Green Deployment、AutoScalingなどいろいろ試してみた)

はじめに

Dockerを本番環境で利用するに当たり、私レベルではDockerのクラスタを管理することはなかなか難しい訳です。凄くめんどくさそうだし。
ということでAWS ECS(EC2 Container Service)ですよ。

記事書くまでも無いかなと思ったんですけど意外と手順がWEBにない(気がしました)。ということで、今回は社内でハンズオンでもやろうかと思って細かく書いてみました。

こんな感じのシナリオをやってみたいと思います。

  1. Dockerのイメージを用意する
  2. ECSの使い方の基本
  3. コンテナのリリース
  4. Blue/Green Deployment
  5. AutoScaling/ScaleIn

前準備:Dockerのイメージを用意する

FROM uzresk/docker-oracle-jdk
MAINTAINER uzresk

ADD demo-1.0.jar /root/demo-1.0.jar
CMD java -jar /root/demo-1.0.jar
  • Docker build
[root@centos7 build_demo_ver1.0]# docker build -t uzresk/demo:ver1.0 /vagrant/build_demo_ver1.0
Sending build context to Docker daemon 33.11 MB
Step 1 : FROM uzresk/docker-oracle-jdk
 ---> df2f575c2a0d
Step 2 : MAINTAINER uzresk
 ---> Using cache
 ---> 1995a4e99748
Step 3 : ADD demo-1.0.jar /root/demo-1.0.jar
 ---> 705df0209779
Removing intermediate container cd9ef33d8812
Step 4 : CMD java -jar /root/demo-1.0.jar
 ---> Running in b7bd939a8b5a
 ---> add0783a851f
Removing intermediate container b7bd939a8b5a
Successfully built add0783a851f
  • 起動します

    • アプリケーションは8080で起動しているのでポートフォワードしておきます。
[root@centos7 build_demo_ver1.0]# docker run -itd -p 80:8080 --name demo uzresk/demo:ver1.0
92bda2419bf7285d78f12be5877ae3242b5b13ac14409b3c47d38e2d74a06464
  • ブラウザでこんな画面がでれば成功です。

image

  • Dockerhubにコミットしてpushしておきます。
[root@centos7 build_demo_ver1.0]# docker commit -m "update ver1.0" demo uzresk/demo:ver1.0
[root@centos7 build_demo_ver1.0]# docker push uzresk/demo:ver1.0

ECSの使い方の基本

AWS ECSとはなんなのか?

  • 今回は利用手順について書こうと思うので割愛しますが、AWS Black Belt ECSを読むのがよろしいかと思います。

構成する順番を抑えよう

  • こんな感じの順番で構成していきます。大事なので押さえておきましょう。
  1. クラスタの作成

    • クラスタを動かすためのEC2インスタンスの設定を行います。具体的にはインスタンスタイプ、インスタンス数、VPC、サブネットの設定になります。
  2. タスク定義

    • クラスタ上で動かすコンテナの情報を登録します。コンテナイメージのURLやCPU、メモリのハード/ソフト制限、アプリケーションで利用する環境変数の定義などを行います。
  3. ロードバランサの作成

    • クラスタの上位に位置するロードバランサの設定を行います。スケールアウトやスケールインしてもロードバランサはサービスを見つけ出し配下に組み込むことができます。
  4. サービスの作成

    • クラスタとサービスを結びつけるのがサービスの役割です。タスクの最少数やAutoScalingの設定を行ったりできます。
    • 1つのクラスタに複数サービスを登録することももちろん可能です。

それではさっそくクラスタの作成からやってみましょう。

クラスタの作成

image

image

  • 正常に作成されると「クラスターの表示」ボタンが押せるようになります。

image

タスク定義

  • 次はタスクの定義です。タスクでは

image

  • タスク定義名を入力し、「コンテナの追加」をクリックします。

image

image

  • 作成を押せばタスク定義の作成が完了します。

ELBの作成

  • ELBは以下の設定で作っておきましょう

    • ELB名:app-demo-lb
    • 種類:アプリケーションロードバランサ
    • 2つのAZそれぞれのSubnetを指定
    • セキュリティグループで80/HTTPを通すように設定
  • ターゲットグループは以下のようにクラスタで設定したインスタンスIDをそれぞれ登録してください。

image

サービスの作成

  • クラスターのTOPからdemo-clusterを選択し、サービスタブで「作成」

image

  • タスク定義とクラスタ名は自動で埋まりますので、サービス名とタスクの数を設定します。
  • 今回はAZにそれぞれコンテナを作りたいので2としました。

image

  • 画面の下の方にあるELBの追加を選択します。

image

  • ELB名は作成したもの、リスナーポートは80、ターゲットグループ名は作成したものを選択します。

image

image

  • 「作成」を押して、サービスの画面をみるとPENDINGになっています。

image

  • 少し経つとRUNNINGになっている事が確認できると思います。

image

  • ELBのエンドポイント/app/をブラウザで叩くと画面が表示されるはずです。

image

コンテナを落としてみるとどうなるのか

  • タスクの一覧から、タスクを一つ消してみましょう。

image

  • 数十秒後に見てみると別のタスクIDのインスタンスが表示されているはずです。

image

  • コンテナが起動する数十秒間の間はアプリケーションロードバランサが生きているタスクの方にうまくルーティングしてくれるのかな?と思ったら「502BadGateway」というエラーが画面に返ってきました。
  • ここはALBのヘルスチェックの閾値を短くすることである程度は短くできそうです。
  • ここをさらに短くするには、コンテナ自体を軽くすることと、すぐに起動できるアプリケーションを利用するしかなさそうですね。

コンテナのリリース

  • 新しいコンテナをリリースするには、タスク定義に新しいリビジョンを登録し、サービスを更新することで実現可能です。さっそくやってみましょう。

image

  • コンテナのバージョンを2.0にして、新しいリビジョンを登録します。

image

  • 追加されたリビジョンを選択し、アクション→サービスの更新を押します。

image

  • タスク定義に新しいリビジョンが指定されていることを確認して、「サービスの更新」

image

  • サービスの「デプロイ」タブを覗くと、今はVer1.0が2つ動いていることが確認できます。

image

  • コンテナを一つ落としてみましょう

image

image

  • 実行中の数がそれぞれ1になり、タスクの一覧からもそれぞれが動いていることがわかりますね。
    image

image


Blue/Green Deployment

  • Blue/GreenDeploymentでは新しいリビジョンのアプリ用に、新しいインスタンスを構築して入れ替える必要があります。
  • この為のパラメータがサービスのデプロイメントオプションにある最大率(maximumPercent)です。2台の時にこの値を200%にしておくと、4台まで同時に動かしておくことができることを意味します。
  • 4台のインスタンス上で動かすにはECSのインスタンス台数を事前に追加しておくか、AutoScalingさせておく必要があります。もしECSインスタンスが2台の状態で4つのコンテナを動かそうとすると以下のようなメッセージがでてしまいます。(ポートかぶってるから上がらないよ。ってことですね)

  • さっそくやってみます

service demo-service was unable to place a task because no container instance met all of its requirements. The closest matching container-instance xxxxxxxxxxxxxxxxxxxx is already using a port required by your task. For more information, see the Troubleshooting section.

image

image

  • この状態でサービスの更新画面でタスク定義を新しいリビジョンに指定して「サービスの更新」を押してみます。
  • おお。4台分のコンテナが起動しましたね。

image

  • ちょっと経つと(3分ほど?)、古いタスクから削除されていきます・・・・

image

  • 最期は新しいタスク定義しか残らなくなりました。自動ですよ。自動。便利ですねー。

image


AutoScaling/ScaleIn

  • 次はオートスケールとスケールインを試してみます。
  • 通常のオートスケールではインスタンスだけでしたが、インスタンス上で動くコンテナもスケールする必要があります。
  • 今回は2つのインスタンスで2つのコンテナで動いていたものを、負荷をかけることにより4つのインスタンス上に4つのコンテナにスケールアウトさせて、スケールインさせたいと思います。

サービスのAutoScaling設定

  • タスクの最大数を4にして、スケーリングポリシーの追加を押します。

image

  • スケールアウトポリシーの設定を行います。
  • CPU使用率の1分間の平均が20%超えた場合2タスクスケールアウトする設定にしました。

image

  • スケールインポリシーの設定を行います。

image

  • ポリシーが追加できたら「保存」を押しましょう

image

  • ポリシーが追加されていることを確認して、「サービスの更新」を押します。

image

  • これでサービスの設定はおしまいです。

ClusterのAutoScaling/ScaleInの設定

  • ECSインスタンスのオートスケールのポリシーは、EC2インスタンスのAutoScalingGroupの設定で行います。
  • 最大数を4にします。

image

  • Scaleout/ScaleInのポリシーを設定します。
  • サービスの設定と同じく、クラスタのCPU使用率が20%以上だと2台スケールアウトするように設定しました。

image

うごかしてみる

  • ECSインスタンス上でCPU使用率を強引に(openssl speed -multi 1)あげてみたのですがうまく動きませんでした。
  • ありがちですけどabで負荷をかけてみます。
  • abをインストール
sudo yum install -y httpd24-tools
  • 負荷をかける
ab -n 100000 -c 100 http://localhost/app/loginForm
  • CloudWatch Alerm(CPUが20%以上)があがる

image

  • サービスの必要数が4に変更される

image

  • インスタンスがオートスケールする

image

  • タスクが起動する

image

  • 負荷を解除する

  • CloudWatch Alerm(CPUが20%より小さい)があがる

image

  • 必要数が2に変更される

image

  • コンテナとインスタンスが2ずつに変わる

  • ここまでやって思ったんですが、インスタンス→コンテナの順に起動されたんですからコンテナ→インスタンスの順に落としていった方がよさそうですね。それはスケーリングポリシーのクールダウン時間で調整ができそうです。

ECSインスタンスの起動を待つのか?

  • ECSのインスタンスの起動を行った後にコンテナが起動されるので結局時間が掛かってしまいます。負荷の時間が予測されるのであればECSインスタンスを事前に起動しておいた方がECSのスケールアウトが高速にできそうです。
  • Dockerのメリットの一つである起動の高速化のメリットを享受するにはこのスケジューリングがキーになりそうですね。

どのインスタンスがスケールインするのか?


さいごに

ここまでコンテナ管理のハードルが下がると、今後はアプリケーションをコンテナにして配布するのが普通になってくると思います。
そうなるときのためにDockerについてしっかりと理解し、良い設計を心がけたいものですね

続きを読む

ELB暖気運転(Pre-Warming)申請についていくつかサポートに問い合わせた

はじめに

急激なトラッフィク増が見込まれる際に、
予めELBをスケール(アウト/アップ)しておいてくれる暖気運転。

実際の申請方法等はこちらのサイト様の記述が詳しいです。

http://dev.classmethod.jp/cloud/aws/elastic-load-balancing-pre-warming/
http://qiita.com/takachan/items/80c343e03789b01adcf9

上記のページを参照していくつか疑問に思ったことがありましたので、
サポートに問い合わせを行いました。

以下に回答の要旨を記載していきます。

補足

  • 問い合わせを行ったサポートプランは「Developer」です。
  • 質問および回答を丸写しで載せるわけではないので、私の解釈の仕方が誤っている可能性があります。

Q1.暖気運転前のELBの処理性能の目安はあるか

申請する項目に、リクエスト数 (リクエスト/秒)やスループット (bit/秒)などがあるので、
「このくらいの数字だったらわざわざ暖気する必要ないよ」という目安が聞ければという意図で聞いてみました。

A1.目安としてお答えできるものはない

  • AWSでは日々設備増強/構成改善を行っており、ELB のベースラインパフォーマンスも変動する
  • サポート内でも具体的な情報は共有されておらず、目安として一概に案内できない
  • 実際に申請が出された後に、ELB担当部署がスケールの必要性も含めて検討している

Q2.暖気運転申請が出されるケースの参考情報はあるか

実際に暖気運転申請が出されるケースとしては、どの程度の規模の数字になるのか。
(1時間でxxxリクエスト…)
(バックエンドのインスタンスとしてがインスタンスタイプxxが○○台…など)
参考として聞ける情報があればと思い質問しました。

A2.参考としてお答えできるものはない

  • A1.とほぼ同様
  • 暖気運転の必要性も兼ねて一度申請を出してもらうことが多い」

※ここで「一旦情報をもらえれば必要性だけでも確認します」と回答いただいたのですが、
 サポートプラン「Developer」のままで確認いただけるのかは聞き損ねました。

Q3.暖気運転により追加料金はかかるか

料金表の記載からかからないだろうと想定していましたが、
念のため聞いてみました。

  • Elastic Load Balancing 時間(または 1 時間未満)あたり
  • Elastic Load Balancing によって処理されるデータ 1 GB あたり

https://aws.amazon.com/jp/elasticloadbalancing/classicloadbalancer/pricing/

A3.いいえ

かかりませんでした。

Q4.ELBのスケールイン時にセッション切れは起こらないか

例えば、AutoScalingによってバックエンドのインスタンスがスケールインすると、
それによってセッション切れが生じるという可能性があります。
(それを防ぐためにキャッシュサーバにセッション情報を外出しにする)

同じようなことが、暖気運転でスケールアウトされたELBの
スケールイン時に起こらないか、という疑問が生じたので質問しました。

A4.基本的に起こらない

  • スケール対象のノードは一定時間維持するようにしている
  • 新規セッションは受け付けないが既存のセッションに対しては一定の猶予時間を設けている
  • 猶予時間は正確な値を公表していないが、数十分以上である

Q5.暖気運転の期間はどこまで指定できるか

スケールさせておくことによる追加料金は発生しないので、
極端な話「1年間ずっと暖めておいてください」もできるのか聞いてみました。

A5.基本的には2週間程度

  • 特別な事情があっても一ヶ月程度
  • それ以上必要な場合は個別にヒアリングして妥当性を判断する

おわりに

以上、質問およびその回答でした。
情報の正確性を保証するものではありませんので、
参考として捉えていただければと思います。

マネージドであるがゆえに、
ユーザーによる設定ではなく申請という形になるのが興味深いですね。

以下、蛇足です。

蛇足1.スケールされたことの確認

IPアドレスによってスケールを確認できるようです。
http://itpro.nikkeibp.co.jp/atcl/column/16/121200298/121200002/

ALB、CLBともスケーリングの仕組みは公開されていないが、各テストケース実施前と実施後にnslookupコマンドを実行し、ALBおよびCLBの状態を監視。IPアドレスが変更された場合はスケールアップ、IPアドレスが増えた場合はスケールアウトが発生したとみなす。

蛇足2.ELBによって自動的にENIもできる

他のENIと同じように、AWSマネジメントコンソールから確認できます。
ちょっと特殊なのは下記のあたり。
– アタッチメントの所有者:amazon-elb
– 説明:ELB <ELB名>

蛇足3.暖気運転?暖機運転?

恐らく暖気運転が正。下記は誤字か。

WS000000.JPG

追記

コメントにてご指摘いただき、「暖機」のほうが日本語として正しそうです!

以上です。

続きを読む

AWS上のZabbixを、耐障害性を考慮した冗長構成としてどう組むか考えてみた

Stylez Advent Calendar 2016の23日目です。

以前、取引先の方と「Zabbix自体には冗長化の機能って無いよね。AWS上でZabbix構築する際、耐障害性を考慮した冗長構成のベストって何?」って話になり、色々議論したことがあったので、その内容をまとめました。

前提

  • AWS上で構築
  • 負荷分散的な要素は考慮しない
  • 監視サービスの継続性を重視

案1. クラスタウェア

zabbixha_clusterware.png

絵が雑ですいません。。。

ネットで探すと良く出てくるパターンでしょうか。
PacemakerとかDRBDでWebやDBのプロセス・データをサーバ間で監視・同期させて、障害が起きたら自動的に切り替える構成です。

オンプレ時代からの枯れた構成ですし、実績は今回紹介するパターンでは最も多そうな気がしますが、クラスタウェアの管理が運用上負担になりそうです。(個人的にトラウマ)

参考:AWS上のZabbixをPacemaker(HeartbeatV2)で冗長化

案2. AutoScaling + RDS

zabbixha_autoscaling.png

WebとDBを分離し、DBはRDSを利用。
WebはAutoScaling(上限下限:1)にして、インスタンスが死んだら自動的に再構築されるという構成です。
クラスタウェア構成と比較して、冗長部分はAWSにお任せするパターンです。

この構成は少しクセがあり、AutoScalingなのでインスタンス起動の度にIPアドレスが変わります。
従って、管理画面アクセス用と監視対象サーバからの通信(アクティブチェック)用の内部ELBを入れる必要があります。
また、Zabbixエージェント側の設定も工夫が必要で、ホスト自動登録設定を仕込んだ際に、内部ELBのIPアドレスでホスト登録されてしまうので、実サーバのIPを通知できるようにエージェント設定を変更する必要があります。

ここまでAutoScalingのクセに悩むのであれば、AutoRecoveryでも良いじゃんって話もありますが、AutoRecoveryだとAZをまたいで起動しないので、AZ障害を鑑みてAutoScalingを選んでいます。

クセをつかむまでは大変ですが、一度設定してしまえば、冗長部分の運用負荷は大きくないので、おすすめです。

案3. Zabbix製バックアップツール

zabbixha_buckuptool.png

Zabbix公式で出ている Zabbix設定バックアップ同期ツール(以下、バックアップツール)というものがあります。
通常、エクスポート/インポート出来る設定項目は限られていて、アクションやユーザとか一部の項目はエクスポートできません。
もちろん、DBダンプすれば可能ですが、まるっとダンプすると監視データごとぶっこぬいてしまうので、監視設定のバックアップという意味合いでは難があります。
このツールを使えば、通常エクスポート出来ない部分含めた設定をファイルとして出したり入れたりすることが出来ます。

従って、このツールを双方のシングルZabbixに入れて、マスターからバックアップしたファイルをスレーブのZabbixに適用し続ければ、設定を同期することが可能になります。

この構成は文字通り設定だけ同期する事になるので、Active/Active構成で動くZabbixが出来上がります。
しかも、スレーブにバックアップ設定を適用する際、障害通知を無効にするオプションが指定できるので、双方のZabbixからアラートが飛ぶという事も回避できます。

問題点として、クラスタ構成と違ってマスター障害時はスレーブ側の障害通知を手動で有効にし、管理画面はスレーブ側を参照する必要があります。
あと、バックアップファイルをスレーブに転送する処理は、自分で作る必要があります。(rsyncするとか、共有ディレクトリに置くとか)
また、Active/Active故に、双方の監視データは異なる値が取れている可能性があります。運用する上ではこのあたり意識する必要がありそうです。

最後に、一番の問題は、このツール自体が、Zabbix社の技術サポート契約に加入する必要があるという事です。
という事で、お金かかります。

総括

個人的にはクラスタの管理はしたくないので、案2か案3。構成のシンプルさを見れば、案3を推したいです。
最近では、Dockerとか今までの常識を覆すようなインフラも出てきて、こういった専用サーバがある前提でという考えも無くなってくるのかもしれません。
他にも良い案知ってる方がいれば教えてください。

続きを読む

AWS CodeDeploy を使うときに気をつけたいこと

エムティーアイ Advent Calender 2016 の23日目の記事です。

AWS CodeDeploy (以下、CodeDeploy) は AWS の中の開発者ツール(通称 Code シリーズ)の中の1つで、デプロイ作業をコードで記述することで自動化できるサービスです。
開発者ツールや CodeDeploy 自体の説明は以下などを参考にしてください。

この記事では実際に CodeDeploy を数ヶ月使っていてハマってしまった経験を書いていきます。
どれも経験に基づいた情報であり個人の見解等も入っていますので、正確な情報は公式ドキュメントや、AWS サポート等へご確認ください。

Hooks スクリプトはどんなときでも動くように

Hooks スクリプトにデプロイ時の様々な付随処理を書くことができます。
スクリプトで目的の処理を実行できるようになったら、いろんな条件でデプロイを試してみてエラーが出ないことを確認します。
例えば、まっさらな状態のインスタンスに対してのデプロイ、アプリケーションが稼働中の状態でのデプロイなどです。

僕の場合はとある C# の Web システムで、開発中は正常にデプロイできてたけど本稼働後にデプロイをさせたら失敗するようになってしまいました。
原因は対象インスタンスへの Web リクエストの停止がうまく行えていなかったために、処理中の dll の置き換えができなくなってしまいました。

また、AutoScaling で新たに生成されたインスタンスに対してのデプロイで失敗してしまうこともありました。
特にこの CodeDeploy のターゲットに AutoScaling グループを指定する場合は注意が必要です。
AutoScaling ではインスタンス生成時に CodeDeploy によるデプロイを実施しますが、デプロイに失敗した場合はインスタンスが正常に起動しなかったものとして、インスタンスのを破棄して再生成します。
すると再度失敗して、破棄と生成の無限ループに突入してしまいます。
気づかなければインスタンスがいつまでも InService にならないだけでなく、インスタンスの料金もかかってしまい、目も当てられなくなります・・・。

PowerShell は 32bit 版が動く

Windows での Hooks スクリプトでは直接 PowerShell を実行することはできず、bat ファイルから powershell script.ps1 のように呼び出して使います。
このとき、たとえ OS が 64bit 版だったとしても 32bit 版の PowerShell が実行されてしまいます。
そのため、一部のコマンドレットが使用できません。

これについて困ってる人は結構いるようで、ググるとそれなりに出てきます。
64bit 版の方を実行させるやり方を書いてる人もいましたが、僕はうまく行きませんでした。
仕方なく通常の Windows コマンドや 32bit 版でなんとか回避しましたが、「こうやると 64bit 版が使えたぜ!」っていう情報があったら教えていただけるとうれしいです。

CodeDeploy で配置したファイルはいじってはいけない

AppSpec ファイルの files セクションに書くことでファイル群の配置を行うことができます。
その配置されたファイルに CodeDeploy 以外で変更を加えてしまうと、次のデプロイ時に失敗してしまいます。

CodeDeploy では2回目以降のデプロイの際、 files の destination で指定されたファイルやディレクトリ以下を削除して新しいファイルをコピーしています。
配置したファイルの更新日などのメタデータを見ているようで、デプロイ後にファイルが更新されていると「勝手に消したらまずいんじゃね?」と判断(イメージです)してデプロイを中断します。

ファイルを更新してしまった場合は手動で削除するか、CodeDeploy の作業ディレクトリにある元ファイルをコピーして元に戻すかしてから再デプロイしましょう。
もしくはインスタンスごと作り直してもいいと思います。

インスタンスの時刻がずれてるとデプロイできない

ある日突然、特定のインスタンスだけデプロイに失敗するようになり、しかもマネジメントコンソールで確認するとデプロイライフサイクルにすら入る前にエラーとなっていました。
インスタンスにログインして CodeDeploy エージェントのログを見るとこんなログがありました。

codedeploy-agent-log.txt
2016-11-30 15:46:26 INFO  [codedeploy-agent(2776)]: [Aws::CodeDeployCommand::Client 400 0.062357 0 retries] poll_host_command(host_identifier:"arn:aws:ec2:ap-northeast-1:NNNNNNNNNNNN:instance/i-XXXXXXXX") Aws::CodeDeployCommand::Errors::InvalidSignatureException Signature expired: 20161130T064626Z is now earlier than 20161130T070808Z (20161130T071308Z - 5 min.)

2016-11-30 15:46:26 ERROR [codedeploy-agent(2776)]: InstanceAgent::Plugins::CodeDeployPlugin::CommandPoller: Cannot reach InstanceService: Aws::CodeDeployCommand::Errors::InvalidSignatureException - Signature expired: 20161130T064626Z is now earlier than 20161130T070808Z (20161130T071308Z - 5 min.)

どうやらインスタンスの時刻同期が長いこと失敗し続けていて、インスタンスの時刻が大幅にずれてしまっていたようです。
CodeDeploy ではインスタンスの時刻が5分以上ずれていると、認証に失敗し CodeDeploy エージェントと CodeDeploy との通信が行えません。

ドキュメントにもちゃんと書いてありました。

Troubleshooting “InvalidSignatureException – Signature expired: [time] is now earlier than [time]” deployment errors

AWS CodeDeploy requires accurate time references in order to perform its operations. If your instance’s date and time are not set correctly, they may not match the signature date of your deployment request, which AWS CodeDeploy will therefore reject.

OneAtATime で最後の1台のデプロイ失敗は無視される

インスタンス1台1台のデプロイの成功・失敗とは別に、デプロイ全体(デプロイ ID ごと)の成功・失敗という概念があります。
通常、Deployment config が OneAtATime の場合、対象インスタンスに順次デプロイしていく途中で1台でも失敗するとデプロイが中断され全体として失敗となります。

しかし、最後の1台(4台構成なら4番目にデプロイされるもの)が失敗しても全体として成功と評価されます。
これもドキュメントに書かれているので仕様のようですが、その理由は “1台ずつオフラインになることを前提とした設定だからいいよね” (超意訳。あってるよね?)
・・・そうっすか笑。

The overall deployment succeeds if the application revision is deployed to all of the instances. The exception to this rule is if deployment to the last instance fails, the overall deployment still succeeds. This is because AWS CodeDeploy allows only one instance at a time to be taken offline with the CodeDeployDefault.OneAtATime configuration.

このため、全体の評価が成功になっていたとしても失敗したインスタンスがないか確認したほうがいいかもしれません。

停止しているインスタンスにもデプロイの試行がされる

CodeDeploy のターゲットはインスタンスの任意のタグを指定することができます。
この場合、指定したタグの付いたインスタンスであれば停止しているインスタンスに対してもデプロイの施行が行われてしまいます。
もちろん、停止しているインスタンスにあるエージェントがこの指示を受け取れるわけはないので、しばらくするとタイムアウトで必ず失敗します。

ちなみにマネジメントコンソールで Deployment Group を作成する際にタグを指定するとデプロイ対象のインスタンスが確認できますが、このタイミングで停止しているインスタンスも破棄されたインスタンスもマッチします。
(破棄されたインスタンスへはデプロイの施行はされません)

AWS サポートに問い合わせたところ「現時点ではそういう動作をするようになっているが、ドキュメントへの記載はなく、好ましい動作とは言えないので開発部門へフィードバックする」との旨の返答をいただきました。
(「単純にバグなのでは?」という気もしますが、AWS サポートは丁寧で非常に好感が持てます!)

とりあえず、何かの都合で一部インスタンスを破棄はせず停止しておきたいような場合の対応は、現時点ではインスタンスのタグを一時的に変更するのが手っ取り早くていいと思います。

最後に

AWS のサービスは実際に使ってみて初めて知る仕様や、ちょっとしたノウハウが必要なものが多くあり苦戦することもありますが、それもまた面白いところかもしれません。
公式ドキュメントでも日本語化されていないものがあったり、日本語ドキュメントには載ってなくて英語版にだけあるといったことも多々あって英語力の低い僕は大変です・・・。

CodeDeploy もそれに漏れず、いろいろありました。
とは言え、その恩恵は大きく、デプロイのコード化で Excel 方眼紙のデプロイ手順ともおさらばできますね!
AWS CodeBuild が2016年の Re:Invent で発表され、デリバリもどんどん楽になりそうです。

続きを読む

Terraformで始めるRolling deployment

:santa:この記事は Recruit Engineers Advent Calendar 2016 の22日目の記事です。:santa:

昨日はmookjpさんのLet It Crashとは何かでした!ゴイスー!

本日はサーバサイドエンジニアとして従事している私がAWSでインフラ構築をした記事となります。
この記事を書く1週間程前はそろそろ記事の下書きをしておくかー。
と、やる気に満ち溢れていたのですが、悲しきかな・・・PS4 proが届き当時の気持ちはどこかに置いてきてしまったようで、急いで先程記事を書き終えました。

はじめに

日々やらねばいけないことが満ち溢れている中、運用サーバトラブルに対して時間は割きたくないものです。例えば、EC2の突然死やメンテナンスです。
限られた時間の中でより効率的に時間を使いたいという欲求を皆様お持ちではないでしょうか。
そこで楽をしようと思い立ち、本エントリーの構成にしたのです。

使うツール

細かいものは省略して大枠だけ。

AWSで利用したサービス

細かいものは省略します。

  • ALB
  • AutoScaling
  • EC2
  • CloudWatch Events
  • Lambda
  • SNS

Rolling deployment

デプロイにおいてTerraformでやっていること

結論だけ先に書くと、

[Terraformでやっていること]

  • DataSourceを使って最新版のAMIを取得
  • ASGのLaunchConfigurationを作成
  • ASGの更新

その他は基本Lambdaに任せています。
AMIの作成はDroneを使ってPackerを実行しています。

デプロイフロー

まずはアプリケーションリリースまでのデプロイフローを見てみましょう。

AWS Design.png

あまり複雑にならないよう心がけたつもりで、それぞれのSTEPで見たときにシンプルになれば良いかなと思いこのフローにしています。

ステップごとに見るフロー

1. AMI作成

CIサーバ

弊社ではGitHub Enterpriseを使っているので、今までJenkins2を使ってCIを回していましたが、やはりコンテナベースでテストを回さないとジョブがconflictするわけです。
一々レポジトリごとにDockerfile用意するのも面倒だったので、OSS版のDroneに切り替えました。
Droneを一言で表すなら「最高」。

DroneからAMIをビルドする

PackerとAnsibleを使ってAMIを作成しています。
後述しますが、この時新しいAMIを作るうえで利用するAMIは、予め用意しておいたベースのAMIで、そこから新しいバージョンのシステムをデプロイ & ビルド(npm install / bundle install…)してAMIを作成しています。

2. ASG更新

Terraformを使ってASGを更新する

LaunchConfigurationの更新は行えないため、Terraformを使ってLaunchConfigurationを作成し、既存のASGに紐付けています。

3. スケールアウト

新しいAMIを利用したEC2インスタンスを立ち上げ

ASGの更新をトリガーにCloudWatch EventsからLambdaを起動しています。
このLambdaでは下記を実行しています。

  1. 更新したAutoScalingGroupのDesiredCapacityを取得
  2. setDesiredCapacity(DesiredCapacity * 2)を実行して、新しいAMIのインスタンスを立ち上げる

ここまでで新しいバージョンのシステムをリリースすることが出来ました。

4. スケールイン

古いAMIインスタンスの破棄方法

特に何かしていません。
setDesiredCapacity()を使って、一時的にスケールアウトしている状態なので、ASGに設定されたcool down経過後は古いインスタンスからスケールインされます。

とはいえ、いきなりTerminateされても困る

Fluentdを使っているので、いきなりTerminateされてバッファがflushされないまま破棄されても困ります。
なので、Lifecycle Hookを使って下記を実行します。

  1. スケールイン前にSNSへ通知を送る
  2. SNS通知をトリガーにLambdaを実行する
  3. LambdaからSSMのRun Commandを実行
    1. Fluentdのバッファをflush
    2. CompleteLifecycleActionを実行

これでFluentdのバッファをflushさせつつ、スケールインさせることが出来ました。

それぞれ工夫したところ

ゴールデンイメージの作成

デプロイの度に1からAnsibleのプロビジョニングを実行していると、とても時間がかかります。
なのでAMIをbase / application_base / applicationと分けていて、アプリケーションの更新だけであれば、baseから作成したapplication_baseを使って新しいAMIを作っています。

それぞれのAMIは、下記の役割で作っています。

base

基本的にOSの設定(Timezone / Kernel parameters…)であったり、インタプリタのインストールであったり頻繁に変更が行われないものをプロビジョニングしています。

application_base

アプリケーションが依存するLinuxライブラリのインストール等を行っています。

application

アプリケーションのデプロイとビルドを行います。npm installnpm run hogeであったりbundle installはここで行っています。

システムの更新であればapplicationのビルドしか行いませんが、他のレポジトリで管理しているbaseもしくはapplication_baseの構成が変わった場合は、そちらのビルドが走ります。

Packer / Ansible

ディレクトリ構成

実際のものとは異なりますが、共通で読み込む変数とそうでないものでPackerもAnsibleもファイルを分けています。↓のcommon*です。

├── provisioners
│   ├── ansible.cfg
│   ├── base.yml
│   ├── inventories
│   │   ├── common.yml
│   │   ├── common_secrets.yml
│   │   ├── development
│   │   │   ├── group_vars
│   │   │   │   └── development
│   │   │   │       ├── secrets.yml
│   │   │   │       └── vars.yml
│   │   │   └── inventory
│   │   ├── production
│   │   │   ├── group_vars
│   │   │   │   └── production
│   │   │   │       ├── secrets.yml
│   │   │   │       └── vars.yml
│   │   │   └── inventory
│   ├── requirements.yml
│   ├── roles
│   ├── site.yml
├── packer.json
└── variables
    ├── base.json
    ├── common.json

ファイル名が*secretsになっているものはAnsible-vaultで暗号化したファイルです。
復号化はプロビジョニング実行時にtemporaryのpasswordファイルを用意してansibleに読み込ませています。
こんな感じです。

packer.json
"extra_arguments": [
  "--tags",
  "{{user `tags`}}",
  "--vault-password-file",
  ".vault"
]

Ansible Galaxy

極力Ansibleのroleは、Ansible Galaxyのroleとして使えるように書いて、GitHub EnterpriseにあるAnsible Galaxy organizationにレポジトリを作っています。
なので、provisioners/requirements.ymlが置いてあります。

site.yml

実際のものとは異なりますが、こんな感じにしてtagでincludeするymlファイルを制御しています。

---
- vars_files:
    - inventories/common.yml
    - inventories/common_secrets.yml
- include: base.yml tags=base
- include: hoge.yml tags=hoge

site.ymlは極力シンプルに。includeしている各ymlからroleを読み込んでいます。

Terraform

ディレクトリ構成

こちらも実際のものとは異なりますが、PackerやAnsible同様、共通で読み込む変数とそうでないものでこんな感じにしています。
また、Stageごとにtfstateを分けています。

├── environments
│   ├── common.tfvars
│   ├── development
│   │   ├── ami.tf
│   │   ├── main.tf
│   │   ├── provider.tf
│   │   ├── terraform.tfvars
│   │   └── variables.tf
│   └── production
│       ├── ami.tf
│       ├── main.tf
│       ├── provider.tf
│       ├── terraform.tfvars
│       └── variables.tf
├── provider.tf
├── vpc.tf
├── terraform.tfvars
└── variables.tf

Terraform Modules

Ansible Galaxy同様、こちらもorganizationを用意しレポジトリを作っています。
なので上のenvironments以下のディレクトリにあるmain.tfはmoduleを読み込み変数をセットするだけに留めています。

Lambda

Runtime

利用しているLambdaは全てnodejs4.3で記述していて、AWSリソースの操作はAWS SDKを利用しています。

デプロイ

apexを利用してLambdaのデプロイを行っています。
基本はapexのMultiple Environmentsに従った構成にしています。

├── functions
│   ├── hoge1
│   │   ├── function.development.json
│   │   ├── function.production.json
│   │   ├── index.js
│   │   └── package.json
│   └── hoge2
│       ├── function.development.json
│       ├── function.production.json
│       ├── index.js
│       └── package.json
├── project.development.json
└── project.production.json

project.jsonはこんな感じにして、

"nameTemplate": "{{.Project.Name}}_{{ .Project.Environment }}_{{.Function.Name}}",

同一ソースで異なるfunction名かつ、それぞれが異なる環境変数の値を保持することが出来ました。

最後に

AMIのビルドまでをアプリケーションのパッケージングに見立て構築しています。
本来、この思想であればコンテナを使うほうが楽だと思っていますが、アプリケーションのcontainerizedが出来ていなかったのでこうなっています。

また、当初はALBのリスナー付け替えでBlue-Green Deploymentにしようか悩んでいたのですが、瞬断が気になるので結局Rolling deploymentを選択しました。

全体的にざっくりとしか書いていません。本当は短い時間の中で紆余曲折あり今の形になっているのですが、それぞれのもっと細かい話は別の記事で書こうと思います。:innocent:

次の記事は、kadoppeさんです!よろしくお願いします!

続きを読む

AWS認定ソリューションアーキテクト-アソシエイト レベル別のおすすめ勉強法 2016/12版

はじめに

受験の背景

ソリューションアーキテクト-アソシエイトの受験費の補助が会社から出るらしいと聞いて、受験を決意。
アドベントカレンダーのネタがなかったので、このタイミングで受けようと決意。
結果なんとか合格出来たので、実際に行った勉強法の共有と、今後勉強する向けのレベル別勉強法ガイドを作ってみました。
年末年始に勉強してみるかーという人の一助になれば。

注意点

筆者の勉強期間と結果は以下で、ホントにギリギリでの合格だったので、勉強法は全然完璧なものではありません。
そんなやつがこんな記事書くのもおこがましいのですが、、、アドベントカレンダーなんで、、、お許しを、、、
大体どんなことやれば受かるのかなというイメージが付くくらいを目的として利用いただければと思います。

  • 勉強期間: 約2週間
  • 模擬試験: 65%: 不合格
  • 本試験: 67%: 合格

レベル別おすすめ勉強法

レベル診断チャート

  1. AWS 認定ソリューションアーキテクト – アソシエイト の試験ガイドのIT全般の知識の箇所をざっと眺めてみる

  2. AWS上で、0からアプリケーション動作環境を構築したことがある or それに準ずる経験がある(ハンズオン系のチュートリアル等)

    • ない → レベル1
    • ある → 3へ
  3. AWS 認定ソリューションアーキテクト – アソシエイト のサンプル問題を解いてみる

    • 正解数が半分未満 → レベル2
    • 正解数が半分以上 → レベル3
    • 全問正解/楽勝 → アソシエイトじゃなくプロフェッショナルの方がいいかも?

レベル別おすすめ勉強法一覧表

勉強法 レベル1 レベル2 レベル3
試験要覧(BluePrint)を確認する
入門系の書籍
試験対策本
AWS活用資料集を読む
模擬試験
出題傾向を把握する
WEBの非公式問題を実施
AWS公式ホワイトペーパーの通読
AWS公式のセミナーに参加 N/A N/A N/A

◎=おすすめ ◯=やれるとよい △=余力があれば -=不要 N/A=不明

全レベル共通のおすすめ

  • 試験要覧をざっと見て範囲を把握すること
  • ある程度学習が進んだら模擬試験受けてレベル感を把握すること
  • 出題傾向を踏まえて活用資料集等を見ること

レベル1

想定: AWSをほとんど触ったことがない人

  • 勉強法を上から順番にやるのがおすすめです
  • ハンズオン形式の入門系の書籍・幅広くサービス解説している書籍を2冊、最初に手を動かしながらやると理解が進みます

レベル2

想定: AWS使ってるけどちゃんとは把握してない人/なんとなく把握しているけど正確さや網羅的には把握してない人

  • 以下の流れがおすすめです

    • 試験要覧(BluePrint)見て知らないサービスを把握する
    • 対策問題集をざっと確認し、自分の知らない・苦手なサービスを把握する
    • 上記サービスを重点的に活用資料集を読んで行くのがいいかと思います
  • 必要に応じて最初に幅広くサービス解説している書籍を読むのもいいかと思います

レベル3

想定: 普段から業務でガッツリAWSを触っている人

  • 以下の流れがおすすめです

    • 試験要覧(BluePrint)見て知らないサービスを把握する
    • 非公式問題集や模擬試験受けて出題傾向把握しつつ、自分の弱い箇所を把握
    • ホワイトペーパーや活用資料集を必要な箇所通読していく

勉強法別の内容解説

入門系の書籍を読む

筆者が利用したのは以下の2冊です。
ハンズオン形式の書籍を参考にして実際に構築するのが一番勉強になりますし、やっていて楽しいです。

その他、他の受験者の方々が利用していた参考書です。書店等でちら見して、合うものを選ぶのが良いかと思います

試験対策本

AWS試験対策本としては現状唯一の書籍です。
章末にサンプル問題が付いており、試験のイメージを持ちながら各サービスの重要なポイントを学んでいくことが出来ます。
サンプル問題のレベルは本番・模擬試験に比べて大分簡単ですし、 最新のサービスに関してやや情報が追いついてない箇所があったりします。

AWS活用資料集を読む

BlackBeltシリーズは非常にわかりやすく、質が高いので勉強になります。
また、模擬試験やWEB上の試験を解いた後に、復習がてら何度も戻ってくることになると思います。
筆者の経験とWEBの情報をざっくりまとめると、サービス別の出題頻度は以下なので、重要なものから優先的に学ぶとよさそうです。

サービス別出題頻度

  • 必須

    • EC2、S3、VPC、S3、Auto Scaling、ELB、Route53、IAM

      • ここやらないと落ちちゃいます
  • ここまでは抑えたい
    • CloudFront、EBS、RDS、CloudWatch、SQS、SWF、SNS、DynamoDB、ElastiCashe

      • ここをきちんと抑えられれば合格するかと思います
  • やっとくと安心
    • CloudFormation, OpsWork, Beanstalk, ElasticMapReduce, Kinesis, Redshift, Glacier, DataPipline, Direct Connect, CloudSearch, Storage Gateway

      • 各1~2問程度出る可能性があったり、選択肢に出てくるサービス群です

上2段をしっかりやれば受かると思います。
やっとくと安心系は、出題傾向踏まえて活用資料集に目を通すと効率がよいかなと思います。

BlackBeltリストがある記事

模擬試験

項目 内容
試験時間 30分間
問題数 20問
受験料 ¥2,160
  • 試験問題はコピーできるのですべてメモして復習しましょう
  • 有料ですが、試験のレベル感がつかめる為、ある程度勉強が進んだら受けるのをおすすめします
  • 複数回受験しても同じ問題が出たりするようなので一度で良いかと思います

出題傾向の把握

模擬試験や本試験を受けて、おおよそ出題されそう・重要であろうテーマのようなものがわかってきます。他の諸先輩方のまとめなども参考にしつつ、内容を確認し、分からない部分はBlackBeltやドキュメントを読むと試験対策として有用かと思います。
以下、筆者の肌感でのメモと、参考になりそうな先輩方の記事を紹介します。

  • EC2/EBS

    • EBSのボリュームタイプ別の使い分け
    • EBSのデータ暗号化方法
    • スポットインスタンス・リザーブドインスタンスのユースケースとコスト
  • S3

    • S3のアクセス制限の使い分け(バケットポリシー/ACL/IAM)
    • S3の結果整合性
    • S3のストレージクラスとGlacierの使い分け
  • VPC/ネットワーク

    • VPCとIGW/CGW/VGW周りとルーティング設定
    • インスタンスにアクセスできない
    • インスタンスからインターネットにアクセスできない(NAT構築した場合など)
    • セキュリティグループとネットワークACLの違い
    • ハイブリッドITアーキテクチャ (例: Direct Connect、Storage Gateway、VPC、ディレクトリサービス)のケーススタディ
  • AutoScaling

    • 基本挙動と起動設定・グループ・ポリシーの中身
  • Route53/CloudFront

    • Route53のレコードタイプ(特にAlias)
    • CloudFrontのユースケースと特徴
  • デプロイ

    • AWS Elastic Beanstalk、AWS CloudFormation、AWS OpsWorksの違いとユースケース
  • AWS全般/セキュリティ/監視

    • AWSの責任共有モデル
    • 侵入テストの手順
    • AWS提供サービス全般の概要と簡単なユースケース
    • AWSの各ストレージサービスの特徴とユースケース
    • CloudWatchのカスタムメトリックスが必要な項目

書いてくとキリがないのと、怒られそうなので、このくらいにします。
以下の記事の方の中の出題傾向や、ハンズオンでやっておくとよいこと等記載されており大変有用でした。

WEBの非公式問題を実施

AWS WEB問題集 – AWS WEB問題集で学習しよう

  • 会員登録をすると無料で29問ほどは問題を解くことが出来る
  • 有料会員になると最大600問以上の問題を解くことが出来るので、黒本形式で勉強を勧めたい方にはおすすめです

AWS公式ホワイトペーパーを読む

一部日本語のものもありますが、英語のドキュメントが多いです。
公式の試験要覧でのおすすめの試験対策として上がっており、網羅的にサービスの解説等載っているので、やるほうがベターではあります。
筆者は全部を読み通す余力がなく、部分的につまみ食いしました。

AWS公式のセミナーに参加

AWS公式で提供しているセミナーがあり、有料でそれなりに良いお値段がしますが、会社から補助等が出るなら参加するとよさげです。

筆者の体験記

筆者の実際の勉強の流れ

全体の所感

模擬試験よりも、本試験の方が一段難易度が高い印象なので、模擬試験でレベル感を把握しておくことが非常に有用だったなと思います。また、主要なサービス関連の問題の出題比率が多く、特に出題されやすい箇所は重点的に学習しておくと点数につながりやすそうですし、実際に現場でも有用な知識なのだと思います。
ソリューションアーキテクト試験自体は、AWS力が劇的に上がる類のものではありませんが、AWSのサービスを網羅的に把握できるので、AWSを使うことへのモチベーションが向上しました。

参考

多くの先達の皆さんの学習法を参考にさせていただきました。
またクラスメソッドさんのブログは各サービスごとの解説等、とても丁寧でお世話になりました。

続きを読む

kube-awsにノードプール機能が追加されました

このエントリーはKubernetes Advent Calendar 2016の5日目の記事です。

4日目はkoudaiiiさんのCronJob(ScheduledJob) – Qiitaでした。これKubernetesのキラーフィーチャーの一つな気がするのですが、あんまり話題になってない気がするのでみんなもっと知ってほしい。分散cronがビルトインなのはKubernetesだけですよ!(例えばMesosのChronosはまだビルトインじゃないですよね?

5日目のこの記事では、kube-awsに最近追加されたNode Pools(ノードプール)という機能について書きます。

TL;DR;

kube-aws v0.9.2-rc.2から、GKEにもある「ノードプール」という便利機能が追加されて、コスト圧縮やMultiAZ・ノードオートスケーリング(Podではなく)の両立がしやすくなりました。

kube-awsとは

kube-awsはKubernetesクラスタをCloudFormationスタックとして構築・管理するためのOSSです。
現在はH/A構成のKubernetesクラスタの作成、更新、削除に対応しています。

手前味噌ですが、kube-aws自体の詳しい説明や使い方については、CloudFormationでCoreOS + Kubernetesクラスタをつくるkube-awsのまとめ – Qiitaを読んでみてください:wink:

ノードプールとは

GKEに実装されている、一つのKubernetesクラスタに複数タイプのWorkerノードを混在させるための機能です。
GCPJブログの記事がわかりやすいので、抜粋させていただきます。

異種構成のクラスタを作成することは、これまでは非常に困難でした。
従来、クラスタを作るときには、ノードのマシン タイプやディスク サイズなどのオプションを選択できました。しかし、それがすべてのノードに適用されるため、クラスタは同種的になっていたのです。
このたび GA リリースされた GKE の新機能であるノード プールは、この問題を解決します。ノード プールは同じ構成のマシンのコレクション、すなわち “プール” です。これにより、クラスタはすべてのノードが同じでなければならない画一的なものではなくなり、複数のノード プールを持てるようになって、従来よりも細かくニーズに対応できるようになりました。
Google Cloud Platform Japan 公式ブログ: Google Container Engine(GKE)に追加されたノード プールの使い方

GKEではノードプール毎に以下の項目がカスタマイズできます。

  • マシンタイプ(AWSでいうインスタンスタイプ)
  • ゾーン(AWSでいうAZ)
  • ノード数

クラスタに0個以上のノードプールを追加することができて、各ノードプールにそれぞれ1個以上のノードが作成されます。ノードプール内のインスタンスは、同じマシンタイプ、ゾーンなどが割り当てられます。

ノードプールがあると何がうれしいのかというと、クラスタ全体で無駄にするリソースが減るということと、H/Aとオートスケーリングの両立、の2点が達成できることです。

クラスタ全体で無駄にするリソースが減る

例えば、メモリを1GB要求するPodと10GB要求するようなPodが混在するような環境の場合、ノードプールがないときは最低でもメモリを10GB以上積んでいるノード(GCPだとマシンタイプ)を選択する必要がありました。それ未満のメモリしか積んでいないノードを選択してしまうと、10GB要求するPodをスケジュールできないからです。

しかし、そうすると例えばそれぞれのPodのレプリカ数が1でいい・・・という場合、無駄なりソースが生まれてしまいます。例えばメモリを10GB搭載しているノードを採用したとすると(実際はそんな中途半端なメモリサイズのマシンタイプはなさそうですが)、要求メモリは合計11GBですがノード数は2となってしまい、20-11=メモリ9GB分に支払っているコストは無駄、ということになってしまいます。

ノードあたりのメモリサイズ: 10GB
要求メモリ: 10+1 GB
確保メモリ: 10*2 GB
無駄になったメモリ: 9 GB (:point_left: 減らしたい)

これを解決する一つの方法は、Podによってスケジュール先のノードプールをわけることです。

例えば、2GBのメモリを搭載しているノードからなるノードプールと、10GBのメモリを搭載しているノードからなるノードプールを用意します。1GB要求するpodは前者のプールに、10GBのpodは後者のプールにスケジュールするようにすれば、先程の例だとクラスタ全体で要求されるリソースはメモリ11GBに対して、確保するりソースは12GBになります。要求11GBに対して確保12GBなので、無駄が1GBになります。

プール1:
ノードあたりのメモリサイズ: 2GB
要求メモリ: 1 GB
確保メモリ: 2 GB
無駄になったメモリ: 1 GB

プール2:
ノードあたりのメモリサイズ: 10GB
要求メモリ: 10 GB
確保メモリ: 10 GB
無駄になったメモリ: 0 GB

合計で無駄になったメモリが9GBから1GBに減った :tada:

極端な例ではありますが、ノードプールがないとこれが全くできませんでした。

// なお、実装としては、それぞれのノードを区別するためのlabelをつけて、podのnode selectorを使ってスケジュール先のノードを選択させます。

H/Aとオートスケーリングの両立

実は、ノードプールがないとこの2つは両立できないのです。

KubernetesにはUbernetes Liteという機能があります。Kubernetesクラスタに複数AZのworkerノードを混ぜると、podを分散してスケジュールしてくれる関係で、結果的にpodがMulti-AZ構成になる、というものです。一つのAZが落ちたとしても、クラスタで稼働しているpodが全て落ちたりはせずにすみます(ただし、replicasetでreplica数は最低2にしましょう!)

一方で、Kubernetesクラスタのノードのオートスケーリングに使うcluster-autoscalerというものがあります。これの仕組みは、Kubernetesクラスタ全体でpendingになっているpodを発見したら、それが「スケジュールされるように必要なノードを追加する」です。おっ、かしこい!と思うじゃないですが。しかし、これには落とし穴があります。READMEに詳しく書かれているので抜粋すると、

The autoscaling group should span 1 availability zone for the cluster autoscaler to work. If you want to distribute workloads evenly across zones, set up multiple ASGs, with a cluster autoscaler for each ASG. At the time of writing this, cluster autoscaler is unaware of availability zones and although autoscaling groups can contain instances in multiple availability zones when configured so, the cluster autoscaler can’t reliably add nodes to desired zones. That’s because AWS AutoScaling determines which zone to add nodes which is out of the control of the cluster autoscaler. For more information, see https://github.com/kubernetes/contrib/pull/1552#discussion_r75533090.
:link:https://github.com/kubernetes/contrib/blob/master/cluster-autoscaler/cloudprovider/aws/README.md

例えば、`1 ノード郡 = 1 ASG = 2 AZ1というセットアップにしたときを考えてみます。

  • myappというreplicasetがmyapp-1~3というpodをスケジュールしている状態(replicas=3)
  • ASGに2つのAZが割り当てられている
  • ASGにnode1とnode2がいる
  • それぞれnodeのキャパはいっぱい

この状態で、myappのレプリカ数を増やしてみます。

ノード名 AZ Pods
node1 ap-northeast-1a myapp-1, myapp-3
node2 ap-northeast-1b myapp-2, anotherapp-1

pendingになっている: myapp-4

nodeのキャパがいっぱいなのでmyapp-4はpendingになりました。

では、ノードを増やします。
ん?どちらのAZのノードを増やすのが理想でしょうか?

答えは、1bです。myappは1aに2つ、1bに1つというようにアンバランスにスケジュールされています。AZに均等にデプロイする(Ubernetes Lite)ために、Kubernetesのスケジューラは1bにpodをスケジュールしようとするからです。

改めてノードを増やしましょう。まず、1bにノードを追加にするため、ASGのDesired Capacityを増やして…。

ん?
Desired Capacityを増やしたら、1aと1bのどちらにノードが追加されるのでしょうか?

未定義なんです…。これはASGの制限?仕様です。場合によって1aが追加されることもあるし、1bが追加されることもあります。もし1aに追加された場合、1bにスケジュールされたがっているpodはpendingのままなので、もう一度cluster-autoscalerによりDesired Capacityが+1されます。今度はノードをAZでバランスするため1bにノードが追加されるでしょう。pendingになってから、必要なノードが揃うまで2回のイテレーションが必要でした。これがREADMEに書かれている”can’t reliably add nodes to desired zones”ということです。

ノードプールがあれば、ノードプール毎に単一のAZに割り当てるようにしておくことで、この問題を回避できます。podに割り当てられているノードのASGにAZが2つ割り当てられているとunreliableになりますが、ASGが一つになっていたらpendingになっているpodのノードが所属しているASGを+1にすれば確実に足りないAZにノードが追加されるからです(長い)

ノードプールまとめ

コストを圧縮したり、もしくはMulti-AZ構成でノードのオートスケーリングを行いたい場合は必須な機能です。

kube-awsのノードプールとは

コスト圧縮やMulti-AZとノードのオートスケーリング両立をサポートできるように、GKEと似たような同名の機能を実装したものです。

2016/12/5時点の仕様は以下のとおりです。

  • メインとなるクラスタと、各ノードプールは個別のCloudFormationスタックになる

    • ただし、クラスタ間で共有する必要があるAWSリソース(特定のSecurityGroupやVPC、RouteTableなど)はCloudFormationのクロススタックリファレンス機能を利用します
    • 結果的に、ノードプールを消さないと、メインとなるクラスタは消せない(事故防止
  • ノードプール毎にメインクラスタのworkerノードと異なる設定ができる
  • ノードプール毎にカスタマイズできるもの
    • Kubeletバージョン
    • CoreOSバージョン
    • KMSキー
    • KeyPair名
    • インスタンスタイプ
    • EBSボリュームのサイズ、タイプ、IOPS
    • cloud-config

2016/12/5時点でできないことは以下のとおりです。

  • 自動的にlabelをつける

    • labelをつけないとpodをノードプールに振り分けられないので、コスト削減の用途だとそのままは使えません
    • cloud-configを変更して、kubeletがノードを登録する前にラベルをつけるようにする必要があります
    • // GKEだとデフォルトでいくつかlabelはつくが、カスタムラベルは手動でつける必要があったはず
  • 自動的にtaintをつける
    • labelと似ているけど逆の機能でtaintというものがあります
    • labelはノードを選択するために使いますが、taintはノードをデフォルトで除外するために使います
    • 特定ノードプールは常に開けておきたい、特定pod専用にしたい・・・というときにlabelより便利です
    • // GKEでもできないはず
  • SpotインスタンスやSpot Fleetを使う
  • ノードプール内のインスタンスのRollingUpdate(インスタンスタイプなどを変更して無停止で1台ずつ入れ替えかえる機能)
    • メインクラスタに対してはkube-aws updateというコマンドでできますが、ノードプール用はまだありません

kube-awsのノードプールを作成する手順

メインクラスタを作成するためのkube-aws (init|render|up)と同じコマンドが、node-poolsサブコマンド配下に追加されているので、それを使います。

$ kube-aws init --cluster-name ${メインクラスタ名} \
      --availability-zone ${AZ名} \
      --key-name ${EC2キーペア名
      --kms-key-arn ${KMSキーARN}
$ kube-aws render
$ kube-aws up
# ここまででメインクラスタが起動して、cluster.yamlが生成されている状態になる。

# ここからノードプール作成
$ kube-aws node-pools init --node-pool-name ${ノードプール名} \
      --availability-zone ${AZ名} \
      --key-name ${EC2キーペア名
      --kms-key-arn ${KMSキーARN}
$ kube-aws node-pools render --node-pool-name ${ノードプール名}
$ kube-aws node-pools up

まとめ

  • kube-awsにもGKEと似たようなノードプール機能が追加されました :tada:
  • ノードプールはkube-aws v0.9.2-rc.2からサポートされています。
  • Kubernetesをコスト削減の目的で導入したい場合や、Multi-AZ・ノードオートスケーリング(Podではなく)の両立をしたい場合にもkueb-awsが利用できるようになりました

続きを読む

re:inventで発表されたX-Rayを試そうとしてみた

プレビューを申し込んですぐに利用可能になったので、早速トライしてみました。

TOPページ

スクリーンショット 2016-12-01 10.01.28.png
previewついてますね。

Get Started

ちゃんとガイド付きです。「Launch ~」を選択すると、サンプルアプリが起動します。
スクリーンショット 2016-12-01 10.01.38.png

ちゃんと削除する方法まで載っていて、CloudFormationあまり使わない人にも親切です。
スクリーンショット 2016-12-01 10.05.58.png

ここから先は従来通りのCloudFormationでのスタック起動作業です。
スクリーンショット 2016-12-01 10.06.10.png

既存アプリに採用する場合

言語がnodeJavaC++の3つに限られる様子です。
スクリーンショット 2016-12-01 10.01.47.png

nodeを選びましたが、インスタンスにデーモンを入れるかsdkをアプリに組み込むかを求められます。
スクリーンショット 2016-12-01 10.01.57.png

なお

テストのCloudFormationですが、基調講演に集中せずに使おうとしたバチがあたったのか、コケました。
スクリーンショット 2016-12-01 10.13.49.png

あとでちゃんとやりなおします。

とりあえずごめんなさいの気持ちと共に基調講演の話を聞く姿勢に戻ります。

追記

default VPCがない状態になってるアカウントで試したために起きた問題っぽいです。
最近作ったアカウントで動かしたところ、問題なく動きました。

[おまけ]X-RayサンプルアプリのCFN

CloudFormationでサンプルアプリ作るらしいので、そのテンプレートを引っこ抜いてみました。
以下がそれ。

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SampleInstanceProfileRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: ''
          Effect: Allow
          Principal:
            Service:
            - ec2.amazonaws.com
          Action: sts:AssumeRole
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/AWSElasticBeanstalkWebTier
  XRayWriteOnlyPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: AWSXRayWriteOnlyPolicy
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Action:
          - sns:Publish
          - xray:PutTelemetryRecords
          - xray:PutTraceSegments
          - dynamodb:PutItem
          Resource:
          - "*"
      Roles:
      - Ref: SampleInstanceProfileRole
  SampleInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: "/"
      Roles:
      - Ref: SampleInstanceProfileRole
  SampleEBServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: ''
          Effect: Allow
          Principal:
            Service: elasticbeanstalk.amazonaws.com
          Action: sts:AssumeRole
          Condition:
            StringEquals:
              sts:ExternalId: elasticbeanstalk
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSElasticBeanstalkService
      - arn:aws:iam::aws:policy/service-role/AWSElasticBeanstalkEnhancedHealth
  ElasticBeanstalkApplication:
    Type: AWS::ElasticBeanstalk::Application
    Properties:
      ApplicationName: 
        Ref: AWS::StackName
  ElasticBeanstalkApplicationVersion:
    Type: AWS::ElasticBeanstalk::ApplicationVersion
    Properties:
      ApplicationName:
        Ref: ElasticBeanstalkApplication
      SourceBundle:
        S3Bucket:
          Fn::Join:
          - "."
          - - aws-xray-assets
            - Ref: AWS::Region
        S3Key: samples/aws-xray-node-sample-app.zip
  ElasticBeanstalkEnvironment:
    Type: AWS::ElasticBeanstalk::Environment
    Properties:
      ApplicationName:
        Ref: ElasticBeanstalkApplication
      Description: AWS X-Ray Getting Started Sample Application
      EnvironmentName:
        Ref: AWS::StackName
      OptionSettings:
      - Namespace: aws:autoscaling:launchconfiguration
        OptionName: InstanceType
        Value: c4.large
      - Namespace: aws:autoscaling:launchconfiguration
        OptionName: IamInstanceProfile
        Value:
          Ref: SampleInstanceProfile
      - Namespace: aws:elasticbeanstalk:environment
        OptionName: ServiceRole
        Value: aws-xray-sample-eb-service-role
      - Namespace: aws:elasticbeanstalk:environment
        OptionName: EnvironmentType
        Value: SingleInstance
      - Namespace: aws:elasticbeanstalk:healthreporting:system
        OptionName: SystemType
        Value: enhanced
      SolutionStackName: 64bit Amazon Linux 2016.09 v3.1.0 running Node.js
      VersionLabel:
        Ref: ElasticBeanstalkApplicationVersion
Outputs:
  ElasticBeanstalkEnvironmentURL:
    Description: URL for the Elastic Beanstalk Getting Started Sample Application
    Value:
      Fn::GetAtt:
      - ElasticBeanstalkEnvironment
      - EndpointURL
  SampleInstanceProfileRole:
    Description: IAM Role used for AWS X-Ray Getting Started Sample Application
    Value:
      Fn::GetAtt:
      - SampleInstanceProfileRole
      - Arn
  SampleInstanceProfile:
    Description: Instance Profile used for AWS X-Ray Getting Started Sample Application
    Value:
      Ref: SampleInstanceProfile
  SampleEBServiceRole:
    Description: IAM Role used for AWS Elastic Beanstalk Service Role
    Value:
      Fn::GetAtt:
      - SampleEBServiceRole
      - Arn

続きを読む