この記事について
コンテナを使ったアプリをデプロイしたい!となると、1つ選択肢に上がるのがAWSのECSではないでしょうか。
この記事では、ごくごく一般的な3層アーキテクチャ(Web+AP+DB)のアプリケーションの本番環境を、ECSとRDSを用いていい具合に構築する手順を示します。
使用する環境サービス
今回使用(関連)するAWSのサービスは以下の通りです。
- ECS (Amazon Elastic Container Service)
- ECR (Amazon Elastic Container Registry)
- RDS (Amazon Relational Database Service)
- ELB (Elastic Load Balancing)
- VPC (Amazon Virtual Private Cloud)
- AWS Route 53
- AWS Cloud Map
前提条件
- Dockerを使用したローカルアプリがすでに開発済みであるとします。
- AWSのアカウントを持っていて、VPCが作成済みであるとします。
- 複数AZにパブリック・プライベートサブネットが作成済みであるとします。
読者に要求する前提知識
- Dockerの基本的な知識
- AWSのセキュリティグループがなんなのか・設定の意味がわかること
- ECSで何ができるか(オーケストレーション)を知っていること
アプリの構造
今回デプロイするアプリは、ローカルでは以下の3つのサービス(コンテナ)で構成されています。
それぞれの概要は以下の通りです。
サービス名 | 内容 | 備考 |
---|---|---|
voting-app | ユーザーに対して投票を促すWebページを表示する。 | Golangで実装 |
vote-api | DBへの読み書きをRESTful APIで提供。 | Golangで実装 |
db | 投票結果を保存するデータベース。 | MySQL |
アプリコードでの準備
アプリというのは、「ローカルで動いているコードをそのまま本番環境に持って来れば動く」というものではないことがほとんどです。
今回の場合、voting-appがvote-apiにアクセスするときのURLと、vote-apiがdbに接続するときの設定が、ローカルと本番環境で違うことが想定されます。
(例)ローカル運用時のコード
例えば、ローカルで動かしているときは以下のようになっていました。
...(略)...
services:
voting-app:
...(略)...
vote-api:
...(略)...
db:
environment:
MYSQL_ROOT_USER: root
MYSQL_ROOT_PASSWORD: pass
MYSQL_DATABASE: sampledb
docker-compose.yml
でこのように設定すると、vote-apiのホストはvote-api
に、データベースのURLホストはdb
になります。
また、MySQLのデータベースでは、以下のように初期設定がなされている状態になります。
- ルートユーザー: root
- ルートユーザーパスワード: pass
- 初期データベース: sampledb
そのため、voting-appがapiに接続する部分と、vote-apiがDBに接続する部分のURLは以下のようになります。
そのため、コードは以下のようになっていました。
// vote-apiのURLを設定
apiURL := &url.URL{}
apiURL.Scheme = "http"
apiURL.Host = "vote-api:9090" // 本番だとHostがvote-apiではなくなる
apiURL.Path = "/"
// ConnectDB DBと接続してポインタを返す
func ConnectDB() (*sql.DB, error) {
dbDriver := "mysql"
// 以下のUser, Pass, Address, Nameが本番環境では変わる
dbUser := "root"
dbPass := "pass"
dbAddress := "db"
dbName := "sampledb"
//db, e := sql.Open("mysql", "root:pass@tcp(mysql:3306)/sampledb")
db, e := sql.Open(dbDriver, dbUser+":"+dbPass+"@tcp("+dbAddress+":3306)/"+dbName)
return db, e
}
しかし、コード内でもコメント注釈を入れた通り、APIのURLとDB設定が変わる以上、このコードが本番環境では動きません。
(例)本番でも動くコード
コンテナ化したアプリでは、設定を環境変数に格納するのはごく一般的な手法です。
今回、ローカルと本番で参照する設定を変えるためにもこの方法を使います。
例えば、本番環境のvoting-appコンテナには環境変数API_URL
を設定し、値に本番のAPIのURLを格納しておくとします。
すると、
- 環境変数
API_URL
が設定ずみ → 本番環境と判断。変数の値をHostとして使用する。 - 環境変数
API_URL
が未設定 → ローカル開発環境と判断。Hostの値をvote-apiとする。
というように、いちいちコードを書き換えなくても両方の環境で動くようにすることができます。
apiURL := &url.URL{}
apiURL.Scheme = "http"
apiURL.Host = "vote-api:9090"
// ここから追加
if urlENV := os.Getenv("API_URL"); urlENV != "" {
//環境変数API_URLが設定されているなら、Hostにその値を利用
apiURL.Host = urlENV + ":9090"
}
//ここまで追加
apiURL.Path = "/"
vote-apiの方でも同様に、環境変数DB_ENV
で環境を判断し、設定を切り替えるようにします。
// ConnectDB DBと接続してポインタを返す
func ConnectDB() (*sql.DB, error) {
dbDriver := "mysql"
dbUser := "root"
dbPass := "pass"
dbAddress := "mysql"
dbName := "sampledb"
//以下追加
if os.Getenv("DB_ENV") == "production" {
dbUser = os.Getenv("DB_USER")
dbPass = os.Getenv("DB_PASS")
dbAddress = os.Getenv("DB_ADDRESS")
}
//ここまで追加
//db, e := sql.Open("mysql", "root:pass@tcp(mysql:3306)/sampledb")
db, e := sql.Open(dbDriver, dbUser+":"+dbPass+"@tcp("+dbAddress+":3306)/"+dbName)
return db, e
}
デプロイ手順
本番環境用のコードに書き換えたところで、ここからは実際にAWSを使ってデプロイを進めていきます。
0. 目指す構成
ECSで作成するサービス
ECSのサービスは、voting-appコンテナをサービス化したweb-service、vote-apiコンテナをサービス化したapi-serviceの2つを用意します。
この2つのサービスを、同一クラスター内に展開します。
サービスごとにクラスターを分けるか、同一クラスタにするか
今回、クラスターへのサービス展開方法は以下の2通りが考えられます。
- web-serviceをのせるクラスターと、api-serviceをのせるクラスターに分ける
- 2つのサービスを同一クラスター内にのせる
クラスターをサービスごとに分けると、それぞれのサービスごとにオーケストレーションをすればいいので運用しやすくなります。ただその分多くインスタンスを立てることになります。
逆にお財布に負担をかけたくない・安上がりに済ませたいという場合は、クラスターを分けないことでインスタンスの節約ができます。
参考:Amazon EC2 Container Service(ECS)の概念整理
1. ECSのクラスター作成
クラスターの実態はEC2インスタンスです。アプリコンテナを起動させ動かすインスタンス群を、ECSではクラスターと呼びます。
ECSで利用するインスタンスには、以下のような環境が必要です。
- Docker
- ECS Container Agent
Dockerアプリを動かすためのインスタンスなので、当然のことながらDockerがインストールされている必要があります。EC2で自分でインスタンスを立てる場合とは違い、ECSクラスターとして立てられたインスタンスにはそのような環境が最初から備わっています。
また、クラスター内でリソース・スケジュール管理を行うために、ECSクラスターに所属するインスタンスの中には皆ECS Container Agentが稼働しています。
ECS Container Agentの実態はDockerコンテナで、実際にECSインスタンスにログインしてdocker ps
を実行することで、その存在を確認することができます。
__| __| __|
_| ( \__ \ Amazon Linux 2 (ECS Optimized)
____|\___|____/
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e2f63b1e13ed amazon/amazon-ecs-agent:latest "/agent" 41 seconds ago Up 40 seconds (healthy) ecs-agent
参考:Amazon EC2 Container Service(ECS)の概念整理
参考:EC2 Container Serviceを使ってみる
以下、ECSのコンソールからクラスターを作成します。
クラスターテンプレを選択
![クラスターテンプレ.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F997f26ff-8074-98d5-1b5f-619ec5e45cbd.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=1a6b30e2d43d6c8af5e0f607c97d10dd)
AWS FargateとEC2はどっちがいいの?
AWS Fargateは、コンテナを起動するインスタンス群の管理をユーザー側が気にすることなく、コンテナイメージ構築することができるサービスです。そのため、Fargateを利用する場合は、利用者はコンテナイメージの開発のみに注力することができます。
その性質上、Fargateはコンテナが起動しているクラスターにssh接続することができません。
初めてECSに触れる方で、何かあったときにインスタンスの中に入ってエラーを調べたい!という方はEC2をオススメします。
参考:AWS Fargateとは?
クラスター名の設定
![クラスタ名.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F277ebfc2-744b-3ad9-4820-4e7ac74e474d.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=9d21915610320f0c1096a4943dbd0524)
インスタンスの設定
![instance setting.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2Ff97d8976-1e00-8b8e-ebcd-ad1a63321cd9.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=ebbc4ad0a5ab08dafcdf6ee77dbb2974)
- プロビジョニングモデル:オンデマンドインスタンス(よくある従量課金)
- EC2インスタンスタイプ:t2.micro(お金をかけたくなかったため)
- インスタンス数:クラスターの中にいくつインスタンスを起動するか。(とりあえず1を選択)
- EC2 Ami Id:インスタンスにのせるOS。Amazon Linux2を選択。
- EBSストレージ:デフォルトは22。とりまこのままで。
- キーペア:インスタンスに接続するためのSSHキー。
AWSに登録ずみのキーからご自分で勝手のいいものを選んでください。
ネットワーク設定
![network.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F956d61af-dc47-619a-884f-19d71c08ba8f.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=6c2ee62bc4b618e9f3cb6695025c9a7c)
- VPC:デフォルトVPCを選択
- サブネット:VPCの中にあったパブリックサブネット3つを指定
- セキュリティグループ:新規作成します。インバウンドルールは後からいじれます。とりあえず今はssh用に22番ポートを開けておきましょう。
その他詳細設定
![role.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F32dcdcb0-7db1-2780-ab90-3a0b8f460a67.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=0c97aa5f06ca6ff661863d378d3e47ac)
IAMロールの設定も何もしなくてOKです。ecsInstanceRole(ECSクラスターへ参加を行う際に必要となるIAMロール)がない場合は自動で作成されます。
参考:Amazon ECS 細かい箇所を整理してみた
青い「作成」ボタンを押すと、クラスターと、クラスターインスタンスが属するセキュリティグループが新規作成されます。
2. RDS用のサブネットグループ作成
AWSでは、DBを複数AZにまたがって配置するようにしないといけません。
そのため、「このサブネットの中のどこかにDBインスタンスを作る」という複数AZサブネットのグループを作る必要があります。
グループの設定
![スクリーンショット 2020-05-31 15.26.28.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2Fed3f798f-5ac8-aee4-72bf-bde62de93b76.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=5a45164adca8bdc9411d27d061323253)
グループにサブネットを追加
![スクリーンショット 2020-05-31 15.26.35.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F667d9548-5b2e-0662-c06e-f1e393d9c0ee.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=d9ce1ddaaceca05aadc8ef95097d8974)
3. RDSインスタンス作成
RDSのコンソールから、DBインスタンスを作成します。
DBの選択
![スクリーンショット 2020-05-31 15.18.01.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F1d54b318-ef46-8084-9e87-ac62a3d032bc.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=ae2edf9e0f1e196791f6250b024fa36f)
認証情報の設定
![スクリーンショット 2020-05-31 15.18.48.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F0522b06d-12ae-ea6a-9f9d-bcef7fc864b9.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=7815bcd8a216af966996ce161cf68aad)
認証情報は、MySQLに最初から設定されているルートユーザーとそのパスワードの設定です。
今回はパスワードを自分で設定したいので、自動生成にはチェックをつけないでおきます。
ここで設定した認証情報は本来自分にしかわからないようにしておくべきです。本記事では、今後の説明をわかりやすくするために以下のようにおいたと仮定して進めます。
- マスターユーザー名: admin
- マスターパスワード: password
VPCの選択
![スクリーンショット 2020-05-31 15.21.39.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F60501f07-d3f4-148c-a115-4cfcbe7a5398.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=21749bd2db49b4573561e86c4fceff68)
追加のネットワーク設定
![スクリーンショット 2020-05-31 15.32.51.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F921e7f17-7a75-2bfc-f4d4-c7f4c36efd5a.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=aa2945a74618d0a06246adf681d6d15d)
- サブネットグループ: さっき作ったもの
- パブリックアクセス可能: なし
- VPCセキュリティグループ: 新規作成
- アベイラビリティーゾーン: 特定のAZを指定して動かしたいわけではないので、指定なし
- データベースポート: MySQLのデフォルト3306番を使用
詳細設定
![スクリーンショット 2020-05-31 19.02.43.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F4ec75f64-e1d6-f035-b5e9-0ef8996fe6db.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=c216bb83eeb9eafe57f5452c37047f4b)
パラメータグループとオプショングループはデフォルトのままにします。
以上の設定を行った上で、DBを作成します。
参考:Amazon RDS for MySQLインスタンス作成手順
エンドポイントの確認
作成したDBインスタンスのエンドポイントを確認しておきます(後で使うので)。
コンソールの以下の箇所で確認できます。
4. セキュリティグループのインバウンド設定
ここまでで、2つのセキュリティグループを新規作成しました。1つはECSクラスター用、1つはRDSインスタンス用です。
今回はECSクラスター内のサービス(=vote_api)から、RDSに接続する必要があります。
また、ECSクラスター内のインスタンスに、各種サービスにアクセスするためのポートも開けなくてはいけません(ここではvoting-appが8080番、vote_apiが9090番とします)。
そのため、セキュリティグループのインバウンド設定を以下のようにします。
- ECSクラスターのセキュリティグループ
- in22 from all(ssh接続用)
- in8080 from all(voting-app用)
- in9090 from all(vote_api用)
- RDSのセキュリティグループ
- in3306 from ECSクラスターグループを設定
(おまけ)クラスタのEC2インスタンスから接続してみる
きちんとECSクラスター内からRDSインスタンスに接続できるかどうか、実際にインスタンスの中に入って確認してみます。
ローカルマシンでターミナルを開いて、以下のコマンドを打つことでECSインスタンスに接続できます。
$ ssh -i "your-key-name.pem" ec2-user@your-instance-public-dns
__| __| __|
_| ( \__ \ Amazon Linux 2 (ECS Optimized)
____|\___|____/
ログインしたサーバー内で以下を実行します。
$ sudo yum update
$ sudo yum install mysql
$ mysql -h your-db-endpoint -u your-root-username -p
Enter password:
MySQL [(none)]>
きちんと接続できました。
試しに、先ほど設定した初期データベースがあるかどうかも確認してみましょう。
MySQL [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| innodb |
| mysql |
| performance_schema |
| first_db |
| sys |
+--------------------+
6 rows in set (0.00 sec)
先ほど設定した通りの初期データベースが作成されていました。
MySQL, ECSサーバーからログアウトするためには、以下のコマンドを打ちます。
MySQL [(none)]> exit
Bye
$ exit
ログアウト
参考:AWSのEC2からRDSに接続してmySQLを操作。EC2踏み台サーバー構築メモ
5. ECRに登録
アプリのコンテナイメージをECSで使えるようにするために、レジストリにイメージをpushしなくてはいけません。
ECRはAWSで提供されているコンテナイメージのレジストリです。
リポジトリの作成
ECRのコンソールを開くと、以下のような画面になります。
リポジトリの作成をクリックします。
![スクリーンショット 2020-05-09 19.14.55.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F8c1a37ab-9389-0f5f-9d01-3596c391df03.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=f2e6586336eee4060530ac5b021542c9)
![スクリーンショット 2020-05-30 19.19.39.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2Fc32a6990-517d-a0fd-5abe-cbc64bf7b319.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=62838982d5ea45cc28bad8d57c52c8ac)
コンテナイメージをpush
リポジトリ名をクリックすると以下のような詳細画面が表示されます。
画面右上にある「プッシュコマンドを表示」ボタンで、実行するコマンドが表示されます。
以下、そのコマンドを1つ1つ追っていきます。
ECRのdockerレジストリにログインする。
以下のコマンドで、ECRレジストリにログインするためのパスワードが取得できます。
$ aws ecr get-login-password --region ap-northeast-1
なので、このパスワードを使って、ログインします。(このコマンドはプッシュコマンドのところに書いてあるものです)
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [your-aws-accountid].dkr.ecr.ap-northeast-1.amazonaws.com
イメージにタグを追加
ECRにpushするためには、pushするdockerイメージに指定形式の名前空間・タグをつける必要があります。
dockerイメージの名前・タグ付けを変更するためのコマンドは以下の通りです。
$ docker tag [旧repository名]:[旧tag名] [新repository名]:[新tag名]
注:これを実行した後も、[旧repository名]:[旧tag名] は残っています。
なので、ローカルマシン上で、pushしたいイメージに以下のようにtag付けします。
(your-ecr-repository-uriはプッシュコマンド&リポジトリの詳細情報ページにも記載してあります)
ローカルマシンで名前・タグ付けを変更するためのコマンドを実行します。
$ docker tag 【現イメージ名】:latest [your-ecr-repository-uri]:latest
実際にpush
一般的に、レジストリにイメージをpushするコマンドはdocker push [image名]:tag
です。
今回の場合は以下のように書きます。
$ docker push [your-ecr-repository-uri]:latest
ECRのdockerレジストリからログアウト
一連の操作が終わったら、ECRのアカウントからログアウトしておきましょう。
$ docker logout [your-aws-account-id].dkr.ecr.ap-northeast-1.amazonaws.com
参考:【AWS】初めてのECR
6. vote-apiのタスク定義
タスクとは、ビジネスロジックを実行するためのコンテナの組(複数個もあり)のことです。
例えば、今回の場合は以下のように当てはめることができます。
- voting-app → Webサーバーとしての役割を果たすタスク
- vote-api → APIのタスク
- (db → RDSのインスタンス)
タスク定義とは、そのタスクを実行するためには、どのコンテナイメージをどんな設定で動かすかを設定することです。
設定できる内容は多岐に渡ります。全容を知りたい方は以下の公式ドキュメントや次の記事を参考にしてください。
参考:公式ドキュメント タスク定義パラメータ
参考:Amazon ECS 細かい箇所を整理してみた
今回はまず、vote-apiのタスクを作成していきます。
起動タイプの互換性の選択
![スクリーンショット 2020-05-09 19.00.45.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2Ff99ff1cb-7628-10c6-ac31-0f53d9fd7c0d.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=abb0c47f1daaedb743038c5aba4b2afd)
タスクとコンテナの定義の設定
![スクリーンショット 2020-06-01 15.43.36.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F56845f1a-19c6-4744-e103-302fcae665c0.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=9aa2cd1a9bab26f9598ec6ac39fa2233)
- タスク定義名:わかりやすいように好きに設定してOKです。
- タスクロール:タスク内でIAMロールが必要ならばそれを指定(今回はなし)
- ネットワークモード:awsvpc
ネットワークモードについては、dockerコンテナ(=タスク)をつなぐネットワークの定義です。
通常のdockerネットワークにもあるnone, host, bridgeの他に、ECSではawsvpcという特別なモードがあります。
これは、1タスクごとに1ENIを取り付けるモードです。今回はこれを選択しました。
参考:ECSでEC2インスタンスを利用する際のネットワークモードについて調べてみた
タスクのコンテナ定義
他の設定は飛ばして、タスクで使用するコンテナを指定するところまできました。
青い「コンテナを追加」ボタンををクリックする。するとタスクの元となるコンテナの詳細設定を行うタブが開きます。
![スクリーンショット 2020-06-01 15.45.20.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2Fdce6cc84-a4c2-7b66-e104-1f78fcf2073b.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=81b595d932f2826236eef44dabf59fbc)
- コンテナ名: 好きなようにしてOKです。
- イメージ: ECRのリポジトリURIを指定。このリポジトリのlatestタグのものが使われます。
- メモリ制限: ハード128を指定
- ポートマッピング: 今回は9090(vote-apiがlistenしているポート)
![api環境変数設定.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F2127f482-4720-2402-e0b6-846b73982fa1.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=2c52020946b210c10beeccae66a0e858)
- DB_ENV: production(本番環境か、開発ローカル環境かの判断に使用)
- DB_USER: さっき設定したRDSのルートユーザー名
- DB_PASSWORD: さっき設定したRDSのルートユーザーパスワード
- DB_ADDRESS: さっき作ったRDSのエンドポイント
ここまで設定ができたら、タスクを作成します。
7. vote-apiのサービス作成
ECSでのサービスとは、どのタスクをどのクラスタで何個起動させるかを決めたまとまりのことです。1サービスに対して1タスクが紐ずけられます。
参考:Amazon EC2 Container Service(ECS)の概念整理
これから作るサービスを起動させたいクラスターの画面から作成することができます。
サービスの設定
![6877.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2Fee92ca59-de43-219a-9664-9b70c7b490af.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=bebfce2c9357f7abd2c980a281e0e447)
- 起動タイプ: EC2互換のタスクを使うのでEC2を選択
- タスク定義: 使用するタスク(今回は前章で作ったタスク名)と、そのバージョンを指定
- クラスター: サービス作成を実行したクラスター名がデフォルトで入力済み
- サービス名: 自分で好きにつけてOK
- サービスタイプ: クラスター内でのタスク配置の種類。今回はREPLICAを選択
- タスクの数: サービスで起動させるタスクの数。今回はとりあえず1にしておく。
- 最小ヘルス数・最大数: デフォルトのまま。
ネットワーク設定
![スクリーンショット 2020-06-01 16.24.25.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F292cff5c-b177-2cb7-0ac7-db89549a4760.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=13162ef8445b063f7db8516035413558)
- クラスターVPC: デフォルトで入力済み
- サブネット: 今回はVPC内にあるパブリックサブネット3つを選択
- セキュリティグループ: ECSクラスターインスタンスに与えられたグループを指定
- パブリックIPの自動割り当て: DISABLEDを選択
サービスディスカバリー
今後、voting-appの方でのサービスから、今作っているvote-apiサービスへと接続する必要があります。
ローカル開発時はdocker-compose.yml
の内容で、他コンテナに接続するときの設定を簡単に指定することができました。
ECSの場合は「現在作成しているサービスに接続するための名前を設定、そのAレコードをRoute53に自動登録されるようにする」という設定を行います。この機能がECSサービスディスカバリーです。
サービスディスカバリーを利用することによって、コンテナ間・サービス間通信を楽に行うことができるようになります。
注:サービスが動いているコンテナのIPアドレスを直で指定して接続すればいいじゃん!という力技は、オーケストレーションが行われたときなどでIPアドレスが変わった場合、動かなくなるのでおすすめできません。あくまで名前解決でサービス接続するのが望ましいです。
参考:ECSのサービスディスカバリーが東京にやってきて、コンテナ間通信の実装が簡単になりました!
参考:ECS サービスディスカバリでFargateのIPを自動的にDNSレコードへ紐づける
これから、vote-apiのサービスに、自分が決めた名前でアクセスできるように、サービスディスカバリーの設定をしていきます。
サービス作成画面の中に、以下のような「サービスの検出(オプション)」という設定箇所があるかと思います。
![スクリーンショット 2020-05-31 19.53.32.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2Feb9bc2cd-3fce-612b-10bc-3e18d6bb15eb.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=68669e06d0ba76bbb04c5cea77620914)
- サービス検出の統合の有効化: onにすることでサービスディスカバリーを利用できる
- 名前空間: 新規作成を選択して、好きな名前をつけます。
- クラスターVPC: 名前空間を展開するVPCネットワーク。サービスを展開するVPCが自動入力済み
- サービスの検出サービスの設定: 新規作成を選択
- サービスの検出名: サービス名が自動入力済み
以上の設定で、api-service.local
という名前でこのサービスにアクセスできるようになります。
ここまでの設定が済んだら、サービス作成をしましょう。
実際にapi-service.local
でサービスにアクセスできるか確認してみる
今作ったvote-apiサービスに、サービスディスカバリーで設定した名前api-service.local
で接続できるか確かめてみましょう。
ECSクラスターが展開されているVPC内のインスタンスの1つにssh接続して、以下のコマンドを叩いてみましょう。
$ dig api-service.local
; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.60.amzn1 <<>> api-service.local
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16007
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;api-service.local. IN A
;; ANSWER SECTION:
api-service.local. 60 IN A 172.31.78.255
;; Query time: 4 msec
;; SERVER: 172.31.0.2#53(172.31.0.2)
;; WHEN: Mon Jun 1 16:26:47 2020
;; MSG SIZE rcvd: 51
$ nslookup api-service.local
Server: 172.31.0.2
Address: 172.31.0.2#53
Non-authoritative answer:
Name: api-service.local
Address: 172.31.78.255
dig
コマンドでサービスコンテナへのAレコードが、nslookup
コマンドでもコンテナへのIPアドレスが確認できました。きちんとRoute53の方で名前解決できるように設定されているようです。
参考:ECS Service Discoveryを試してみる
実際に、api-service.local
という名前でAPIを叩いてみました。
$ curl api-service.local:9090
Hello World
$ curl api-service.local:9090/vote/
[]
期待通りの動作が行われました。確かに、vote-apiのサービスには、api-service.local
というホストが設定されているようです。
Cloud Map上でも確認
サービスディスカバリーの実態は、Cloud Mapの機能をECSの画面で利用している状態です。
なので、AWS Cloud Mapのコンソールからでも、名前空間local
とサービスapi-service
が確認できます。
参考:AWS Cloud Mapを試してみる
8. voting-appのタスク定義
vote-apiのタスク定義と手順は同じです。設定内容だけvoting-appに合わせて変更します。(変更部分を太文字にしました)
- 起動タイプの互換性: EC2
- タスク定義名: 自分の好きな名前
- タスクロール: なし
- ネットワークモード: default
ネットワークモードをdefaultに変更したことによって、コンテナ定義のポートマッピング部分が「ホストポートとコンテナポートの2つを決める」というように変わっています。
- コンテナ名: 好きな名前
- イメージ: ECRにpushしたvoting-appのリポジトリ
- メモリ制限: 基本的にハード128
- ポートマッピング: 8080:8080(voting-appがlistenしているポート)
また、vote-apiのURLを環境変数で取得するようにしているので、以下の設定も行います。
- API_URL: api-service.local
9. ロードバランサの作成
voting-appが起動されているサービスに接続するためのロードバランサを作成します。
ロードバランサの種類の選択
![スクリーンショット 2020-06-03 16.29.02.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2Fb3f3be9a-e935-22a8-ab74-a6630d4f3bb2.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=66685d0e74da237903f17b38a526e499)
基本設定
まずは、ロードバランサ自体の設定を行います。
![スクリーンショット 2020-06-03 16.29.44.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2Faf12ae06-a2a3-f02d-8dfe-c798fbb79082.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=7d8fffa42a5bee1edab0bb4525e8aca5)
- 名前: 自分の好きな名前をつけます
- スキーム: パブリックにアクセスしたいので、インターネット向けを選択
- IPアドレスタイプ: IPv4を選択
![スクリーンショット 2020-06-03 16.29.52.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F6e20a2a1-552b-dabb-1238-67504e4db855.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=2695668f7bbb6e1a496502d6eb571d78)
参考:Application Load Balancerで設定する4種類のポート番号の意味を理解しよう
アベイラベリティーゾーンでは、ロードバランサを設置するネットワークを指定します。
VPCは、ECSクラスターを展開しているVPCを選択し、各AZごとにパブリックなサブネットを指定します。
セキュリティグループの設定
次に、ロードバランサに付与するセキュリティグループを指定します。
![スクリーンショット 2020-06-03 16.30.19.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F639acb03-3d4d-1d9b-0378-0afece106aa5.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=6cc7451a3a2e8a7ed996d6d1fcd99ae7)
ルーティングの設定
ルーティング設定では、ロードバランサが受け取ったトラフィックを、どこの何番ポートに転送するかを指定します。
参考:Application Load Balancerで設定する4種類のポート番号の意味を理解しよう
![スクリーンショット 2020-06-03 16.32.00.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2Fbdd76bec-299b-0ecb-412d-4fefb461a471.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=3358f2b810cb07a02b30c27de2263a7c)
ヘルスチェックには手をつけずに、ターゲットグループを以下のように設定します。
- ターゲットグループ: 新規作成
- 名前: わかりやすい名前をつけます
- ターゲットの種類: 今回は、voting-appがあるECSクラスターインスタンスに転送したいので、インスタンスを選択
- プロトコル: voting-appにはhttpトラフィックを流す
- ポート: voting-appがlistenしている8080番ポートを指定
ターゲットの登録
新しく作成したターゲットグループに、どのインスタンスを属させるかの設定をここで行うことができます。
![スクリーンショット 2020-06-03 16.32.37.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F520877%2F866a372d-321c-33a8-860e-8d965a39129b.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=bfcf48dac40da11a64bee3a2fde79903)
インスタンスの欄に、現在稼働中のEC2インスタンス一覧が表示されています。追加したいインスタンスにチェックを入れて、「登録済みに追加」ボタンを押すことで追加ができます。
今回は、この後voting-appのECSサービスを作るところでこの設定を行いたいので、何もせずに飛ばします。
参考:Dockerコンテナの作成からECSの動的ポート+ALBでロードバランスするまで【cloudpack大阪ブログ】
ここまでの設定が行えたら、ロードバランサを作成します。
10. voting-appのサービス作成
基本的には、vote-apiのときと手順は同様です。以下に設定を示します。
- サービスの設定: vote-apiと同様に設定
- ネットワーク設定: タスクのネットワーク設定がawsvpcではないので、なし
- サービスディスカバリー: voting-appにアクセスするようなサービスがないので、不要
vote-apiとは違うところが、voting-appサービスとロードバランサを連携させる設定を行うところです。
- ロードバランサの種類: 先ほど作ったのがApplication Load Balancerなのでそれにチェック
- サービス用のIAMロールの選択: 自動でecsServiceRoleが選ばれている
- ロードバランサ名: 先ほど作ったものを選択
- コンテナの選択: voting-appのタスク定義時に指定したコンテナを選択
ここまで設定できている状態で、コンテナの選択の部分の「ロードバランサに追加」ボタンをクリックすると、以下のような項目が追加で表示されます。
ターゲットグループ名のところに、先ほど新規作成したグループを指定します。
すると、他の項目が自動で入力されます。
以上の設定を行った上で、サービスを作成します。
実際に本番環境のアプリにアクセスしてみる
ブラウザにロードバランサのパブリックDNS名を入力・アクセスすることで、本番環境のアプリを見ることができます。お疲れ様でした。
参考文献
ECSの公式解説スライド
参考:AWS Black Belt Online Seminar 2016 Amazon EC2 Container Service
参考:[AWS Black Belt Online Seminar] Amazon Elastic Container Service (Amazon ECS) 資料及び QA 公開