Passion make things more better

Ruby on Rails / React.js / Swift / AWS / Docker

RailsをAWS Fargateにデプロイする (AWS CLI / ECS CLIを使用)

AWS CLIとECS CLIを用いてRailsをFargateにデプロイする方法について書きます。 イメージのビルドや自動デプロイの仕組みはまた別で書こうと思っています。今回は、MacOSで実行することを想定しています。

目次

  • CLIツールのインストール
  • IAMアカウントおよびロールの作成
  • アカウント設定 / ESC設定
  • ECRの作成 & Push
  • クラスタの作成
  • デプロイについて
  • スケールについて
  • DB Migration等のコマンドについて

CLIツールのインストール

ターミナルでAWSを操作できるように、CLIツールをインストールします。今回はbrewを使ってインストールします。 他の方法は以下を参照してください。

# インストール
$ brew install awscli

# 確認
$ aws --version

# インストール
$ brew install amazon-ecs-cli

# 確認
$ ecs-cli --version

IAMアカウントおよびロールの作成

ターミナルでAWSを操作するためのアカウントを作成します。 こちらは、画面上で行います。IAMのページにアクセスして行ってください。

グループの作成

初めにポリシー(アカウントがどの操作を行えるかを制限するもの)を紐づけたグループを作成します。

今回の要件で必要となるポリシーは以下です。

  • AmazonEC2ContainerRegistryFullAccess
  • AmazonEC2ContainerServiceFullAccess
  • AmazonECSTaskExecutionRolePolicy
  • CloudWatchLogsFullAccess

ユーザーの作成

続いてIAMユーザーを作成します。先ほど作ったグループを紐づけてユーザーアカウントの作成を完了させてください。完了したら、以下を必ず控えておいてください。

  • アクセスキーID
  • シークレットアクセスキー

ロールの作成

最後にロールの作成を行います。Fargateでは、ecsTaskExecutionRoleが必要となります。以下を参考にして、作成を行ってください。

Amazon ECS タスク実行 IAM ロール

アカウント設定 / ESC設定

ターミナル上で操作するための情報が出揃ったので、続いてAWSのアカウントの設定およびESCを操作するための設定を行います。

AWS CLIの設定

先ほど作成したIAMの認証情報を設定します。PROFILE_NAMEのところには、サービス等の名前を入れておくと管理しやすいと思います。

$ aws configure --profile PROFILE_NAME

AWS Access Key ID [None]: ACCESS_KEY_ID
AWS Secret Access Key [None]: SECRET_ACCESS_KEY
Default region name [None]: ap-northeast-1
Default output format [None]: json

これを実行すると、~/.aws以下にconfigcredentialの2つのファイルが作成されます。

aws configureだけを実行して設定をするパターンがありますが、それだと複数のアカウントで運用する場合に切り替えが面倒になるので、私はprofile名を設定して運用しています。

ECS CLIの設定

AWS CLIと同様に、ECS CLIの設定も行います。CLUSTER_NAMECONFIG_NAMEには、PROFILE_NAMEと同様にサービス等の名前を入れておくと管理しやすいと思います。

※もし本番環境とテスト環境を分ける場合などはそれも含めておくと管理しやすくなります。

# 認証情報の設定
$ ecs-cli configure profile --profile-name PROFILE_NAME --access-key ACCESS_KEY_ID --secret-key SECRET_ACCESS_KEY

# Fargateに関する設定
$ ecs-cli configure --cluster CLUSTER_NAME --default-launch-type FARGATE --region ap-northeast-1 --config-name CONFIG_NAME

ECRの作成 & Push

ビルドしたDockerイメージはECRにPushするようにします。 ECRでリポジトリを作成し、URLを控えてください。

docker-compose.ymlの作成

最新のECS CLIであれば、Docker ComposeのVersion3の記述を使うことができます。

以下のように作成することができます。

version: '3'
services:
  app:
    build:
      context: ./
      dockerfile: Dockerfile
    image: ECR_REPOSITORY_URL
    ports:
      - "3000:3000"
    environment:
      RAILS_ENV: production
    logging:
      driver: awslogs
      options:
        awslogs-group: SERVICE_NAME
        awslogs-region: ap-northeast-1
        awslogs-stream-prefix: PREFIX

この場合、ECS上ではbuildは無視されます。ローカルでのbuildのために定義しています。

イメージの作成 ~ ECRへのPush

以下のコマンドを実行してECRへのPushを行うことができます。

# イメージビルド
$ docker-compose -f docker-compose.yml build

# AWS ECRへのログイン
$ $(aws ecr get-login --no-include-email --region ap-northeast-1 --profile PROFILE_NAME)

# ECRへのPush
$ docker push ECR_REPOSITORY_URL:latest

クラスタの作成

VPC / Subnet / Security Groupの作成

