💪

BeanstalkからFargateに移行するためにやったこと

2022/05/18に公開

はじめに

元々、AWS Elastic Beanstalkで動いていたAPIサーバをFargateに移行しました。
移行作業で行ったことを書いていきます。

なぜ移行したのか

  • 既存インフラのコード化を進める際にBeanStalkをコード化するよりもFargateにしようと思った
  • 開発環境はDockerを利用しているため、本番環境もDockerを使用して環境による差異をできるだけ少なくしておきたかった
  • エンジニアが少なくインフラエンジニアもいない状況のため、インフラをできるだけ管理しなくて良い構成にしておきたかった

以上の理由から移行することにしました。

その1 Dockerfileを本番用に修正

まず、開発環境でDockerを利用していたものの、Fargateで利用することを考慮していなかったため、Docker環境を修正しました。
ちなみにAPIサーバはLaravelを使用しており、Nginx & Laravelの構成にしました。

一点だけNginxとLaravel間はTCPで通信しているのですが、
ローカルとFargateではネットワークが異なるため、

default.conf
fastcgi_pass ${PHP_HOST}:9000;
Dockerfile
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を利用することでタスク定義の更新とサービス更新(デプロイ)が自動化できます。

.github/workflows/deploy.yml
    - 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イメージのタグに指定することができます。

.github/workflows/deploy.yml
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