DockerでのIPv6サポートはまだ進行中のようでデフォルトでは有効になっていません。
有効にするにもいくつかの段階があるようなので順番に見ていきましょう。
0. コンテナ内はIPv4のままでIPv6アドレスにマップする
一番簡単な方法ですが、コンテナ自体はIPv4のみで構成されたままにしておいて、ポートマップする際にホストが持つIPv6アドレスにマップしてしまう方法です。内部的にはIPv4ですが、外部からのはIPv6でアクセスできます。
services:
sample:
ports:
- [2001:db8::2]:443:443
1. デフォルトネットワークでIPv6を有効にする
まずはdocker0ネットワークにてIPv6を有効にする方法です。
これは/etc/docker/daemon.json に設定を行います。
{
"ipv6": true,
"fixed-cidr-v6": "2001:db8::/64"
}
設定後dockerdを再起動するとdocker0ネットワークにIPv6アドレスが割り当てられます。
※IPv6アドレスはドキュメント用のものなので実際にはユニークローカルや手持ちの未割当のものなどから適当なプレフィクスを割り当ててください。
2. composeでIPv6を利用する
先の方法でdocker0にてIPv6が使えるようになるのですが、composeを利用した時に生成されるものなど他のdocker networkでは相変わらずIPv4しか利用できません。従って明示的にdocker0(ネットワーク名はbridge)を指定しないといけないケースがあります。
docker compose を利用する場合には以下のように指定します。
networks:
bridge:
external: true
services:
sample:
networks:
bridge:
まず外部ネットワークとしてbridge(docker0)を利用可能にし、コンテナのネットワークとしてbridgeを利用します。
3. IPv6利用可能なネットワークを生成する
筆者のように基本composeを使っているようなケースでは別にdocker0がIPv6対応していなくてもかまわないかもしれません。
そういう場合には、1の方法(daemon.json)ではなく予めネットワークを一つ作っておくことも可能です。
docker network create --ipv6 --subnet="2001:db8::/64" bridge6
これでIPv6が利用可能なbridge6というネットワークが生成されます。compose.yamlではbridge6を使用すればよいのでdaemon.jsonへの追記(=docker0への設定変更)は不要になります。
ここまででIPv6が使えるようになったように思えるのですが、まだ少し足りません。
コンテナネットワークはIPv6が利用可能なのですが、外部との接続(=ポートマップ)はdocker-proxyを介して行われます。IPv4だとdocker-proxyは補助的なもので通常はiptablesでNATが組まれると思います。
この違いは
- ホストのプロセスを介して中継される=効率が若干悪い?
- コンテナプロセスから見たアクセス元がコンテナネットワークのホスト側ゲートウェイになる
効率はともかくとして、アクセスログを残したい場合などには困ったものですね。
0の方法も同様ですが、NAT64を経由するのでゲートウェイのIPv4アドレスからのアクセスとなります。どのみちアクセス元が不明なのであれば0の方法が簡単ですね。
4. ip6tablesを利用可能にする
IPv4同様にIPv6でもiptablesを使う方法はないのか?と思ったらちゃんと用意されていました。
{
"experimental": true,
"ip6tables": true
}
daemon.jsonにip6tablesという設定項目がありました。ただ、これはまだ未完成のものらしくexperimentalを有効にしないと使えません。
この設定を加えておくことで1や3で設定したネットワークはNATを介して外部と接続可能になります。ただ、この機能はまだexperimental扱いなのでご自身の環境にて問題がないか確認の上で利用してください。
一つ見つけた問題はこの設定を有効にするとネットワークインタフェースのブリッジがおかしくなります。筆者はNanoPiの3つあるポートをブリッジで束ねてスイッチングハブ代わりに使っているのですが、この設定を有効にするとIPv6は全く通してくれなくなり、IPv4も遅延が発生しています。ブリッジ自体が消されているわけでもないので何かの設定が干渉しているようです。問題が発生している機材を起点とする通信に異常はなく、あくまでもブリッジを通過するトラフィックだけに問題が発生しているようです。
ブリッジを有効にした機材でdockerを使うケースはまれだと思いますが同様の使い方をされている場合にはご注意ください。
追記) どうもパケットフィルター回りがブリッジに影響しているようで以下の設定で異常は解消するっぽいです。いずれにせよ開発途上ということで。
$ sysctl -w net.bridge.bridge-nf-call-ip6tables=0
2022.10.18追記) 筆者環境だけかもしれませんが、/etc/sysctl.d/ にて net.bridge.bridge-nf-call-ip6tables=0 に設定してもどこかで 1に戻されてしまいます。この場合、以下のようにブリッジ経由の通信を中継するようにしておけば解決します。
iptables -A FORWARD -m physdev --physdev-is-bridged -j ACCEPT
ip6tables -A FORWARD -m physdev --physdev-is-bridged -j ACCEPT
ただ、dockerでip6tablesを有効にしていない(=デフォルト状態)場合にはnet.bridge.bridge-nf-call-ip6tables=1, フォワードの設定なしでも特にブリッジパケットがブロックされないのですよね。。。