Usual Software Engineer

よくあるソフトウェアエンジニアのブログ

ゼロから始める AWS Fargate 実践編

概要編に引き続き、 AWS Fargate 実践編になります。

innossh.hatenablog.com

実際に Terraform などのコードをサンプルのリポジトリにあげていて、 VPC の作成から ECS クラスタの作成、更にはモニタリングのための CloudWatch のリソースまで用意しているので実践と言うにふさわしいのではないかなと思います。 README に手順が書いてあるので試したい人は参考にしてください。

github.com

サンプルでは ECS クラスタ上で動かすアプリケーションとして、 Redash を選びました。理由は DB と Cache を使うこと、それから Web UI に限らず worker のプロセスもあるので、自前のアプリケーションを動かす際の参考として適していると思いました。実践編の記事、 6 月中に書くぞ とか言ってましたが、フラグ回収して 7 月になってしまいましたw では張り切って実践編に入りましょう。

最初に念のため、サンプルに関しての注意書きをしておきます。

  • RDS, ElastiCache ともにマルチ AZ は無効にしている
  • RDS, ElastiCache ともにパブリックサブネットに作成しており、踏み台サーバはなしで固定 IP で接続許可している
  • PostgreSQL のデータベースもまとめて Terraform で作成している
  • Fargate タスクのオートスケーリングは未設定

このあたりは 怠惰で Fargate 用のサンプルと割り切ってプロダクション用の設定を省略した形です。 また、 VPC 関連のリソースの細かい説明は割愛します。パブリックサブネットとプライベートサブネットがそれぞれ 3 つずつ作成されるようになっています。

実践編は以下の 3 ステップで説明します。

Terraform による ECS クラスタのセットアップ

S3 bucket の準備

まずは S3 bucket を 1 つ用意して terraform.tfstate を管理します。チームなど複数人でリソースを操作する場合や、 機密情報を GitHub リポジトリに入れたくない場合に今では必須となっていますね。

aws --profile dev s3api create-bucket --bucket <BUCKET_NAME> --acl private --create-bucket-configuration LocationConstraint=ap-northeast-1

これだけ用意すればサンプルの Terraform コードを適用できます。

ECS クラスタの作成

さっそく Terraform を実行します。

cd ./terraform
terraform apply

実行時に必要な variable が 3 つあるので、 terraform.tfvars を用意するか実行時の引数 -var を使用すると良いでしょう。もちろん実行後のプロンプトで入力しても良いです。

terraform.tfvars の例:

cidr_office = "x.x.x.x/32" # 接続を許可する IP
owner = "innossh" # Owner タグに設定する名前
s3_bucket_name = "my-bucket-name" # ALB のログを保存する S3 バケット名

作成される AWS 上のリソースは以下になります。

  • ECS クラスタ
  • VPC 関連
  • RDS
    • Redash 用の DB
  • ElastiCache
    • Redash 用の Redis
  • ALB
    • Redash の Web UI のエンドポイント用
  • IAM role
    • ECS 上のタスクに割り当てる用
  • Security group
    • ECS 上のタスクに割り当てる用など
  • CloudWatch 関連

ちなみに ECS クラスタは名前を決めるだけで作成できます。

aws-fargate-example/ecs.tf at v0.0.1 · innossh/aws-fargate-example · GitHub

resource "aws_ecs_cluster" "fargate" {
  name = "${var.site_id}-fargate"

  tags = {
    Owner = "${var.owner}"
  }
}

Terraform の適用は RDS と ElastiCache の作成があるので完了まで時間がかかります。完了すると RDS に接続できるようになります。

postgresql データベースの作成

今度は postgresql provider を使用したデータベースの作成を適用します。 同じディレクトリで同時に RDS 作成 と postgresql provider を適用しようとするとエラーとなるため、 -target 指定を頑張って切り抜けるかディレクトリを分けるかの二択になるわけですが、サンプルでは潔くディレクトリを分けています。

cd ./terraform/postgresql
terraform apply

