最新のアプリケーションは、ネットワークを広範に利用します。 ビルド時には、Linux ディストリビューションのパッケージ リポジトリのパッケージに apt-get/dnf/yum/apk install
共通です。 実行時に、アプリケーションは connect()
内部の postgres または mysql データベースに対して何らかの状態を保持しながら、TCPおよびUDPポートを介してAPIとUIを呼び出し listen()
、 accept()
公開することができます。 一方、開発者は、オフィスでも自宅でも、モバイルでもVPNでも、どこからでも作業できる必要があります。 Docker Desktop は、これらすべてのシナリオで、これらすべてのユースケースでネットワークが "適切に機能する" ように設計されています。 この投稿では、これを実現するために使用するツールと手法について、誰もが好きなプロトコルスイートであるTCP/IPから始めて説明します。
TCP/IP
コンテナーが外部に接続する場合は、TCP/IP を使用します。 Linux コンテナーには Linux カーネルが必要なため、Docker Desktop にはヘルパー Linux VM が含まれています。 そのため、コンテナーからのトラフィックはホストではなく Linux VM から発信され、深刻な問題が発生します。
多くのIT部門は、「 VPN経由でホスト から発信されたトラフィックのみを転送する」などのVPNポリシーを作成します。 その目的は、ホストが誤ってルーターとして機能し、安全でないトラフィックをインターネットから安全な企業ネットワークに転送するのを防ぐことです。 そのため、VPN ソフトウェアが Linux VM からのトラフィックを認識した場合、VPN 経由でルーティング されず 、コンテナーが内部レジストリなどのリソースにアクセスできなくなります。
Docker Desktop は、 MirageOS Unikernel プロジェクトの ネットワークプロトコルライブラリの上に OCaml で書かれた TCP/IP スタックである vpnkit を介して、ユーザレベルですべてのトラフィックを転送することで、この問題を回避します。次の図は、ヘルパー VM から vpnkit 経由、およびインターネットへのパケットのフローを示しています。
VM は起動時に、DHCP を使用してアドレスを要求します。 リクエストを含むイーサネットフレームは、Mac の virtio デバイス または Windows の「ハイパーバイザーソケット」(AF_VSOCK) を介して、共有メモリーを介して VM からホストに送信されます。 Vpnkit には、要求を DHCP (ミラージュ/チャールア) サーバに転送する仮想イーサネットスイッチ ( ミラージュvnetif ) が含まれています。
VM は、VM の IP アドレスとゲートウェイの IP を含む DHCP 応答を受信すると、ゲートウェイのイーサネット アドレス(ミラージュ/ARP)を検出する ARP 要求を送信します。 ARP応答を受信すると、パケットをインターネットに送信する準備が整います。
vpnkit は、新しい宛先 IP アドレスを持つ発信パケットを検出すると、リモート マシン (ミラージュ/ミラージュ tcpip) を表す仮想 TCP/IP スタックを作成します。 このスタックはLinuxのスタックのピアとして機能し、接続を受け入れてパケットを交換します。 コンテナが TCP 接続を確立するために呼び出す connect()
と、Linux は SYNchronize フラグが設定された TCP パケットを送信します。 Vpnkit は SYNchronize フラグを監視し、ホストから自身を呼び出し connect()
ます。 が connect()
成功すると、vpnkit は TCP SYNchronize パケットで Linux に応答し、TCP ハンドシェイクを完了します。 Linuxでは成功し、 connect()
データは両方向(ミラージュ/ミラージュフロー)にプロキシされます。 もし connect()
が拒否された場合、vpnkit は TCP RST(リセット)パケットで応答し、 connect()
Linuxの内部でエラーを返します。 UDP と ICMP も同様に処理されます。
低レベルのTCP/IPに加えて、vpnkitにはDNSサーバー(mirage/ocaml-dns)やHTTPプロキシ(mirage/cohttp)などの多数の高レベルのネットワークサービスが組み込まれています。 これらのサービスは、構成に応じて、仮想IPアドレス/ DNS名を介して直接アドレス指定することも、発信トラフィックを照合して動的にリダイレクトすることによって間接的にアドレス指定することもできます。
TCP/IP アドレスを直接操作するのは困難です。 次のセクションでは、Docker Desktop がドメイン ネーム システム (DNS) を使用して、人間が判読できる名前をネットワーク サービスに付ける方法について説明します。
ティッカー
Docker Desktop の内部には、複数の DNS サーバーがあります。
コンテナからのDNSリクエストは、最初に内部の dockerd
サーバーによって処理され、同じ内部ネットワーク上の他のコンテナの名前を認識します。 これにより、コンテナーは内部 IP アドレスを知らなくても簡単に相互に通信できます。 たとえば、この図には、 "nginx"、 "golang"、 "postgres"の3つのコンテナがあり、 docker/awesome-composeの例から引用しています。 アプリケーションを起動するたびに、内部IPアドレスが異なる場合がありますが、内部の内部DNSサーバーのおかげで、コンテナは人間が読める名前で簡単に相互に接続できます dockerd
.
他のすべての名前検索は、(CNCF から )CoreDNS に送信されます。要求は、ドメイン名に応じて、ホスト上の 2 つの異なる DNS サーバーのいずれかに転送されます。 ドメイン docker.internal
は特殊で、現在のホストの有効な IP アドレスに解決される DNS 名 host.docker.internal
が含まれています。 すべてが完全にコンテナ化されていることが望ましいですが、アプリケーションの一部を単純な古いホストサービスとして実行することが理にかなっている場合があります。 特別な名前 host.docker.internal
を使用すると、コンテナーは、IP アドレスのハードコーディングを気にすることなく、移植可能な方法でこれらのホスト サービスに接続できます。
ホスト上の 2 番目の DNS サーバーは、標準の OS システム ライブラリを介して他のすべての要求を解決することによって、他のすべての要求を処理します。 これにより、名前が開発者のWebブラウザで正しく解決された場合、開発者のコンテナでも正しく解決されます。 これは、一部の要求が企業VPNを介して送信される図に示すように、高度なセットアップで特に重要です(例: internal.registry.mycompany
) 他のリクエストは通常のインターネットに送信されます(例: docker.com
).
DNSについて説明したので、HTTPについて説明しましょう。
HTTP(S) プロキシ
一部の組織では、インターネットへの直接アクセスをブロックし、フィルタリングとロギングのためにすべてのトラフィックをHTTPプロキシ経由で送信する必要があります。 これは、ビルド中のイメージのプルと、コンテナーによって生成される送信ネットワーク トラフィックに影響します。
HTTP プロキシを使用する最も簡単な方法は、環境変数を使用して Docker エンジンをプロキシで明示的にポイントすることです。 これには、プロキシを変更する必要がある場合、変数を更新するためにDockerエンジンプロセスを再起動する必要があり、顕著な不具合が発生するという欠点があります。 Docker Desktop は、アップストリーム プロキシに転送するカスタム HTTP プロキシを vpnkit 内で実行することで、これを回避します。 アップストリーム プロキシが変更されると、内部プロキシが動的に再構成されるため、Docker エンジンを再起動する必要がなくなります。
Mac では、Docker デスクトップはシステム環境設定に保存されているプロキシ設定を監視します。 コンピューターがネットワークを切り替えると(WiFiネットワーク間やセルラーなど)、Docker Desktopは内部HTTPプロキシを自動的に更新するため、開発者がアクションを実行しなくてもすべてが引き続き機能します。
これは、コンテナが相互に通信したり、インターネットに接続したりするのをほぼカバーしています。 開発者はコンテナとどのように話しますか?
ポートフォワーディング
アプリケーションを開発するときは、Web ブラウザーなどのデバッグ ツールからアクセスできるホスト ポートで UI と API を公開できると便利です。 Docker Desktop は Linux VM 内で Linux コンテナーを実行するため、VM ではポートが開いていますが、ツールはホストで実行されているという切断があります。 ホストからVMに接続を転送するための何かが必要です。
Webアプリケーションのデバッグを検討してください:開発者は docker run -p 80:80
、コンテナのポート80がホストのポート80で公開され、 http://localhost 経由でアクセスできるように要求します。 Docker API 呼び出しは、通常どおりホスト上に書き込まれ /var/run/docker.sock
ます。 Docker Desktop が Linux コンテナーを実行している場合、Docker エンジン (dockerd
上の図) は、ホスト上でネイティブに実行するのではなく、ヘルパー Linux VM 内で実行される Linux プログラムです。 そのため、Docker Desktop には、ホストから VM に要求を転送する Docker API プロキシが含まれています。 セキュリティと信頼性のため、要求はネットワーク経由で TCP 経由で直接転送されません。 代わりに、Docker Desktop は、上の図でラベル付けされた vpnkit-bridge
プロセスを介して、共有メモリハイパーバイザーソケットなどの安全な低レベルのトランスポートを介して Unix ドメインソケット接続を転送します。
Docker API プロキシは、単に要求を前後に転送するだけではありません。 また、要求と応答をデコードおよび変換して、開発者のエクスペリエンスを向上させることもできます。 開発者が を使用してポート docker run -p 80:80
を公開すると、Docker API プロキシは要求をデコードし、内部 API を使用してプロセスを介して com.docker.backend
ポート転送を要求します。 ホスト上の何かがすでにそのポートでリッスンしている場合は、人間が判読できるエラーメッセージが開発者に返されます。 ポートが空いている場合、com.docker.backend プロセスは接続の受け入れを開始し、 上で vpnkit-bridge
実行されるプロセス vpnkit-forwarder
を介してコンテナに転送します。
Docker デスクトップは、ホスト上の "root" または "管理者" では実行されません。 開発者はヘルパーVM内のルートになるために使用できます docker run –privileged
が、ハイパーバイザーはホストが常に完全に保護されたままであることを保証します。 これはセキュリティには最適ですが、macOSではユーザビリティの問題を引き起こします:Unixの「特権ポート」、つまりポート番号1024と見なされる場合、開発者はポート80(docker run -p 80:80
)をどのように公開<ことができますか? 解決策は、Docker Desktopには、rootとして launchd
実行され、「このポートをバインドしてください」APIを公開する小さなヘルパー特権サービスが含まれていることです。 これは、「非rootユーザーが特権ポートをバインドすることを許可しても安全ですか?」という疑問を提起します。
もともと特権ポートの概念は、ポートがサービスの認証に使用されていた時代に由来しています:ホストのHTTPデーモンは、rootを必要とするポート80にバインドされていたため、管理者が手配したに違いありません。 サービスを認証する最新の方法はTLS証明書と ssh
指紋を使用することであるため、Docker Desktopが起動する前にシステムサービスがポートをバインドしている限り(macOSは起動時に launchd
ポートをバインドすることでこれを調整します)、混乱やサービス拒否は発生しません。 したがって、最新のmacOSでは、すべてのIP(0.0.0.0
または INADDR_ANY
)で特権ポートをバインドすることは、特権のない操作になっています。 Docker Desktopがポートをバインドするために特権ヘルパーを使用する必要があるケースは、特定のIPが要求された場合(例: docker run -p 127.0.0.1:80:80
)、これはまだmacOS上でルートが必要です。
概要
アプリケーションには、Dockerイメージのプル、Linuxパッケージのインストール、データベースバックエンドとの通信、APIとUIの公開など、多くの日常的なアクティビティのために信頼性の高いネットワーク接続が必要です。 Dockerデスクトップは、オフィス、自宅、信頼性の低いWi-Fiでの旅行中など、さまざまなネットワーク環境で実行されます。 一部のマシンには、制限付きのファイアウォールポリシーがインストールされています。 他のマシンには高度なVPN構成があります。 これらすべての環境でのこれらすべてのユースケースについて、Docker Desktopは「機能する」ことを目的としているため、開発者はアプリケーションのデバッグではなく、アプリケーションの構築とテストに集中できます。
この種のツールの構築が面白そうに思える場合は、Dockerデスクトップネットワーキングをさらに改善してください https://www.docker.com/career-openings。
ドッカーコン2022
5月10日火曜日に開催されるDockerCon2022にご参加ください。 DockerCon は、次世代の最新アプリケーションを構築している開発者や開発チームにとってユニークな体験を提供する、無料の 1 日の仮想イベントです。 コードからクラウドにすばやく移行する方法と開発の課題を解決する方法について学びたい場合は、DockerCon 2022 でアプリケーションの構築、共有、実行に役立つ魅力的なライブ コンテンツが提供されます。 今すぐご登録ください https://www.docker.com/dockercon/