2019/06/06
Qiitaのストックを整理するためのサービス「Mindexer(ミンデクサー)」のインフラをEC2からFargateに置き換えました。その際に得られたTipsをまとめた記事となります。
AWSの環境は全てTerraformで構築しています。開発環境のみECSのEC2起動モードも利用しています。
ソースコードは、こちらで公開しています。
https://github.com/nekochans/qiita-stocker-terraform
なお、ECSのスペックについては本番を想定した設定となっていませんのでご注意ください。
AWS + Laravel + Vue.js でQiitaのストックを整理するサービスを作りました!【個人開発】
にて解説ししていますので、こちらも合わせて見ていただけますと幸いです😊
バックエンド、フロントエンドの技術についてもソースコード付きで解説しています。
実際の構成はTerraformのプロジェクトをご確認いただくのが早いと思いますが、ここでは設定のポイント等を解説したいと思います。
ECS(EC2)、Fargate共に利用しています。
CodeBuildプロジェクトを作成し、ECRへのプッシュを行なっています。(最終的には、CodePipelineを利用したFargateへのデプロイを構築する予定。)
ECRのライフサイクルポリシー
ライフサイクルポリシーを設定しておくことで不要になったイメージを自動で削除することができます。
ECRには月500MBのストレージの無料利用枠が用意されていますが、これ以上となると利用料が発生するため、ライフサイクルを設定しておくといいかと思います。
AWSドキュメント Amazon ECR ライフサイクルポリシー
下記は、イメージを5つまで保持し、6つめ以上の古いイメージは自動で削除される設定です。
locals {
lifecycle_policy = <<EOF
{
"rules": [
{
"rulePriority": 10,
"description": "Expire images count more than 5",
"selection": {
"tagStatus": "any",
"countType": "imageCountMoreThan",
"countNumber": 5
},
"action": {
"type": "expire"
}
}
]
}
EOF
}
開発環境のみで使用しています。
FargateではコンテナホストにSSHするこができないため、開発中にSSHでコンテナの動作確認等を行いたい場合などに利用しています。
EC2インスタンスをECSのクラスタに所属させる設定
デフォルトでは、EC2のコンテナインスタンスはデフォルトのクラスタで起動されます。
そのため、デフォルト以外のクラスタで起動するには、クラスタを指定する必要があります。
この設定が、Terraformで構築している際にわかりにくかったので、記載しておきます。
EC2ユーザーデータを使用して、コンテナインスタンスの環境変数を設定します。
ここでは、ECS_CLUSTER
にコンテナ名を指定しています。
ECS_CLUSTER
このエージェントが確認するクラスター。この値を定義しない場合、default クラスターが想定されます。default クラスターが存在しない場合は、Amazon ECS コンテナエージェントによってその作成が試みられます。default 以外のクラスターを指定した場合、そのクラスターが存在しないと、登録は失敗します。
参考:AWSドキュメント Amazon ECS コンテナエージェントの設定
#!/bin/bash
cat << EOT >> /etc/ecs/ecs.config
ECS_CLUSTER=${cluster_name}
EOT
data "template_file" "user_data" {
template = "${file("../../../../modules/aws/api/user-data/userdata.sh")}"
vars {
cluster_name = "${aws_ecs_cluster.api_ecs_cluster.name}"
}
}
resource "aws_instance" "ecs_instance" {
// その他の設定は省略
user_data = "${data.template_file.user_data.rendered}"
}
Fargateは本番での運用を想定し、Green/Blueデプロイの設定も行なっています。
Terraformでの構築においてECS(EC2)との設定の大きな違いは、EC2インスタンスの設定が不要なところです。
(上記で設定した、コンテナインスタンスの環境変数の設定ももちろん不要)
ここでは、Blue/Greenデプロイについて解説します。
マネジメントコンソールからの設定については、AWS FargateでBlue/Greenデプロイを行うという記事の中で解説しております。
ターゲットを切り替えることでBlue/Greenデプロイを実行するため、fargate_api_blue
,fargate_api_green
の2つのターゲットグループを作成します。
resource "aws_alb_target_group" "fargate_api_blue" {
name = "${lookup(var.fargate, "${terraform.env}.name", var.fargate["default.name"])}-blue"
port = 80
protocol = "HTTP"
vpc_id = "${lookup(var.vpc, "vpc_id")}"
health_check {
path = "/api/statuses"
timeout = 5
healthy_threshold = 5
unhealthy_threshold = 2
interval = 20
matcher = 200
}
target_type = "ip"
}
resource "aws_alb_target_group" "fargate_api_green" {
name = "${lookup(var.fargate, "${terraform.env}.name", var.fargate["default.name"])}-green"
port = 80
protocol = "HTTP"
vpc_id = "${lookup(var.vpc, "vpc_id")}"
health_check {
path = "/api/statuses"
timeout = 5
healthy_threshold = 5
unhealthy_threshold = 2
interval = 20
matcher = 200
}
target_type = "ip"
}
resource "aws_codedeploy_app" "fargate_api" {
compute_platform = "ECS"
name = "${lookup(var.fargate, "${terraform.env}.name", var.fargate["default.name"])}"
}
resource "aws_codedeploy_deployment_group" "fargate_api_blue_green_deploy" {
app_name = "${aws_codedeploy_app.fargate_api.name}"
deployment_group_name = "blue-green"
service_role_arn = "${aws_iam_role.codedeploy_for_fargate_role.arn}"
deployment_config_name = "CodeDeployDefault.ECSAllAtOnce"
auto_rollback_configuration {
enabled = true
events = ["DEPLOYMENT_FAILURE"]
}
blue_green_deployment_config {
deployment_ready_option {
action_on_timeout = "CONTINUE_DEPLOYMENT"
}
terminate_blue_instances_on_deployment_success {
action = "TERMINATE"
termination_wait_time_in_minutes = "1"
}
}
deployment_style {
deployment_option = "WITH_TRAFFIC_CONTROL"
deployment_type = "BLUE_GREEN"
}
ecs_service {
cluster_name = "${aws_ecs_cluster.api_fargate_cluster.name}"
service_name = "${aws_ecs_service.api_fargate_service.name}"
}
load_balancer_info {
target_group_pair_info {
prod_traffic_route {
listener_arns = ["${aws_alb_listener.fargate_alb.arn}"]
}
target_group {
name = "${aws_alb_target_group.fargate_api_blue.name}"
}
target_group {
name = "${aws_alb_target_group.fargate_api_green.name}"
}
}
}
}
IAMロールの設定などは割愛していますが、簡単にBlue/Greenデプロイの設定ができるのでとても便利ですね!
ECS(EC2)とFargateではコンテナ間の通信方法が異なります。
link
パラメータを設定しコンテナ間の通信を許可する上記の内容を踏まえて、Nginxの設定ファイルをECS(EC2)とFargateの両方で使用できるよう作成します。
テンプレートdefault.conf.template
を作成し、PHP_HOST
を切り替え可能とします。
location ~ \.php$ {
fastcgi_pass ${PHP_HOST}:9000;
}
FROM nginx:1.15.5-alpine
ENV PHP_HOST=127.0.0.1
ADD ./docker/nginx/config/default.conf.template /etc/nginx/conf.d/default.conf.template
RUN mkdir -p /var/www/html/public
ADD ./public/ /var/www/html/public
CMD /bin/sh -c 'sed "s/\${PHP_HOST}/$PHP_HOST/" /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g "daemon off;"'
ECS(EC2)のタスクの定義の中で、default.conf
に設定するための環境変数を設定します。
(Fargateのタスクの定義では環境変数の設定を行わないので、Dockerfileで設定している PHP_HOST=127.0.0.1
が設定される)
"links": ["php"],
"environment": [
{
"name": "PHP_HOST",
"value": "php"
}
:
タスクの定義の環境変数は、パラメータストアから取得しています。プログラムに機密情報を保持する必要がなくなるというメリットがあります。
設定方法は下記の通りです。
2. AWS Secrets ManagerからParameter StoreをTerraformで作成
の解説については、下記の記事をご確認ください。
AWS Secrets ManagerからParameter StoreをTerraformで作成する
Terraformで作成したタスクの定義がECSサービスで動作している状態でTerraformでタスクの定義を変更すると、現在動いているタスクの定義を削除してから新しいリビジョンのタスクの定義が作成されます。
サービス自体は停止しませんが、直前のタスクの定義のリビジョンが削除されてしまうのでご注意ください。
以下についても、今後対応する予定です。
デプロイの自動化
現時点では、CodeBuildでECRへプッシュするところまでしか自動化することができていません。
今後は、CodePipelineを使って、GitHubにプッシュされたら自動でデプロイが実行されるまでの仕組みを構築しようと思っています。
Datadogを利用したコンテナの監視
Amazon ECS CloudWatch のメトリクスではクラスタ、およびクラスタ内のサービス単位のメトリクスはサポートされていますが、タスク単位のメトリクスはサポートされていません。
Datadogを利用して、タスク単位でリソースを監視できるようにしていきたいと思っています。
AWS ドキュメント Amazon ECS CloudWatch のメトリクス
Datadog Docs ECS Fargate