DockerコンテナのIPv6設定:ステップバイステップDual-stackガイド

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

ある朝、新しいサーバーにデプロイしていたとき、ホスティングがVPSに立派なIPv6アドレスを割り当てていたことに気づいた——それなのにDockerコンテナはすべてIPv4のみで動いていた。もったいない。約2時間かけてドキュメントを読んで試行錯誤した末、ホストにIPv6があってもDockerが自動的にIPv6を使わない理由がやっとわかった。

VPSにIPv6が設定済みなのにコンテナがIPv4でしか通信できない場合、この記事がその問題を解決する——daemonの有効化からDocker Composeでの完全なdual-stackセットアップまで。

Quick Start:DockerでIPv6を5分で有効にする

手早く済ませたい場合は、最小限の3ステップで完了する:

ステップ1:daemon.jsonファイルを編集する

ファイル /etc/docker/daemon.json を開く(または新規作成する):

sudo nano /etc/docker/daemon.json

ファイルに貼り付ける(すでに内容がある場合はマージする):

{
  "ipv6": true,
  "fixed-cidr-v6": "fd00::/80"
}

ステップ2:Dockerを再起動する

sudo systemctl restart docker

ステップ3:確認する

docker network inspect bridge | grep -A 5 "EnableIPv6"

"EnableIPv6": true が表示されればDockerが設定を反映している。それ以降にデフォルトのbridgeネットワークで作成したコンテナには、自動的にIPv6アドレスが割り当てられる。

より深く理解する:DockerはIPv6をどう処理するか?

実際のプロジェクトでDocker Composeを初めて使ったとき、かなりの凡ミスを犯した。その中で最も典型的だったのが、IPv6を有効にしたのにコンテナが接続できない理由をずっと悩み続けたことだ。理由は、Dockerには複数のネットワークタイプがあり、それぞれ個別に設定が必要だからだ。

Dockerのネットワークタイプ

  • bridge(デフォルト):同じホスト上のコンテナがNATを介して通信する
  • host:コンテナがホストのネットワークを使用し、ホストにIPv6があれば動作する
  • overlay:Docker Swarmで使用し、個別の設定が必要
  • macvlan:コンテナが物理ネットワーク上で独自のMAC/IPアドレスを持つ

daemon.jsonでipv6: trueを設定しても、影響を受けるのはデフォルトのbridgeネットワークだけだ。Docker Composeで作成するカスタムネットワークはすべて——ここを見落とす人が多い——個別にIPv6を宣言しなければならない。

使用するIPv6アドレス範囲

内部環境(外部へのルーティングが不要な場合)では、ULA(Unique Local Address)範囲を使用する——IPv4の192.168.x.xに相当するものだ:

fd00::/8   ← プライベート、内部利用で問題なし

一般的な例:

  • fd00::/80 — デフォルトbridgeネットワーク用
  • fd00:db8:1::/64fd00:db8:2::/64 — カスタムネットワーク用

/64はRFC準拠のIPv6サブネット標準だ。/80でも動作するが、一部のネットワークツールやルーターは/64以外のプレフィックスを誤って処理することがある。本番環境では/64を使っておくほうが確実だ。

Dual-stack:IPv4とIPv6を同時に運用する

Dual-stackとはコンテナに両方を持たせることだ:旧システムとの互換性のためのIPv4と、将来に向けたIPv6。どちらかを選ぶ必要はない——そして、これがデフォルトで使うべき設定でもある。

CLIでdual-stackカスタムネットワークを作成する

docker network create \
  --driver bridge \
  --subnet 172.20.0.0/24 \
  --gateway 172.20.0.1 \
  --ipv6 \
  --subnet fd00:db8::/64 \
  --gateway fd00:db8::1 \
  myapp-network

結果を確認する:

docker network inspect myapp-network | grep -A 20 '"IPAM"'

出力にはIPv4とIPv6の両方の設定ブロックが含まれる:

"IPAM": {
    "Driver": "default",
    "Config": [
        {
            "Subnet": "172.20.0.0/24",
            "Gateway": "172.20.0.1"
        },
        {
            "Subnet": "fd00:db8::/64",
            "Gateway": "fd00:db8::1"
        }
    ]
}

コンテナのIPv6アドレスを確認する

docker run -d --name web --network myapp-network nginx:alpine

