弁護士ドットコム株式会社 Creators’ blog

弁護士ドットコムがエンジニア・デザイナーのサービス開発事例やデザイン活動を発信する公式ブログです。

Alpine LinuxでのDNSトラブルシューティング:解決策と試行錯誤

はじめに

こんにちは。 弁護士ドットコム SRE室の原口です。

通常は SRE室のメンバーやアプリケーションエンジニアのみなさんと協力しながら、クラウドインフラの改善に取り組んでいます。

当社では、AI 技術を活用した「チャット法律相談 (α版)」を提供したり、「みんなの法律相談」にAI タイトル自動生成機能を搭載するなど AI 技術を用いたサービス開発に力を入れています。

その中には、最近目覚ましい進歩を遂げている ChatGPT を使ったものもあります。

Microsoft が提供するAzure OpenAI Serviceを利用すると、API を通じて ChatGPT と連携できます。

その際に DNS の名前解決で問題が発生し、対応が必要となりました。

この記事では、その問題と解決策についてまとめました。

ちなみに、この記事のタイトルも Azure OpenAI Service に考えてもらいました。

問題発生

アプリケーションは、Amazon ECS で動いていて、コンテナの OS は Alpine 3.16 です。

コンテナから Azure OpenAI Service の REST API エンドポイントを呼び出そうとすると、エラーが発生すると連絡を受けたので調査を始めました。

AWSのAmazon ECSコンテナから、AzureのOpenAI ServiceへのHTTPSアクセスが直接できないことを示したシステム概要図
システム概要図

さっそくコンテナにログインして curl コマンドを使って確認したところ、確かにアクセスできていませんでした。

ECS $ curl https://hogehoge-bengo4-com.openai.azure.com/
curl: (6) Could not resolve host: hogehoge-bengo4-com.openai.azure.com

curl のエラーを見ると名前解決できていないようですので、確認したところ IP アドレス(A レコード)が引けていませんでした。

コンテナの OS には、 dig が入っていなかったので nslookup を利用しました。

ECS $ nslookup hogehoge-bengo4-com.openai.azure.com
Server:         172.30.0.2
Address:        172.30.0.2:53

Non-authoritative answer:
hogehoge-bengo4-com.openai.azure.com    canonical name = francecentral.api.cognitive.microsoft.com
francecentral.api.cognitive.microsoft.com       canonical name = cognitivefcprod.trafficmanager.net
cognitivefcprod.trafficmanager.net      canonical name = cognitivefcprod.azure-api.net
cognitivefcprod.azure-api.net   canonical name = apimgmttmbjlofj5ehtdabszqszkhumthnfrkrglszplhmntli.trafficmanager.net
apimgmttmbjlofj5ehtdabszqszkhumthnfrkrglszplhmntli.trafficmanager.net   canonical name = cognitivefcprod-francecentral-01.regional.azure-api.net

私の手元にあるローカルの PC からは問題なく名前解決できていましたので、環境の問題だろうと考えて調査を進めました。

原因

結論から申しますと、 2 つの条件が重なって今回のような現象が起きていました。

  • Amazon Route 53 Resolver(Amazon Provided DNS)は EDNS0 に対応済みで、 512 バイト以上のパケットを返す可能性がある
  • Alpine 3.18 以前では、以下の理由で DNS の最大パケットサイズが 512 バイトまでに制限されている
    • UDP で 512 バイト以上の通信を行える EDNS0 に対応していない
    • 応答が 512 バイトを超えた場合、TCP で再問い合わせする TCP フォールバックに対応していない

いろいろな試行錯誤を行った結果、上記の結論に達しました。

Alpine 3.18 以前については、AWS のドキュメントにも記載があり、注意が必要です。

Alpine Linux limits DNS response size to 512 bytes. docs.aws.amazon.com

なお Alpine 3.18 では musl libc 1.2.4 になり、 TCP フォールバックに対応しているので、 512 バイト以上のパケットでも動作します。 alpinelinux.org

原因特定のための試行錯誤

ここからは原因を特定するために、試行錯誤したことをメモとして残しています。

