こんにちは、DMM WEBCAMP AdventCalender の20日目を担当します、AWSとDockerが好きな@tkt_horikoshiです。
今回はコマンドラインベースでECSを構築できるAWS Copilotを試してみたので使い心地を共有していきたいと思います。
はじめに
DMM WEBCAMPでエンジニアメンターを始めてからまだ日は浅いですが、受講生さんの中にDockerで開発環境を整えている方がいらっしゃるみたいでした。
せっかくDockerで開発してるならEC2に直接ファイルを置いたりせずにコンテナベースでアプリケーションを実行できるようにしたい。でも、何から始めたらいいかわからん。
そんな思いを抱いている人が一歩踏み出すきっかけになれば幸いです。
ECSとは
ECSはAmazon Elastic Container Serviceの略称で、Dockernizeされたアプリケーションをデプロイし、実行環境を提供するサービスです。コンピューティングリソースにEC2とFargateを選択できますが、特にFargateはコンテナ向けコンピューティングエンジンでプロビジョニングおよびメンテナンスコストが不要になるといった特徴があります。
とても魅力的なサービスで、私も本業でお世話になっているのですが、イチから構築するとなるとそこそこコストがかかるというのも事実です。
AWS Copilotを触ってみる
AWS CopilotはコマンドラインベースでコンテナアプリケーションをサクッとECSへデプロイできるというものです。コマンドラインからECS作れるなんてにわかに信じがたいですがやっていこうと思います。
基本用語
Copilotを利用するうえで理解しておきたいコンセプトについてについてまとめました。Copilotを使ってコマンドラインから下記をそれぞれ作成することができます。
コンセプト | 説明 |
---|---|
Application | 例えばチャットアプリや、ブログサイトといった開発プロダクトそのものを示す概念です。ApplicationにはService, Environmentといった概念が含まれています。 |
Service | ServiceはFrontend, Backendといったアプリケーションの構成要素を示します。 |
Environment | Environmentはテスト環境, プロダクション環境というようなアプリケーションの動作環境の役割を示します。 |
Job | イベント駆動で一時的に実行される処理(ECSタスク)を示します。 |
Pipeline | ビルド、テスト、デプロイといった一連の作業を行うリリースフローを示します。 |
今回の記事では主にApplication, Service, Environmentについて触れていきます。
本日のゴール
Copilotを使ってアプリケーションをECSにデプロイし、Railsのウェルカム画面を表示してみたいと思います。
アプリケーションの準備
Railsの準備
Dockerfile
まずは、アプリケーションをDockerで動くように設定します。ルートディレクトリにDockerfileを配置します。
FROM ruby:2.7
RUN mkdir /app
WORKDIR /app
RUN curl https://deb.nodesource.com/setup_12.x | bash
RUN curl 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-get update -qq && \
apt-get install -y nodejs yarn
COPY Gemfile* ./
RUN bundle install
COPY . .
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
config.hostsの指定
ドメイン許可のためconfig/environments/development.rb
, config/environments/production.rb
にconfig.hosts
を追加してドメインを指定しておきます。
今回は、テスト環境とプロダクション環境それぞれのサブドメインも許可したいので下記のような指定となります。
Rails.application.configure do
...
config.hosts << ".takat0-h0rikosh1.com"
...
end
Nginxの準備
Nginxも同様にDockerで動くように設定します。ルートディレクトリにnginx
というディレクトリをきって各種設定ファイルを配置します。
${SERVER_HOST}
を環境変数で切り替えられるようにする都合で下記通り設定ファイルのテンプレートを用意しました。
upstream app {
server ${SERVER_HOST}:3000;
}
server {
listen 80;
server_name localhost;
root /app/public;
client_max_body_size 100m;
error_page 404 /404.html;
error_page 505 502 503 504 /500.html;
try_files $uri/index.html $uri @app;
keepalive_timeout 5;
location / {
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
proxy_connect_timeout 30;
}
}
続いてDockerfileです。CMDでenvsubst
を使いで変数を差し替えて/etc/nginx/conf.d/nginx.conf
に出力します。SERVER_HOST
のデフォルトはlocalhost
としてます。
FROM nginx:alpine
RUN rm -f /etc/nginx/conf.d/*
ADD nginx.conf.template /etc/nginx/conf.d/nginx.conf.template
ENV SERVER_HOST localhost
CMD envsubst \
'$$SERVER_HOST' \
< /etc/nginx/conf.d/nginx.conf.template > \
/etc/nginx/conf.d/nginx.conf \
&& nginx -g 'daemon off;'
動作確認
docker-compose.ymlを作成して動作確認してみます。
version: "3.8"
services:
app:
build: .
volumes:
- .:/app
ports:
- "3000:3000"
environment:
RAILS_ENV: development
proxy:
build:
context: nginx
ports:
- 80:80
depends_on:
- app
environment:
SERVER_HOST: host.docker.internal
コンテナを起動立ち上げてみましょう
$ docker-compose up
Recreating copilot-playground_app_1 ... done
Recreating copilot-playground_proxy_1 ... done
Attaching to copilot-playground_app_1, copilot-playground_proxy_1
app_1 | => Booting Puma
app_1 | => Rails 6.0.3.4 application starting in development
app_1 | => Run `rails server --help` for more startup options
app_1 | Puma starting in single mode...
app_1 | * Version 4.3.7 (ruby 2.7.2-p137), codename: Mysterious Traveller
app_1 | * Min threads: 5, max threads: 5
app_1 | * Environment: development
app_1 | * Listening on tcp://0.0.0.0:3000
app_1 | Use Ctrl-C to stop
ひとまず手元での動作確認はできました。
Copilotを使ってデプロイしてみる
Install
$ brew install aws/tap/copilot-cli
参考: https://github.com/aws/copilot-cli
Applicationを作成する
自身の所有するドメインを指定してApplicationを作成します。Applicationの名前はwelcome
にします。ルートディレクトリで copilot app init を実行します。
$ copilot app init welcome --domain takat0-h0rikosh1.com
✔ Created the infrastructure to manage services and jobs under application welcome.
✔ The directory copilot will hold service manifests for application welcome.
Recommended follow-up actions:
- Run `copilot init` to add a new service or job to your application.
なにやら copilot init を実行することを進められます。
Serviceの作成とテスト環境のプロビジョン・デプロイ
実行ログに促されるまま copilot init を実行するとステップバイステップでServiceの設定を進めていくことができます。
$ copilot init
Welcome to the Copilot CLI! We're going to walk you through some questions
to help you get set up with an application on ECS. An application is a collection of
containerized services that operate together.
はじめにServiceのワークロードタイプについて聞かれるのでここはLoad Balanced Web Service
を選択します。名前をどうするか聞かれるのでappとしました。
Which workload type best represents your architecture? [Use arrows to move, type to filter, ? for more help]
> Load Balanced Web Service
Backend Service
Scheduled Job
What do you want to name this Load Balanced Web Service? [? for help]
app
続いて、Dockerfileの選択を迫られます。ここではRailsのDockerfileを選択します。
for welcome? [Use arrows to move, type to filter, ? for more help]
> ./Dockerfile
nginx/Dockerfile
Enter custom path for your Dockerfile
Use an existing image instead
最後に、テスト環境へデプロイするからどうか尋ねられるので y
と入力します。
Would you like to deploy a test environment? [? for help] (y/N)
y
ここまでCLIとの対話が完了するとすごい勢いでプロビジョニングが始まり、数分後にデプロイが完了します。実行ログの最後にURLが表示されるのでブラウザで表示してみます。
おー、出ました。
念の為、AWSマネジメントコンソールに入ってざっと内容を確認してみることにします。
ネットワークの構成はどうでしょうか?
Environmentの粒度でVPCも丸ごと用意してくれちゃってるみたいです。
すごいですね、コマンドラインだけでDNSの設定を含めECSでアプリケーションを動かすところまでたどり着きました。
SidecarパターンでNginxを動かす
既にお気づきの方もいらっしゃるかと思いますが、この時点ではブラウザからのアクセスがALBを通じてRailsのサーバーに向かってしまいます。
そこで、Railsサーバーのコンテナが動いているECSタスクにNginxのコンテナを動かしてALBのトラフィックをプロキシできるようにします。
ECSタスク内であるコンテナに対して補佐的な役割のコンテナを動かすアプローチをSidecarパターンと呼びます。
ECRにプッシュする
NginxのDockerイメージをECRにプッシュします。
https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/getting-started-cli.html
# リポジトリ作成
$ aws ecr create-repository --repository-name welcome/proxy
# イメージプッシュ
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 593653836732.dkr.ecr.ap-northeast-1.amazonaws.com && \
docker build -t welcome/proxy nginx && \
docker tag welcome/proxy:latest 593653836732.dkr.ecr.ap-northeast-1.amazonaws.com/welcome/proxy:latest && \
docker push 593653836732.dkr.ecr.ap-northeast-1.amazonaws.com/welcome/proxy:latest
マニフェストファイル修正
copilot init を実行した時点で設定情報が記載されたマニフェストファイルが作成されているのでこれを下記のように修正します。
name: app
type: Load Balanced Web Service
image:
build: ./Dockerfile
port: 3000
http:
path: 'app'
# 追加
healthcheck: '/'
targetContainer: 'proxy'
cpu: 256
memory: 512
count: 1
# 追加
sidecars:
proxy:
port: 80
image: 593653836732.dkr.ecr.ap-northeast-1.amazonaws.com/welcom/proxy:latest
デプロイ
Serviceをデプロイする copilot svc deploy コマンドを実行します。
$ copilot svc deploy
以下の通り、ECSタスクに app
と proxy
のコンテナが実行されてることが確認できました。
追加した proxy
コンテナへロードバランシングできていることが確認できました。
ここまでで、Nginxでプロキシできるようになりました。
Production環境のデプロイ
それでは、プロダクション環境のデプロイを試したいと思います。
環境変数の設定
今回はウェルカム画面を出したいだけなので実際には省きましたが、Production環境デプロイする場合はRAILS_ENV
にproduction
を設定する必要があると思うのですが、その際はマニフェストファイルに下記を追記します。
# 省略
environments:
production:
variables:
RAILS_ENV: production
参考: https://aws.github.io/copilot-cli/docs/developing/environment-variables/
Environment作成
Environmentを作成する copilot env init のコマンドを--prod
付きで実行します。名前はproduction
としました。
$ copilot env init \
--name production \
--default-config \
--prod
ここまでで、VPC
とECS Cluster
が作成されていることが確認できます。
ご覧の通り、サービスの実行まだ行われてません。
Serviceデプロイ
copilot svc deploy を本番環境へ向けて実行してみることにします。
$ copilot svc deploy --env production
# 省略
✔ Deployed app, you can access it at https://app.production.welcome.takat0-h0rikosh1.com.
おー、出ました。
コマンド2発でProductioin環境のデプロイが完了しました。
Clean up
下記のコマンドでCopilotで作成したApplicationに含まれる全てのリソースがキレイに消えてくれます。不要であれば、無駄にお金を請求されないうちに消しておきましょう。
$ copilot app delete
最後に
AWS Copilotを使ってコマンドラインからECSを構築してみました。思っていた以上にサクサクできてしまいすごい時代になったものですね。特にネットワークの構成を意識させずにアプリケーションをホスティングするところまで持っていけるところがすごいです。使いこなせたら生産性がかなり上がりそうです。
時間を掛けずに試せるし、作るも消すもコマンドラインで自由自在ですので、アプリケーションを Dockernize したいけど腰が重いなあー、と感じていた人にとてもオススメです。
興味のある方はチャレンジしてみてください。
今回のソースコードはこちらに置いておきます。
https://github.com/takat0-h0rikosh1/dmm-webcamp-advent-calender-2020
参考
- https://aws.github.io/copilot-cli/
- https://github.com/aws/copilot-cli
- https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/Welcome.html
- https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/what-is-ecr.html
- https://qiita.com/minamijoyo/items/63ae57b99d4a4c5d7987
- https://qiita.com/kodai_0122/items/67c6d390f18698950440
- https://qiita.com/eighty8/items/0288ab9c127ddb683315