一般Docker都是直接使用IPV4地址就行,但是一些特殊的镜像就需要容器内有IPV6地址用起来效果才好。像qt这些下载软件。我不用这个,但是用adguardhome设置DNS服务器,所以要求ipv6能通。
当我们开通IPV6以后,一般客户端会同时存在多个 IPv6 地址:
- Loopback 地址:
::1,等价于 IPv4 中的 127.0.0.1 - 链路本地地址(Link Local Address):
fe80::/10网段,只在区域中是合法地址,类似于 IPv4 中的 169.254.0.0/16。fe80开头 不可路由,电脑自动生成 - 唯一区域地址(Unique Local Addresses):局域网地址,只能用于本地通信,和 IPv4 的专用网络地址
10.0.0.0/8、172.16.0.0/12和192.168.0.0/16类似,在 RFC 4193 中定义。固定前缀是fc00::/7,分为fc00::/8和fd00::/8两部分,fc00::/8目前作为保留地址没有定义,fd00::/8是现在使用的 ULA 地址,其中 40bit 随机生成以避免冲突,可以使用 ULA 生成工具 来生成。 - 全局地址(Global Address Type):通常意义上的公网地址,以 2XXX: 开头。但 2002: 开头的是特殊的 6to4 过渡地址,不是原生的纯 IPv6 网路地址。电信的是240e开头,联通2408开头,移动2409开头
-
本地站点地址(Site Local Address):fec0::/10 网段,私网地址,目前在 RFC 规范中已被废弃
image.png
1. IPV6地址获取方式
在路由器中开启ipv6时,会让选择获取方式,不同系统获取方式显示的稍有不同。我这里了解到大约几种,大家可以对照自己的看一下。
需要明确:无状态和有状态,仅仅指的是IPv6地址的获取方式。
1.1 SLAAC
SLAAC( Stateless Address Autoconfiguration,无状态地址配置)是IPv6地址自动配置协议的一种,主要用于在网络上进行IPv6地址的分配。
SLAAC是IPv6的内置特性,是通过网络前缀和MAC地址构造IPv6地址的方式来实现地址配置。它不需要任何服务器或其他配置信息,且可以快速地完成地址配置。SLAAC是一种基于前缀的地址配置方法,使用前缀将一个ipv6地址划分为IPv6网络地址和IPv6主机地址两部分,网络地址由网络管理员配置,而主机地址由主机本地自动产生。对于一个无状态的网络环境,它是一种简单快捷的方式。
SLAAC通过局域网中节点自动就近获取IP地址,提高了地址分配的利用率,减少了管理员的工作量。但是,SLAAC在地址分配时缺乏安全性,犯罪分子可能会通过MAC地址来获取到IPv6地址。同时,SLAAC无法为节点提供其他配置(如DNS信息等),对于像VoIP和视频会议等应用是不利的。
我用的老毛子潘多拉固件,开启ipv6后,就是用的这种获取方式。
1.2 DHCPv6
DHCPv6 (Dynamic Host Configuration Protocol version 6) 与SLAAC不同的是,DHCPv6(有状态,stateful)是一种动态IP地址分配,且需要在网络中使用DHCP服务器。
DHCPv6是一种动态IP地址分配方法,它通过DHCP服务器和客户端之间的数据交换,完成IPv6地址的配置。对于网络管理员来说,他需要配置DHCP服务器信息,而客户端需要启用DHCP协议。DHCPv6可以用于分配地址及其他IPv6配置信息(如DNS服务器信息),使用起来更加灵活,在基于状态的网络环境中十分实用。
DHCPv6具有更高的优先级,它可以分配到IPv6地址以外的配置信息,例如网关、DNS服务器等,更满足实际的需要。但是,DHCPv6需要网络管理员提供专用的DHCP服务器,大大增加了管理员的管理量和维护成本,同时,服务器出现故障将会导致客户无法分配IPv6地址
1.3 SLAAC+DHCPv6
地址由slaac获取,但是DNS由DHCPv6通告
注意:上面是IPV6地址获取方式,但是有的路由器中是没有上面的内容的。而是让你选择 ipv6 的连接方式,一般的有桥接、Native模式、NAT6模式等。
1. Native
Native模式意味着原生支持,即设备直接使用IPv6地址进行通信,无需转换或隧道化。
Native模式适用于多数情况,它类似于DHCP(动态主机配置协议)自动分配IPv6地址。在这种模式下,设备可以获得公网IPv6地址,能够直接访问公共互联网。
2.NAT6模式
NAT6是Network Address Translation for IPv6的缩写,即IPv6网络地址转换。使用NAT6模式时,尽管终端设备可以获得IPv6地址,但这个地址是局域网的IPv6地址。在这种模式下,终端设备只能在局域网内部使用IPv6地址进行通信,无法直接访问公共互联网。它适用于路由器做管理网关的IPv6地址前缀代理,且上一级为光猫拨号的情况。
2. Docker 的 IPv6 支持
为 Docker 配置 IPv6 有三种常见的思路。
- 一种是通过 HOST 模式,直接使用主机网络,但是可能引来外部扫描和攻击,存在十分严重的安全性问题。尤其是用来挂bt的,被别人看的一清二楚。IPv6 号称可以为每一粒沙子都分配一个 IP 地址,因此完全可以为每个容器都分配一个独立的 IPv6 地址,重新找回 IPv6 超大地址空间的安全优势。
这种方式虽然获取到了IPV6地址,且大部分情景下使用没问题,但特殊场景受限于 docker的网络模型也会有一定的弊端。-Docker网络 - 简书
- 一种是通过 HOST 模式,直接使用主机网络,但是可能引来外部扫描和攻击,存在十分严重的安全性问题。尤其是用来挂bt的,被别人看的一清二楚。IPv6 号称可以为每一粒沙子都分配一个 IP 地址,因此完全可以为每个容器都分配一个独立的 IPv6 地址,重新找回 IPv6 超大地址空间的安全优势。
- 第二种是启用官方的 IPv6 支持,在官方文档中有说明,但是启用后仅能获取到私网(link-local)IPv6 地址,想要获取公网(global)IPv6 地址,还要指定
--fixed-cidr-v6,在目前国内运行商随机分配前缀的情况下,配置较为麻烦。
- 第二种是启用官方的 IPv6 支持,在官方文档中有说明,但是启用后仅能获取到私网(link-local)IPv6 地址,想要获取公网(global)IPv6 地址,还要指定
-
- 第三种也就是本文使用的方法是通过 macvlan,为 docker 创建一个支持 IPv6 的网络,自动从上级路由获取公网 IPv6 地址。
docker network create -d macvlan --subnet=192.168.1.0/24 --gateway=192.168.1.1 --ipv6 --subnet=240x:xxxx:xxxx:xxxx::/64 --gateway=240x:xxxx:xxxx:xxxx::1 -o parent=eth0 adnet- docker network create -d macvlan 指定创建的网络类型是macvlan
- --subnet=192.168.1.0/24 macvlan的ipv4网段,这里写你的内网网段即可,最后一位是0(/24默认不要动)
- --gateway=192.168.1.1 macvlan的ipv4网关,最后一位是1(其实就是路由器的本地IP地址)
- --ipv6 启用ipv6支持
- --subnet=240x:xxxx:xxxx:xxxx::/64 macvlan的ipv6前缀
- --gateway=240x:xxxx:xxxx:xxxx::1 macvlan的ipv6网关(其实就是前缀前4个网段::1)
- -o parent=eth0 桥接网络走的是eth0接口(也就是前面我们ifconfig命令得出的接口名称)
- adnet macvlan网络的名称,可以随意更改
其中,ipv6的前缀可以用命令获取:
ip -6 route show,自己那是什么就写什么。ipv6的网关可以不用写,会按规则自动生成
image.png
注意:使用macvlan网络时,由于指定了ipv6的前缀,而ipv6地址是不定时变化的。当ipv6地址变了,我们设置的前缀还是原来的,会不会出问题呢?我确实遇到了这个问题,但是目前用macvlan网络的容器(我是adguardhome)还是能正常访问ipv6的,我也搞不懂为什么。也不知道怎么处理,就先这样用着。
Docker安装AdGuard Home,接入路由器,过滤局域网广告 - 简书
3. macvlan模式下,容器和宿主机互通
在 macvlan 模式下,出于安全考虑,默认禁止宿主机与容器的通信,也就是宿主机无法访问以及反代容器。但是局域网中的其他设备可以访问到容器。此时的网络拓扑如下图所示,可以将接入 macvlan 的容器看作一个直接接入局域网的设备(蓝色线),只是无法与宿主机(以及宿主机中使用 brigde 模式的设备)直接通信(红色线)。

