こんにちは READYFOR で SRE として働いている @jedipunkzです。READYFOR では最近、EC2 で提供していたサービスを AWS ECS へ移行しました。この記事では AWS ECS 選定のポイント、その ECS 周辺の仕組み、また開発者に提供している機能について紹介したいと思います。
ECS 導入判断のポイント
自分が入社した 2020年時点で READYFOR では古くからあったシステムが EC2 上でアプリケーションが稼働してサービス提供していました。アプリケーションのデプロイの仕組みも古くなっていて改善したいという思いから ECS 化を進めることになったのですが、その際の利用する技術の候補としては下記がありました。
- ECS on EC2 Compute
- ECS on Fargate Compute
- EKS on EC2 Compute
- EKS on Fargate Compute
Fargate はコンテナの起動が遅い他、今までは幾つか問題があったのですが 2021年現在は個人的には、それなりの Web サービス提供基盤として充分なものと考えています。また上記にあるように EKS も選定候補として残っていたのですが下記のポイントがネックになり、今回はメインの Web サービスの基盤としては不採用になりました。
- ALB Ingress Controller 等、基本的なインフラの仕組みがセルフマネージドな Pod になる
- ALB Ingress Controller, Metrics Server など、2020年評価時点でソフトウェアの未熟さが目立っていた (現在だいぶ改善されていることを確認しています)
- インフラのコードが yaml になる問題 (Terraform であれば可読性がそれなりに保たれる)
- 機密情報の格納・展開に難あり(今は改善しているそうです)
評価を行ったのが2020年なので若干情報が既に古くなっている可能性がありますが、上記のように幾つか問題がありました。特に SRE メンバが扱うインフラのコードが Terraform だけで完結しないというのは個人的に問題に感じていました。Terraform であれば可読性がある程度保たれるのと、継続的に .tf コードを修正してインフラを管理出来ます。またロードバランサの起動に必要な ALB Ingress Server やオートスケールに必要な Metrics Server などインフラ基盤としての機能をセルフマネージするのはとても気にあるポイントでした。Kubernetes や園周辺のソフトウェアはものすごい勢いで開発が進んでいます。自分たちのインフラ基盤としての機能を司るそれらのソフトウェアをメンテナンスを伴って更新するのは、Web サービスを事業としている READYFOR にとっては避けたいところでした。(もちろんインフラ自体をサービスとして提供されている企業様などは全く別の話になると思います)
最終的に ECS on Fargate を採用したのですが、下記の選定ポイントを具体的にコードを書きつつ技術検証・比較を行っていきました。ECS on Fargate は今現在では何不自由なく機能しました。特に READYFOR では Datadog を採用しているので監視やメトリクス・ログ収集の所で運用コストをだいぶ削減出来ました。
- コード化
- オートスケール
- ロードバランサ
- 機密情報の格納展開
- CI/CD, GitOps
- ロギング
- メトリクス収集
- モニタリング
ECS 周辺の仕組みの紹介
ECS 化に伴っていくつかの作り込みをしています。主に使っている技術は
- GitHub Actions ワークフロー (aws 公式のもの)
- Go 言語による Lambda Function
- Go 言語による Slack ボット
ここからは READYFOR SRE が作り込んだ仕組みについて紹介していきます。
ECS へのアプリケーションデプロイの仕組み
ECS のデプロイ方法として CodeDeploy による BlueGreen Deployment を選択しました。(今後は AppMesh 等を用いた他のデプロイ手段も検討する予定ではいます) READYFOR では下記の流れで ECS へデプロイを行っています。
- (1) ECR Login (aws-actions/amazon-ecr-login を利用)
- (2) Docker ビルド & プッシュ
- (3) ECS Task 定義のレンダリング (aws-actions/amazon-ecs-render-task-definition 利用)
- (4) ECS + CodeDeploy デプロイ (aws-actions/amazon-ecs-deploy-task-definition 利用)
上記括弧内に記した AWS 公式の GitHub Actions ワークフローを利用しています。AWS API への追従という点・また不具合対応という観点で安心して利用出来ると判断しました。
PR 作成による自動環境構築の仕組み
READYFOR の SRE チームではプログラミング言語として Go 言語を採用しているのですが Go を用いた Lambda 関数と GitHub Actions ワークフローを使ってデベロッパの方々が PR を GitHub 上に作成すると、自動的に ALB を含む ECS 環境を構築する仕組みを作りました。具体的には GitHub Actions ワークフローで下記の流れで処理を行っています。
- (1) Docker ビルド & プッシュ
- (2) Lambda 関数で ALB, ECS Service, Task Definition, その他リソースの作成と ECS Cluster へのデプロイ
- (4) PR を形成するリソースの構成情報の DybamoDB へのデータ格納
- (5) PR ブラウザでアクセス出来る ALB DNS 名をコメント
この仕組によって、開発者の方もしくは SRE メンバが開発環境を別途構築する手間を削減させています。環境構築が終わると GitHub PR のコメントに動作確認 URL を出力するようになっています。
また PR の数はデベロッパの人数に応じて増大します。よって上記の PR 毎の ALB, ECS 環境の数が肥大化することが懸念でしたので、同じ Lambda 関数を Cloudwatch Events で定期実行し、1週間以上経過した PR の 環境は定期的に削除するよう仕組み化しています。s
データベースインポートする ECS ボット
上記の PR 毎の開発環境へ最新のデーターベース (個人情報はマスクされている) をインポートする操作についても自動化を試みました。この仕組は slack-go/slack を利用し Slack ボットの開発を行いデベロッパ自身に Slack 入力してもらうことで開発環境へ最新のデータベースをインポートしてもらうものです。叩いている API は ECS API で具体的には func (*ECS) RunTask です。Command をオーバーライドしつつ Docker コンテナ上でコマンドを叩いて処理を実行しています。
各 PR の ECS Task は DB 接続等の情報がすでに入っているのも好都合でした。RunTask し、コンテナ内 (レポジトリ内) のスクリプトを実行することで下記の処理を行ってデータベースインポートしています。
- (1) (事前に定期的に処理されている) Glue Job が個人情報をマスクしつつ最新のデータベースを作成し更に S3 バケットへダンプ結果を格納
- (2) ECS Task コンテナ内スクリプトが S3 バケットからダンプ結果を取得
- (3) 更にコンテナ内にすでに設定されているデーターベース接続のための環境変数 (データーベース名, ユーザ名, パスワード) を用いいてデーターベースインポート実施
これによって、各エンジニアは自分の作成した環境に対して最新のデータベースを適用できます。更に個人情報がマスクされているので安心して動作確認が行えます。
デベロッパへのフルセットな環境の提供
2020年に自分が入社した時点と異なり、今ではバックエンドも複数環境ありフロントも SPA 化されています。よって上記で紹介した PR 環境 (個別のバックエンドレポジトリ毎) だけでは、開発者の要望に応える事が出来ない状況になりました。よって、下記のような Terraform Map の仕組みを使って、各 aws リソース (ECS, ALB, Cloudfront, Autoscale, Elasticache, Elasticsearch, IAM, Route53, S3 など..) をすべてカバーする環境を構築しました。環境を追加したい場合は Map に要素を追加し GitHub PR をマージするだけで環境構築が完了します。
devgroups = { env1 = { ecs_platform_version = "1.4.0" es_instance_type = "t3.small.elasticsearch" ec2_image_id = "ami-0ce107ae7af2e92b5" }, env2 = { ecs_platform_version = "1.4.0" es_instance_type = "t3.small.elasticsearch" ec2_image_id = "ami-0ce107ae7af2e92b5" }, ... }
GitHub Actions Dispatch によるフルセットな環境へのアプリケーションデプロイ
READYFOR ではアプリケーションデプロイ(テスト)は全て GitHub Actions で実行しています。各ブランチプッシュ・マージをトリガにしてワークフローを実行する仕組みを基本としつつも、任意のブランチを各環境へデプロイする仕組みとして GitHub Actions の Dispatch Workflow を利用しています。
ブラウザの GitHub UI 上で上記の通り、環境名とブランチ名を入力することで任意のブランチを各環境へデプロイ出来ます。ただデベロッパ向けにはこれで良いのですが、PdM や QA の方々向けに提供するにしても、この機能を使うにはレポジトリの Write 権限が必要となっています。PdM や QA の方々向けに Write 権限を付けるのも問題と感じたので、READYFOR SRE チームでは GitHub Workflow Dispatch API を実行する Slack ボットを Go 言語で開発し、機能と権限を PdM, QA の方々向けに提供しています。