前言
繼之前的 Docker Nginx,這次雷雷想要讓這個 Nginx container 拿到 IPv6,這樣就不用過路由器跟 Docker 的兩個 NAT,可以改善延遲 yay
條件
- 跑 Docker 的主機 / Container / VM 有 RA
- 一段可配置的 v6 Prefix (/80 以上)
實作
我們以 Alpine Linux 為 Host 作為例子。
1. Docker 開啟 IPv6
為了安全,我們預設讓 bridge 發放 fd00 開頭的 Private IP 地址,而不是預計之後要發的 Public IP。
~$ nano /etc/docker/daemon.json
{
"ipv6": true,
"fixed-cidr-v6": "fd00::/80"
}
可以自己換成自己喜歡的 Private IP Range。
完成後 service docker restart
,確認 Bridge network 上的 Container 已經可以拿到 Private IPv6 了。
2. NDP Proxy
雷雷的 SLAAC 是廣播在 eth0
上的。
為了讓 Docker 建立的其他網卡也能收到路由資訊,我們要建立一個 NDP Proxy。
用 ip -6 neigh
的方法要一個一個 IP 指定,太麻煩了,所以我們要用 ndppd
來解決。
在後面我們用 HOST_V6_IFACE
來代指這個 eth0
。
2.a 編譯安裝 ndppd
因為 Alpine Linux 上沒有 ndppd
,所以我們要先編譯他。
使用 Ubuntu / Debian 系的同學可以跳過這步 (?)
#!/bin/sh
export NDPPD_VERSION=master
apk --no-cache add --virtual .build-dependencies make g++ linux-headers patch wget ca-certificates libnl3-dev glib-dev \
&& mkdir -p /usr/src \
&& wget -qO- https://github.com/lekoOwO/ndppd/archive/${NDPPD_VERSION}.tar.gz | tar -xzC /usr/src \
&& cd /usr/src/ndppd-${NDPPD_VERSION} \
&& sed 's/\(${LIBS}\).*\(${OBJS}\)/\2 \1/' -i Makefile
&& sed -i 's/return strerror_r(errno, buf, sizeof(buf));/\/\/ return strerror_r(errno, buf, sizeof(buf));\n return "errno: "+errno;/g' src/logger.cc
# 編譯安裝
CXXFLAGS=" -I/usr/include/libnl3 " LDFLAGS=" -w " make
CXXFLAGS=" -I/usr/include/libnl3 " LDFLAGS=" -w " make install
# 安裝執行時期依賴
apk add --no-cache libnl3 iproute2
# 新增 service 檔
cat << EOF > /etc/init.d/ndppd
#!/sbin/openrc-run
depend() {
need net
}
command="/usr/local/sbin/ndppd"
command_args="-d -p /run/${RC_SVCNAME}.pid -c /etc/ndppd.conf"
pidfile="/run/${RC_SVCNAME}.pid"
EOF
2.b 在 Docker 裡新增用來發放 IPv6 的 network
~# docker network create --ipv6 --subnet={MY_SUBNET}::/{MY_CIDR} {MY_NETWORK_NAME}
注意到 CIDR 至少要在 80 以上,太小會沒辦法發。
將回傳的 hash 記錄下來,用 ip a
檢查新增的網卡名字 (postfix 應該會跟 hash 前幾位一樣)
例如我的是 br-179d885229e7
,在後面稱作 MY_DOCKER_IFACE
2.c ndppd 設定
將設定檔內的花括號變數填入你自己的參數:
# /etc/ndppd.conf
# route-ttl <integer> (NEW)
# This tells 'ndppd' how often to reload the route file /proc/net/ipv6_route.
# Default value is '30000' (30 seconds).
route-ttl 30000
# address-ttl <integer> (NEW)
# This tells 'ndppd' how often to reload the IP address file /proc/net/if_inet6
# Default value is '30000' (30 seconds).
address-ttl 30000
# proxy <interface>
# This sets up a listener, that will listen for any Neighbor Solicitation
# messages, and respond to them according to a set of rules (see below).
# <interface> is required. You may have several 'proxy' sections.
proxy {HOST_V6_IFACE} {
# router <yes|no|true|false>
# This option turns on or off the router flag for Neighbor Advertisement
# messages. Default value is 'true'.
router yes
# timeout <integer>
# Controls how long to wait for a Neighbor Advertisment message before
# invalidating the entry, in milliseconds. Default value is '500'.
timeout 500
# autowire <yes|no|true|false>
# Controls whether ndppd will automatically create host entries
# in the routing tables when it receives Neighbor Advertisements on a
# listening interface. The the default value is no.
# Note: Autowire will ignore all rules with 'auto' or 'static' given it
# is expected that the routes are already defined for these paths
autowire no
# keepalive <yes|no|true|false>
# Controls whether ndppd will automatically attempt to keep routing
# sessions alive by actively sending out NDP Solicitations before the the
# session is expired. The the default value is yes.
keepalive yes
# retries <integer>
# Number of times a NDP Solicitation will be sent out before the daemon
# considers a route unreachable. The default value is 3
retries 3
# promiscuous <yes|no|true|false>
# Controls whether ndppd will put the proxy listening interface into promiscuous
# mode and hence will react to inbound and outbound NDP commands. This is
# required for machines behind the gateway to talk to each other in more
# complex topology scenarios. The the default value is no.
promiscuous no
# ttl <integer>
# Controls how long a valid or invalid entry remains in the cache, in
# milliseconds. Default value is '30000' (30 seconds).
ttl 30000
# rule <ip>[/<mask>]
# This is a rule that the target address is to match against. If no netmask
# is provided, /128 is assumed. You may have several rule sections, and the
# addresses may or may not overlap.
rule {MY_SUBNET}::/{MY_CIDR} {
# Only one of 'static', 'auto' and 'interface' may be specified. Please
# read 'ndppd.conf' manpage for details about the methods below.
# 'auto' should work in most cases.
# static (NEW)
# 'ndppd' will immediately answer any Neighbor Solicitation Messages
# (if they match the IP rule).
# iface <interface>
# 'ndppd' will forward the Neighbor Solicitation Message through the
# specified interface - and only respond if a matching Neighbor
# Advertisement Message is received.
# auto (NEW)
# Same as above, but instead of manually specifying the outgoing
# interface, 'ndppd' will check for a matching route in /proc/net/ipv6_route.
# auto
iface {MY_DOCKER_IFACE}
# autovia <yes|no|true|false>
# Any addresses updated using NDP advertisments will use a gateway to
# route traffic on this particular interface (only works wiith the iface
# rule type). Default is no
autovia yes
# Note that before version 0.2.2 of 'ndppd', if you didn't choose a
# method, it defaulted to 'static'. For compatibility reasons we choose
# to keep this behavior - for now (it may be removed in a future version).
}
}
然後我們啟用它,並讓它開機自動啟動:
~# rc-update add ndppd default
~# service ndppd start
3. 將 Container 加入網路
最後將你想 expose 的 Container 加入 MY_NETWORK_NAME
就大功告成啦!
感謝
謝謝 勝勝 幫我做 thumbnail yay