但 macvlan 之间可以通信,因此通过新建一个虚拟接口,然后修改路由,使得
主机 <-> 容器变为主机 <-> 虚拟接口 <-> 容器,就可以打通容器与宿主机之间的通信。
3.1 配置
-
- 创建虚拟网卡接口,类型macvlan
ip link add joinnet link eth0 type macvlan mode bridge -
- 设置网卡接口ip
ip addr add 192.168.1.245 dev joinnet -
- 启动接口
ip link set joinnet up -
- 设置路由,宿主机和容器打通,此处添加的是
容器IP
ip route add 192.168.1.110 dev joinnet - 设置路由,宿主机和容器打通,此处添加的是
至此,即可实现宿主机到容器的访问。
修改完成后的网络拓扑类似下图。通过手动配置静态路由,将 192.168.1.110 的路由指向新建的 joinnet虚拟接口,由于该接口视作一个局域网设备可以访问到容器,因此从主机 -> 容器变得可达。(粉色线)
反过来,若是 macvlan 下的容器需要访问主机,直接访问主机 IP 也是不通的(红色线),但此时访问主机可以直接使用虚拟接口的 IP 来替代(绿色线),即在 macvlan 容器下使用 192.168.1.245:port 即可正常访问到主机服务。

3.2 开机自动配置
由于上面的虚拟网络接口配置在宿主机重启后会丢失,因此需要创建开机启动脚本,以实现自动配置。
-
- 网络配置脚本 net_docker_macvlan.sh
#!/bin/bash # macvlan 接口名称[可修改] MACVLAN_INTERFACE="joinnet" # 桥接网口[需修改] PARENT_INTERFACE="eth0" # macvlan 接口 IP [需修改] IP_ADDRESS="192.168.1.245/24" # 所有路由列表 [需修改] ROUTES=("192.168.1.110" "192.168.1.111") start_macvlan() { ip link add $MACVLAN_INTERFACE link $PARENT_INTERFACE type macvlan mode bridge ip addr add $IP_ADDRESS dev $MACVLAN_INTERFACE ip link set $MACVLAN_INTERFACE up for route in "${ROUTES[@]}"; do ip route add $route dev $MACVLAN_INTERFACE done } stop_macvlan() { for route in "${ROUTES[@]}"; do ip route del $route dev $MACVLAN_INTERFACE || true done ip link set $MACVLAN_INTERFACE down || true ip addr del $IP_ADDRESS dev $MACVLAN_INTERFACE || true ip link del $MACVLAN_INTERFACE || true } case "$1" in start) start_macvlan ;; stop) stop_macvlan ;; *) echo "Usage: $0 {start|stop}" exit 1 esac
赋予脚本可执行权限:sudo chmod +x ./net_docker_macvlan.sh
-
- 设置上面的脚本开机自启
创建自启脚本:sudo vim /etc/systemd/system/net_docker_macvlan.service,内容如下:
[Unit] Description=Setup macvlan shim for docker to allow to route to host BindsTo=docker.service Requires=docker.service After=docker.service [Service] Type=oneshot ExecStart=/usr/xxx/xxx/xxx/net_docker_macvlan.sh start ExecStop=/us/xxx/xxx/xxx/net_docker_macvlan.sh stop RemainAfterExit=yes Restart=no [Install] WantedBy=multi-user.target - 设置上面的脚本开机自启
如果是群晖用户,由于群晖的 docker systemed 不是 docker.service,因此需要修改 BindsTo、Requires 和 After,根据 DSM 版本不同,服务名称可能也不同,可以通过以下方式查看:systemctl list-units --type=service | grep docker。
当然也可以使用 network.target,当网络服务设置完成后启动,不强制依赖 docker,是一种更宽松的设置方式。
最后使用 systemctl start net_docker_macvlan启动脚本了。
systemctl enable net_docker_macvlan开机自启

