给Docker加上容器加上ipv6支持

最近沉迷撸码,好久没动服务器了,然而昨天推代码的时候发现推不上去,折腾了半天发现硬盘被之前的 webhook 下载的视频塞满了。

倒腾了半天才腾出了几个 G 空间,于是想升级下配置了,调研了小半天后换了台 100G SSD 的新机子,还附带了个 ipv6 地址,既然送了不能不用啊,于是赶紧安排上。

结果发现域名的 AAAA 记录直接指向 v6 地址后访问不了,搜索了下发现 docker 默认是没开 ipv6 支持的,很多教程都说带上--ipv6 运行 docker 服务,然后在创建容器的时候指定 ipv6 的子网,但是查了下 docker-compose 对这玩意的支持并不太好,要是每次重建容器都要设置,那可太麻烦了。

后来搜索到docker-ipv6nat这么一个玩意,原理是创建一个 docker 网卡,然后用 ip6tables 把数据包处理后转发到这个网卡,之后直接将容器 connect 到这个网卡,就可以正常访问 ipv6 外网了,顺带一提项目里也有吐槽一下 docker 的 ipv6 支持问题:

Currently, you can let Docker give each container an IPv6 address from your (public) pool, but this has disadvantages:

  • Giving each container a publicly routable address means all ports (even unexposed / unpublished ports) are suddenly reachable by everyone, if no additional filtering is done (docker/docker#21614)
  • By default, each container gets a random IPv6, making it impossible to do properly do DNS; the alternative is to assign a specific IPv6 address to each container, still an administrative hassle (docker/docker#13481)
  • Published ports won't work on IPv6, unless you have the userland proxy enabled (which, for now, is enabled by default in Docker)
  • The userland proxy, however, seems to be on its way out (docker/docker#14856) and has various issues, like:
  • It can use a lot of RAM (docker/docker#11185)
  • Source IP addresses are rewritten, making it completely unusable for many purposes, e.g. mail servers (docker/docker#17666, docker/libnetwork#1099)

按照教程,首先跑起docker-ipv6nat的 docker 容器,然后创建一块 docker 网卡:

docker run -d --restart=always -v /var/run/docker.sock:/var/run/docker.sock:ro \
    --privileged --net=host robbertkl/ipv6nat
docker network create --ipv6 --subnet=fd00:dead:beef::/48 ipv6_nat
sh

如果你是直接docker run,那么直接使用以下命令将容器连接到 ipv6 网卡:

docker network connect ipv6_nat 容器名
sh

我是使用 docker-compose 来编排容器的,因此我需要编辑 docker-compose.yml,让需要连接 ipv6 的容器加入新创建的网卡,由于我所有容器的进出口都通过 caddy,所以只需要让 caddy 连接网卡即可:

version: '3.7'
services:
    caddy:
        image: blabla/caddy
        networks:
            - default
            - ipv6
        restart: always
networks:
    ipv6:
        external: true
        name: ipv6_nat
yml

如果容器不需要连接其他容器,可以把 networks 中的 default 去掉,之后用 docker-compose 重新运行容器,通过域名就可以访问到由 caddy 反代的容器了。