LoginSignup
155
143

More than 3 years have passed since last update.

Dockerコンテナで作ったアプリをECS+RDSでデプロイする

Posted at

この記事について

コンテナを使ったアプリをデプロイしたい!となると、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つのサービス(コンテナ)で構成されています。
スクリーンショット 2020-06-02 14.14.17.png
それぞれの概要は以下の通りです。

サービス名 内容 備考
voting-app ユーザーに対して投票を促すWebページを表示する。 Golangで実装
vote-api DBへの読み書きをRESTful APIで提供。 Golangで実装
db 投票結果を保存するデータベース。 MySQL

アプリコードでの準備

アプリというのは、「ローカルで動いているコードをそのまま本番環境に持って来れば動く」というものではないことがほとんどです。
今回の場合、voting-appがvote-apiにアクセスするときのURLと、vote-apiがdbに接続するときの設定が、ローカルと本番環境で違うことが想定されます。

(例)ローカル運用時のコード

例えば、ローカルで動かしているときは以下のようになっていました。

docker-compose.yml
...(略)...
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は以下のようになります。
スクリーンショット 2020-06-05 21.10.26.png

そのため、コードは以下のようになっていました。

voting-app.go
// vote-apiのURLを設定
apiURL := &url.URL{}
apiURL.Scheme = "http"
apiURL.Host = "vote-api:9090"  // 本番だとHostがvote-apiではなくなる
apiURL.Path = "/"
vote-api.go
// 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とする。

というように、いちいちコードを書き換えなくても両方の環境で動くようにすることができます。

voting-app.go
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で環境を判断し、設定を切り替えるようにします。

vote-api.go
// 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. 目指す構成

最終的には、以下のような構成になります。
スクリーンショット 2020-06-04 20.53.12.png

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
クラスターとして起動するインスタンスの種類を選びます。
今回はLinuxをのせたEC2がいいので、「EC2 Linux+ネットワーキング」を選びます。

AWS FargateとEC2はどっちがいいの?

AWS Fargateは、コンテナを起動するインスタンス群の管理をユーザー側が気にすることなく、コンテナイメージ構築することができるサービスです。そのため、Fargateを利用する場合は、利用者はコンテナイメージの開発のみに注力することができます。
その性質上、Fargateはコンテナが起動しているクラスターにssh接続することができません。

初めてECSに触れる方で、何かあったときにインスタンスの中に入ってエラーを調べたい!という方はEC2をオススメします。
参考:AWS Fargateとは?

クラスター名の設定

クラスタ名.png
これから作るクラスターの名前です。わかりやすい名前を好きにつけてください。

インスタンスの設定

instance setting.png
クラスターに属するEC2インスタンスの設定です。項目の設定は以下の通り。

  • プロビジョニングモデル:オンデマンドインスタンス(よくある従量課金)
  • EC2インスタンスタイプ:t2.micro(お金をかけたくなかったため)
  • インスタンス数:クラスターの中にいくつインスタンスを起動するか。(とりあえず1を選択)
  • EC2 Ami Id:インスタンスにのせるOS。Amazon Linux2を選択。
  • EBSストレージ:デフォルトは22。とりまこのままで。
  • キーペア:インスタンスに接続するためのSSHキー。
    AWSに登録ずみのキーからご自分で勝手のいいものを選んでください。

ネットワーク設定

network.png
クラスターをどのVPC・サブネットで動かすかの設定です。

  • VPC:デフォルトVPCを選択
  • サブネット:VPCの中にあったパブリックサブネット3つを指定
  • セキュリティグループ:新規作成します。インバウンドルールは後からいじれます。とりあえず今はssh用に22番ポートを開けておきましょう。

その他詳細設定

role.png
この部分は手をつけずにおいておきます。

IAMロールの設定も何もしなくてOKです。ecsInstanceRole(ECSクラスターへ参加を行う際に必要となるIAMロール)がない場合は自動で作成されます。
参考:Amazon ECS 細かい箇所を整理してみた

青い「作成」ボタンを押すと、クラスターと、クラスターインスタンスが属するセキュリティグループが新規作成されます。

