2022年5月、IPv4アドレスが枯渇してきていることもあって、IPv6 onlyな環境がVPSの一般サービスとして出てきました。 OSレベルではIPv4/IPv6のデュアルスタックで両方のネットワーク環境にサーバもクライアントも対応しつつあるのですが、 世の中にはまだAAAAレコードを持たないHTTP/HTTPSサーバも結構あって、DockerをIPv6 only環境で動かすのに一苦労しました。
今回、私自身が Rocky Linux 8.5 の IPv6 only 環境で Docker / Alpine Linux を動かした記録をメモで残しておきます。
Dockerのインストール
まず最初にdnfコマンドを使ってDockerをインストールします。
sudo dnf update -y sudo dnf install -y dnf-utils device-mapper-persistent-data lvm2 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf makecache sudo dnf install -y docker-ce sudo systemctl start docker sudo systemctl enable docker
(最近のCentOS系Linuxではyumは推奨されていないので、dnfコマンドを使用しました。)
Dockerを動かすユーザの権限設定
root以外でdockerを動かす場合、自分自身のユーザ($USER)をdockerグループに所属させておきます。
sudo groupadd docker # groupadd: group 'docker' already exists sudo usermod -aG docker $USER
IPv6対応のdaemon.jsonを作成
dockerデーモンの起動オプション/etc/docker/daemon.jsonにIPv6対応の設定を追加します。
sudo vi /etc/docker/daemon.json
作成するファイルの中身は以下の通りです。
{
"ipv6": true,
"fixed-cidr-v6": "fd11:2233:4455:6677::/64",
"registry-mirrors": ["https://mirror.gcr.io"]
}
Dockerのデフォルトのレジストリregistry-1.docker.ioがIPv4のアドレスしか返さないのでIPv6 only環境だと通信できません。この場合、IPv6に対応しているGoogleのレジストリミラー https://mirror.gcr.io を設定しておきます。
fixed-cidr-v6で設定しているfd11:2233:4455:6677::/64は、自分で設定するユニークローカルアドレス (ULA: unique local address) で、IPv6のプライベートアドレスに相当するものです。自分で使う場合は、fdから始まるアドレスで任意のアドレスを指定してください。
dockerの再起動
これらの設定を有効にするにはdockerを再起動します。
sudo systemctl restart docker
もしもエラーが出た場合はdaemon.jsonの設定ファイルを見直して修正します。
ip6tablesでNATを追加
daemon.jsonのfixed-cidr-v6で設定した内部ネットワークfd11:2233:4455:6677::/64から外に通信ができるようにNATを設定します。
sudo ip6tables -t nat -A POSTROUTING -s fd11:2233:4455:6677::/64 ! -o docker0 -j MASQUERADE
引数に-t natを指定してNATテーブルが正しく追加されているかどうか確認します。
sudo ip6tables -t nat -L
以下の出力にMASQUERADE all fd11:2233:4455:6677::/64 anywhereの行があれば大丈夫です。
Chain PREROUTING (policy ACCEPT) target prot opt source destination Chain INPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all fd11:2233:4455:6677::/64 anywhere Chain OUTPUT (policy ACCEPT) target prot opt source destination
再起動時のrc.localの設定
再起動してもNATの設定が有効となるように、rc.localの設定をします。
sudo vi /etc/rc.d/rc.local
rc.localは過去の互換性のために残されているファイルですが、最後に以下の一行を追加します。
#!/bin/bash # THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES # # It is highly advisable to create own systemd services or udev rules # to run scripts during boot instead of using this file. # # In contrast to previous versions due to parallel execution during boot # this script will NOT be run after all other services. # # Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure # that this script will be executed during boot. touch /var/lock/subsys/local ip6tables -t nat -A POSTROUTING -s fd11:2233:4455:6677::/64 ! -o docker0 -j MASQUERADE
rc.localの設定が初めてであれば、以下のコマンドを実行して再起動時に実行されるようにします。
sudo chmod +x /etc/rc.d/rc.local sudo systemctl start rc-local
docker infoで現在の設定を確認
docker infoを実行して現在の設定を確認します。
docker info
こんな感じの出力がされるはずです。
Client: Context: default Debug Mode: false Plugins: app: Docker App (Docker Inc., v0.9.1-beta3) buildx: Docker Buildx (Docker Inc., v0.8.1-docker) scan: Docker Scan (Docker Inc., v0.17.0) Server: ~中略~ Docker Root Dir: /var/lib/docker Debug Mode: false Registry: https://index.docker.io/v1/ Labels: Experimental: false Insecure Registries: 127.0.0.0/8 Registry Mirrors: https://mirror.gcr.io/ Live Restore Enabled: false
Registry Mirrors:にhttps://mirror.gcr.io/があればokです。
Alpine Linux の Dockerfile を作成
適当なディレクトリでDockerfileを作成します。
vi Dockerfile
Dockerfileの中身は以下の通りです。
FROM alpine:3.15.4 RUN apk update && apk add bind-tools curl CMD ["/bin/sh"]
alpine:3.15.4のイメージをpullした後、digコマンドとcurlコマンドが使いたいのでbind-toolsとcurlのapkパッケージをインストールして、デフォルトでシェルを起動しています。
docker build
タグ名は何でも良いのですが、ipv6/alpineでdocker buildします。
docker build -t ipv6/alpine .
ビルド時に以下のような感じでIPv6のサーバからapkのパッケージがダウンロードできれば成功です。
Sending build context to Docker daemon 3.072kB Step 1/3 : FROM alpine:3.15.4 3.15.4: Pulling from library/alpine df9b9388f04a: Pull complete Digest: sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454 Status: Downloaded newer image for alpine:3.15.4 ---> 0ac33e5f5afa Step 2/3 : RUN apk update && apk add bind-tools curl ---> Running in a1ebb762c17e fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/main/x86_64/APKINDEX.tar.gz fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/community/x86_64/APKINDEX.tar.gz v3.15.4-78-ge239b26b75 [https://dl-cdn.alpinelinux.org/alpine/v3.15/main] v3.15.4-79-gaf675e239c [https://dl-cdn.alpinelinux.org/alpine/v3.15/community] OK: 15855 distinct packages available (1/18) Installing fstrm (0.6.1-r0) (2/18) Installing krb5-conf (1.0-r2) (3/18) Installing libcom_err (1.46.4-r0) (4/18) Installing keyutils-libs (1.6.3-r0) (5/18) Installing libverto (0.3.2-r0) (6/18) Installing krb5-libs (1.19.3-r0) (7/18) Installing json-c (0.15-r1) (8/18) Installing protobuf-c (1.4.0-r0) (9/18) Installing libuv (1.42.0-r0) (10/18) Installing xz-libs (5.2.5-r1) (11/18) Installing libxml2 (2.9.13-r0) (12/18) Installing bind-libs (9.16.27-r0) (13/18) Installing bind-tools (9.16.27-r0) (14/18) Installing ca-certificates (20211220-r0) (15/18) Installing brotli-libs (1.0.9-r5) (16/18) Installing nghttp2-libs (1.46.0-r0) (17/18) Installing libcurl (7.80.0-r1) (18/18) Installing curl (7.80.0-r1) Executing busybox-1.34.1-r5.trigger Executing ca-certificates-20211220-r0.trigger OK: 15 MiB in 32 packages Removing intermediate container a1ebb762c17e ---> 18d7e9a3b51b Step 3/3 : CMD ["/bin/sh"] ---> Running in ccda19d240b3 Removing intermediate container ccda19d240b3 ---> ff8bbe4b67c8 Successfully built ff8bbe4b67c8 Successfully tagged ipv6/alpine:latest
エラーが出なければ成功です。おめでとうございます。
※ registry-1.docker.io でエラーが出る場合
もしもdocker build時に以下のエラーが出る場合は、registry-1.docker.io がIPv6のAAAAレコードを持っていないため、IPv4アドレスで通信しようとして失敗しています。
Sending build context to Docker daemon 4.096kB Step 1/3 : FROM alpine:3.15.4 Get "https://registry-1.docker.io/v2/": dial tcp 34.203.135.183:443: connect: network is unreachable
この場合は、前述のdaemon.jsonで"registry-mirrors": ["https://mirror.gcr.io"]を指定して、IPv6で通信が可能なミラーサーバを指定する必要があります。 一度、失敗するとdocker内で変にダウンロードURLがキャッシュされてしまうようで、設定を変更したら、FROM alpine:3.15.4をFROM alpine:3.15.2のように別のバージョンをダウンロードして試してみてください。
※ dl-cdn.alpinelinux.org でエラーが出る場合
docker build時に以下のエラーが出る場合は、docker内からIPv6 only環境での外への通信ができないで失敗しています。
fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/main/x86_64/APKINDEX.tar.gz ERROR: https://dl-cdn.alpinelinux.org/alpine/v3.15/main: temporary error (try again later) WARNING: Ignoring https://dl-cdn.alpinelinux.org/alpine/v3.15/main: No such file or directory fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/community/x86_64/APKINDEX.tar.gz ERROR: https://dl-cdn.alpinelinux.org/alpine/v3.15/community: temporary error (try again later) WARNING: Ignoring https://dl-cdn.alpinelinux.org/alpine/v3.15/community: No such file or directory
前述のdaemon.jsonで設定した"fixed-cidr-v6": "fd11:2233:4455:6677::/64"で指定した内部ネットワークから外に通信ができないといけないため、ip6tablesのNATの設定が有効になっているかどうかを確認する必要があります。
また、2022年5月3日現在、dl-cdn.alpinelinux.orgがデュアルスタックになってIPv6のAAAAレコードを持っているため、IPv6 only環境でも通信ができるはずです。
$ dig dl-cdn.alpinelinux.org. A ; <<>> DiG 9.11.26-RedHat-9.11.26-6.el8 <<>> dl-cdn.alpinelinux.org. A ~中略~ ;; QUESTION SECTION: ;dl-cdn.alpinelinux.org. IN A ;; ANSWER SECTION: dl-cdn.alpinelinux.org. 1420 IN CNAME dualstack.d.sni.global.fastly.net. dualstack.d.sni.global.fastly.net. 30 IN A 151.101.110.133
$ dig dl-cdn.alpinelinux.org. AAAA ; <<>> DiG 9.11.26-RedHat-9.11.26-6.el8 <<>> dl-cdn.alpinelinux.org. AAAA ~中略~ ;; QUESTION SECTION: ;dl-cdn.alpinelinux.org. IN AAAA ;; ANSWER SECTION: dl-cdn.alpinelinux.org. 1484 IN CNAME dualstack.d.sni.global.fastly.net. dualstack.d.sni.global.fastly.net. 30 IN AAAA 2a04:4e42:1a::645
もしも、AAAAレコードを返さないサーバだった場合、IPv4アドレスで通信しようとして失敗してしまいますので、この場合は、Dockerfile で RUN sed -r "s;//dl-cdn.alpinelinux.org/alpine;//ftp.udx.icscoe.jp/Linux/alpine;g" -i /etc/apk/repositories を実行するなどして、IPv6で通信が可能なミラーサーバを指定してください。ちなみにAlpine Linuxのオフィシャルのミラーサーバの一覧は https://mirrors.alpinelinux.org/ で確認できます。
docker run
ビルド時に指定したタグ名ipv6/alpineでdocker runします。-iオプションでインタラクティブに対話しながらシェルを実行できます。
docker container run -it ipv6/alpine
無事にシェルに入れたら、curlコマンドでIPv6のサーバとHTTP通信できるかどうか確認します。
/ # cd /tmp /tmp # curl http://google.com/ <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> <TITLE>301 Moved</TITLE></HEAD><BODY> <H1>301 Moved</H1> The document has moved <A HREF="http://www.google.com/">here</A>. </BODY></HTML> /tmp #
301のレスポンスが表示されればIPv6 onlyで疎通できたので成功です。おめでとうございます。
※ busyboxのwgetはIPv6対応が不完全
ちなみに、busyboxのwgetはIPv6対応が完全ではないため、apk add wgetしてwgetコマンドを置き換えておくか、curlコマンドを使うようにしておくと良いでしょう。
IPv6 only環境でインターネットがもっと使えるようになると嬉しいですね。