ECS FargateでDNSキャッシュを利用するためのワークアラウンド
要約
- ECS Fargate ではタスク定義で DNS 設定を行うことができない
- 名前解決結果をキャッシュしたいコンテナの起動時に
/etc/resolv.conf
を上書きするワークアラウンドがある
やりたいこと
やりたいことは ECS Fargate タスクのコンテナでの名前解決結果をキャッシュすることです。
動機としては以下の2つです。
-
AWS Route 53 Resolver のレートリミットを回避したい
- ENI ごとに 1,024 パケット/秒のリミット
- 名前解決によるレイテンシを小さくしたい
Fargate での問題
ECS タスク定義には dnsServers
というパラメータがありますが、Fargate では利用することができません。
その理由は、Fargate では awsvpc ネットワークモードしか利用できないためです。
dnsServers
パラメータは下記に記載しているように awsvpc ネットワークモードではサポートされていません。
注記
awsvpc ネットワークモードを使用するタスクもしくは Windows コンテナでは、このパラメータはサポートされません。
この件については AWS の containers-roadmap にも issue が挙げられていますが2024年現在は対応されていません。
ワークアラウンド
上記の問題を回避するためのワークアラウンドとして、コンテナの起動時に /etc/resolv.conf
を書き換える方法があります。
(というかそれしか無いかもしれません)
DNS キャッシュサーバーをサイドカーコンテナとして起動しておき、メインのコンテナでは起動時に /etc/resolv.conf
を以下のような内容で上書きします。
nameserver 127.0.0.1
nameserver 169.254.169.253
search ap-northeast-1.compute.internal
1行目の nameserver 127.0.0.1
によってデフォルトの DNS サーバーを Fargate タスク内で動いている DNS キャッシュサーバーコンテナに設定します。
DNS キャッシュサーバーコンテナが動作していない場合に備えて2行目で AWS Route 53 Resolver (169.254.169.253) にフォールバックするようにしています。
4行目の search ap-northeast-1.compute.internal
は ap-northeast-1 リージョンで Fargate タスクを立ち上げた際にデフォルトで設定されている内容です。
リージョンごとにドメインが変わります。
もし不要であれば削除して問題ありません。
具体例
DNS キャッシュサーバーとして CoreDNS を使った具体例を示します。
CoreDNS イメージ定義
Corefile で AWS Route 53 Resolver (169.254.169.253) への forward 設定と cache の有効化をおこないます。
ここでは IP が共通でやりやすいため 169.254.169.253 を使っていますが、VPC+2 のアドレスでも問題ありません。
. {
bind 127.0.0.1
forward . 169.254.169.253
cache
}
FROM coredns/coredns:1.11.1
COPY ./Corefile /etc/coredns/Corefile
CMD [ "-conf", "/etc/coredns/Corefile" ]
アプリイメージ定義
テスト用のアプリとして、1秒ごとに www.google.com の名前解決をし続けるイメージを作成します。
#!/bin/bash
cat /resolv.conf | tee /etc/resolv.conf
exec "$@"
#!/bin/bash
while true; do
dig www.google.com
sleep 1
done
nameserver 127.0.0.1
nameserver 169.254.169.253
search ap-northeast-1.compute.internal
FROM debian:bullseye-slim
RUN apt -y update && apt -y install dnsutils
COPY ./entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
COPY ./resolv.conf /resolv.conf
COPY ./main.sh /main.sh
RUN chmod +x /main.sh
ENTRYPOINT [ "/entrypoint.sh" ]
CMD [ "/main.sh" ]
ポイントとしては、前述のように entrypoint.sh
で /etc/resolv.conf
を上書きしている点です。
タスク定義
CoreDNS コンテナとアプリコンテナを同じタスクに入れます。
CoreDNS コンテナが起動する前にアプリコンテナが起動してしまわないよう dependsOn
で起動順序を指定しています。
{
"containerDefinitions": [
{
"essential": true,
"image": "012345678901.dkr.ecr.ap-northeast-1.amazonaws.com/app:latest",
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group" : "/ecs/app",
"awslogs-create-group": "true",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "app"
}
},
"name": "app",
"dependsOn": [
{
"containerName": "coredns",
"condition": "START"
}
]
},
{
"essential": true,
"image": "012345678901.dkr.ecr.ap-northeast-1.amazonaws.com/coredns:latest",
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group" : "/ecs/app",
"awslogs-create-group": "true",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "coredns"
}
},
"name": "coredns"
}
],
"cpu": "256",
"executionRoleArn": "arn:aws:iam::012345678901:role/ecsTaskExecutionRole",
"family": "app",
"memory": "512",
"networkMode": "awsvpc",
"requiresCompatibilities": [
"FARGATE"
]
}
動作
上記のタスクを Fargate タスクとして実行すると、アプリコンテナのログとして1秒おきに CloudWatch Logs に以下のような dig の実行結果が出力されます。
; <<>> DiG 9.16.48-Debian <<>> www.google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22181
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: e861b1440ec42525 (echoed)
;; QUESTION SECTION:
;www.google.com. IN A
;; ANSWER SECTION:
www.google.com. 249 IN A 142.251.42.164
;; Query time: 52 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Fri Apr 05 15:44:29 UTC 2024
;; MSG SIZE rcvd: 85
このログから、アプリコンテナの DNS サーバーが 127.0.0.1:53 (CoreDNSコンテナ)に設定されており、正しく名前解決できていることがわかります。
(本当にキャッシュされているかどうかはコンテナの中に入って tcpdump とかで確認する必要があると思いますが、面倒なので今回は CoreDNS へ DNS クエリが送られていることのみ確認)
おわりに
今回紹介した方法はあくまでワークアラウンドであり、公式で動作が保証されたものではありません。
実際に利用される場合は十分に検証を行ったうえでご利用ください。
Discussion