BeanstalkからFargateに移行するためにやったこと
はじめに
元々、AWS Elastic Beanstalkで動いていたAPIサーバをFargateに移行しました。
移行作業で行ったことを書いていきます。
なぜ移行したのか
- 既存インフラのコード化を進める際にBeanStalkをコード化するよりもFargateにしようと思った
- 開発環境はDockerを利用しているため、本番環境もDockerを使用して環境による差異をできるだけ少なくしておきたかった
- エンジニアが少なくインフラエンジニアもいない状況のため、インフラをできるだけ管理しなくて良い構成にしておきたかった
以上の理由から移行することにしました。
その1 Dockerfileを本番用に修正
まず、開発環境でDockerを利用していたものの、Fargateで利用することを考慮していなかったため、Docker環境を修正しました。
ちなみにAPIサーバはLaravelを使用しており、Nginx & Laravelの構成にしました。
一点だけNginxとLaravel間はTCPで通信しているのですが、
ローカルとFargateではネットワークが異なるため、
fastcgi_pass ${PHP_HOST}:9000;
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;"'
${PHP_HOST}
としてDocker起動時にsed
で書き換えることにしました。
その2 既存インフラのコード化
次に既存インフラのコード化していきました。
既存インフラは既にサービスとして動いていたので、Terraformを使用して慎重にコード化していきました。
最初はAWS CDKで構築しようか迷っていたのですが、情報量の多さからTerraformを選択しました。
その3 Fargate環境の作成
次にFargate環境をTerraformで作成していきました。
-
ECR
Dockerイメージをプッシュする場所としてECRを作成しました。
M1 MacでビルドしたイメージはFargateのLinux環境で使用できなかったため、DockerのビルドとプッシュはGitHub Actionsで自動化することにしました。 -
ECS
クラスター作成、タスク定義作成、サービスの作成を行った後でサービスの更新(デプロイ)はGitHub Actionsで自動化することにしました。
amazon-ecs-render-task-definition
を利用することでタスク定義の更新とサービス更新(デプロイ)が自動化できます。
- name: Render Amazon ECS task definition for first container
id: render-web-container
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: web
image: amazon/amazon-ecs-sample-1:latest
environment-variables: |
LOG_LEVEL=info
ENVIRONMENT=prod
- name: Modify Amazon ECS task definition with second container
id: render-app-container
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
container-name: app
image: amazon/amazon-ecs-sample-2:latest
- name: Deploy to Amazon ECS service
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.render-app-container.outputs.task-definition }}
service: my-service
cluster: my-cluster
デプロイ作業はすべてGitHub Actionsで自動化することにしました。
タグのプッシュ(例:v1.0.0)をトリガーにしてコンテナのimageタグを更新するようにすることで、コンテナのimageタグとgitのタグを揃えることができたので満足しています。
こんな感じでgitのタグをdockerイメージのタグに指定することができます。
on:
push:
tags:
- v*
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set Docker Tag Env
run: echo "IMAGE_TAG=$(echo ${{ github.ref }} | sed -e "s#refs/tags/##g")" >> $GITHUB_ENV
- name: Push image to ecr
env:
ECR_REGISTRY: ${{ steps.amazon-ecr-login.outputs.registry }}
ECR_REPOSITORY: ${{ secrets.ECR_REPOSITORY }}
run: |
docker build \
-f Dockerfile \
-t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
その4 切り替え作業
十分にテストを行った後、リスナーを切り替えて完了しました。特に大きな問題は起きなくてほっとしました。
その他
マイグレーション
現在はexecコマンドでコンテナに入ってmigrateしています。自動化すべきか検討しています。
バッチ処理
ECSのタスクスケジューリングで実行しています。
非同期処理
キューを実行する環境をサイドカーとして動かしています。
{
"command": ["php", "artisan", "queue:work", "redis", "--sleep=3", "--tries=3"],
"name": "laravel-worker"
...
}
さいごに
個人的には詰まりポイントが多くて大変だったのですが、移行して運用が楽になりました。
Beanstalk(EC2)運用で課題を感じている人の参考になれば嬉しいです。
Discussion