スキップしてメイン コンテンツに移動

【Day 33】コンテナ名で名前解決するサービスディスカバリ(DNS) 〜変動するIPアドレスの完全撲滅・Docker内蔵DNS(127.0.0.11)の仕組み・Composeサービス名通信・ネットワークエイリアス・C# HttpClient/EF Core現場実践〜

Day 33 アイキャッチ
▲ Day 33: コンテナ名で名前解決するサービスディスカバリ(DNS)
🐳

「コンテナを再起動するたびに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` に手動で書き込んで名前解決していた現場も少なくありません。

⚠️ 旧来の「固定IP+hosts手動管理」が抱えていた3大悲劇
  • 変更コストと伝搬漏れ: 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手動管理 vs Docker内蔵DNS
▲ 【図解①】旧来のhosts手動更新地獄 vs 現代のDocker組み込みDNS自動解決

上の図解が示すとおり、手動管理の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アドレスが設定されていることが分かります:

# コンテナ内部のDNS設定を確認する $ cat /etc/resolv.conf # 出力結果: nameserver 127.0.0.11 options ndots:0

この 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がコンテナ名から最新IPを即時解決する3ステップルーティング

💡 Docker内蔵DNS(127.0.0.11)の名前解決3ステップ

  1. ステップ① コンテナ内からの名前解決要求: C#アプリが `http://api/weather` を呼び出すと、OSはDNSサーバー `127.0.0.11` に「`api` のIPアドレスを教えて」と問い合わせます。
  2. ステップ② Docker内部ルーティングテーブルの照会: `127.0.0.11` はDockerデーモンと直結しており、同じネットワーク内にいるコンテナ名・Composeサービス名・エイリアス一覧を保持しています。該当するコンテナの「現在の内部IP(例: `172.20.0.3`)」を即座に回答します。
  3. ステップ③ 外部インターネットへのフォワード: もし問い合わせ先がコンテナ名ではなく外部ドメイン(例: `nuget.org` や `google.com`)だった場合、内蔵DNSはホストマシンの外部DNS(社内DNSや8.8.8.8等)へ透過的にリクエストを中継(Forward)します。

この洗練された仕組みのおかげで、コンテナが再起動してIPアドレスがどれだけ変わろうとも、Dockerデーモンが内部テーブルを数ミリ秒で更新するため、アプリ側は一切の設定変更なしに通信を継続できるのです。

3 Docker Composeにおけるサービス名通信とネットワークエイリアス(aliases)

実際の開発現場において、このサービスディスカバリの恩恵を最大限に受けるのがDocker Composeです。
Docker Composeを使うと、同一ファイル内に記述されたサービス群は自動的に専用のユーザー定義ブリッジネットワークを作成し、サービス名がそのままホスト名(DNS名)として登録されます。

# docker-compose.yml: サービス名通信とエイリアス設定の実例 services: web-front: build: ./FrontendApp ports: - "8080:80" environment: # サービス名「order-api」で直接HTTP通信できる! - OrderApiUrl=http://order-api:8080 networks: - backend-net order-api: build: ./OrderApi environment: # サービス名「sqlserver」で直接接続! - ConnectionStrings__Default=Server=sqlserver,1433;Database=OrderDb;User Id=sa;Password=P@ssw0rd123;TrustServerCertificate=True networks: backend-net: # ネットワークエイリアス(別名)の付与! aliases: - api.internal.local - legacy-order-service sqlserver: image: mcr.microsoft.com/mssql/server:2022-latest environment: - ACCEPT_EULA=Y - MSSQL_SA_PASSWORD=P@ssw0rd123 networks: - backend-net networks: backend-net: driver: bridge
Composeサービス名とネットワークエイリアス
▲ 【図解③】Composeサービス名による直接通信と複数別名(aliases)の柔軟な活用

🌟 ネットワークエイリアス(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)」があります。

⚠️ C#エンジニアが本番でハマる「古いIPの掴みっぱなし現象」

単に `new HttpClient()` をシングルトンで使い回すと、初回名前解決時のIPアドレス(ソケット接続)を無限にキャッシュし続けます。
その結果、相手の `order-api` コンテナがデプロイや障害で再起動して新IPになった瞬間、「相手は正常稼働しているのに、自アプリからは HttpRequestException (SocketException: Connection refused) が出続ける」という怪奇現象が発生します。

この問題をエレガントに解決するのが、ASP.NET Coreの標準機能である IHttpClientFactory と PooledConnectionLifetime の設定です。

// Program.cs: C# (.NET 8) での安全なサービス名HttpClient登録 var builder = WebApplication.CreateBuilder(args); // 1. 環境変数または appsettings.json からサービスURLを取得(デフォルトはサービス名指定) string orderApiBase = builder.Configuration["OrderApiUrl"] ?? "http://order-api:8080"; // 2. IHttpClientFactory と SocketsHttpHandler によるDNS定期再解決の設定 builder.Services.AddHttpClient("OrderApiClient", client => { client.BaseAddress = new Uri(orderApiBase); client.DefaultRequestHeaders.Add("Accept", "application/json"); client.Timeout = TimeSpan.FromSeconds(10); }) .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler { // ★最重要: 接続プールの有効期限を2分に設定! // コンテナ再起動でIPアドレスが更新されても、最長2分でDNSを再解決して新IPへ自動追従する PooledConnectionLifetime = TimeSpan.FromMinutes(2) }); // 3. EF Core DBコンテキストの登録(接続文字列にコンテナ名を指定) string dbConn = builder.Configuration.GetConnectionString("Default") ?? "Server=sqlserver,1433;Database=OrderDb;User Id=sa;Password=P@ssw0rd123;TrustServerCertificate=True"; builder.Services.AddDbContext<OrderDbContext>(options => options.UseSqlServer(dbConn)); var app = builder.Build();
C#現場のサービスディスカバリ実践設計
▲ 【図解④】.NET HttpClient/EF Coreでのサービス名接続とDNSキャッシュ(TTL)対策

