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が必要となります。以下を参考にして、作成を行ってください。
アカウント設定 / 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以下にconfigとcredentialの2つのファイルが作成されます。
※aws configureだけを実行して設定をするパターンがありますが、それだと複数のアカウントで運用する場合に切り替えが面倒になるので、私はprofile名を設定して運用しています。
ECS CLIの設定
AWS CLIと同様に、ECS CLIの設定も行います。CLUSTER_NAMEとCONFIG_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_NAMEとCONFIG_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