コンテナ通信の理解¶
このセクションでは、Docker デフォルトのブリッジ・ネットワーク内部におけるコンテナ通信について説明します。このネットワークは bridge
という名称の bridge
ネットワークであり、Docker インストール時に自動的に作成されます。
注釈
Docker ネットワーク機能 を使えば、デフォルトのブリッジ・ネットワークに加え、自分で定義したネットワークも作成できます。
外の世界との通信¶
コンテナが世界と通信できるかどうかは、2つの要素が左右します。1つめの要素は、ホストマシンが IP パケットを転送できるかどうかです。2つめはホスト側の iptables
が特定の接続を許可するかどうかです。
IP パケット転送(ip packet forwarding)は、 ip_forward
システム・パラメータで管理します。このパラメータが 1
の時のみ、パケットは通信できます。通常、Docker サーバはデフォルトの設定のままでも --ip-forward=true
であり、Docker はサーバの起動時に ip_forward
を 1
にします。もし --ip-forward=false
をセットし、システム・カーネルが有効な場合は、この --ip-forward=false
オプションは無効です。カーネル設定の確認は、手動で行います。
$ sysctl net.ipv4.conf.all.forwarding
net.ipv4.conf.all.forwarding = 0
$ sysctl net.ipv4.conf.all.forwarding=1
$ sysctl net.ipv4.conf.all.forwarding
net.ipv4.conf.all.forwarding = 1
Docker を使う多くの環境で ip_forward
の有効化が必要となるでしょう。コンテナと世界が通信できるようにするには、この設定が最低限必要だからです。また、複数のブリッジをセットアップする場合は、コンテナ間での通信にも必要となります。
デーモン起動時に --iptables=false
を設定したら、Docker はシステム上の iptables
ルールセットを一切変更しません。そうでなければ、Docker サーバは DOCKER
フィルタ・チェーンの転送ルールを追加します。
Docker は DOCKER
フィルタ・チェーンのために、既存のルールを削除・変更しません。そのため、ユーザが必要であれば、コンテナに対して更なるアクセス制限するといった、高度なルールも作成できます。
Docker のデフォルト転送ルールは、全ての外部ソースの IP アドレス対して許可しています。コンテナを特定の IP アドレスやネットワークに対してのみ接続したい場合には、 DOCKER
フィルタ・チェーンの一番上にネガティブ・ルールを追加します。例えば、コンテナが外部の IP アドレス 8.8.8.8 をソースとするもの しか 許可しない場合には、次のようなルールを追加します。
$ iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP
ext_if の場所は、インターフェースが提供するホスト側に接続できる名前です。
コンテナ間の通信¶
2つのコンテナが通信できるかどうかは、オペレーティング・システム・レベルでの2つの要素に左右されます。
- コンテナのネットワーク・インターフェースがネットワーク・トポロジに接続されていますか? デフォルトの Docker は、全てのコンテナを
docker0
ブリッジに接続するため、コンテナ間でのパケット通信が可能な経路を提供します。他の利用可能なトポロジに関するドキュメントについては、後述します。
iptables
は特定の接続を許可していますか? Docker はデーモンの起動時に--iptables=false
を設定したら、システム上のiptables
に対する変更を一切行いません。そのかわり、Docker サーバはFORWARD
チェーンにデフォルトのルールを追加する時、デフォルトの--icc=true
であれば空のACCEPT
ポリシーを追加します。もし--icc=false
であればDROP
ポリシーを設定します。
--icc=true
のままにしておくか、あるいは --icc=false
にすべきかという方針の検討には、 iptables
を他のコンテナやメインのホストから守るかどうかです。例えば、恣意的なポート探査やコンテナに対するアクセスは、問題を引き起こすかもしれません。
もし、より高い安全のために --icc=false
を選択した場合は、コンテナが他のサービスと相互に通信するには、どのような設定が必要でしょうか。この答えが、 --link=コンテナ名_または_ID:エイリアス
オプションです。これについては以前のセクションで、サービス名について言及しました。もし Docker デーモンが --icc=false
と iptables=true
のオプションを指定したら、 docker run
は --link=
オプションの情報を参照し、他のコンテナが新しいコンテナの公開用ポートに接続できるよう iptables
の ACCEPT
ルールのペアを追加します。この公開用ポートとは、 Dockerfile
の EXPOSE
行で指定していたものです。
注釈
--link=
で指定する コンテナ名
の値は、Docker が自動的に割り当てる stupefied_pare
のような名前ではなく、 docker run
の実行時に --name=
で名前を割り当てておく必要があります。ホスト名でなければ、Docker は --link=
オプションの内容を理解できません。
Docker ホスト上で iptables
コマンドを実行したら、 FORWARD
チェーンの場所で、デフォルトのポリシーが ACCEPT
か DROP
かを確認できます。
# もし--icc=false なら DROP ルールはどのようになるでしょうか:
$ sudo iptables -L -n
...
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
DROP all -- 0.0.0.0/0 0.0.0.0/0
...
# --icc=false の下で --link= を指定したら、
# 特定のポートに対する ACCEPT ルールを優先し
# その他のパケットを DROP するポリシーを適用します。
$ sudo iptables -L -n
...
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
DROP all -- 0.0.0.0/0 0.0.0.0/0
Chain DOCKER (1 references)
target prot opt source destination
ACCEPT tcp -- 172.17.0.2 172.17.0.3 tcp spt:80
ACCEPT tcp -- 172.17.0.3 172.17.0.2 tcp dpt:80
注釈
ホストを広範囲にわたって公開する iptables
のルールは、各コンテナが持つ実際の IP アドレスを通して公開されますのでご注意ください。そのため、あるコンテナから別のコンテナに対する接続は、前者のコンテナ自身が持っている IP アドレスからの接続に見えるでしょう。
参考
- Understand container communication
- https://docs.docker.com/engine/userguide/networking/default_network/container-communication/