コンテナデプロイ基盤の検証

はじめに

はてなサマーインターン2018の大規模システム開発コースの成果報告をします。

今年は、メンターのid:cohalzさん、id:wtatsuruさんの下、実際に使われているサービスをAmazon ECS(Elastic Container Service)にデプロイする基盤を構築しました。 コンテナでサービスを本番運用するために、AutoScaleの検証や、デプロイ時間の計測、改善策の検証を行いました。また、開発、デプロイフローを楽にするために、AWS CodeBuild、CodePipelineを使ってCI/CDの構築も行いました。これにより、PullRequestごとにCIが走り、masterにマージされたら自動でECSにデプロイすることができるようになります。高速なデプロイ切り替えを行うために、Blue-Green Deploymentの検討も行いました。 他にも、MicroScannerを用いてセキュリティスキャンを行い、パッケージの脆弱性を確認を行いました。 これらの構成の再現性を持たせるために、CloudFormationで構成を管理することもやっていきました。


既存の課題

急なアクセス数増加への対応

問題点

現在は、負荷を見てそれに耐えられる台数を決めていました。 しかし、負荷が少ないときは、リソースが余ってしまいます。 負荷が増えても、EC2インスタンスを手動で増やす必要があります。 そのため、想定以上の急なアクセスがあった場合、対応が遅れてしまうことがあります。

解決策

コンテナ化し、スケーリングしやすくします。 AutoScaleを導入し、負荷に応じてタスク数を変えます。

CI/CD管理コスト

問題点

CIにJenkinsを使っていますが、 Jenkinsサーバの管理コストがかかっていました。 さらに、CDは行っておらず、リリースプロセスは人手で行っていました。

解決策

CI/CDには、CodeBuild、CodePipelineを用います。 CodeBuildでは、テストや、Dockerイメージの作成を行います。 CodePipelineでは、CodeBuildで作ったイメージを使って、ECSに自動デプロイを行うようにしました。

AWSでのサービスのコンテナ化

AWSでコンテナアプリケーションを運用する際、ECS(Elastic Container Service)が利用できます。 同じようにコンテナクラスタを運用することができるサービスにEKS(Elastic Kubernetes Service)があります。 EKSを採用しなかったのは、AWSにおいてKubernetesの導入・管理コストが高かったためです。 なぜなら後述するAWS Fargateは現時点ではEKSに対応しておらず、EKSを運用するとなるとクラスタのノードのEC2の管理をする必要があるからです。 ECSならコンテナノードにFargateを用いることができるため、ECSを採用しました。

ECS内の構成

今回構築したコンテナクラスタの中は以下のようになっています。 コンテナノードはEC2ではなくFargateを採用しました。

f:id:cohalz:20180918102529p:plain

AWS Fargateについて

AWS Fargateは2018年7月に東京リージョンにリリースされた新しい起動タイプで、EC2の代わりにコンテナクラスタに配置することができます。Fargateでは、EC2でのときと違って、リソース管理やホストOSのことを考える必要がなくなります。Fargateは標準で各サブネットのAZごとに配置されます。 Fargateは、awsvpcモードを使用が必須になるため、ENIの付け替えが必要になりデプロイ時間が長くなります。

ハマリポイント

1サービスあたりのFargateタスク数、Fargate全体でのPublicIP数が20までと少なく、複数のタスクを立てて入れ替える検証を行う際に簡単に到達してしまうので注意が必要です。

docs.aws.amazon.com

開発・デプロイフロー

f:id:cohalz:20180918102601p:plain

CodeBuildによるCI

CodeBuildはソースプロバイダにGitHubを選択した際、WebHookを受け取れるため、 PR発行ごとにdockerイメージをビルドして、そのイメージを使って、テストを行います。 結果はPRに表示されます。

f:id:cohalz:20180918102619p:plain

CodePipelineによるデプロイ

masterブランチにマージされたことをトリガーに、デプロイを行います。 ソースプロバイダをGitHubに、ビルドにはCodeBuild、デプロイ先はECSに設定します。 このときハマったのが、CodePipelineがGitHubから取ってくるソースコードはgit cloneでとって来られるわけではなく、zipでダウンロードされることです。これにより、CodePipelineから動かすCodeBuildにおいては、リポジトリ内のシェルスクリプトの権限が変わっていたり、ビルド中にgitコマンドを使おうとしてもリポジトリとして認識されなかったりします。

AutoScaleの検証

AutoScaleの検証も行いました。AutoScaleの設定項目では、平均CPU使用率、平均メモリ使用量、ALBへのリクエスト数に対して閾値が決められます。 今回は平均CPU使用率50%で発火するように設定し負荷試験を行った結果、AutoScaleイベントの発火からサービスインまで2分かかりました。

f:id:cohalz:20180918102656p:plain

f:id:cohalz:20180918102710p:plain
AutoScaleにより台数が増え,それぞれのCPU負荷は下がっている事がわかる

デプロイ時間

デプロイ時間の計測

ECSで10個のタスクを10個入れ替え、計測したところ、以下のようになりました。

  1. 6個のタスクを作成し、入れ替えを行う。このとき古いタスクはすべて落とす(10分)
  2. 4個のタスクを起動する(2分)

計12分かかりました。 つまり、デプロイしたとき、新規バージョンで不具合が出た場合、 前バージョンへ戻すときも同様にかかります。

f:id:cohalz:20180918102803p:plain
デプロイ時のサーバ入れ替えの様子

これを短縮するために、Blue-Green Deploymentを検討しました。

Blue-Green Deploymentの検討

検討のために、BlueとGreenの環境を用意しDNSで切り替えるようにしました。 実際にBlueからGreenに切り替えを行い、Route53のヘルスチェック(30秒毎)を設定したところ、切り替え後のチェックからすぐにGreenにアクセスされていることが確認できました。

f:id:cohalz:20180918102913p:plain

セキュリティスキャン

MicroScannerというツールの検証を行いました。

github.com

コンテナ内にセキュリティスキャンをかけてくれるツールで、Dockerfile内にMicroScannerを実行するように記述することでスキャンした結果をJSON形式で返却します。

Dockerfile

# 中略
ADD https://get.aquasec.com/microscanner /
RUN chmod +x /microscanner
ARG token
RUN /microscanner ${token}
$ docker build --build-arg=token=<TOKEN> --no-cache .

診断結果は以下のようなJSON形式で出力されます。 現段階では、MicroScanerをどうCodeBuildに組み込んで結果を可視化するかどうかが課題となっています。

"vulnerability_summary": {
    "total": 1,
    "low": 1,
    "negligible": 120
  }

また、MicroScannerの有料プランの検証も行いました。 有料プランでは以下のようなWebの管理画面でセキュリティスキャンの結果を見ることができます。

f:id:cohalz:20180918102930p:plain

f:id:cohalz:20180918102941p:plain

f:id:cohalz:20180918102953p:plain

CloudFormation

この構成の構築を簡単に再現するためにAWS CloudFormationを使いました。 デプロイフローに関してはecs-refarch-continuous-deploymentを参考にしました。

github.com

終わりに

今回は、AWS上にコンテナをデプロイする基盤を構築、検証しました。 今後の展望としては、Blue-Green Deploymentの手法をCodePipelineで実現させ、前バージョンへコマンドで一つですぐに戻せるようにできるようになればと思っています。

この記事は、id:guni1192がお送りいたしました。 ありがとうございました!!