上の図解のとおり、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応答と疎通をテストする」のがプロの鉄則です。

# 1. コンテナ内から特定のサービス名のIP解決を直接テスト(nslookup) docker exec -it web-front nslookup order-api # 正常時の出力例: Server: 127.0.0.11 Address: 127.0.0.11#53 Name: order-api Address: 172.20.0.3 # 2. curl によるHTTP疎通テスト(ヘッダーとステータス確認) docker exec -it web-front curl -I http://order-api:8080/health # 3. Alpine等の軽量コンテナで ping による低レイヤー疎通確認 docker exec -it web-front ping -c 3 order-api # 4. ネットワークに参加している全コンテナの名前とIPを一括調査 docker network inspect backend-net --format '{{json .Containers}}' | jq .

🛠️ トラブルシューティング:「サービス名が名前解決できない!」ときの3大チェック

  1. 同一のカスタムネットワークに所属しているか?
    異なる Compose ファイルや別々のネットワークに所属しているコンテナ同士は、名前解決テーブルが隔離されています。`docker network inspect` で双方が同じネットワーク内に存在するか確認してください。
  2. デフォルトブリッジ(docker0)を使っていないか?
    `docker run` を単体で実行してネットワークを指定しなかった場合、デフォルトブリッジとなり組み込みDNSが無効化されます。`--network <ネットワーク名>` を必ず指定しましょう。
  3. アンダースコア(_)を含むホスト名になっていないか?
    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とポートが衝突したときの回避法は?」「環境変数を使ってポート番号を柔軟に外出しする方法とは?」
現場で誰もが一度は頭を抱えるポートトラブルを完全克服します。次回もお楽しみに!

コメント

このブログの人気の投稿

【Perfume】Perfume 3rd Tour 「JPN」さいたまスーパーアリーナに行って参りました~

Perfume 3rd Tour 「JPN」1月28日、29日の2daysで参戦して参りました。 ファンクラブ会員の選考抽選会に28日、29日の両日で申し込んでいたんですが、両方とも当選していたという嬉しい誤算。 仕事だぁ、なんだかんだと紆余曲折ありまして28日の参戦は危ぶまれていたのですが、なんとかかんとか28日は40分遅刻しながらも参戦、29日はフル参戦する事が出来ました(∩´∀`)∩

Perfume、2025年末で“コールドスリープ(活動休止)”へ──東京ドーム2daysの記録と、公式・各界・ファンの声

  1) 公式発表・基本情報 活動休止(コールドスリープ)を正式発表 2025年9月21日、公式サイト「 Perfumeより 」で、2026年から一度コールドスリープに入ると発表。再始動の意思(“新しいPerfume”として戻る)も明記。 Perfume Official Site 今後の運用告知(配信・上映など) 同21日付の「 今後のPerfumeに関して 」で、9/23(火・祝)東京ドーム公演の LIVESHIP全世界生配信 および 全国ディレイビューイング 決定を案内。特設サイトも併記。 Perfume Official Site +1 公式グッズ **「Perfume Calendar 2026」**発売決定(B2壁掛け/表紙+2カ月1枚×6枚+ポスター)。テーマは“フルーツ”。 Perfume Official Site 2) 東京ドーム2days(9/22・23)要点 公演タイトル: ZO/Z5 Anniversary “ネビュラロマンス” Episode TOKYO DOME 。日程・開場/開演・会場・物販等の実務情報は特設で公開。 Perfume Official Site 9/23公演を世界配信(LIVESHIP) /ディレイビューイング実施。 Perfume Official Site 海外向け報道(英語)も「 indefinite hiatus 」として広く配信。 The Times of India 国内英字メディアも速報。 朝日新聞 3) 現地レポ( メリ爺 )から見える空気 あなたの現地記録(9/24公開)を、ファクトと現場情景の両面で反映: 両日参戦 。22日は アリーナ 、23日は 1階席から俯瞰 。前日の発表を受け、開演前のザワつき→MC後に“いつもの一体感”へ。開演は22日 18:45 頃。オープニングは**P³ツアー東京ドーム(2020/2)**と同趣向で“回収”の演出。 メリ爺のゲーム万歳 MC所感 :解散ではなく前向きな休止で不安を和らげる内容。 かしゆか の涙、 のっち の気丈さ、 あ〜ちゃん の思い出語り──3人の結束と「 また戻る 」約束が強く刻まれた。 メリ爺のゲーム万歳 体感時間 : “3時間弱”級のボリューム ながら“過去一...

ニンテンドースイッチでオンラインゲームを遊ぶには?

モンスターハンターライズが発売になって、家族でモンハン楽しみたい人が増えたみたいですね。 某ブログの検索キーワードを調べると、モンハンを家族でやるにはどうしたらいいのか?を調べている人が沢山いるのがわかります。 ※これでも一部  家族で協力するにはSwitch2台、ソフトも2つ購入する必要があります。 1台のスイッチでは遊べませんし、ソフト1つでも遊べません。 でも、1台のスイッチで別々アカウントがあれば交代で遊ぶこともできますよ。セーブデータもしっかり分かれます。 Switchのオンラインゲームを家族で遊ぶ方法については以下の記事にまとめてますのでよろしければ参考にしてみてくださいね。 家族でモンスターハンターライズDL版のオンラインプレーを楽しむ方法