クラスタを作成するにあたり、VPC、Subnet、Security Groupを事前に作成しておく必要があります。 クラスタを作成する際に指定をしなければ自動で作成されるのですが、自分で作成しておいた方が管理しやすいので、私は事前に作っておくようにしています。

VPCとSubnetについては、AWSネットワークルールを参考にして作ってみてください。 Fargateで配置するコンテナはPublicなSubnet(インターネットゲートウェイでインターネットとつながっている状態)に配置する必要があるので、そこだけ注意してください。

コマンド実行

クラスタを作る際には、ecs-cli upコマンドを使用します。PROFILE_NAMECONFIG_NAMEに関しては、「アカウント設定 / ESC設定」で指定したものと同じにしてください。

$ ecs-cli up \
    --security-group SECURITY_GROUP_ID \
    --vpc VPC_ID \
    --subnets SUBNET_ID_1,SUBNET_ID_2 \
    --aws-profile PROFILE_NAME \
    --ecs-profile PROFILE_NAME \
    --cluster-config CONFIG_NAME

デプロイについて

ロードバランサーとターゲットグループの作成

サービスを起動する際に、ロードバランサーまたはターゲットグループを指定する必要があるので、事前に作成しておきます。作成時の注意としては、ターゲットグループを作成する際に、「ターゲットの種類」の項目で「IP」を指定するようにしてください。

ecs-params.ymlの作成

デプロイをする際に、どのVPC, Subnetに配置するか等を指定する必要があります。それらを指定するためにecs-params.ymlというファイルを作成します。

subnet IDとsecurity Group IDを自分で作成したものに置き換えてください。

version: 1
task_definition:
  task_execution_role: ecsTaskExecutionRole
  ecs_network_mode: awsvpc
  task_size:
    mem_limit: 1.0GB
    cpu_limit: 512
run_params:
  network_configuration:
    awsvpc_configuration:
      subnets:
        - "subnet ID 1"
        - "subnet ID 2"
      security_groups:
        - "security group ID"
      assign_public_ip: ENABLED

デプロイコマンド

デプロイをするために、ecs-cli compose service upコマンドを使用します。

$ ecs-cli compose \
    --project-name PROJECT_NAME \
    --file docker-compose.yml \
    --ecs-params ecs-params.yml \
    service up \
    --target-group-arn TARGET_GROUP_ARN \
    --container-name app \
    --container-port 3000 \
    --cluster CLUSTER_NAME \
    --aws-profile PROFILE_NAME \
    --ecs-profile PROFILE_NAME \
    --cluster-config CONFIG_NAME \
    --create-log-groups \
    --force-deployment

これでクラスター内にサービスと1つのタスクが起動するようになります。 もしサービスがすでに作成されていた場合は、タスクのみが新規で作成され、立ち上がり次第古いものと入れ替わります。

スケールについて

先ほどのコマンドでデプロイを行なった場合に、タスクの数は1となります。ここからタスクの数をさらに増やしたいという場合は、スケールさせるためのコマンドを実行する必要があります。

$ ecs-cli compose \
    --project-name PROJECT_NAME \
    service scale 2 \
    --cluster CLUSTER_NAME \
    --aws-profile PROFILE_NAME \
    --ecs-profile PROFILE_NAME \
    --cluster-config CONFIG_NAME

こちらを実行するとタスクが2つに増え、自動で不足している分のタスクが起動します。

DB Migration等のコマンドについて

Docker Composeファイルの作成

Dockerは1コンテナ1プロセスが原則ですので、この状態だとrake db:migrate等のコマンドを実行することができません。なので、そういったタスクを実行するためのDocker Composeファイルを作成します。

例として、rake db:migrateを実行するためのDocker Composeファイル(db-migration.yml)を作成します。 app用のRailsイメージのコマンドを上書きすることで同じものを利用することができます。

version: '3'
services:
  app:
    image: ECR_REPOSITORY_URL
    command: ["bundle", "exec", "rails", "db:migrate"]
    environment:
      RAILS_ENV: production
    logging:
      driver: awslogs
      options:
        awslogs-group: SERVICE_NAME
        awslogs-region: ap-northeast-1
        awslogs-stream-prefix: PREFIX

コマンド

先ほど作成したdb-migration.ymlを指定して、タスクを実行します。ここで、PROJECT_NAMEの部分は通常のappとは別の名前にしてください。もし同様の名前で実行してしまった場合に、app用のタスク定義が変わってしまい、アプリケーションが起動しなくなってしまいます。

$ ecs-cli compose \
    --project-name PROJECT_NAME \
    --file db-migration.yml \
    --ecs-params ecs-params.yml \
    up \
    --cluster CLUSTER_NAME \
    --aws-profile PROFILE_NAME \
    --ecs-profile PROFILE_NAME \
    --cluster-config CONFIG_NAME

参考