【Docker Network 第3章】-net = hostオプションを理解する


シリーズ

【Docker Network 第1章】
【Docker Network 第2章】

はじめに

コンテナは現在、あらゆる開発サイクルにおいて必要とされている機能になっています。 コンテナネットワークがどのように機能するかを理解することは私たちにとって不可欠であります。これは、サービス通信の観点から重要であるだけでなく、インフラストラクチャセキュリティの重要な側面を形成しているからという理由でもあります。

この記事では、Dockerコンテナーで使用できるさまざまなネットワークモードについて簡単に学習し、ホストモードネットワークについて詳しく解説していきます。

ネットワークモードの概要

None

Noneは、コンテナがネットワークスタックを受信するという点で単純ですが、外部ネットワークインターフェイスがありません。 その代わりに、ループバックインターフェイスを受信します。 rktコンテナプロジェクトとDockerコンテナプロジェクトはどちらも、NoneまたはNullネットワークが使用されている場合に同様の動作を提供します。 コンテナネットワーキングのこのモードには、コンテナのテスト、後のネットワーク接続のためのコンテナのステージング、外部通信を必要としないコンテナへの割り当てなど、さまざまな用途があります。

ブリッジモード

Linuxブリッジは、同じホスト上のコンテナーが通信できるホスト内部ネットワークを提供しますが、各コンテナーに割り当てられたIPアドレスはホストの外部からアクセスできません。 ブリッジネットワーキングは、シングルホストネットワーキングを提供するNATおよびポートマッピングにiptablesを活用します。 ブリッジネットワークはデフォルトのDockerネットワークタイプ(つまり、docker0)であり、仮想ネットワークインターフェイスペアの一方の端がブリッジとコンテナの間に接続されます。

作成フローの例を次に示します。

  1. ホストにブリッジがプロビジョニングされます。
  2. 各コンテナの名前空間は、そのブリッジ内でプロビジョニングされます。
  3. コンテナのethXは、プライベートブリッジインターフェイスにマッピングされます。
  4. NATを使用したIptablesは、各プライベートコンテナとホストのパブリックインターフェイスの間のマッピングに使用されます。

NATは、ホストを超えた通信を提供するために使用されます。 ブリッジネットワークは、ポートの競合の問題を解決し、1つのホストで実行されているコンテナにネットワークの分離を提供しますが、NATの使用に関連するパフォーマンスコストがあります。

ホストモード

このアプローチでは、新しく作成されたコンテナーがそのネットワーク名前空間をホストと共有し、より高いパフォーマンスを提供し、NATの必要性を排除します。 ただ、欠点としてポートの競合が発生します。 コンテナはホストのすべてのネットワークインターフェースにアクセスできますが、特権モードで展開されていない限り、コンテナはホストのネットワークスタックを再構成しないことがあります。

ホストネットワークは、Mesos内で使用されるデフォルトのタイプです。 つまり、フレームワークでネットワークタイプが指定されていない場合、新しいネットワーク名前空間はコンテナではなくホストネットワークに関連付けられます。 ネイティブネットワークと呼ばれることもあるホストネットワークは、概念的に単純であり、理解、トラブルシューティング、および使用が容易になります。

オーバーレイ

オーバーレイはネットワークトンネルを使用して、ホスト間で通信を提供します。これにより、あるホストから次のホストにネットワークサブネットをトンネリングすることで、コンテナを同じマシン上にあるかのように動作させることができます。本質的には、1つのネットワークを複数のホストにまたがっています。仮想拡張可能ローカルエリアネットワーク(VXLAN)など、多くのトンネリングテクノロジが存在します。

VXLANは、マルチホストネットワークが1.9リリースでネイティブ機能として導入されたDockerlibnetworkに最適なトンネリングテクノロジーです。この機能の導入により、DockerはHashiCorpのSerfをゴシッププロトコルとして活用することを選択しました。これは、ネイバーテーブルの交換と収束時間の効率性のために選択されました。

他のトンネリングテクノロジーのサポートが必要な場合は、Flannelが最適です。 udp、vxlan、host-gw、aws-vpc、またはgceをサポートしており、クラウドプロバイダーのトンネルタイプごとに、アカウントまたは仮想プライベートクラウド(VPC)専用に、プロバイダーのルーティングテーブルにルートが作成されます。パブリッククラウドのサポートは、とりわけ、オーバーレイがハイブリッドクラウドのユースケースに最適に対応し、パブリックポートを開かなくてもスケーリングと冗長性を提供することを考えると、オーバーレイドライバーにとって特に重要です。

マルチホストネットワークでは、Dockerデーモンを起動するときに追加のパラメーターと、Key-Valueストアが必要です。一部のオーバーレイは、分散キー値ストアに依存しています。コンテナオーケストレーションを行っている場合は、分散型のKey-Valueストアがすでに存在します。

