ホスト上にコンテナのポートを割り当て

このセクションでは、 Docker のデフォルト・ブリッジ内にあるコンテナに対して、ポートを割り当てる方法を説明します。このネットワークは bridge という名称の bridge ネットワークであり、Docker インストール時に自動的に作成されるものです。

注釈

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

デフォルトの Docker コンテナは外の世界と通信できます。しかし、外の世界からはコンテナに接続できません。外に対するそれぞれの接続は、ホストマシン自身が持つ IP アドレスから行われているように見えます。これはホストマシン上の iptables マスカレーディング機能によるルールであり、Docker サーバ起動時に自動的に作成されます。

$ sudo iptables -t nat -L -n
...
Chain POSTROUTING (policy ACCEPT)target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
...

Docker サーバが作成するマスカレード・ルールは、外の世界からコンテナに IP アドレスで接続できるようにします。

もしもコンテナが内側への(incoming)接続を受け付けたいのなら、 dockerrun の実行時に特別なオプション指定が必要です。

まずは、 dockerrun-P--publish-all=true|false を指定します。これは全体的なオプションであり、イメージの Dockerfile 内にある EXPOSE 命令で各ポートを指定するか、あるいは、コマンドラインで --expose<ポート> フラグを使って指定します。いずれかを使い、ホスト上の エフェメラル・ポート範囲内(ephemeral port range) にあるポートに割り当てられます。以後、どのポートに割り当てられているか調べるには dockerport コマンドを使います。エフェメラル・ポート範囲とは、カーネル・パラメータの /proc/sys/net/ipv4/ip_local_port_range で指定されており、典型的な範囲は 32768 ~ 61000 です。

割り当て(マッピング)を明示するには、 -p指定--publish=指定 オプションを使います。これは、Docker サーバのどのポートを使うか明示するものであり、ポートの指定が無ければ、エフェメラル・ポート範囲から割り当てられます。この指定を使い、コンテナの任意のポートに割り当て可能です。

どちらにしろ、Docker がネットワーク・スタックでどのような処理を行っているのかは、NAT テーブルを例に垣間見えます。

# Docker で -P 転送を指定した時、NAT ルールがどうなるか$ iptables -t nat -L -n
...
Chain DOCKER (2 references)target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:49153 to:172.17.0.2:80# Docker で -p 80:80 を指定した時、NAT ルールがどうなるかChain DOCKER (2 references)target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80

Docker はこれらコンテナ側のポートを 0.0.0.0 、つまりワイルドカード IP アドレスで公開しているのが分かります。これはホストマシン上に到達可能な全ての IP アドレスが対象です。制限を加えたい場合や、ホストマシン上の特定の外部インターフェースを通してのみコンテナのサービスへ接続したい場合は、2つの選択肢があります。

1つは dockerrun の実行時、 -pIP:ホスト側ポート:コンテナ側ポート-pIP::ポート を指定し、特定の外部インターフェースをバインドする指定ができます。

あるいは、Docker のポート転送で常に特定の IP アドレスに割り当てたい場合には、システム全体の Docker サーバ設定ファイルを編集し、 --ip=IP_ADDRESS オプションを使います。編集内容が有効になるのは、Docker サーバの再起動後なのでご注意ください。

注釈

ヘアピン NAT を有効にすると( --userland-proxy=false )、コンテナのポート公開は、純粋な iptables のルールを通して処理され、特定のポートを結び付けようとする処理は、一切ありません。つまり、コンテナが使おうとしているポートを、Docker の他の何かのサービスが使おうとしても阻止できません。このような衝突があれば、Docker はコンテナに対する経路を優先するよう、iptabels のルールを書き換えます。

--userland-proxy パラメータは、デフォルトでは true (有効)であり、コンテナ内部とコンテナ外からの通信を可能にするユーザ領域を提供します。無効化しますと、Docker はどちらの通信に対しても MASQUERADE iptables ルールを追加し、 net.ipv4.route_localnet カーネル・パラメータを使い、ホストマシンがローカルのコンテナが公開しているポートに対し、通常はループバック・アドレスを用いて通信します。どちらを選ぶかは、性能を根拠として選びます。