LCL Engineers' Blog

バス比較なび・格安移動・バスとりっぷを運営する LCLの開発者ブログです。

ECS サービスディスカバリでFargateのIPを自動的にDNSレコードへ紐づける

モバイルアプリエンジニアの山下です。
先日の記事ではデプロイ基盤を構築し、Fargateへ自動デプロイすることができました。

techblog.lclco.com

これによりEC2の操作を意識することなく、柔軟にコンテナ数をスケーリングできるようになったのですが、LCLの用途として一つ課題がありました。
Fargateはデプロイするたびにコンテナを作り変えるため毎回IPが変わってしまいます。

LCLでは各サービスでステージング環境やテスト環境を用意しており、これらは認証サーバを経由してアクセスするようにしています。
そのため、認証サーバ上のnginxの設定に参照先のIPをセットしているのですが、このままではデプロイされたタスクのIPを毎回手動で変更しなければいけません。 これではデプロイ自動化どころかFargateを運用することが難しいです。

そこで、ECSのサービスディスカバリという機能を使ってこの課題を解決しました。

Amazon ECS サービスディスカバリ | Amazon Web Services ブログ

手順

名前空間を作成

まずは名前空間を作成します。後にこれを認証サーバ上のnginxの設定にセットします。

LCLではここを以下のように環境ごとに分けました。

  • test.internal
  • production.internal

先頭にサービス名が付くため、例えば「hoge」というサービス名で立ち上げた際はhoge.test.comといったレコード名になります。

名前空間はAWS CLIで作成することが可能です。

$ aws servicediscovery create-private-dns-namespace --name test.internal --vpc vpc-xxxxxxx

# 一覧を確認
$ aws servicediscovery list-namespaces

Route 53のHosted zones画面から追加が確認できればOKです。

f:id:lcl-engineer:20181031173650p:plain

サービスの検出 設定

名前空間を作成するとECSのサービス作成時に「サービスの検出(オプション)」で設定することができます。
名前空間の項目はプルダウンで表示されますが、ここで表示されるものはサービスディスカバリで作成したもののみで事前にRoute53コンソールから作成したものは表示されないことに注意してください。

f:id:lcl-engineer:20181031173657p:plain

サービスディスカバリの設定自体はこれだけです。コンテナのPrivate IPとDNSレコードの紐づけは自動的にやってくれます。

サービス作成後にRoute 53のHosted zonesの一覧にある今回追加した名前空間を参照するとAレコードが自動で追加されているのが確認できます。

f:id:lcl-engineer:20181101111915p:plain

認証サーバ(nginx)の設定

認証サーバのnginxの設定で以下のようにproxy_passを変更し、再起動することで無事にIPを認識することができました。

server {
        ...
        server_name hoge.com;

        location / {
                 ...
-                proxy_pass 10.0.X.X;
+               proxy_pass http://hoge.test.internal/;
        }
}

しかし、その後にデプロイしたタイミングでドメインのIPが変わった際にnginxがIPを認識してくれない状態がありました。
どうやらproxy_passにドメインを使っている場合、名前解決はnginx起動時に行われるため、取得したIPはnginxの再起動もしくはHUPを受け取るまでnginxにキャッシュされて解放されないようです。

server {
        ...
        server_name hoge.com;

        location / {
                 ...
-               proxy_pass http://hoge.test.internal/;
+              resolver 10.0.X.X valid=2s;
+              set $endpoint "hoge.test.internal";
+              proxy_pass http://$endpoint;
        }
}

こちらの記事を参考にさせていただきました。
nginxのproxy_passにIPではなくホスト名を使うときの注意点 | Ore no homepage

以上で無事に自動的に通信制御をすることができました。

名前空間の削除

サービスディスカバリで作成したHosted Zoneは、サービスディスカバリ以外で管理することができないようです。 そのため、Route 53 のコンソール画面からは、自動作成されたHosted Zoneを削除できずエラーが発生します。

削除についてもAWS CLIから行います。

aws servicediscovery list-services コマンドで、自動作成したHosted Zoneが関連づけられたサービスディスカバリサービスのService Discovery Service IDを確認します。

$ aws servicediscovery list-services
{
    "Services": [
        {
            "Id": "<Service Discovery Service ID>",
            "Arn": "arn:aws:servicediscovery:ap-northeast-1:<Account ID>:service/<Service Discovery Service ID>",
            "Name": "<Service Discovery Service Name>"
        }
    ]
}

次にaws servicediscovery list-namespaces コマンドでサービスディスカバリの名前空間を取得し、自動作成されたHosted ZoneのName Space IDを確認します。

$ aws servicediscovery list-namespaces
{
    "Namespaces": [
        {
            "Id": "<Name Space ID>",
            "Arn": "arn:aws:servicediscovery:ap-northeast-1:<Account ID>:namespace/<Name Space ID>",
            "Name": "<Hosted Zone Name>",
            "Type": "DNS_PRIVATE"
        }
    ]
}

aws servicediscovery delete-serviceで該当のHosted Zoneに関連付けられたサービスを削除します。

$ aws servicediscovery delete-service --id <Service Discovery Service ID>

aws servicediscovery delete-namespaceで該当のHosted Zoneを削除します。

$ aws servicediscovery delete-namespace --id <Name Space ID>

以上で削除できればOKです。

さいごに

サービスディスカバリを使うことで無事に一通り環境を整えることができました。
AWSを使うと想像していたよりも簡単にインフラ環境を作れるので面白いですね。

今回もモバイルアプリ関係ではない記事となってしまいましたが、そろそろアプリの話も書いていきたいと思います。
あと1ヶ月でAdvent Calendarもはじまるのでチーム全体でアウトプット活動をしていきたいです 💪