2. RDS用のサブネットグループ作成

AWSでは、DBを複数AZにまたがって配置するようにしないといけません。
そのため、「このサブネットの中のどこかにDBインスタンスを作る」という複数AZサブネットのグループを作る必要があります。

グループの設定

スクリーンショット 2020-05-31 15.26.28.png
名前と説明は、わかりやすいようにつけておけばOKです。
どのVPC上のサブネットに対するグループなのかもここで設定します。今回は、実際にサービスをのせるVPCを選択します。

グループにサブネットを追加

スクリーンショット 2020-05-31 15.26.35.png
選んだVPC上のサブネットから、グループに追加するものを選びます。
今回はDBのパブリックアクセスを許可しないようにしたいので、プライベートサブネットを複数AZになるように追加します。

3. RDSインスタンス作成

RDSのコンソールから、DBインスタンスを作成します。

DBの選択

スクリーンショット 2020-05-31 15.18.01.png
MySQL5.7のDBを選択します。

認証情報の設定

スクリーンショット 2020-05-31 15.18.48.png
DBインスタンス識別子は、インスタンスにつく名前だと思ってください。これも好きな名前をつけてOKです。

認証情報は、MySQLに最初から設定されているルートユーザーとそのパスワードの設定です。
今回はパスワードを自分で設定したいので、自動生成にはチェックをつけないでおきます。
ここで設定した認証情報は本来自分にしかわからないようにしておくべきです。本記事では、今後の説明をわかりやすくするために以下のようにおいたと仮定して進めます。

  • マスターユーザー名: admin
  • マスターパスワード: password

VPCの選択

スクリーンショット 2020-05-31 15.21.39.png
DBインスタンスをおくVPCを指定します。今回サービスを展開するVPCを指定すればいいでしょう。

追加のネットワーク設定

スクリーンショット 2020-05-31 15.32.51.png
VPCよりも詳細なネットワークの設定です。ここでは以下のようにしました。

  • サブネットグループ: さっき作ったもの
  • パブリックアクセス可能: なし
  • VPCセキュリティグループ: 新規作成
  • アベイラビリティーゾーン: 特定のAZを指定して動かしたいわけではないので、指定なし
  • データベースポート: MySQLのデフォルト3306番を使用

詳細設定

スクリーンショット 2020-05-31 19.02.43.png
最初のデータベース名のところに、作りたいDBの名前を指定してください。この設定がないと、起動時に初期データベースが作られません。

パラメータグループとオプショングループはデフォルトのままにします。

以上の設定を行った上で、DBを作成します。
参考:Amazon RDS for MySQLインスタンス作成手順

エンドポイントの確認

作成したDBインスタンスのエンドポイントを確認しておきます(後で使うので)。
コンソールの以下の箇所で確認できます。
スクリーンショット 2020-06-01 17.38.31.png

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.13.32.png
リポジトリの作成をクリックします。

スクリーンショット 2020-05-09 19.14.55.png
リポジトリの名前をつけます。これもわかりやすいように好きにつけて大丈夫です。
イメージスキャン設定はいじらなくてもOKです。

スクリーンショット 2020-05-30 19.19.39.png
無事に作成成功。ここにローカルのvoteコンテナイメージを入れる。後1個作っておく。

コンテナイメージをpush

リポジトリ名をクリックすると以下のような詳細画面が表示されます。
68747470733png
画面右上にある「プッシュコマンドを表示」ボタンで、実行するコマンドが表示されます。
以下、そのコマンドを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
FargateとEC2、どちらのタイプのクラスターで動かすタスクなのかを設定します。
今回はEC2タイプのクラスターを作成したので、タスクもEC2互換性のあるものを選択します。

タスクとコンテナの定義の設定

スクリーンショット 2020-06-01 15.43.36.png

  • タスク定義名:わかりやすいように好きに設定してOKです。
  • タスクロール:タスク内でIAMロールが必要ならばそれを指定(今回はなし)
  • ネットワークモード:awsvpc

