【Day 33】コンテナ名で名前解決するサービスディスカバリ(DNS) 〜変動するIPアドレスの完全撲滅・Docker内蔵DNS(127.0.0.11)の仕組み・Composeサービス名通信・ネットワークエイリアス・C# HttpClient/EF Core現場実践〜
「コンテナを再起動するたびにIPアドレスが変わってしまう… 接続先IPを毎回書き換えるなんて無理では?」
前回(Day 32)は、Dockerネットワークの全体像と3大ドライバー(Bridge / Host / None)、そしてユーザー定義ブリッジによる隔離について学びました。
そこで30年来のベテランエンジニアが必ず抱くのが「IPアドレスの変動」に関する切実な不安です。
「物理サーバーやオンプレミスVMなら、サーバーに固定プライベートIP(例: 192.168.1.10)を割り当て、C#の接続文字列や設定ファイルに書き込んでいた。だが、Dockerコンテナは起動・破棄のたびに `172.20.0.2` や `172.20.0.5` などIPがコロコロ変わる。hostsファイルを手作業で書き換えるのか? どうやってWeb APIからDBを見失わずに接続し続けられるのか?」
その答えこそが、Dockerが標準提供する「組み込みDNSサーバー(Embedded DNS Server: 127.0.0.11)」によるサービスディスカバリ(名前解決)です!
本講義では、動的IPの苦悩を完全消滅させるDNSの仕組みから、Composeでの通信手法、そしてC# (.NET) 現場で致命的な障害を生む「DNSキャッシュ問題(TTL)」の完全回避策まで徹底解説します!
INDEX 本日の講義メニュー
- 第1章: なぜIPアドレス直接指定は破綻するのか? 〜hostsファイル管理の限界とDockerの動的IP〜
- 第2章: Docker内蔵DNSサーバー(127.0.0.11)の驚くべき仕組み
- 第3章: Docker Composeにおけるサービス名通信とネットワークエイリアス(aliases)
- 第4章: C# (.NET) 現場での実践:HttpClientと接続文字列のサービスディスカバリ設計
- 第5章: 現場必須のDNS疎通確認コマンド(nslookup, ping, curl)とトラブルシューティング
- 第6章: 第7部核心!理解度チェッククイズ(全3問・詳細解説付き)
- 次回予告: 【Day 34】ポートマッピング(-p 8080:80)とポート競合の解決法
1 なぜIPアドレス直接指定は破綻するのか? 〜hostsファイル管理の限界とDockerの動的IP〜
オンプレミスの物理サーバーや固定VMの時代、システム構築時の定石は「固定プライベートIPアドレス(Static IP)の払い出し」でした。
開発サーバー `192.168.10.21`、検証DB `192.168.10.55`、本番API `10.0.2.100` といったIPアドレスを表計算ソフト(Excel)のIP台帳に几帳面に記録し、各サーバーの `C:\Windows\System32\drivers\etc\hosts` や `/etc/hosts` に手動で書き込んで名前解決していた現場も少なくありません。
- 変更コストと伝搬漏れ: DBサーバーのリプレイスでIPが1つ変わるだけで、接続元の全Webサーバー・バッチサーバーのhostsを手作業で書き換えて回る必要があった。
- Gitリポジトリとの不協和音: 接続文字列に生IPアドレスを書いてしまうと、開発機(192.168.x.x)とステージング(10.0.x.x)でコードや設定を共有できず、コミット事故の温床になった。
- コンテナの動的ライフサイクルとの完全な不一致: コンテナは「使い捨て(Ephemeral)」が基本思想です。障害発生時やスケールアウト時に数秒で再起動されますが、そのたびに全く異なる空きIPが割り当てられるため、固定IP前提の設計は即死します。
Dockerコンテナの世界では、「IPアドレスに依存した設計」は完全にアンチパターンです。
コンテナが破棄されて再生成されても、割り当てIPが `172.20.0.2` から `172.20.0.6` にシフトしても、呼び出し側は「サービス名(例: `api` や `sqlserver`)」という普遍の名前だけを指定して通信できなければなりません。
この「サービス名から現在の生きたIPアドレスを動的に引き当てる仕組み」を、クラウドネイティブの世界ではサービスディスカバリ(Service Discovery)と呼びます。
上の図解が示すとおり、手動管理のhostsファイルとIP台帳の苦しみから開発者を救い出すのが、Dockerが自動で提供する「内蔵DNSによるサービスディスカバリ」です。
開発者はもう、サーバーのIPアドレスを調べる必要も、メモに残す必要もありません。
2 Docker内蔵DNSサーバー(127.0.0.11)の驚くべき仕組み
「でも、Docker内部でどうやって名前解決しているの? 特別なDNSソフトウェア(BINDやdnsmasqなど)をインストールしているの?」と疑問に思うかもしれません。
実は、DockerにはLinuxカーネルとDockerデーモンが協調して動作する軽量な「組み込みDNSサーバー(Embedded DNS)」が標準で備わっています。
ユーザー定義ブリッジ(User-defined Bridge)に参加しているコンテナの内部に入り、`/etc/resolv.conf` を確認してみると、非常に興味深いIPアドレスが設定されていることが分かります:
この 127.0.0.11 こそが、Docker組み込みDNSのリッスンアドレスです。
コンテナ内のあらゆるネットワークリクエスト(C#の `HttpClient`、EF CoreのDB接続、`curl`、`ping` など)がホスト名を解決しようとすると、OSは自動的にこの `127.0.0.11` にDNSクエリ(ポート53)を送信します。
💡 Docker内蔵DNS(127.0.0.11)の名前解決3ステップ
- ステップ① コンテナ内からの名前解決要求: C#アプリが `http://api/weather` を呼び出すと、OSはDNSサーバー `127.0.0.11` に「`api` のIPアドレスを教えて」と問い合わせます。
- ステップ② Docker内部ルーティングテーブルの照会: `127.0.0.11` はDockerデーモンと直結しており、同じネットワーク内にいるコンテナ名・Composeサービス名・エイリアス一覧を保持しています。該当するコンテナの「現在の内部IP(例: `172.20.0.3`)」を即座に回答します。
- ステップ③ 外部インターネットへのフォワード: もし問い合わせ先がコンテナ名ではなく外部ドメイン(例: `nuget.org` や `google.com`)だった場合、内蔵DNSはホストマシンの外部DNS(社内DNSや8.8.8.8等)へ透過的にリクエストを中継(Forward)します。
この洗練された仕組みのおかげで、コンテナが再起動してIPアドレスがどれだけ変わろうとも、Dockerデーモンが内部テーブルを数ミリ秒で更新するため、アプリ側は一切の設定変更なしに通信を継続できるのです。
3 Docker Composeにおけるサービス名通信とネットワークエイリアス(aliases)
実際の開発現場において、このサービスディスカバリの恩恵を最大限に受けるのがDocker Composeです。
Docker Composeを使うと、同一ファイル内に記述されたサービス群は自動的に専用のユーザー定義ブリッジネットワークを作成し、サービス名がそのままホスト名(DNS名)として登録されます。
🌟 ネットワークエイリアス(aliases)の強力な使い所
Composeのサービス名だけでなく、`networks.<ネットワーク名>.aliases` を指定することで、コンテナに複数の別名(FQDNや短縮名)を付与できます。
- レガシーシステムの移行: 旧環境のコードにハードコードされていたドメイン(例: `api.internal.local`)をエイリアスに設定することで、C#コードを1行も修正せずにコンテナ環境へ移行できる。
- 本番FQDNのモック化: 本番で利用する外部SaaSドメインをローカルのモックコンテナのエイリアスにしておくことで、本番と同一の設定値でローカル結合テストを行える。
4 C# (.NET) 現場での実践:HttpClientと接続文字列のサービスディスカバリ設計
それでは、C# (.NET 8 / .NET 9) のコードでこのサービスディスカバリを安全に扱う実装方法を見ていきましょう。
ASP.NET Core Web APIやコンソールアプリでコンテナ名を利用する際、絶対に避けては通れない「.NETのDNSキャッシュ問題(TTL)」があります。
単に `new HttpClient()` をシングルトンで使い回すと、初回名前解決時のIPアドレス(ソケット接続)を無限にキャッシュし続けます。
その結果、相手の `order-api` コンテナがデプロイや障害で再起動して新IPになった瞬間、「相手は正常稼働しているのに、自アプリからは HttpRequestException (SocketException: Connection refused) が出続ける」という怪奇現象が発生します。
この問題をエレガントに解決するのが、ASP.NET Coreの標準機能である IHttpClientFactory と PooledConnectionLifetime の設定です。
上の図解のとおり、PooledConnectionLifetime を短時間(例: 1〜5分)に設定しておくことで、コンテナ環境特有の「動的IPアドレス切り替え」に完全対応できます。
EF CoreなどのADO.NET系プロバイダ(Microsoft.Data.SqlClient)も、内部の接続プールが切断を検知すると自動的に再接続時にDNS解決を行うため、サービス名指定(`Server=sqlserver`)で安全に運用できます。
5 現場必須のDNS疎通確認コマンド(nslookup, ping, curl)とトラブルシューティング
コンテナ同士が「名前で繋がらない!」というトラブルに直面したとき、ホストマシンからいくらpingを打っても問題は切り分けられません。
「コンテナの内部に入って、127.0.0.11のDNS応答と疎通をテストする」のがプロの鉄則です。
🛠️ トラブルシューティング:「サービス名が名前解決できない!」ときの3大チェック
- 同一のカスタムネットワークに所属しているか?
異なる Compose ファイルや別々のネットワークに所属しているコンテナ同士は、名前解決テーブルが隔離されています。`docker network inspect` で双方が同じネットワーク内に存在するか確認してください。 - デフォルトブリッジ(docker0)を使っていないか?
`docker run` を単体で実行してネットワークを指定しなかった場合、デフォルトブリッジとなり組み込みDNSが無効化されます。`--network <ネットワーク名>` を必ず指定しましょう。 - アンダースコア(_)を含むホスト名になっていないか?
RFC規約上、DNSホスト名にアンダースコア(_)が含まれると一部のHTTPクライアントやライブラリ(.NETやJava等)で名前解決に失敗する場合があります。Composeのサービス名にはハイフン(`-`)を使用するのがベストプラクティスです。
6 第7部核心!理解度チェッククイズ
本日の重要ポイントを3問のクイズでおさらいしましょう! 各枠をクリックすると正解と解説が展開します。
Q1: Dockerのユーザー定義ネットワークにおいて、コンテナ内の `/etc/resolv.conf` に設定されている組み込みDNSサーバーのIPアドレスはどれでしょうか?
正解: 127.0.0.11
【解説】: Dockerはコンテナ専用のループバックアドレス空間にある `127.0.0.11` で内蔵DNSサーバーを起動します。コンテナからの名前解決要求はこのアドレスに送られ、Dockerデーモンが管理する内部テーブルから相手コンテナの最新IPアドレスが即座に返却されます。
Q2: Docker Compose環境で、C# Web APIからSQL Serverコンテナへ接続する際の接続文字列として最も推奨される指定方法はどれでしょうか?
正解: Server=sqlserver,1433;Database=OrderDb;...(Composeサービス名を直接指定する)
【解説】: 動的に変化するIPアドレス(例: 172.20.0.4)やホスト側のIP(localhost等)を直接ハードコードしてはいけません。Composeで定義したサービス名(`sqlserver`)を指定することで、Docker内蔵DNSが常に最新のIPアドレスを自動解決し、環境差分やコンテナ再起動にも耐えられる堅牢な構成になります。
Q3: C# (.NET) アプリケーションでコンテナ名を使ったHTTP通信を行う際、「コンテナ再起動によるIP変更に追従できなくなる問題(DNSキャッシュ問題)」を回避するための正しい設定はどれでしょうか?
正解: IHttpClientFactory を利用し、SocketsHttpHandler の PooledConnectionLifetime を適切な時間(数分程度)に設定する。
【解説】: .NETの標準的なHttpClientは一度接続したソケットのDNS結果を保持し続ける傾向があります。`SocketsHttpHandler.PooledConnectionLifetime`(接続プールの生存期間)を数分(例: 2分)に制限することで、定期的に接続を再オープンし、Docker内蔵DNSに対して最新のIPアドレスを再問い合わせさせることができます。
🚀 次回予告:【Day 34】ポートマッピング(-p 8080:80)とポート競合の解決法
次回は、コンテナ間ネットワークと並ぶ最重要テーマ「ホストとコンテナのポート境界線(ポートフォワーディング)」を徹底解剖します!
「-p 8080:80 と書いたとき、ホストOSとコンテナの間でパケットはどう流れているのか?」「ホストのIISやSQL Serverとポートが衝突したときの回避法は?」「環境変数を使ってポート番号を柔軟に外出しする方法とは?」
現場で誰もが一度は頭を抱えるポートトラブルを完全克服します。次回もお楽しみに!
コメント
コメントを投稿