オーバーレイは、ホスト間の通信の課題に焦点を当てているため、2つの異なるオーバーレイネットワークに接続されている同じホスト上のコンテナは、相互にセグメント化されているためローカルブリッジを介して相互に通信できません。

アンダーレイ

アンダーレイネットワークドライバーは、ホストインターフェイス(つまり、eth0の物理ネットワークインターフェイス)を、ホスト上で実行されているコンテナーまたはVMに直接公開します。 このようなアンダーレイドライバーの2つは、メディアアクセス制御仮想ローカルエリアネットワーク(MACvlan)とインターネットプロトコルVLAN(IPvlan)です。 MACvlanおよびIPvlanドライバーの操作と動作は、ネットワークエンジニアには非常によく知られています。 どちらのネットワークドライバーも、ブリッジネットワークよりも概念的に単純であり、ポートマッピングの必要性を排除し、より効率的です。 さらに、IPvlanにはL3モードがあり、多くのネットワークエンジニアの共感を呼んでいます。 ほとんどのパブリッククラウドの制限(または機能の欠如)を考えると、アンダーレイは、オンプレミスのワークロード、セキュリティ上の懸念、トラフィックの優先順位、またはコンプライアンスに対処する必要がある場合に特に役立ち、ブラウンフィールドでの使用に最適です。 アンダーレイネットワーキングでは、VLANごとに1つのブリッジが必要になる代わりに、サブインターフェイスごとに1つのVLANが可能になります。

ホストモードネットワークの詳細を理解する

ホストモードは非常に単純に見えますが、展開するときに留意する必要のある項目がいくつかあります。 例を見て、私たちが話していることを確認しましょう。 まず、ホスト上で基本的なWebコンテナを開始しましょう。

docker run -d --name=web1 --net=host vaibhavthakur/docker:webinstance1

dockerrunコマンドで「–net = host」フラグを渡しています。 また、ポートマッピングを指定していません。 イメージがダウンロードされると、dockerはイメージを「web」と呼ばれるコンテナとして実行します。 このコンテナをデーモンとして実行するようにdockerに指示したので、次はコンテナのbashシェルに接続しましょう。


docker exec -it web1 /bin/bash

これにより、コンテナ内のシェルに移動し、コンテナで使用可能なすべてのネットワークアダプタを確認する必要があります。 以下に示すように行うことができます。

root@docker-1:~# docker exec -it web1 /bin/bash
[root@docker-1 /]# ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:9F:DE:3C:3C  
          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
          inet6 addr: fe80::42:9fff:fede:3c3c/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:180 (180.0 b)