ネットワークモードについては、dockerコンテナ(=タスク)をつなぐネットワークの定義です。
通常のdockerネットワークにもあるnone, host, bridgeの他に、ECSではawsvpcという特別なモードがあります。
これは、1タスクごとに1ENIを取り付けるモードです。今回はこれを選択しました。

参考:ECSでEC2インスタンスを利用する際のネットワークモードについて調べてみた

タスクのコンテナ定義

他の設定は飛ばして、タスクで使用するコンテナを指定するところまできました。
スクリーンショット 2020-05-09 20.23.46.png
青い「コンテナを追加」ボタンををクリックする。するとタスクの元となるコンテナの詳細設定を行うタブが開きます。

スクリーンショット 2020-06-01 15.45.20.png

  • コンテナ名: 好きなようにしてOKです。
  • イメージ: ECRのリポジトリURIを指定。このリポジトリのlatestタグのものが使われます。
  • メモリ制限: ハード128を指定
  • ポートマッピング: 今回は9090(vote-apiがlistenしているポート)

api環境変数設定.png
「環境変数」と書かれているところまで飛ばしました。
ここで、コンテナに渡す環境変数を設定できます。上記で示したコードを使用している今回は、以下のように設定しました。

  • DB_ENV: production(本番環境か、開発ローカル環境かの判断に使用)
  • DB_USER: さっき設定したRDSのルートユーザー名
  • DB_PASSWORD: さっき設定したRDSのルートユーザーパスワード
  • DB_ADDRESS: さっき作ったRDSのエンドポイント

ここまで設定ができたら、タスクを作成します。

7. vote-apiのサービス作成

ECSでのサービスとは、どのタスクをどのクラスタで何個起動させるかを決めたまとまりのことです。1サービスに対して1タスクが紐ずけられます。
参考:Amazon EC2 Container Service(ECS)の概念整理

これから作るサービスを起動させたいクラスターの画面から作成することができます。
service start.png

サービスの設定

6877.png
サービス名、コンテナの種類や数などを指定します。

  • 起動タイプ: EC2互換のタスクを使うのでEC2を選択
  • タスク定義: 使用するタスク(今回は前章で作ったタスク名)と、そのバージョンを指定
  • クラスター: サービス作成を実行したクラスター名がデフォルトで入力済み
  • サービス名: 自分で好きにつけてOK
  • サービスタイプ: クラスター内でのタスク配置の種類。今回はREPLICAを選択
  • タスクの数: サービスで起動させるタスクの数。今回はとりあえず1にしておく。
  • 最小ヘルス数・最大数: デフォルトのまま。

参考:Amazon ECS 細かい箇所を整理してみた

ネットワーク設定

スクリーンショット 2020-06-01 16.24.25.png
タスクのネットワーク設定にawsvpcを選択していた場合、サービス作成時にこの設定をする必要があります。タスクごとに与えられたENIをどのネットワーク・セキュリティグループに属させるかの定義をここですることができます。

  • クラスター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
ここで以下のように設定を行います。

  • サービス検出の統合の有効化: 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が確認できます。
スクリーンショット 2020-06-01 17.31.27.png
参考:AWS Cloud Mapを試してみる

8. voting-appのタスク定義

vote-apiのタスク定義と手順は同じです。設定内容だけvoting-appに合わせて変更します。(変更部分を太文字にしました)

  • 起動タイプの互換性: EC2
  • タスク定義名: 自分の好きな名前
  • タスクロール: なし
  • ネットワークモード: default

ネットワークモードをdefaultに変更したことによって、コンテナ定義のポートマッピング部分が「ホストポートとコンテナポートの2つを決める」というように変わっています。
1.png

  • コンテナ名: 好きな名前
  • イメージ: 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
まず、どの種類のロードバランサを作成するかを選択します。
今回の場合はvoting-appに接続するためのhttpトラフィックを扱いたいので、Application Load Balancerを使います。

基本設定

まずは、ロードバランサ自体の設定を行います。

スクリーンショット 2020-06-03 16.29.44.png

  • 名前: 自分の好きな名前をつけます
  • スキーム: パブリックにアクセスしたいので、インターネット向けを選択
  • IPアドレスタイプ: IPv4を選択