# 方法1:inspect
docker inspect web | grep -A 5 "GlobalIPv6"

# 方法2:コンテナ内で確認する
docker exec web ip -6 addr show

Docker ComposeでIPv6を設定する

実際のところ、docker network createコマンドを手動で実行することはほとんどない。Docker Composeでネットワークを管理するほうがずっとすっきりする:

services:
  web:
    image: nginx:alpine
    networks:
      - frontend

  api:
    image: node:18-alpine
    networks:
      - frontend
      - backend

  db:
    image: postgres:15
    networks:
      - backend

networks:
  frontend:
    driver: bridge
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: 172.20.0.0/24
          gateway: 172.20.0.1
        - subnet: fd00:db8:1::/64
          gateway: fd00:db8:1::1

  backend:
    driver: bridge
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: 172.21.0.0/24
          gateway: 172.21.0.1
        - subnet: fd00:db8:2::/64
          gateway: fd00:db8:2::1

スタックを起動してサービス間のpingを確認する:

docker compose up -d
docker compose exec web ping6 api

応用:コンテナをIPv6で外部公開する

外部クライアントからIPv6でコンテナに接続したい場合、要件に応じて2つのアプローチがある。

方法1:ポートマッピング(最もシンプル)

docker-compose.ymlで:

services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"       # IPv4
      - "[::]:80:80"  # IPv6 explicit

ホストでIPv6 forwardingが有効になっており、daemon.jsonにipv6: trueが設定されている場合、80:80は通常IPv4とIPv6の両方に自動的にマッピングされる——2行目を個別に宣言する必要はない。

方法2:NDP Proxy(パブリックIPv6を持つVPS向け)

ホスティングからパブリックIPv6範囲(例:2001:db8::/48)が提供されているVPSなら、NDP proxyを使ってコンテナにパブリックIPv6を割り当てられる——NATを経由せずに。

ホストでNDP proxyを有効にする:

sudo sysctl net.ipv6.conf.all.proxy_ndp=1

コンテナにproxy entryを追加する:

sudo ip -6 neigh add proxy 2001:db8::2 dev eth0

この方法はより複雑だが、コンテナが真のパブリックIPv6を必要とする場合に明確なメリットがある。NATなし、ポートマッピングなし——トラフィックがコンテナに直接届く。パブリックIPv6範囲を持つベアメタルや専用サーバーに最適だ。

実践的なTips

1. コンテナ内からIPv6接続を確認する

docker exec -it mycontainer ping6 google.com
docker exec -it mycontainer curl -6 https://ipv6.google.com

2. サブネットの重複を避ける

各ネットワークは固有のサブネットを持たなければならない——IPv4とIPv6の両方で。重複するとDockerはエラーを返すが、メッセージが読みにくいことがある。使用中のすべてのサブネットを素早く確認するには:

docker network ls -q | xargs docker network inspect | grep '"Subnet"'

3. 再起動後もIPv6 forwardingを維持する

再起動後も設定が消えないよう/etc/sysctl.confに追加する:

net.ipv6.conf.all.forwarding=1

再起動せずに即座に適用する:

sudo sysctl -p

4. コンテナ内のNginxはIPv6を個別にlistenする必要がある

コンテナでNginxを動かす場合、serverブロックに次の行を追加する:

server {
    listen 80;
    listen [::]:80;  # IPv6のためにこの行を追加する

    server_name example.com;
}

5. tcpdumpでトラフィックをデバッグする

sudo tcpdump -i docker0 ip6

IPv6トラフィックが正しく流れていないと思ったときにこのコマンドを実行する——インターフェースにIPv6パケットが流れているかどうかがすぐにわかる。

まとめ

DockerにIPv6を設定する際に覚えておくべき3つのレイヤーがある:daemon、network、そしてcontainerだ。各レイヤーは独立している——daemonで有効にしても、カスタムネットワークが自動的にIPv6を持つわけではない。ほとんどのプロジェクトは、daemon.jsonにipv6: trueを設定し、Composeファイルにenable_ipv6: trueを追加するだけで完了する。

私はすべてのプロジェクトで最初からdual-stackをセットアップするようにしている——設定を数行追加するだけで、ホスティングがIPv4ポリシーを変更した際や、コンテナ間のネットワークデバッグが必要な場面で後々悩まずに済む。

Share: