コンテナ通信の理解

このセクションでは、Docker デフォルトのブリッジ・ネットワーク内部におけるコンテナ通信について説明します。このネットワークは bridge という名称の bridge ネットワークであり、Docker インストール時に自動的に作成されます。

注釈

Docker ネットワーク機能 を使えば、デフォルトのブリッジ・ネットワークに加え、自分で定義したネットワークも作成できます。

外の世界との通信

コンテナが世界と通信できるかどうかは、2つの要素が左右します。1つめの要素は、ホストマシンが IP パケットを転送できるかどうかです。2つめはホスト側の iptables が特定の接続を許可するかどうかです。

IP パケット転送(ip packet forwarding)は、 ip_forward システム・パラメータで管理します。このパラメータが 1 の時のみ、パケットは通信できます。通常、Docker サーバはデフォルトの設定のままでも --ip-forward=true であり、Docker はサーバの起動時に ip_forward1 にします。もし --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=falseiptables=true のオプションを指定したら、 docker run--link= オプションの情報を参照し、他のコンテナが新しいコンテナの公開用ポートに接続できるよう iptablesACCEPT ルールのペアを追加します。この公開用ポートとは、 DockerfileEXPOSE 行で指定していたものです。

注釈

--link= で指定する コンテナ名 の値は、Docker が自動的に割り当てる stupefied_pare のような名前ではなく、 docker run の実行時に --name= で名前を割り当てておく必要があります。ホスト名でなければ、Docker は --link= オプションの内容を理解できません。

Docker ホスト上で iptables コマンドを実行したら、 FORWARD チェーンの場所で、デフォルトのポリシーが ACCEPTDROP かを確認できます。

# もし--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 アドレスからの接続に見えるでしょう。