postgresql ディレクトリ用 terraform.tfvars の例:

db_root_password = "XXXXXX" # RDS の root パスワード
owner = "innossh" # Owner タグに設定する名前

ここで同時に Secrets Manager に Redash 用の DB ユーザのパスワードを保存しています。正確に言うと DB 接続の URL の形式で保存しているのですが、これは Fargate 上で Redash のコンテナを動かす際に環境変数としてそのまま渡せるように意図しています。ついでに Redis 接続の URL や REDASH_COOKIE_SECRET なども保存しています。

aws-fargate-example/secretsmanager.tf at v0.0.1 · innossh/aws-fargate-example · GitHub

resource "aws_secretsmanager_secret" "fargate_redash" {
  count = "${length(local.secrets)}"

  name        = "${lookup(local.secrets[count.index], "name")}"
  description = "${lookup(local.secrets[count.index], "description")}"

  tags = {
    Owner = "${var.owner}"
  }
}
resource "aws_secretsmanager_secret_version" "fargate_redash" {
  count = "${length(local.secrets)}"

  secret_id     = "${aws_secretsmanager_secret.fargate_redash.*.id[count.index]}"
  secret_string = "${lookup(local.secrets[count.index], "secret_string")}"
}

もし自前の Docker イメージを使用するために Docker プライベートレジストリを使用する場合は、公式ドキュメントのように JSON 形式で Secrets Manager に認証情報を保存します。それ以外の環境変数用の値であればプレーンテキストで単一の文字列を保存するようにしてください。

これで Terraform による AWS のリソースのセットアップは完了です。

ECS CLI によるサービスの実行

今度は ECS CLI を使用して Redash のコンテナをデプロイします。 Fargate の用語で言うと、 Redash の複数のタスク定義をもったサービスをクラスタにデプロイします。 概要編でも説明しましたが、 docker-compose.ymlecs-params.yml を使用して ECS CLI のコマンドでサービスを実行できます。

docker-compose.yml

本筋とずれるので Docker Compose の詳しい説明は省略しますが、 2 年前に書いたベストプラクティスはこちら。

innossh.hatenablog.com

今回は公式の Redash の docker-compose.yml をもとにしていますが、ログの設定は Fargate 用の特別なものになります。

aws-fargate-example/docker-compose.yml at v0.0.1 · innossh/aws-fargate-example · GitHub

  server:
    image: redash/redash:7.0.0.b18042
...
    logging:
      driver: awslogs
      options:
        awslogs-group: /ecs/dev-fargate/redash
        awslogs-region: ap-northeast-1
        awslogs-stream-prefix: ecs

上記のように、 awslogs ログドライバーを使用しています。ロググループは Terraform で作成したものを指定していますね。

ecs-params.yml

一方 ecs-params.yml は初見の人が多いと思います。

aws-fargate-example/ecs-params.sample.yml at v0.0.1 · innossh/aws-fargate-example · GitHub

version: 1
task_definition:
  ecs_network_mode: awsvpc
  task_execution_role: "arn:aws:iam::FIXME:role/dev-fargate-redash"
  task_size:
    cpu_limit: 512
    mem_limit: 2048
  services:
    server:
      essential: true
      secrets:
        - value_from: "arn:aws:secretsmanager:ap-northeast-1:FIXME:secret:dev-fargate/redash-db-url-FIXME"
          name: REDASH_DATABASE_URL
        - value_from: "arn:aws:secretsmanager:ap-northeast-1:FIXME:secret:dev-fargate/redash-redis-url-FIXME"
          name: REDASH_REDIS_URL
        - value_from: "arn:aws:secretsmanager:ap-northeast-1:FIXME:secret:dev-fargate/redash-cookie-secret-FIXME"
          name: REDASH_COOKIE_SECRET
        - value_from: "arn:aws:secretsmanager:ap-northeast-1:FIXME:secret:dev-fargate/redash-secret-key-FIXME"
          name: REDASH_SECRET_KEY
