(自分用の備忘録)
2021/04/21: 追記
本問題は2021/04/12にリリースされたDocker version 20.10.6で修正された模様.
やっぱ仕様ではなくバグだった.
ワホーイ
Before docker 20.10, published ports were accessible through both IPv4 and IPv6 by default, but the API only included information about the IPv4 (0.0.0.0)
ということでDocker version 20.10.6に上げると解決します.
やりたいこと
デュアルスタックのDockerホスト上で、Dockerプロキシを利用してIPv4シングルスタックのDockerコンテナでIPv6のサービスをしたい。
共通環境
Ubuntu 20.04
# cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.1 LTS"
# uname -a
Linux server 5.8.0-36-generic #40~20.04.1-Ubuntu SMP Wed Jan 6 10:15:55 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
期待している挙動
以下のようなdocker-composeで適当なDockerコンテナを起動し、ports
でポートフォワーディングを実施する。
version: '3'
services:
grafana:
image: grafana/grafana
ports:
- 3000:3000
コンテナ起動後、該当のポートフォワーディングを行うdocker-proxyプロセスが同時に立ち上がる。
root 120970 0.0 0.0 475320 3912 ? Sl 18:27 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 3000 -container-ip 172.18.0.4 -container-port 3000
- IPv4のトラフィック
iptablesに以下のようなDNATルールが登場するため、docker-proxyプロセスに達する前に処理される。
# iptables -t nat -L DOCKER
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- anywhere anywhere
RETURN all -- anywhere anywhere
DNAT tcp -- anywhere anywhere tcp dpt:3000 to:172.18.0.3:3000
ちなみに返りのパケットはPOSTROUTINGでIPマスカレードされる
# iptables -t nat -L POSTROUTING
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 anywhere
MASQUERADE all -- 172.18.0.0/16 anywhere
MASQUERADE tcp -- 172.18.0.3 172.18.0.3 tcp dpt:3000
MASQUERADE tcp -- 172.18.0.4 172.18.0.4 tcp dpt:8888
- IPv6のトラフィック
IPv6の場合IPv4とは異なりDNATのルールが挿入されず、上述docker-proxyプロセスがL4〜レベルで変換を実施する。
実際の挙動
これまで
確認したバージョン: Docker version 19.03.14, build 5eb3275d40
Type
がIPv6
になっており、IPv4/IPv6の両方でLISTENすることがわかる。
参考:
netstatやlsofでLISTENしているアドレスポートが IPv6と表示されてもIPv4でアクセスできる罠
# lsof -i:3000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
docker-pr 120970 root 4u IPv6 1065660 0t0 TCP *:3000 (LISTEN)
最近(20.10.6)まで
確認したバージョン: Docker version 20.10.2, build 2291f61
Type
がIPv4
になっており、IPv4でしかLISTENしていない。
そのためIPv6でサービス提供が行えていないない
# lsof -i:3000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
docker-pr 14845 root 4u IPv4 6231154 0t0 TCP *:3000 (LISTEN)
対処方法
Docker versionを20.10.6以降にすると以前(version 19)までの挙動に戻る。
公式ドキュメントを参照して指定したバージョンのdocker-ce, docker-ce-cliをinstallする。
- installできるdocker-ceのバージョンを確認
# apt-cache madison docker-ce | head -n 4
docker-ce | 5:20.10.6~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
docker-ce | 5:20.10.5~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
docker-ce | 5:20.10.4~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
docker-ce | 5:20.10.3~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
- 現在のdocker-ceをuninstall
# apt-get remove docker-ce docker-ce-cli
- 指定したバージョンののdocker-ce, docker-ce-cliをinstall
# apt-get install docker-ce=<VERSION_STRING> docker-ce-cli=<VERSION_STRING> containerd.io
所感と今後
このような仕様変更が行われた経緯に関してはま調べられていないが、めっちゃ困る
追記: version 20.10.6で修正されました.
Docker Engine release note