結果がわかっていれば、ムダな検証もありますがこういう試行錯誤を大事にしていきたいと思っています。

CNAME の階層を削ってみる

感覚的に CNAME の段数が多いと感じたので、試しに 1 段減らしてみたらコンテナの中からでもアクセスできました。

ECS $ curl http://francecentral.api.cognitive.microsoft.com -H "HOST:hogehoge-bengo4-com.openai.azure.com"
{"error":{"code":"404","message": "Resource not found"}}

EC2 からはアクセスできるか

AWS 環境固有の問題なのか確認するために、 EC2 を立ち上げて確認しましたが CNAME を削らなくてもアクセス可能でした。

EC2 $ curl http://hogehoge-bengo4-com.openai.azure.com/
{"error":{"code":"404","message": "Resource not found"}}

後日談になりますが、このとき EC2 で実行していた OS は Amazon Linux 2 でした。

OS が Alpine ではないため問題が発生しなかったんですね。と原因がわかった後に気が付きました。

実際、コンテナの OS に public.ecr.aws/amazonlinux/amazonlinux:2 を利用した場合は、 ECS 上でも問題なく動作しました。

AWS 以外の DNS サーバーを用いて名前解決できるか

いったんコンテナの中からの確認作業に戻り、Google Public DNS を用いて名前解決したところ問題なく名前解決できることも確認できました。

問い合わせる DNS サーバーによって正常に動作する場合もあることから、 DNS サーバーとの相性でもあるのだろうか。と考えるきっかけになりました。

ECS $ nslookup hogehoge-bengo4-com.openai.azure.com 8.8.8.8
Server:         8.8.8.8
Address:        8.8.8.8:53

Non-authoritative answer:
hogehoge-bengo4-com.openai.azure.com    canonical name = francecentral.api.cognitive.microsoft.com
francecentral.api.cognitive.microsoft.com       canonical name = cognitivefcprod.trafficmanager.net
cognitivefcprod.trafficmanager.net      canonical name = cognitivefcprod.azure-api.net
cognitivefcprod.azure-api.net   canonical name = apimgmttmbjlofj5ehtdabszqszkhumthnfrkrglszplhmntli.trafficmanager.net
apimgmttmbjlofj5ehtdabszqszkhumthnfrkrglszplhmntli.trafficmanager.net   canonical name = cognitivefcprod-francecentral-01.regional.azure-api.net
cognitivefcprod-francecentral-01.regional.azure-api.net canonical name = apicbe42a699f474d6780749e80fef93051dpjz6pxizkielenqw2ope.francecentral.cloudapp.azure.com
Name:   apicbe42a699f474d6780749e80fef93051dpjz6pxizkielenqw2ope.francecentral.cloudapp.azure.com
Address: 20.111.5.9

調査で学んだこと

bind-tools をインストールすると nslookup で名前解決できる

Alpine 3.16 でも、以下のコマンドで bind-tools をインストールすると名前解決できるようになりますが、 curlping は引き続き失敗します。 このことから、 bind-tools は独自の実装で名前解決をしていると考えています。

apk add --no-cache bind-tools

Alpine 3.18.2 でも nslookup は失敗する

逆に Alpine 3.18.2 だと curlping は名前解決しますが、 nslookup では名前解決できませんでした。 bind-tools をインストールすると名前解決できるようになりました。

どうして Google Public DNS を指定したら名前解決できたのか

DNS サーバーからの UDP パケットが、 512 バイトを超える DNS サーバーと超えない DNS サーバーがある。ということが原因でした。

いくつかの DNS サーバーに対して調査を進めました。

Amazon Route 53 Resolver への問い合わせ

Amazon Route 53 Resolver で名前解決した場合、 dig コマンドで確認すると、 OPT PSEUDOSECTION の項目にudp: 4096 との記載があります。

これは最大で 4096 バイトの UDP パケット送られる可能性があることを示しています。

dig hogehoge-bengo4-com.openai.azure.com 

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096

Google Public DNS への問い合わせ

