ホスト上にコンテナのポートを割り当て¶
このセクションでは、 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)接続を受け付けたいのなら、 docker run
の実行時に特別なオプション指定が必要です。
まずは、 docker run
で -P
か --publish-all=true|false
を指定します。これは全体的なオプションであり、イメージの Dockerfile
内にある EXPOSE
命令で各ポートを指定するか、あるいは、コマンドラインで --expose <ポート>
フラグを使って指定します。いずれかを使い、ホスト上の エフェメラル・ポート範囲内(ephemeral port range) にあるポートに割り当てられます。以後、どのポートに割り当てられているか調べるには docker port
コマンドを使います。エフェメラル・ポート範囲とは、カーネル・パラメータの /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つは docker run
の実行時、 -p IP:ホスト側ポート:コンテナ側ポート
か -p IP::ポート
を指定し、特定の外部インターフェースをバインドする指定ができます。
あるいは、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
カーネル・パラメータを使い、ホストマシンがローカルのコンテナが公開しているポートに対し、通常はループバック・アドレスを用いて通信します。どちらを選ぶかは、性能を根拠として選びます。
関連情報¶
参考
- Bind container ports to the host
- https://docs.docker.com/engine/userguide/networking/default_network/binding/