本記事で目指す構成
① GitHubへpush
② CircleCiでビルドが走る
③ テスト(Rspec)に通過した場合、Dockerイメージを作成してECRへpush
④ 最新のDockerイメージを利用し、ECSのタスク定義を更新してデプロイ完了
対象読者
- ECSに全く触れた事が無い人
- とりあえず手を動かして雰囲気を掴みたい人
- 就活のためにポートフォリオを作成中の人
簡単なRailsアプリ(「Hello World!」と表示するだけ)をAWS ECSにデプロイするまでの手順。がっつりその後の運用まで考慮しているわけではなく、あくまで参考程度にしかならないためその点はご注意ください。
人によって多分色々なやり方がありそうなので、一度流れを掴んだ後は各自お好みで設定していただきたいです。
デプロイ用のアプリを用意する段階から全てハンズオン形式(スクショも多数)で記載しており、書いてある通りに進めれば基本的には上手くいくはず。
要所要所で任意の値(プロフィール名やアプリ名など)を設定する部分があるので、不安な場合は全て筆者と同じように「sample-app」などで統一すると良いかもです。
※初心者向けと銘打っているものの、「まずは実際に手を動かして雰囲気を掴む」という目的に徹しているため、各用語に関する説明はほとんど説明していません。
※あくまで「AWS超初心者でもとりあえず書いてある通りに従えばそれっぽくデプロイできる」というのがコンセプト。理論派の方はあらかじめ他の記事でECR・ECSの概念について学習してから入るのをおすすめします。(筆者は体で覚える派なので...)
※AWSの各リソース名などに関しては基本的に任意なので各自お好みで(ただし、ecs-cliでコマンドを打つ際やCircleCiのconfig内の記述をそれに合わせる必要あり)。また、特に触れていない部分に関してはとりあえずデフォルトの状態もしくは空欄で大丈夫だと思います。
※スクショ撮ったタイミングが違ったりしてリビジョンナンバーなどにバラつきがあるかもしれませんが、無視してください(汗)
仕様
- 言語: Ruby2.6
- フレームワーク: Rails6
- データベース: MySQL5.7
- アプリケーションサーバー: Puma
- Webサーバー: Nginx
下準備編
まず、ECSにデプロイするための簡単なRailsアプリを用意。
サンプル
$ git clone https://github.com/kazama1209/sample-app.git
$ cd sample-app
セットアップ
$ docker-compose build
$ docker-compose run web bundle exec rails webpacker:install
# ↑のコマンドでエラーが出た場合
$ docker-compose run web yarn install --check-files
$ docker-compose up -d
$ docker-compose run web bundle exec rails db:create
master.keyを作成
↑からリポジトリをクローンした場合、config/master.keyが存在しないはず。master.keyが無いとデプロイ時にエラーが起きるため、以下の手順によりここで生成しておく。
$ rm config/credentials.yml.enc
$ docker-compose run -e EDITOR=vim web rails credentials:edit
参照:
Rails on Dockerでcredentialsをeditしたい
ActiveSupport::MessageEncryptor::InvalidMessage
localhostにアクセス
http://localhost
にアクセスしていつもの画面が表示されればOK。
トップページ「Hello World!」を作成
今回の最終目的である「Hello World!」と返すトップページを作成する。
class HomeController < ApplicationController
def index
end
end
<h1>Hello World!</h1>
Rails.application.routes.draw do
root 'home#index'
end
「Hello World!」が返ってくればサンプルアプリの準備は完了。
デプロイ編
アプリの準備ができたので、ECSにデプロイしていく。
各種ツールをインストール
今回、ECSにデプロイするにあたり以下2つのツールを使用する。
$ brew install awscli
$ brew install amazon-ecs-cli
aws configureを設定
上記のツールを使用するためにaws configureの設定を行う。
IAMユーザーを作成
AWSのコンソールからサービス→IAMを選択し、「ユーザーの追加」をクリック。
任意のユーザー名(今回は「sample-app」)を入力し、「プログラムによるアクセス」にチェックをつけて次のステップへ。
「既存のポリシーを直接アタッチ」から以下の2つのポリシーをアタッチして次のステップへ。
- AmazonECS_FullAccess
- AmazonEC2ContainerRegistryFullAccess
タグに関しては今回はは無視で次のステップへ。
最後に入力情報の確認画面が表示されるので、特に問題無ければ「ユーザーの作成」をクリック。
ユーザーの作成に成功すると「アクセスキー」「シークレットアクセスキー」の2つが発行されるので、メモを取るなりcsvファイルをダウンロードするなり大事に保管。
ターミナルで「aws configure」を実行
$ aws configure --profile <先ほど作成したIAMユーザー名(今回は「sample-app」)>
AWS Access Key ID # 先ほど作成したアクセスキー
AWS Secret Access Key # 先ほど作成したシークレットアクセスキー
Default region name # ap-northeast-1
Default output format # json
それぞれ上記のように入力。
追加でポリシーを作成
先ほどIAMユーザーを作成した際、
- AmazonECS_FullAccess
- AmazonEC2ContainerRegistryFullAccess
2つのポリシーをアタッチしたが、これだけだとこの後に使用する「ecs-cli」というツールの中で権限エラーが発生するため、ここで別途追加しなければならない。
AWSのコンソールからサービス→IAM→ポリシーを選択し、「ポリシーの作成」をクリック。
JSONタブを開いて以下の記述を行う。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"iam:AttachRolePolicy",
"iam:AddRoleToInstanceProfile",
"iam:CreateInstanceProfile",
"iam:CreateRole",
"iam:DeleteInstanceProfile",
"iam:DeleteRole",
"iam:DetachRolePolicy",
"iam:PassRole",
"iam:RemoveRoleFromInstanceProfile",
"ec2:DeleteInternetGateway",
"ec2:DeleteSecurityGroup",
"ec2:DeleteRouteTable"
],
"Resource": "*"
}
]
}
ポリシーの名前や説明を適当に入力し、「ポリシーの作成」をクリック。
ポリシーをユーザーにアタッチ
AWSのコンソールからサービス→IAM→ユーザーを選択し、「アクセス権限の追加」をクリック。
「既存のポリシーをアタッチ」から先ほど作成したポリシーを選択し、アクセス権限を追加。
キーペアを作成
後々EC2内へ入る際などに必要になるのでキーペアを作成しておく。
AWSのコンソールからサービス→EC2→キーペアを選択し、「キーペアの作成」をクリック。
名前とファイル形式を入力し、「キーペアを作成」をクリック。
$ mv Downloads/sample-app.pem .ssh/
$ chmod 600 ~/.ssh/sample-app.pem
完了すると「.pem」形式のファイルがダウンロードされるので、「.ssh」ディレクトリに移動させて権限を変更する。
クラスターを作成
コンソールから手動でぽちぽち作成する事も可能だが、vpcやサブネットなども一緒に作る必要があるため、今回はecs-cliでまとめて作成してしまう。
次のコマンドを実行。
$ ecs-cli configure profile --profile-name <任意のプロフィール名> --access-key <先ほど作成したアクセスキー> --secret-key <先ほど作成したシークレットアクセスキー>
$ ecs-cli configure --cluster <任意のクラスター名> --default-launch-type EC2 --config-name <任意の設定名> --region ap-northeast-1
$ ecs-cli up --keypair <先ほど作成したキーペア> --capability-iam --size 2 --instance-type t2.small --cluster-config <任意の設定名> --ecs-profile <任意のプロフィール名>
キーなどの各値は各自異なる。
筆者の場合は下記のような感じ。
$ ecs-cli configure profile --profile-name sample-app --access-key ******************** --secret-key ****************************************
$ ecs-cli configure --cluster sample-app-cluster --default-launch-type EC2 --config-name sample-app-cluster --region ap-northeast-1
$ ecs-cli up --keypair sample-app --capability-iam --size 2 --instance-type t2.small --cluster-config sample-app-cluster --ecs-profile sample-app
INFO[0006] Using recommended Amazon Linux 2 AMI with ECS Agent 1.44.3 and Docker version 19.03.6-ce
INFO[0007] Created cluster cluster=sample-app-cluster region=ap-northeast-1
INFO[0009] Waiting for your cluster resources to be created...
INFO[0009] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS
INFO[0070] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS
INFO[0131] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS
VPC created: vpc-*****************
Security Group created: sg-*****************
Subnet created: subnet-*****************
Subnet created: subnet-*****************
Cluster creation succeeded.
上手くいくと↑のようにクラスター用のVPC、セキュリティグループ、サブネットなどが自動で作成される。
AWSのコンソールからサービス→Elastic Container Service→クラスターを選択し、無事作成されていれば成功。
RDSを作成
データベースとして使うRDSを作成。
AWSのコンソールからサービス→RDSを選択し、「データベースの作成」をクリック。
- 作成方法: 標準作成
- エンジンのタイプ: MySQL
- DBインスタンスサイズ: 無料利用枠
- DBインスタンス識別子: sample-app-db
- マスターユーザー名: root
- パスワード: password
※この辺は全て任意。
- vpc: 先ほど作成したvpc
- サブネットグループ: 新しいDBサブネットグループの作成
- パブリックアクセス: あり
※今回はあくまで練習なので「あり」を選択しているが、本番環境では「なし」にした方が良いかも。
最初のデータベース名: sample_app_production
※記事冒頭で用意したサンプル(sample-app)を使用する場合、※データベース名はsample-appの「config/database.yml」内で定義しているため「sample_app_production」で固定
※特に触れていない部分は空欄もしくはデフォルトのままでOK。
問題なければ「データベースの作成」をクリック。
↑こんな感じで作成されていれば成功。
また、セキュリティグループの設定も必要なので「VPCセキュリティグループ」の下に記載されているリンクをクリック。
「インバウンドルールの編集」から次のように設定。
- タイプ: MYSQL/Aurora
- プロトコル: TCP
- ポート範囲
- ソース: 0.0.0.0/0
$ mysql -h <RDSのエンドポイント> -u <RDSのユーザー名> -p
試しにターミナルで↑のコマンドを叩き、接続できれば成功。
ロードバランサーを作成
AWSのコンソールからサービス→EC2→ロードバランサーを選択し、「ロードバランサーの作成」をクリック。
3種類あるが、「Application Load Balancer」を選択。
- 名前: sample-app-alb ※任意
- リスナー: そのままでOK
- VPC: 先ほど自動作成されたものを選択
- subnet: 同上
先に進むとセキリュティグループの設定画面になるので、「新しいセキリュティグループを作成する」から適当にセキュリティグループを作成。
ターゲットグループの設定。
- ターゲットグループ: 新しいターゲットグループ
- 名前: sample-app-alb-tg ※任意
クラスター作成時に自動で作られたEC2を登録し、確認画面から問題なければ「作成」をクリックして完了。
ECRにdockerイメージをpush
AWSのコンソールからサービス→Amazon Elastic Container Registryを選択し、「リポジトリの作成」をクリック。
それぞれ適当なリポジトリ名を入力し、「リポジトリを作成」をクリック。
プッシュコマンドを表示し、書いてある通り上から順に4つ実行していく。
※2番目のコマンドでbuildを行う際は-fでDockerfileのコンテキストを変える。
# Rails(本番用のDockerfileを使用する)
$ docker build -f ./prod.Dockerfile . -t sample-app-rails
# Nginx
$ cd containers/nginx
$ docker build -f ./Dockerfile . -t sample-app-nginx
本番用のDockerfile
$ touch prod.Dockerfile
FROM ruby:2.6.6
ENV LANG C.UTF-8
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \
apt-get install nodejs
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
apt-get update && apt-get install -y yarn
RUN mkdir /sample-app
WORKDIR /sample-app
ADD Gemfile /sample-app/Gemfile
ADD Gemfile.lock /sample-app/Gemfile.lock
RUN gem install bundler:2.1.4
RUN bundle install
ADD . /sample-app
# Nginxと通信を行うための準備
RUN mkdir -p tmp/sockets
VOLUME /sample-app/public
VOLUME /sample-app/tmp
RUN yarn install --check-files
RUN SECRET_KEY_BASE=placeholder bundle exec rails assets:precompile
基本的に開発用のものと同じだが、最後の数行でNginxと通信を行うための準備などを行っている。
全て打ち終わったらリポジトリを確認し、イメージが追加されていれば成功。
タスクの作成
先ほどpushしたイメージをもとに、タスクの作成を行う。
$ mkdir ecs
$ touch ecs/docker-compose.yml
version: 2
services:
app:
image: # ECRのリポジトリURI(Rails)
command: bash -c "bundle exec rails db:migrate && bundle exec rails assets:precompile && bundle exec puma -C config/puma.rb"
environment: # 練習なので直書きスタイルだが、実際はdotenvなどを使った方が良いと思う。
RAILS_ENV: production
RAILS_MASTER_KEY: # config/master.keyの値
DATABASE_NAME: sample_app_production
DATABASE_USERNAME: root
DATABASE_PASSWORD: password
DATABASE_HOST: # RDSのエンドポイント
TZ: Japan
working_dir: /sample-app
logging:
driver: awslogs
options:
awslogs-region: ap-northeast-1
awslogs-group: sample-app-production/app
awslogs-stream-prefix: sample-app-production
nginx:
image: # ECRのリポジトリURI(Nginx)
ports:
- 80:80
links:
- app
volumes_from:
- app
working_dir: /sample-app
logging:
driver: awslogs
options:
awslogs-region: ap-northeast-1
awslogs-group: sample-app-production/nginx
awslogs-stream-prefix: sample-app-production
次のコマンドを実行。
$ ecs-cli compose --project-name sample-app-task -f ./ecs/docker-compose.yml up --create-log-groups --cluster-config sample-app-cluster --ecs-profile sample-app
上手く行った場合、実行中のタスクに「1」と表示される。
最後に、ロードバランサーのDNS名をURLに貼り付けてアクセス。
ローカル環境で作成したものと同じように「Hello World!」と表示されれば成功。
※デプロイに際して何か不具合があった場合はCloudWatchのログを確認して修正。
サービスの作成
クラスターとタスクだけでもアプリは動くが、その中間に「サービス」と呼ばれるものを作成すると、コンテナが止まった際に再起動をかけてくれたりロードバランサーを通じてオートスケーリングしてくれたり何かと便利ぽいので作成しておく。
AWSのコンソールからサービス→Amazon Elastic Container Service→ クラスター名をクリックし、「サービス」タブを開いて作成ページに進む。
- 起動タイプ: EC2
- タスク定義: sample-app-task ※先ほど作成したもの
- クラスター: sample-app-cluster ※同上
- サービス名: sample-app-service ※任意
- その他: 画像の通り
- ロードバランサーの種類: Application Load Balancer
- ロードバランサー名: 先ほど作成したもの
- その他: 画像の通り
- ターゲットグループ名: 先ほど作成したもの
- その他: 画像の通り
最後に確認画面が表示されるので、問題無ければ作成をクリック。
無事作成されれば完了。
このままだと2つのタスク(片方はecs-cliでターミナルから開始したもの、もう片方はサービスの作成により開始されたもの)が実行中になってしまっているため、前者は停止してしまってOK。
おまけ(CircleCiと連携して自動デプロイ)
このままだと変更点があるたびに手動で「ビルド→プッシュ→タスク再定義」といった面倒な作業が必要になるため、「CirlcleCiにプッシュ→ビルド&テスト→ECR・ECSへ自動デプロイ」といった良くある仕組みを構築していく。
Rspecを導入
まず、デプロイ前にテストを行うためにRspecを導入する。
gemをインストール
group :development, :test do
gem 'rspec-rails'
end
# Gemfileを更新したので再度ビルド
$ docker-compose build
各種ファイルを作成&編集
$ docker-compose run web bundle exec rails generate rspec:install
create .rspec
create spec
create spec/spec_helper.rb
create spec/rails_helper.rb
--format documentation
↑の1行を追記しておくと、Rspecを実行した際の出力表示が見やすくなる。
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
必須ではないが、後ほどテスト用のヘルパーメソッドを作成する事になった場合、ファイルの置き場として「spec/support」を使用するので一応設定しておく。
デフォルトではコメントアウトされているので、それを外せばOK。
config.generators do |g|
g.test_framework :rspec,
view_specs: false,
helper_specs: false,
controller_specs: false,
routing_specs: false
end
このままだとrails g
コマンドを打ち込んだ際に自動で諸々のテストファイルが作成されてしまうので、余計なものを作成したくない場合は「config/application.rb」で設定を行う。
※この辺はお好みで。
rspecを実行
$ docker-compose run web bundle exec rspec
No examples found.
Finished in 0.00276 seconds (files took 0.12693 seconds to load)
0 examples, 0 failures
まだ何もテストを書いていないので、当然こうなる。
とりあえずRequest Specを書いてみる
手始めに、リクエストに対して正常なレスポンスが返ってくるかどうかを確認するためのRequest Specを書いてみる。
テストファイルを作成
$ docker-compose run web bundle exec rails g rspec:request home
create spec/requests/homes_spec.rb
require 'rails_helper'
RSpec.describe "Home", type: :request do
describe "GET /" do
it "works successfully" do
get root_path
expect(response).to have_http_status(200)
end
end
end
「/」にアクセスした際、 200番のステータスコードが返ってくるかどうかのテスト。
$ docker-compose run web bundle exec rspec
Home
GET /
works successfully
Finished in 0.53664 seconds (files took 8.4 seconds to load)
1 example, 0 failures
再度rspecを実行し、問題無くパスしていれば成功。
CircleCIと連携
次に、実際にCircleCiと連携するための設定を行う。
gemをインストール
group :development, :test do
gem 'database_cleaner'
gem 'rspec_junit_formatter'
gem 'webdrivers', '~> 3.0'
end
# Gemfileを更新したので再度ビルド
$ docker-compose build
各種ファイルを作成&編集
$ mkdir .circleci
$ touch .circleci/config.yml
$ touch config/database.yml.ci
$ docker-compose run web bundle exec rails db:schema:dump
version: 2
jobs:
build:
docker:
- image: circleci/ruby:2.6.6-node-browsers
environment:
- BUNDLER_VERSION: 2.1.4
- RAILS_ENV: 'test'
- image: circleci/mysql:5.7
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
- MYSQL_ROOT_HOST: '127.0.0.1'
working_directory: ~/sample_app
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "Gemfile.lock" }}
- v1-dependencies-
- run:
name: install dependencies
command: |
gem install bundler -v 2.1.4
bundle install --jobs=4 --retry=3 --path vendor/bundle
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
# database setup
- run: mv ./config/database.yml.ci ./config/database.yml
# database setup
- run:
name: setup database
command: |
bundle exec rake db:create
bundle exec rake db:schema:load
# install yarn
- run:
name: install yarn
command: yarn install
# install webpack
- run:
name: install webpack
command: bundle exec bin/webpack
# run tests
- run:
name: run rspec
command: |
mkdir /tmp/test-results
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
circleci tests split --split-by=timings)"
bundle exec rspec \
--format progress \
--format RspecJunitFormatter \
--out /tmp/test-results/rspec.xml \
--format progress \
$TEST_FILES
# collect reports
- store_test_results:
path: /tmp/test-results
- store_artifacts:
path: /tmp/test-results
destination: test-results
test:
adapter: mysql2
encoding: utf8
pool: 5
username: 'root'
port: 3306
host: '127.0.0.1'
database: sample_app_test
RSpec.configure do |config|
# config DataBaseCleaner
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
Rails.application.load_seed
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `rails
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 0) do
end
CircleCiとGitHubを接続
↑CircleCiのダッシュボードから連携したいリポジトリを探し、「Set Up Project」をクリック。画面に表示される指示に従い設定。
これで今後GitHubへ新しいプッシュを行った際、「.circleci/config.yml」に書いた内容に基づき自動でビルド&テストが走るようになる。
特に問題が無ければ「SUCCESS」と表示されるはず。
これで初期設定は完了。
自動デプロイ(ECR・ECS)
CircleCiのバージョン2.1から追加されたOrbを使い、masterブランチに変更が加えられた際、CircleCiでのビルド&テストを行い自動でイメージを作成しECRへプッシュし、ECSのサービスを更新してタスクの再定義を行うようにする。
環境変数の登録
あらかじめCircleCiの設定画面からデプロイに必要な環境変数を登録しておく。
- AWS_ACCESS_KEY_ID
- 先ほど作成したIAMユーザーのアクセスキー
- AWS_SECRET_ACCESS_KEY
- 先ほど作成したIAMユーザーのシークレットキー
- AWS_ACCOUNT_ID
- AWSのアカウントID(コンソールの「マイアカウント」から確認可能)
- AWS_REGION
- ap-northeast-1
- AWS_ECR_ACCOUNT_URL
- ECRのリポジトリURI(例: <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com)
- DATABASE_HOST
- RDSのエンドポイント(例: ********.ap-northeast-1.rds.amazonaws.com)
- DATABASE_USERNAME
- RDSのユーザー名(例: root)
- DATABASE_PASSWORD
- RDSのパスワード(例: password)
- DATABASE_NAME
- 使用するデータベース名(例: sample_app_production)
- RAILS_MASTER_KEY
- config/master.keyの値
- TZ
- JAPAN
- MY_APP_PREFIX
- 任意(例: sample-app)
- 変数を使い回してなるべくコンパクトに書けるようにクラスター名やタスク名は共通のワードを含めて作成しておいた方が良い(sample-app-cluster、sample-app-taskなど)。
.circleci/config.ymlを編集
version: 2.1
orbs:
aws-ecr: circleci/aws-ecr@6.7.0
aws-ecs: circleci/aws-ecs@1.1.0
jobs:
test:
docker:
- image: circleci/ruby:2.6.6-node-browsers
environment:
- BUNDLER_VERSION: 2.1.4
- RAILS_ENV: 'test'
- image: circleci/mysql:5.7
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
- MYSQL_ROOT_HOST: '127.0.0.1'
working_directory: ~/project
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "Gemfile.lock" }}
- v1-dependencies-
- run:
name: install dependencies
command: |
gem install bundler -v 2.1.4
bundle install --jobs=4 --retry=3 --path vendor/bundle
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
- run: mv ./config/database.yml.ci ./config/database.yml
- run:
name: setup database
command: |
bundle exec rake db:create
bundle exec rake db:schema:load
- run:
name: install yarn
command: yarn install
- run:
name: install webpack
command: bundle exec bin/webpack
- run:
name: run rspec
command: |
mkdir /tmp/test-results
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
circleci tests split --split-by=timings)"
bundle exec rspec \
--format progress \
--format RspecJunitFormatter \
--out /tmp/test-results/rspec.xml \
--format progress \
$TEST_FILES
- store_test_results:
path: /tmp/test-results
- store_artifacts:
path: /tmp/test-results
destination: test-results
workflows:
version: 2
test_and_deploy:
jobs:
- test
# ビルドした後にイメージをECRへプッシュ
- aws-ecr/build-and-push-image:
requires:
- test
account-url: AWS_ECR_ACCOUNT_URL
region: AWS_REGION
aws-access-key-id: AWS_ACCESS_KEY_ID
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
create-repo: true
dockerfile: ./prod.Dockerfile
repo: "${MY_APP_PREFIX}-rails"
tag: "${CIRCLE_SHA1}"
filters:
branches:
only:
- master
# ECSのサービスを更新してタスクを再定義
- aws-ecs/deploy-service-update:
requires:
- aws-ecr/build-and-push-image
family: "${MY_APP_PREFIX}-task"
cluster-name: "${MY_APP_PREFIX}-cluster"
service-name: "${MY_APP_PREFIX}-service"
container-image-name-updates: "container=app,tag=${CIRCLE_SHA1}"
各環境変数に間違いが無いか良く確認しておく事。
<h1>Hello World!</h1>
<p>Completed auto deploy with CircleCi</p>
自動デプロイが上手くいったかわかりやすいようにトップページを少し変えておく。
masterブランチに変更を加える
実際にmasterブランチにプッシュして変更を加えてみると、CircleCi上でデプロイ込みのjobsが動き始める。
※全てのフローが完了するまでに大体10〜15分くらいかかるので注意。
再度ロードバランサーのDNS名にアクセスし、先ほどの変更がちゃんと更新されていれば成功。
※反映されるまで多少時間がかかるので気長に待つ。
古いタスクと新しいタスクの2つが実行されているが、時間の経過で古い方は勝手に消されるため(サービスのおかげ?)そのまま放置でOK。
お疲れ様でした。
あとがき
自分もまだまだ勉強中の身なので、何かあれば随時更新予定です。
現状、コンソール上で手を動かしながら行う作業とターミナルでコマンドを叩いて行う作業がごちゃ混ぜになってしまっているため、できれば全て後者に統一したいと考えていいます。
Terraformとかも使って一発バシっとできるようにしたい...。
どこか詰まった部分やもっとこうした方が良いなどあればコメントいただけると嬉しいです。
(2021年4月3日追記: Terraformで一発デプロイできるようになりました。)
個人的に詰まった部分
EC2のインスタンスタイプ
- 「t2.small」以上を推奨。無料枠での使用だと「t2.micro」が定番だと思うが、筆者の場合、t2.microだとメモリ不足になる不具合が生じた。
service sample-app-service was unable to place a task because no container instance met all of its requirements. The closest matching container-instance c4b1a3e7-3209-408c-9501-7b3ea30f97f7 has insufficient memory available. For more information, see the Troubleshooting section.
参照: https://aws.amazon.com/jp/premiumsupport/knowledge-center/ecs-container-instance-cpu-error/
本記事内だと、クラスター作成時のコマンド
$ ecs-cli up --keypair sample-app --capability-iam --size 2 --instance-type t2.small --cluster-config sample-app-cluster --ecs-profile sample-app
↑この部分でインスタンスタイプを指定している。
Nginxの設定
- nginx.confファイルの中身は各自変更しないと正常に動かない部分があるので、ググりながら適宜修正する必要がある。
appコンテナとnginxコンテナの接続が上手くいかないと
2020/09/13 20:02:57 [crit] 7#7: *456 connect() to unix:///sample-app/tmp/sockets/puma.sock failed (2: No such file or directory) while connecting to upstream, client: *********, server: localhost, request: "GET / HTTP/1.1", upstream: "http://unix:///sample-app/tmp/sockets/puma.sock:/500.html", host: "***********"
↑こんな感じのエラーで延々と悩まされる。
各環境変数の設定
- CircleCiに登録する環境変数の値がしっかり合っているか何度も確認した方が良い。ビルドするのにやたら時間がかかるので、1回失敗するとそれだけでかなりの時間の無駄になる。