一方で、 Google Public DNS へ問い合わせた場合は、 udp: 512 が返ってくるため、最大でも 512 バイトの UDP パケットが送られてきます。

dig hogehoge-bengo4-com.openai.azure.com @8.8.8.8

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512

このため、 Google Public DNS では問題なく名前解決ができていました。

Google Public DNS の仕様を確認しましたが、 常に 512 バイト以下の UDP パケットを送るという記載は見つけられませんでした。

ですが、他の事例でも DNS メッセージサイズが大きくなって名前解決に失敗していると思われる場合に、Google Public DNS を指定すると名前解決できている事例があります。

そのことから、おそらく 512 バイト以内の UDP パケットを送るようになっているのだと推測しています。

EDNS0 については、 JPRS のページに詳しい解説がありましたのでご紹介いたします。 jprs.jp

対応案の検討

原因がわかったところで、対応案を考えました。

代表的な対応案は以下のとおりです。

対応案1: Amazon API Gateway をリバースプロキシにする

コンテナから名前解決できないのであれば、代わりに外部のサービスで名前解決して Azure OpenAI Service にアクセスすればいいと思いつきました。

そこで、 Amazon API Gateway をリバースプロキシとして使用しようと考えました。

AWSのAmazon ECSコンテナから、AzureのOpenAI ServiceへのHTTPSアクセスが直接できないため、API Gatewayをリバースプロキシとして利用したシステム概要図
システム概要図

他の案と比較した結果、アプリケーション側の修正が URL の変更だけになり迅速に対応できそうなので、この案を採用しました。

将来的に Alpine 3.18 にバージョンアップして、API Gateway が不要になった場合も、 アクセス先 URL を変更するだけで迅速かつ安全に対応できると考えました。

対応案2: Alpine 3.18にする。または、コンテナのOSを別のものにする

OS のバージョンアップや変更となると全体的な動作確認なども必要で、当然すぐには無理なので採用しませんでした。

簡単な動作確認を行ったところ、Alpine 3.18 では curl コマンドで問題なく Azure OpenAI Service に接続できました。

動作確認の内容は、以下のとおりです。

OS と musl のバージョンを確認します。

ECS $ cat /etc/alpine-release
3.18.2

ECS $ ldd --version
musl libc (x86_64)
Version 1.2.4

curl コマンドでアクセスできることが確認できました。 Alpine をバージョンアップすると今回の問題は解決しそうです。

ECS $ curl https://hogehoge-bengo4-com.openai.azure.com/
{"error":{"code":"404","message": "Resource not found"}}

余談ですが、F1 大好きな私は Alpine を「アルパイン」とは読めず「アルピーヌ」と読んでしまいます。

文書にするときは良いのですが、会議などで発声するときはいつも困っています。

対応案3: DNS サーバーとして Google Public DNS を指定する

いろいろと試行錯誤を行っていると、 Google Public DNS(有名な、8.8.8.8 です)を利用して名前解決すると問題なくアクセスできることがわかりました。

/etc/resolv.conf に追加してしまえば問題は解決しそうでした。

試した限りだと名前解決には失敗しませんでした。

しかし、コンテナ全体に影響が及ぶ可能性や、 Google Public DNS の仕様変更で予期せぬ障害発生のリスクがあると考えたため採用しませんでした。

最後に

今回は DNS サーバーでの名前解決に関する問題が発生し、 EDNS0 や TCP フォールバックという機能をきちんと理解していなかったために、問題解決までに時間がかかりました。

ある程度は理解していると思っていたサービスにおいても、深い部分でのトラブルが起こり得ることを再認識し、学び続けることの大切さを感じました。

同時に、メンバーと一緒に問題を解決する楽しみを感じることができ、大変貴重な時間となりました。

この記事が問題解決の手助けとなり、役に立てていただければ幸いです。

私たちは、法律相談ポータル「弁護士ドットコム」などを運用しています。一緒に楽しく成長していけるエンジニアの方々のご応募を、心よりお待ちしています!

最後までお読みいただき、ありがとうございました。