概要

ECS CLI を使って Rails をデプロイする方法を書いています。

ECS の起動タイプには FARGATE を指定します。
デプロイするアプリには Rails の前にリバースプロキシとして Nginx も含んでいます。

ECS CLI の設定

まず、クラスターを作成するために ECS CLI の設定をします。
CLI から AWS API を呼び出せるように認証情報が必要です。

ecs-cli configure profile --access-key AWS_ACCESS_KEY_ID --secret-key AWS_SECRET_ACCESS_KEY --profile-name rails-app

そしてクラスター設定を作成します。

ecs-cli configure --cluster rails-app --default-launch-type FARGATE --config-name rails-app --region ap-northeast-1

クラスターの作成

クラスターの作成は ecs-cli up コマンドを使用します。 ここで ECS の起動タイプに FARGATE を指定しています。

ecs-cli up --cluster-config rais-app --ecs-profile rails-app

コマンドを実行すると、新しく VPC と 2 つのサブネットが作成されます。
これらの ID はのちに構成ファイルを作るときに必要になるので控えておきましょう。

また、デプロイしたアプリにアクセスできるように VPC のセキュリティグループの設定を変更してポート 80 からの接続を許可しておきます。

セキュリティグループの ID を調べるには以下のコマンドを実行します。

aws ec2 describe-security-groups --filters Name=vpc-id,Values=VPC_ID --region ap-northeast-1

上記で調べたセキュリティグループのポート 80 への接続を許可するには以下のコマンドを実行します。

aws ec2 authorize-security-group-ingress --group-id SECURITY_GROUP_ID --protocol tcp --port 80 --cidr 0.0.0.0/0 --region ap-northeast-1

Docker イメージの作成

次にデプロイに使用する Docker イメージを作成します。
作成するのは Rails と Nginx の 2 つです。

Rails

イメージをビルドするためのファイルを用意します。
まずは Ruby のイメージを使ってコンテナの中に入ります。

docker run --rm -it -v ${PWD}:/usr/src/app ruby:2.7 bash

コンテナの中に入ったら rails new でファイルを生成します。

cd /usr/src/app
gem install rails
rails new webapp --skip-bundle

そして生成したファイルでイメージをビルドします。
以下がビルドに使用する Dockerfile です。

FROM ruby:2.7

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt update -yqq && apt install -yqq yarn

WORKDIR /usr/src/app
COPY Gemfile Gemfile
RUN bundle install

COPY . /usr/src/app
RUN bin/rails webpacker:install
EXPOSE 3000

CMD ["bin/rails", "s", "-b", "0.0.0.0"]
cd webapp
docker build . -t webapp

Nginx

Nginx へのアクセスを Rails へ振り分けるために設定ファイル nginx.conf を作成します。

アクセスログやエラーログの指定は行なっていません。
これは Docker イメージのデフォルトではそれぞれログの出力先が標準出力と標準エラー出力になっているためです。

FARGATE では同じタスクに属するコンテナは localhost 経由で接続できるようになっています。
よって、proxy_pass では localhost で通信先を指定しています。

user       nobody nogroup;
worker_processes  5;

events {
  worker_connections  4096;  ## Default: 1024
}

http {
  default_type application/octet-stream;
  log_format   main '$remote_addr - $remote_user [$time_local]  $status '
    '"$request" $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

  sendfile     on;
  tcp_nopush   on;

  server {
    listen    80;

    location / {
      proxy_pass    http://localhost:3000;
    }
  }
}

こちらも以下のような Dockerfile を作成してビルドします。

FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
docker build . -t proxy

ECR へイメージをプッシュ

作成したイメージを ECR へプッシュします。
ECR へのプッシュは ecs-cli push で行えます。

ecs-cli push webapp --ecs-profile rails-app
ecs-cli push proxy --ecs-profile proxy

コマンドを実行してプッシュに成功すると、イメージ名の前にプレフィックスとして AWS のアカウント ID が含まれているイメージタグが出力されます。
このイメージタグは ECS の構成ファイルを作成するときに使用します。

ECS の構成ファイルを作成

デプロイ用の構成ファイルを作成していきます。
まずは docker-compose.yml から。

version: "3"
services:
  proxy:
    image: <AWS_ACCOUNT_ID>.dkr.ecr.ap-northeast-1.amazonaws.com/proxy
    ports:
      - 80:80
    logging:
      driver: awslogs
      options:
        awslogs-group: rails-app
        awslogs-region: ap-northeast-1
  webapp:
    image: <AWS_ACCOUNT_ID>.dkr.ecr.ap-northeast-1.amazonaws.com/webapp
    logging:
      driver: awslogs
      options:
        awslogs-group: rails-app
        awslogs-region: ap-northeast-1

logging でドライバーに awslogs を指定することで標準出力されたログが CloudWatchLogs へ書き込まれるようになります。

そして、ECS 固有の設定ファイルである ecs-params.yml を作成します。
subnets や security_groups のところにはクラスター作成のときに出力された ID をそれぞれ入力してください。

version: 1
task_definition:
  task_execution_role: ecsTaskExecutionRole
  ecs_network_mode: awsvpc
  task_size:
    mem_limit: 0.5GB
    cpu_limit: 256
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 service up --create-log-groups --cluster-config rails-app --ecs-profile rails-app