はじめに
dockerの勉強にawsのコンテナオーケストレーションツールの一つであるECSをecs-cliを使って触ってみました。ECSには Task Definitions
という複数のコンテナの定義を行うところがあり、いってみればdocker-compose.ymlみたいなものです。
ecs-cliを使えば、docker-compose.ymlの情報から Task Definitions
の定義を勝手に作ってくれます。なので、ecsのタスク定義の書き方が全くわからなくても(僕自身も書き方知らない)、docker-compose.ymlが書けさえすれば、ECSを使って何か作れます。
便利そうなので試してみました。
作ったもの
五目並べを作りました。
ログイン不要で遊べます。別タブで開くと別ユーザー扱いになるので、タブを二つ開けば一人でも遊べます(楽しくはない)。
使った技術としては、フロントはnext、websocketを使ってデプロイにnowを利用。
サーバー側はexpress、redisを使ってデプロイにecsを利用しました。
アプリケーション自体の説明は特にせず、ecs周りの作業の流れを中心に書いていきます。
サーバー側のアプリケーションのイメージをdocker hubにpush
※実際にECSを本番環境で使う場合はdocker hubに上げるのではなくECRにイメージをあげるのが普通のよう。
FROM node:11-alpine
COPY ./ /var/work
# イメージの作成
$ docker build ./ -t pokotyan/redis-express
# イメージのpush
$ docker push pokotyan/redis-express:latest
docker-compose.ymlの準備
redis:
image: redis
ports:
- 6379:6379
app:
image: pokotyan/redis-express:latest # pushしたdocker hubのイメージ
working_dir: '/var/work'
ports:
- 8999:8999
links:
- redis
environment:
- PORT=8999
command:
sh -c 'npm i && node index.js'
ecs-cliのインストール
$ sudo curl -o /usr/local/bin/ecs-cli https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-darwin-amd64-latest
Password:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 37.6M 100 37.6M 0 0 190k 0 0:03:22 0:03:22 --:--:-- 206k
$ sudo chmod +x /usr/local/bin/ecs-cli
$
$ ecs-cli --version
ecs-cli version 1.14.0 (1327575)
$
ecs-cliで利用するアカウントの設定
$ ecs-cli configure profile --profile-name default --access-key hoge --secret-key huga
INFO[0000] Saved ECS CLI profile configuration default.
$
~/.ecs/credentials
にこんな感じで設定されます。
$ vim ~/.ecs/credentials
version: v1
default: default
ecs_profiles:
default:
aws_access_key_id: hoge
aws_secret_access_key: huga
awsコンソール上でキーペアを作成
クラスタの作成
$ ecs-cli configure --region ap-northeast-1 --cluster gomoku-narabe-cluster
INFO[0000] Saved ECS CLI cluster configuration default.
$
~/.ecs/config
が生成されます。
$ cat ~/.ecs/config
version: v1
default: default
clusters:
default:
cluster: gomoku-narabe-cluster
region: ap-northeast-1
default_launch_type: ""
$
クラスタの起動
--keypair
には作成したキーペアのキーペア名 ecs1
を入れます
$ ecs-cli up --keypair ecs1 --capability-iam --instance-type t2.micro
INFO[0000] Using recommended Amazon Linux 2 AMI with ECS Agent 1.28.0 and Docker version 18.06.1-ce
INFO[0001] Created cluster cluster=gomoku-narabe-cluster region=ap-northeast-1
INFO[0001] Waiting for your cluster resources to be created...
INFO[0002] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS
INFO[0062] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS
INFO[0123] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS
VPC created: vpc-0b43bf1e36fbc01b1
Security Group created: sg-080d991cce3df9220
Subnet created: subnet-04a85d1e7691360de
Subnet created: subnet-06fab8574826ab488
Cluster creation succeeded.
$
composeを使ってタスクを動かす
※ecs-cli compose up
はdocker-compose.ymlがあるdirに移動してから実行
実行したところ、reason="RESOURCE:MEMORY"
でメモリが足りず失敗しました。
$ ecs-cli compose up
WARN[0000] Skipping unsupported YAML option for service... option name=networks service name=app
WARN[0000] Skipping unsupported YAML option for service... option name=networks service name=redis
INFO[0003] Using ECS task definition TaskDefinition="kurohige-server:1"
INFO[0004] Couldn't run containers reason="RESOURCE:MEMORY"
INFO[0004] Couldn't run containers reason="RESOURCE:MEMORY"
$
docker-compose.ymlにmem_limitを追加し、
mem_limit: 268435456
再度実行。起動できました。
$ ecs-cli compose up
WARN[0000] Skipping unsupported YAML option for service... option name=networks service name=redis
WARN[0000] Skipping unsupported YAML option for service... option name=networks service name=app
INFO[0000] Using ECS task definition TaskDefinition="kurohige-server:5"
INFO[0000] Starting container... container=4ccea8db-e34b-4729-b85e-9e400993bdb4/redis
INFO[0000] Starting container... container=4ccea8db-e34b-4729-b85e-9e400993bdb4/app
INFO[0000] Describe ECS container status container=4ccea8db-e34b-4729-b85e-9e400993bdb4/app desiredStatus=RUNNING lastStatus=PENDING taskDefinition="kurohige-server:5"
INFO[0000] Describe ECS container status container=4ccea8db-e34b-4729-b85e-9e400993bdb4/redis desiredStatus=RUNNING lastStatus=PENDING taskDefinition="kurohige-server:5"
INFO[0013] Describe ECS container status container=4ccea8db-e34b-4729-b85e-9e400993bdb4/app desiredStatus=RUNNING lastStatus=PENDING taskDefinition="kurohige-server:5"
INFO[0013] Describe ECS container status container=4ccea8db-e34b-4729-b85e-9e400993bdb4/redis desiredStatus=RUNNING lastStatus=PENDING taskDefinition="kurohige-server:5"
INFO[0020] Started container... container=4ccea8db-e34b-4729-b85e-9e400993bdb4/app desiredStatus=RUNNING lastStatus=RUNNING taskDefinition="kurohige-server:5"
INFO[0020] Started container... container=4ccea8db-e34b-4729-b85e-9e400993bdb4/redis desiredStatus=RUNNING lastStatus=RUNNING taskDefinition="kurohige-server:5"
$ ecs-cli ps
Name State Ports TaskDefinition Health
4ccea8db-e34b-4729-b85e-9e400993bdb4/app RUNNING 13.231.51.240:8999->8999/tcp kurohige-server:5 UNKNOWN
4ccea8db-e34b-4729-b85e-9e400993bdb4/redis RUNNING 13.231.51.240:6379->6379/tcp kurohige-server:5 UNKNOWN
$
ただ、サーバーの起動はしているみたいですがフロントからサーバーに接続できてない感じに。
起動しているコンテナインスタンスにssh接続して(事前にセキュリティグループにmyIPを穴あけしておく)
docker logsを見たところ起動はしていそうでした。
$ ssh -i ~/.ssh/ecs1.pem ec2-user@13.231.51.240
The authenticity of host '13.231.51.240 (13.231.51.240)' can't be established.
ECDSA key fingerprint is SHA256:59y2Px6+eBZcyemqy5vIMuU7wE8nVU1MWATN+Jc6AHQ.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '13.231.51.240' (ECDSA) to the list of known hosts.
Received disconnect from 13.231.51.240 port 22:2: Too many authentication failures
Disconnected from 13.231.51.240 port 22
$
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a7d4643a69c pokotyan/redis-express:latest "sh -c 'npm i && nod…" About an hour ago Up About an hour 0.0.0.0:8999->8999/tcp kurohige-server_app_1
713c6af50434 redis "docker-entrypoint.s…" 34 hours ago Up About an hour 0.0.0.0:6379->6379/tcp kurohige-server_redis_1
$
$ docker logs 2a7d4643a69c
npm WARN kurohige-server@1.0.0 No description
npm WARN kurohige-server@1.0.0 No repository field.
up to date in 2.461s
Server started on port 8999 :)
$
$ docker logs 713c6af50434
1:C 21 Apr 2019 12:35:57.692 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 21 Apr 2019 12:35:57.692 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 21 Apr 2019 12:35:57.692 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 21 Apr 2019 12:35:57.694 * Running mode=standalone, port=6379.
1:M 21 Apr 2019 12:35:57.694 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 21 Apr 2019 12:35:57.694 # Server initialized
1:M 21 Apr 2019 12:35:57.694 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 21 Apr 2019 12:35:57.694 * DB loaded from disk: 0.000 seconds
1:M 21 Apr 2019 12:35:57.694 * Ready to accept connections
1:C 21 Apr 2019 12:41:23.767 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 21 Apr 2019 12:41:23.767 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 21 Apr 2019 12:41:23.767 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 21 Apr 2019 12:41:23.768 * Running mode=standalone, port=6379.
1:M 21 Apr 2019 12:41:23.768 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 21 Apr 2019 12:41:23.768 # Server initialized
1:M 21 Apr 2019 12:41:23.769 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 21 Apr 2019 12:41:23.769 * DB loaded from disk: 0.000 seconds
1:M 21 Apr 2019 12:41:23.769 * Ready to accept connections
1:C 21 Apr 2019 13:14:13.476 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 21 Apr 2019 13:14:13.476 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 21 Apr 2019 13:14:13.476 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 21 Apr 2019 13:14:13.477 * Running mode=standalone, port=6379.
1:M 21 Apr 2019 13:14:13.477 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 21 Apr 2019 13:14:13.477 # Server initialized
1:M 21 Apr 2019 13:14:13.477 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 21 Apr 2019 13:14:13.477 * DB loaded from disk: 0.000 seconds
1:M 21 Apr 2019 13:14:13.477 * Ready to accept connections
1:C 21 Apr 2019 13:34:15.934 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 21 Apr 2019 13:34:15.934 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 21 Apr 2019 13:34:15.934 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 21 Apr 2019 13:34:15.935 * Running mode=standalone, port=6379.
1:M 21 Apr 2019 13:34:15.935 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 21 Apr 2019 13:34:15.935 # Server initialized
1:M 21 Apr 2019 13:34:15.935 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 21 Apr 2019 13:34:15.935 * DB loaded from disk: 0.000 seconds
1:M 21 Apr 2019 13:34:15.935 * Ready to accept connections
1:C 22 Apr 2019 21:12:00.435 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 22 Apr 2019 21:12:00.435 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 22 Apr 2019 21:12:00.435 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 22 Apr 2019 21:12:00.444 * Running mode=standalone, port=6379.
1:M 22 Apr 2019 21:12:00.444 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 22 Apr 2019 21:12:00.445 # Server initialized
1:M 22 Apr 2019 21:12:00.445 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 22 Apr 2019 21:12:00.446 * DB loaded from disk: 0.001 seconds
1:M 22 Apr 2019 21:12:00.446 * Ready to accept connections
$
原因
8999のポートを公開していたが、8999をセキュリティグループで穴あけしていないことが原因でした。
以下のように修正。
redis:
image: redis
ports:
- 6379:6379
mem_limit: 268435456
app:
image: pokotyan/redis-express:latest
mem_limit: 268435456
working_dir: '/var/work'
ports:
- 80:8999 # 8999:8999から変更
links:
- redis
environment:
- PORT=8999
command:
sh -c 'npm i && node index.js'
80のポートなら ecs-cli compose up
で作成されるセキュリティグループでデフォルトで穴あけされている。なので、フロント側では以下で接続できます。
import io from 'socket.io-client';
// const socket = io('http://54.178.145.131:8999'); // 修正前はこうなってた。
const socket = io('http://54.178.145.131'); // 修正後はポート番号80でアクセス
うまくいった作業ログ
$ ecs-cli up --keypair ecs1 --capability-iam --instance-type t2.micro
INFO[0000] Using recommended Amazon Linux 2 AMI with ECS Agent 1.28.0 and Docker version 18.06.1-ce
INFO[0001] Created cluster cluster=gomoku-narabe-cluster region=ap-northeast-1
INFO[0001] Waiting for your cluster resources to be created...
INFO[0002] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS
INFO[0062] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS
INFO[0123] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS
VPC created: vpc-0b43bf1e36fbc01b1
Security Group created: sg-080d991cce3df9220
Subnet created: subnet-04a85d1e7691360de
Subnet created: subnet-06fab8574826ab488
Cluster creation succeeded.
$
$ ecs-cli compose up
WARN[0000] Skipping unsupported YAML option for service... option name=networks service name=app
WARN[0000] Skipping unsupported YAML option for service... option name=networks service name=redis
INFO[0000] Using ECS task definition TaskDefinition="kurohige-server:7"
INFO[0001] Starting container... container=635174f8-b35c-453a-a157-ba0adb565209/app
INFO[0001] Starting container... container=635174f8-b35c-453a-a157-ba0adb565209/redis
INFO[0001] Describe ECS container status container=635174f8-b35c-453a-a157-ba0adb565209/app desiredStatus=RUNNING lastStatus=PENDING taskDefinition="kurohige-server:7"
INFO[0001] Describe ECS container status container=635174f8-b35c-453a-a157-ba0adb565209/redis desiredStatus=RUNNING lastStatus=PENDING taskDefinition="kurohige-server:7"
INFO[0013] Describe ECS container status container=635174f8-b35c-453a-a157-ba0adb565209/app desiredStatus=RUNNING lastStatus=PENDING taskDefinition="kurohige-server:7"
INFO[0013] Describe ECS container status container=635174f8-b35c-453a-a157-ba0adb565209/redis desiredStatus=RUNNING lastStatus=PENDING taskDefinition="kurohige-server:7"
INFO[0019] Started container... container=635174f8-b35c-453a-a157-ba0adb565209/app desiredStatus=RUNNING lastStatus=RUNNING taskDefinition="kurohige-server:7"
INFO[0019] Started container... container=635174f8-b35c-453a-a157-ba0adb565209/redis desiredStatus=RUNNING lastStatus=RUNNING taskDefinition="kurohige-server:7"
$ecs-cli ps
Name State Ports TaskDefinition Health
635174f8-b35c-453a-a157-ba0adb565209/app RUNNING 54.95.78.71:80->8999/tcp kurohige-server:7 UNKNOWN
635174f8-b35c-453a-a157-ba0adb565209/redis RUNNING 54.95.78.71:6379->6379/tcp kurohige-server:7 UNKNOWN
$
$
$
$ curl http://54.95.78.71:80/status
OK
$
フロントのデプロイ
ここまでの手順で、ローカルのSSRからECSで立てたEC2のIPに直接接続すれば五目並べが動く状態にはなります。
しかし、ローカルだけで動くアプリケーションではなんとも寂しいですよね。これをwebに公開するためには、フロントの環境のデプロイをしておく必要があります。
SSR環境のデプロイにはnowを使うことにしました。フロントはnextを使って構築しました。nextをnowを使ってデプロイする時にはまったところは別記事を書いています。
https://qiita.com/pokotyan/items/283a4ff1ba195585ba2a
これでフロント環境のデプロイ環境は整いました。
サーバーのhttps化
nowでデプロイしたSSRのサーバーは自動でhttps化がされています。なので、サーバー側もhttpsにしていないと Mixed Content
のエラーが出てフロントからサーバーに接続できません。
Mixed Content: The page at '<URL>' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint '<URL>'. This request has been blocked; the content must be served over HTTPS.
なので、サーバー側もhttpsにしていきます。
流れとしては
alb作成 => ドメイン購入、DNS設定 => 証明書の作成 => albに証明書を設定
みたいな感じです。
albのヘルスチェック用のapiを作る
albを作成する前にalbのヘルスチェックを通すための簡易的なapiを作っておきます。
/status
でアクセスしたら200 OKを返すようなAPIを用意しました。
albを作成する
リスナーは一旦、httpのみで作成します。(あとでhttpsのリスナーを追加する)
vpcを選択する際には、 ecs で作成したec2が存在するvpcを選びます。
※ecs-cli up
を実行した際のログをよく見ると以下のようにVPCを作成している
VPC created: vpc-0b43bf1e36fbc01b1
セキュリティグループはecsで作成したやつを選びます。
※ecs-cli up
を実行した際のログをよく見ると以下のようにセキュリティグループを作成している
Security Group created: sg-080d991cce3df9220
ターゲットグループを作成します。
ヘルスチェックのurlは事前に作っておいた /status
を指定します。
ターゲットグループにecsが自動で作成したec2を追加します。
ALBを作成し終えたら、ターゲットグループにec2がひもづいてstatusが healthy
になるのを待ちます。
ここまでの手順でフロント側からsocketサーバーにALBのurlで接続できるようになります。
import io from 'socket.io-client';
const socket = io(
'http://gomoku-narabe-lb-1337428841.ap-northeast-1.elb.amazonaws.com'
);
ドメインを取得、DNSの設定する。
お名前.comから適当なドメインを購入。(gomoku.xyzを購入しました)
また、本筋からそれますが、お名前.comでドメインを購入すると、自動更新設定がONになっているのでOFFにしておきましょう。
お名前.comのDNS設定のところで CNAMEレコード
を追加。 VALUE
にはALBのurlを入れます。
しばらく(30分以上くらい)待つと設定が反映されます。
$ dig CNAME www.gomoku.xyz
; <<>> DiG 9.10.6 <<>> CNAME www.gomoku.xyz
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57860
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.gomoku.xyz. IN CNAME
;; ANSWER SECTION:
www.gomoku.xyz. 3570 IN CNAME gomoku-narabe-lb-1337428841.ap-northeast-1.elb.amazonaws.com.
;; Query time: 36 msec
;; SERVER: 103.246.80.40#53(103.246.80.40)
;; WHEN: Sun May 19 01:40:36 JST 2019
;; MSG SIZE rcvd: 117
$
これで http://www.gomoku.xyz/status
にアクセスするとOKがちゃんと返ってきます。
$ curl http://www.gomoku.xyz/status
OK
証明書の発行
ドメインの準備はできたので、証明書の発行を行います。
手順は以下を参考にしました。
https://qiita.com/at-946/items/1e8acea19cc0b9f31b98#5-1-acm%E3%81%A7ssl%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%82%92%E7%99%BA%E8%A1%8C%E3%81%99%E3%82%8B
AWSの Certificate Manager
からパブリック証明書のリクエストを行います。
ドメイン名はお名前.comで設定した値を入れます。
AWSによるドメインの検証はDNSを使うようにします。すると この CNAMEレコード
をDNSサーバーに設定してくれ!という画面になるので、お名前.com上で設定します。
証明書をALBに設定する
前の手順で作成したALBにHTTPSのリスナーを追加します。
デフォルトアクションを追加し、転送先にALB作成時に作成したターゲットグループを指定します。
$ curl https://www.gomoku.xyz/status
OK
$
これでフロントからは以下のようにしてサーバーへ接続できます。
import io from 'socket.io-client';
const socket = io('https://www.gomoku.xyz');
これで、晴れてフロント、サーバー共にhttpsにできたので、web上にアプリケーションを公開できる状態になりました。
ecsのサービスを作成する
あとで書く
参考
https://dev.classmethod.jp/cloud/aws/using-ecs-cl/
https://anorlondo448.hatenablog.com/entry/2018/02/27/022103
https://hawksnowlog.blogspot.com/2017/01/startup-multiple-containers-with-ecs-cli.html
https://qiita.com/KoichiSugimoto/items/474580938548c4d18e4e
https://qiita.com/toshihirock/items/824a86da51015350a051
https://qiita.com/uchihara/items/8647c94f7af607a9a236
https://sil.hatenablog.com/entry/rails-ecs-cli-deploy