...
run_params:
  network_configuration:
    awsvpc_configuration:
      subnets:
        - "subnet-FIXME"
        - "subnet-FIXME"
        - "subnet-FIXME"
      security_groups:
        - "sg-FIXME"
      assign_public_ip: DISABLED

FIXME は正しい ARN で置換すべきところです。それぞれ設定値に関しては以下になります。

  • task_execution_role
    • タスク実行用の IAM role の指定
  • essential
    • true の場合、そのコンテナが停止した時に他のコンテナもあわせて停止する。常駐しないコンテナは false にしておく
  • secrets
    • Secrets Manager の ARN を指定し、環境変数の設定が可能
  • awsvpc_configuration
    • タスクが使用するサブネット、セキュリティグループの指定。パブリック IP をアサインすることも可能

中途半端な説明なので、詳しくは公式ドキュメントを参照するのがいいでしょうね。

サービスの実行

ではようやくサービスの実行ですが、残念ながら ecs-params.yml が未対応で設定できないパラメータもあるため、実行時にオプションとして指定しているものもあります。コマンドは以下のようになります。

ecs-cli compose --aws-profile dev --cluster dev-fargate --project-name redash service up --launch-type FARGATE --target-group-arn <TARGET_GROUP_ARN> --container-name server --container-port 5000 --tags Owner=<OWNER>

一つポイントとして、 --target-group-arn で Terraform で作成した ALB のターゲットグループを指定しているのですが、そのターゲットグループはターゲットタイプを IP としており、ターゲットのコンテナは Fargate が自動的に登録してくれるようになっています。

aws-fargate-example/alb.tf at v0.0.1 · innossh/aws-fargate-example · GitHub

resource "aws_lb_target_group" "fargate_redash_http" {
  name = "fargate-redash-http"
  port = 5000
  protocol = "HTTP"
  vpc_id = "${aws_vpc.main.id}"
  deregistration_delay = 300
  slow_start = 30
  target_type = "ip"

  health_check {
    interval = 30
    path = "/login"
    protocol = "HTTP"
    healthy_threshold = 3
    unhealthy_threshold = 3
    matcher = "200,302"
  }
}

ECS CLI の実行が成功すると、ブラウザで ALB のエンドポイントにアクセスすることで Redash の Web UI が見れるようになります。

f:id:innossh:20190707185723p:plain
Redashの初期セットアップページ

動作確認とモニタリング

Redash の Web UI が見れるということは正常に動作していそうですが、マネージメントコンソールでも確認してみましょう。 ALB の状態は下のようになっています。ターゲットは IP になっていますね。

f:id:innossh:20190707191758p:plain
ターゲットグループ

こちらは ECS のタスクの状態になります。

f:id:innossh:20190707191343p:plain
サービス内のタスク

更に詳細をみると、下のキャプチャのように 3 つのコンテナが動作しています。うち 1 つは DB のマイグレーション実行用なのですでに実行を終えています。前述の essentialfalse のコンテナなので、タスク自体には問題がありません。

f:id:innossh:20190707192300p:plain
タスクの詳細

ここからはモニタリングについて紹介します。まずは何と言ってもコンテナのログですね。同じくマネージメントコンソールのタスクの詳細からそのままログの確認ができます。もちろん AWS CLI で CloudWatch のログを確認することもできますが。

f:id:innossh:20190707193346p:plain
コンテナのログ

続いてメトリクスについてです。何も設定しなくても Fargate のタスクの CPU とメモリについては CloudWatch で確認できます。

f:id:innossh:20190707194625p:plain
ECS メトリクス

そして概要編でも取り上げた CloudWatch Metric Filter からのカスタムメトリクスも取得できています。エラーログも警告ログも出ていないので、数値が 0 の地味なグラフになっていますがw

f:id:innossh:20190707195053p:plain
カスタムメトリクス

以上で、前回から続いた ”ゼロから始める AWS Fargate” を終わります。 Fargate でシームレスな開発と運用コストを抑えつつ、プロダクション環境にも対応した Web サービスの公開ができることがよくわかりました。

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門