スクリーンショット 2020-06-03 16.29.52.png
リスナーでは、ロードバランサが受け付けるトラフィックの種類を指定します。
今回は、インターネットからvoting-appに向けてのhttp通信をロードバランサでさばきたいので、httpプロトコル・80番ポートを指定します。

参考:Application Load Balancerで設定する4種類のポート番号の意味を理解しよう

アベイラベリティーゾーンでは、ロードバランサを設置するネットワークを指定します。
VPCは、ECSクラスターを展開しているVPCを選択し、各AZごとにパブリックなサブネットを指定します。

セキュリティグループの設定

次に、ロードバランサに付与するセキュリティグループを指定します。

スクリーンショット 2020-06-03 16.30.19.png
今回の場合は、http(80番ポート)のインバウンドを受け付けるように設定します。
既にそのようなグループを作成済みの場合はそれを、ない場合は新規作成して付与します。

ルーティングの設定

ルーティング設定では、ロードバランサが受け取ったトラフィックを、どこの何番ポートに転送するかを指定します。
参考:Application Load Balancerで設定する4種類のポート番号の意味を理解しよう

スクリーンショット 2020-06-03 16.32.00.png

ヘルスチェックには手をつけずに、ターゲットグループを以下のように設定します。

  • ターゲットグループ: 新規作成
  • 名前: わかりやすい名前をつけます
  • ターゲットの種類: 今回は、voting-appがあるECSクラスターインスタンスに転送したいので、インスタンスを選択
  • プロトコル: voting-appにはhttpトラフィックを流す
  • ポート: voting-appがlistenしている8080番ポートを指定

ターゲットの登録

新しく作成したターゲットグループに、どのインスタンスを属させるかの設定をここで行うことができます。

スクリーンショット 2020-06-03 16.32.37.png

インスタンスの欄に、現在稼働中のEC2インスタンス一覧が表示されています。追加したいインスタンスにチェックを入れて、「登録済みに追加」ボタンを押すことで追加ができます。
今回は、この後voting-appのECSサービスを作るところでこの設定を行いたいので、何もせずに飛ばします。
参考:Dockerコンテナの作成からECSの動的ポート+ALBでロードバランスするまで【cloudpack大阪ブログ】

ここまでの設定が行えたら、ロードバランサを作成します。

10. voting-appのサービス作成

基本的には、vote-apiのときと手順は同様です。以下に設定を示します。

  • サービスの設定: vote-apiと同様に設定
  • ネットワーク設定: タスクのネットワーク設定がawsvpcではないので、なし
  • サービスディスカバリー: voting-appにアクセスするようなサービスがないので、不要

vote-apiとは違うところが、voting-appサービスとロードバランサを連携させる設定を行うところです。
スクリーンショット 2020-06-03 16.33.18.png

  • ロードバランサの種類: 先ほど作ったのがApplication Load Balancerなのでそれにチェック
  • サービス用のIAMロールの選択: 自動でecsServiceRoleが選ばれている
  • ロードバランサ名: 先ほど作ったものを選択
  • コンテナの選択: voting-appのタスク定義時に指定したコンテナを選択

ここまで設定できている状態で、コンテナの選択の部分の「ロードバランサに追加」ボタンをクリックすると、以下のような項目が追加で表示されます。
スクリーンショット 2020-06-03 16.33.42.png
ターゲットグループ名のところに、先ほど新規作成したグループを指定します。
すると、他の項目が自動で入力されます。

以上の設定を行った上で、サービスを作成します。

実際に本番環境のアプリにアクセスしてみる

これでデプロイが完了しました。
スクリーンショット 2020-06-05 1.10.14.png

ブラウザにロードバランサのパブリックDNS名を入力・アクセスすることで、本番環境のアプリを見ることができます。お疲れ様でした。

参考文献

ECSの公式解説スライド
参考:AWS Black Belt Online Seminar 2016 Amazon EC2 Container Service
参考:[AWS Black Belt Online Seminar] Amazon Elastic Container Service (Amazon ECS) 資料及び QA 公開

155
143
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
155
143