ens4      Link encap:Ethernet  HWaddr 42:01:0A:80:0F:E8  
          inet addr:10.128.15.232  Bcast:0.0.0.0  Mask:255.255.255.255
          inet6 addr: fe80::4001:aff:fe80:fe8/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1460  Metric:1
          RX packets:99119 errors:0 dropped:0 overruns:0 frame:0
          TX packets:10016 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:236101229 (225.1 MiB)  TX bytes:881725 (861.0 KiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:336 errors:0 dropped:0 overruns:0 frame:0
          TX packets:336 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:35505 (34.6 KiB)  TX bytes:35505 (34.6 KiB)

ここで、コンテナを終了し、ホストで同じコマンドを実行する必要があります。

root@docker-1:~# ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:9fff:fede:3c3c  prefixlen 64  scopeid 0x20<link>
        ether 02:42:9f:de:3c:3c  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2  bytes 180 (180.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1460
        inet 10.128.15.232  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::4001:aff:fe80:fe8  prefixlen 64  scopeid 0x20<link>
        ether 42:01:0a:80:0f:e8  txqueuelen 1000  (Ethernet)
        RX packets 99198  bytes 236116665 (236.1 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 10074  bytes 888616 (888.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 336  bytes 35505 (35.5 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 336  bytes 35505 (35.5 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

root@docker-1:~# 

コマンドの出力はまったく同じであることがわかります。 これは、ホストIPを使用してコンテナにアクセスできる必要があることを意味します。 クラウドプロバイダーを使用している場合、ポート80で受け入れるファイアウォールルールがマシンで有効になっていることに注意してください。

インスタンスのパブリックIPでサービスにアクセスしてみましょう。
web1コンテナ内で実行されているサービスからの応答を確認できます。

コンテナの実行中にポートマッピングを提供せず、ホストノードネットワークを明示的に指定したため、ポート80でコンテナ内で実行されているApacheサーバーにアクセスできることに注意してください。

では、別のことを試してみましょう。 このホストでホストモードネットワークを使用して同じサービスの別のコンテナを実行し、何が起こるかを確認します。

docker run -it --name web2 --net=host vaibhavthakur/docker:webinstance2

注意深くみてみると、コンテナは起動すらしませんでした。 ログを見てみましょう。

root@docker-1:~# docker run -it --name web2 --net=host vaibhavthakur/docker:webinstance2
Unable to find image vaibhavthakur/docker:webinstance2' locally
webinstance2: Pulling from vaibhavthakur/docker
Image docker.io/jonlangemak/docker:webinstance2 uses outdated schema1 manifest format. Please upgrade to a schema2 image for better future compatibility. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/
a3ed95caeb02: Already exists 
3a5831f32025: Already exists 
1070eeab2b98: Already exists 
86ba7b11cc42: Already exists 
8ea0f21db18b: Already exists 
772c76531fbf: Already exists 
04120008298a: Already exists 
6a221adc032a: Already exists 
4a835ccd3c00: Already exists 
78e705e491cf: Already exists 
c343940bb393: Already exists 
663a5158a2b7: Already exists 
09cf647f3fa5: Pull complete 
Digest: sha256:62883de5c6d7786ec4267b8a0ccb9b104dea7523c9db934ecb911b78a5d983ee
Status: Downloaded newer image for vaibhavthakur/docker:webinstance2
2019-12-26 04:57:29,606 CRIT Supervisor running as root (no user in config file)
2019-12-26 04:57:29,619 INFO supervisord started with pid 1
2019-12-26 04:57:29,621 INFO spawned: 'httpd' with pid 6
2019-12-26 04:57:29,664 INFO exited: httpd (exit status 1; not expected)
2019-12-26 04:57:29,665 INFO received SIGCLD indicating a child quit
2019-12-26 04:57:30,668 INFO spawned: 'httpd' with pid 7
2019-12-26 04:57:30,707 INFO exited: httpd (exit status 1; not expected)
2019-12-26 04:57:30,707 INFO received SIGCLD indicating a child quit
2019-12-26 04:57:32,711 INFO spawned: 'httpd' with pid 8
2019-12-26 04:57:32,749 INFO exited: httpd (exit status 1; not expected)
2019-12-26 04:57:32,750 INFO received SIGCLD indicating a child quit
2019-12-26 04:57:35,755 INFO spawned: 'httpd' with pid 9
2019-12-26 04:57:35,796 INFO exited: httpd (exit status 1; not expected)
2019-12-26 04:57:35,796 INFO received SIGCLD indicating a child quit
2019-12-26 04:57:36,798 INFO gave up: httpd entered FATAL state, too many start retries too quickly

したがって、これは、エントリポイントプロセスがすぐに終了したため、コンテナが起動できなかったことを明確に示しています。 もう少し深く掘り下げて、コンテナの中に入りましょう。

root@docker-1:~# docker exec -it web2 /bin/bash
[root@docker-1 /]# service httpd status
httpd dead but subsys locked
[root@docker-1 /]# service httpd start
Starting httpd: (98)Address already in use: make_sock: could not bind to address [::]:80
(98)Address already in use: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
Unable to open logs
                                                           [FAILED]

最初にhttpdサービスのステータスを確認したところ、停止していることがわかりました。 次に、起動しようとしましたが、ポート80にバインドできなかったため、再度失敗しました。もう一度テストを実行して、ポート80のステータスを確認してみましょう。

[root@docker-1 /]# netstat -plnt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
tcp        0      0 127.0.0.53:53               0.0.0.0:*                   LISTEN      -                   
tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      -                   
tcp        0      0 :::80                       :::*                        LISTEN      -                   
tcp        0      0 :::22                       :::*                        LISTEN      -                   
[root@docker-1 /]# 

ポート80が何らかのプロセスによって占有されていることがはっきりとわかります。このプロセスは、web1という名前のコンテナです。 したがって、web2コンテナがweb1が実行されているのと同じホスト上のポート80にバインドしようとしたため、失敗したと結論付けて良いでしょう。 それでは、web1コンテナを強制終了し、web2コンテナでhttpdサービスを開始してみましょう。

web1を強制終了した後、web2でhttpdを起動し、成功しました。

[root@docker-1 /]# service httpd start
Starting httpd:                                            [  OK  ]
[root@docker-1 /]# 

最後のテストとして、ポート80でインスタンスにアクセスしましょう。

web2コンテナ内で実行されているサービスからの応答を確認できました。

まとめ

ネットワークトラフィックはdocker0ブリッジとiptablesポートマッピングを通過する必要がないため、ホストモードネットワークはコンテナのネットワークパフォーマンスを向上させることを理解することが重要です。 この記事で、ホストモードネットワークとは何か、および他のネットワークオプションとの違いについて明確に説明してきました。 ご不明な点がございましたら、お気軽にお問い合わせください。

コンテナの監視を検討している場合は、MetricFireを介してHosted Prometheusで監視できます。 こちらから無料トライアルにサインアップするか、デモを予約して直接お問い合わせください。 詳細については、Dockerネットワークに関する3部構成のシリーズをご覧ください。このシリーズでは、さまざまな種類のDockerネットワークについて詳しく説明しています。