Ubuntu Docker 部署 OpenWrt 软路由 —— 双栈 + IPv6 中继透传完整指南
本文是一份可直接落地的完整操作手册,涵盖从硬件规划、Ubuntu 系统配置、Docker 网络创建、OpenWrt 部署、双栈 IPv4/IPv6 配置、防火墙规则、容器服务部署到公网暴露的全部细节。所有命令均可直接复制执行。
一、方案概述与架构设计
1.1 为什么选择这套方案?
在 Ubuntu 上通过 Docker 部署 OpenWrt 软路由,并在同一宿主机上运行 Jellyfin、Nextcloud 等容器服务,是一种资源利用率高、架构清晰、易于维护的组合方案。
核心优势:
- OpenWrt 容器资源占用极低(<100MB 内存),性能接近物理机
- Ubuntu 宿主机可同时运行多种服务,充分利用硬件
- 通过 Macvlan 网络,所有容器获得独立内网 IP,网络透明
- 所有流量经过 OpenWrt 统一管控
- 通过 Nginx 反向代理安全暴露服务到公网
- 双栈支持:IPv4 走 NAT,IPv6 通过中继透传实现端到端公网访问
1.2 IPv6 方案设计思路
| 协议 | 分配方式 | 路由方式 | 防火墙 |
|---|---|---|---|
| IPv4 | OpenWrt DHCP 分配 192.168.10.x | OpenWrt NAT 转发 | 端口转发(Port Forwarding) |
| IPv6 | 主路由(光猫)RA 直接分配公网地址 | OpenWrt 中继透传(Relay) | 后缀匹配规则放行入站 |
为什么 IPv6 要使用中继透传而非由 OpenWrt 分配?
- 家庭宽带 IPv6 前缀由 ISP 动态分配,OpenWrt 作为二级路由如果自己分配 IPv6,需要做前缀转换(NPTv6)或 NAT66,增加复杂性和性能损耗
- 中继透传模式下,内网设备直接从光猫获取公网 IPv6 地址,实现真正的端到端通信,不需要额外 NAT
- OpenWrt 只负责转发 RA(路由器通告)、DHCPv6 和 NDP(邻居发现协议)报文
二、网络拓扑与 IP 规划
2.1 完整网络拓扑图
┌─────────────────────────────────┐
│ 互联网 │
└────────────────┬────────────────┘
│
▼
┌─────────────────────────────────┐
│ 光猫(主路由) │
│ │
│ IPv4: 192.168.1.1 │
│ IPv6: 240e:390:1234:5678::1/64 (ISP 分配,前缀动态) │
│ 功能: 拨号、DHCPv4、RA(路由器通告)、DHCPv6 │
│ 端口转发规则: 443 → 192.168.1.100:443, 80 → 192.168.1.100:80 │
│ 角色: 第一道门,只开放 443/80 两个端口 │
└───────────────┬─────────────────┘
│
▼
┌─────────────────────────────────┐
│ OpenWrt 容器(第二层网关) │
│ │
│ WAN 口 (eth1): │
│ IPv4: 192.168.1.100 (光猫 DHCP 分配) │
│ IPv6: 240e:390:1234:5678::100/64 (光猫 DHCPv6 分配) │
│ │
│ LAN 口 (eth0): │
│ IPv4: 192.168.10.1 (静态,内网网关) │
│ IPv6: 中继模式,不分配地址,透传 RA/DHCPv6/NDP │
│ │
│ 防火墙规则: │
│ - IPv4 端口转发: WAN 443/80 → 192.168.10.53:443/80 │
│ - IPv6 入站放行: 后缀匹配 443 端口到 Nginx 容器 │
│ - 禁止 WAN 侧 SSH/LuCI 访问 │
│ │
│ 角色: 第二道门,路由转发 + 防火墙过滤 │
└────────────────┬────────────────┘
│
▼
┌──────────────────────────────────┐
│ br-lan 网桥(第三层) │
│ │
│ 宿主机 IP: 192.168.10.2 (IPv4) + 公网 IPv6 (由光猫直接分配) │
│ 成员: eth1, eth2, eth3, wlan0 (WiFi AP), 所有容器 │
│ 角色: 内网二层交换机,连接所有设备 │
└───┬────────────┬──────────┬──────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌─────────┐ ┌─────────────────┐
│ 有线设备群 │ │ 无线设备群 │ │ Docker 容器群 │
│ │ │ │ │ │
│ 电脑 .10.100 │ │ 手机/笔记本 │ │ Nginx Proxy Manager .10.53 │
│ NAS .10.101 │ │ (WiFi 连接) │ │ Jellyfin .10.50 │
│ 电视 .10.102 │ │ IPv6 由光猫直接 │ │ Nextcloud .10.60 │
│ IPv4: OpenWrt DHCP │ │ 分配 (SLAAC) │ │ Transmission .10.51 │
│ IPv6: 光猫直接分配 │ │ │ │ Portainer .10.52 │
│ (SLAAC) │ │ │ │ ddns-go .10.54 │
│ │ │ │ │ 所有容器固定 MAC,IPv6 后缀固定 │
└──────────┘ └─────────┘ └─────────────────┘
2.2 完整 IP 规划表
| 层级 | 设备/接口 | IPv4 | IPv6 | 获取方式 |
|---|---|---|---|---|
| 0 | 光猫 | 192.168.1.1 | 240e:xxx::1/64 | 静态 + ISP 分配 |
| 1 | OpenWrt WAN | 192.168.1.100 | 240e:xxx::100/64 | DHCPv4 + DHCPv6 |
| 1 | OpenWrt LAN | 192.168.10.1 | 中继(不分配) | 静态 |
| 2 | 宿主机 br-lan | 192.168.10.2 | 240e:xxx::xxxx/64 | 静态 + SLAAC |
| 3 | Nginx | 192.168.10.53 | 240e:xxx::0242:acff:fe10:0035/64 | 静态 + SLAAC |
| 3 | Jellyfin | 192.168.10.50 | 240e:xxx::0242:acff:fe10:0032/64 | 静态 + SLAAC |
| 3 | Nextcloud | 192.168.10.60 | 240e:xxx::0242:acff:fe10:003c/64 | 静态 + SLAAC |
| 3 | ddns-go | 192.168.10.54 | 240e:xxx::0242:acff:fe10:0036/64 | 静态 + SLAAC |
| 3 | 内网设备 | 192.168.10.100-200 | 240e:xxx::xxxx/64 | DHCPv4 + SLAAC |
关键设计说明:
- IPv6 前缀
240e:xxx::/64是动态的,由 ISP 分配,每次重拨可能变化 - 容器 IPv6 地址的后缀(
::0242:acff:fe10:0035等)基于固定 MAC 地址通过 EUI-64 算法生成,永久不变 - 防火墙使用后缀匹配,无视前缀变化
三、宿主机 Ubuntu 基础配置
3.1 安装 Docker
# 安装 Docker(使用阿里云镜像加速)
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
# 将当前用户加入 docker 组
sudo usermod -aG docker $USER
# 注销并重新登录,然后验证
docker --version
3.2 安装必要工具
sudo apt update
sudo apt install -y bridge-utils hostapd net-tools sysstat curl wget vim
3.3 配置 IPv6 内核参数(关键)
编辑 /etc/sysctl.d/99-ipv6.conf:
sudo tee /etc/sysctl.d/99-ipv6.conf << 'EOF'
# ==== IPv6 转发(容器和网桥需要)====
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.default.forwarding = 1
# ==== 禁用临时地址 ====
# 让 DDNS 绑定固定的 EUI-64 地址,而不是会变化的临时地址
net.ipv6.conf.all.use_tempaddr = 0
net.ipv6.conf.default.use_tempaddr = 0
# ==== 允许接收 RA(路由器通告)====
# 2 表示总是接受 RA,即使 forwarding=1 也接受
net.ipv6.conf.all.accept_ra = 2
net.ipv6.conf.default.accept_ra = 2
# ==== 允许 NDP 代理 ====
net.ipv6.conf.all.proxy_ndp = 1
net.ipv6.conf.default.proxy_ndp = 1
EOF
# 应用配置
sudo sysctl -p /etc/sysctl.d/99-ipv6.conf
# 验证
sysctl net.ipv6.conf.all.forwarding
# 应输出: net.ipv6.conf.all.forwarding = 1
3.4 配置 netplan(网桥 + 宿主机 IPv4/IPv6)
编辑 /etc/netplan/01-netcfg.yaml:
sudo tee /etc/netplan/01-netcfg.yaml << 'EOF'
network:
version: 2
ethernets:
eth0:
dhcp4: false
dhcp6: false
eth1:
dhcp4: false
dhcp6: false
eth2:
dhcp4: false
dhcp6: false
eth3:
dhcp4: false
dhcp6: false
bridges:
br-lan:
interfaces: [eth1, eth2, eth3]
addresses:
- 192.168.10.2/24
gateway4: 192.168.10.1
nameservers:
addresses:
- 114.114.114.114
- 2400:3200::1
parameters:
stp: false
forward-delay: 0
EOF
# 应用配置
sudo netplan apply
# 验证
ip addr show br-lan
# 应显示 inet 192.168.10.2/24
注意:IPv6 地址不在此处配置,由光猫通过 RA 自动分配。
3.5 开启网卡混杂模式(永久生效)
混杂模式让物理网卡能接受多个 MAC 地址的数据包,这是 Macvlan 正常工作的前提。
sudo tee /etc/systemd/system/promisc.service << 'EOF'
[Unit]
Description=Enable promiscuous mode for network interfaces
After=network.target
Wants=network.target
[Service]
Type=oneshot
ExecStart=/usr/sbin/ip link set eth0 promisc on
ExecStart=/usr/sbin/ip link set eth1 promisc on
ExecStart=/usr/sbin/ip link set eth2 promisc on
ExecStart=/usr/sbin/ip link set eth3 promisc on
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable promisc.service
sudo systemctl start promisc.service
sudo systemctl status promisc.service
3.6 验证宿主机网络
# IPv4 连通性
ping -c 3 192.168.10.1
# 等待片刻让 IPv6 RA 到达,然后检查 IPv6
sleep 10
ip -6 addr show br-lan | grep global
# 如果看到 240e:xxx 开头的地址,说明宿主机已获得公网 IPv6
# 如果没有,等待更久或检查光猫的 RA 配置
四、WiFi AP 配置(可选)
如果需要将无线网卡作为 AP 发射 WiFi 信号,配置 hostapd。
4.1 检查无线网卡是否支持 AP 模式
iw list | grep -A 10 "Supported interface modes" | grep "AP"
# 如果有输出,说明支持 AP 模式
4.2 创建 hostapd 配置文件
sudo tee /etc/hostapd/hostapd.conf << 'EOF'
interface=wlan0
bridge=br-lan # 关键:让无线网卡加入 br-lan 网桥
driver=nl80211
hw_mode=g # 2.4GHz,改为 a 为 5GHz
channel=6
wmm_enabled=1
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
ssid=你的WiFi名称
wpa_passphrase=你的WiFi密码(至少8位)
EOF
4.3 启动 hostapd
# 指向配置文件
sudo sed -i 's|#DAEMON_CONF=""|DAEMON_CONF="/etc/hostapd/hostapd.conf"|' /etc/default/hostapd
# 取消屏蔽并启动
sudo systemctl unmask hostapd
sudo systemctl enable hostapd
sudo systemctl start hostapd
# 验证
sudo systemctl status hostapd
# 应该显示 active (running)
五、Docker 双栈 Macvlan 网络创建
5.1 什么是 Macvlan?为什么用它?
Macvlan 是 Linux 内核的网络虚拟化技术,允许在一张物理网卡上创建多个虚拟接口,每个接口有独立的 MAC 地址和 IP 地址。
| 特性 | Docker Bridge(默认) | Macvlan |
|---|---|---|
| 容器 IP | 私有 IP(172.17.x.x) | 物理网络真实 IP |
| 外部访问 | 需端口映射 | 直接通过 IP 访问 |
| 网络性能 | 经过 NAT,有损耗 | 接近物理网卡 |
| 能否做网关 | ❌ 不能 | ✅ 可以 |
| 适用场景 | 普通微服务 | 软路由、网络基础设施 |
OpenWrt 必须使用 Macvlan,因为它需要独立 IP 作为内网网关。
5.2 创建三个双栈 Macvlan 网络
# 1. OpenWrt LAN 网络(绑定 br-lan 网桥)
docker network create -d macvlan \
--subnet=192.168.10.0/24 --gateway=192.168.10.1 \
--ipv6 --subnet=fd00:10::/64 \
-o parent=br-lan \
macvlan_lan
# 2. OpenWrt WAN 网络(绑定 eth0 物理网卡)
# 注意:IPv4 使用光猫网段 192.168.1.0/24
docker network create -d macvlan \
--subnet=192.168.1.0/24 --gateway=192.168.1.1 \
--ipv6 --subnet=fd00:1::/64 \
-o parent=eth0 \
macvlan_wan
# 3. 服务容器网络(绑定 br-lan 网桥)
docker network create -d macvlan \
--subnet=192.168.10.0/24 --gateway=192.168.10.1 \
--ipv6 --subnet=fd00:10::/64 \
-o parent=br-lan \
macvlan_services
为什么 IPv6 子网填 fd00::/64(ULA)?
- 这只是 Docker 网络的占位配置,让 Docker 网络支持 IPv6
- 实际容器的公网 IPv6 地址由光猫通过 SLAAC 直接分配,不依赖这个子网
fd00::/64是 ULA(唯一本地地址),不会影响公网路由
验证网络创建:
docker network ls
# 应显示 macvlan_lan, macvlan_wan, macvlan_services
docker network inspect macvlan_services | grep -A 10 "IPAM"
# 应显示 IPv4 和 IPv6 配置
六、OpenWrt 容器部署与详细配置
6.1 拉取镜像
docker pull sulinggg/openwrt:x86_64
# 国内用户备用: docker pull registry.cn-hangzhou.aliyuncs.com/suling/openwrt:x86_64
6.2 创建配置目录并运行容器
mkdir -p ~/docker-services/openwrt_config
docker run --restart always --name openwrt -d \
--network macvlan_lan \
--ip 192.168.10.1 \
--privileged \
-v ~/docker-services/openwrt_config:/etc \
sulinggg/openwrt:x86_64 /sbin/init
# 连接到 WAN 网络
docker network connect macvlan_wan openwrt
# 验证容器运行
docker ps | grep openwrt
6.3 进入 OpenWrt 容器
docker exec -it openwrt /bin/sh
6.4 配置 IPv4 网络
编辑 /etc/config/network:
vi /etc/config/network
完整内容:
config interface 'loopback'
option ifname 'lo'
option proto 'static'
option ipaddr '127.0.0.1'
option netmask '255.0.0.0'
config interface 'lan'
option type 'bridge'
option ifname 'eth0'
option proto 'static'
option ipaddr '192.168.10.1'
option netmask '255.255.255.0'
option gateway '192.168.10.1'
option dns '114.114.114.114 2400:3200::1'
config interface 'wan'
option ifname 'eth1'
option proto 'dhcp'
config interface 'wan6'
option ifname 'eth1'
option proto 'dhcpv6'
6.5 配置 IPv6 中继透传(核心配置!)
编辑 /etc/config/dhcp:
vi /etc/config/dhcp
完整内容:
# ========== DNSMASQ 基础配置 ==========
config dnsmasq
option domainneeded '1'
option boguspriv '1'
option filterwin2k '0'
option localise_queries '1'
option rebind_protection '1'
option rebind_localhost '1'
option local '/lan/'
option domain 'lan'
option expandhosts '1'
option nonegcache '0'
option authoritative '1'
option readethers '1'
option leasefile '/tmp/dhcp.leases'
option resolvfile '/tmp/resolv.conf.auto'
option nonwildcard '1'
option localservice '1'
option ednspacket_max '1232'
option dns_redirect '0'
# ========== LAN 接口(IPv4 DHCP + IPv6 中继)==========
config dhcp 'lan'
option interface 'lan'
option start '100'
option limit '150'
option leasetime '12h'
# IPv4 服务器
option dhcpv4 'server'
# IPv6 中继模式:把光猫的 RA/DHCPv6/NDP 透传到内网
option dhcpv6 'relay' # DHCPv6 中继
option ra 'relay' # RA 中继(核心!)
option ndp 'relay' # NDP 中继
option ra_slaac '1' # 允许 SLAAC
# ========== WAN 接口(IPv4 DHCP 客户端)==========
config dhcp 'wan'
option interface 'wan'
option ignore '1'
# ========== WAN6 接口(IPv6 中继上游)==========
config dhcp 'wan6'
option interface 'wan6'
option ignore '1'
option ra 'relay'
option dhcpv6 'relay'
option ndp 'relay'
option master '1' # 标记为 IPv6 中继的主上游
6.6 启用 IPv6 转发
echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.conf
echo "net.ipv6.conf.default.forwarding=1" >> /etc/sysctl.conf
sysctl -p
6.7 配置防火墙
编辑 /etc/config/firewall:
vi /etc/config/firewall
完整内容(在原有内容基础上添加):
# ==========================================
# ==== IPv4 端口转发(双重转发第一重)====
# ==========================================
config redirect
option name 'HTTPS_to_Nginx'
option src 'wan'
option src_dport '443'
option dest 'lan'
option dest_ip '192.168.10.53'
option dest_port '443'
option proto 'tcp'
config redirect
option name 'HTTP_to_Nginx'
option src 'wan'
option src_dport '80'
option dest 'lan'
option dest_ip '192.168.10.53'
option dest_port '80'
option proto 'tcp'
# ==========================================
# ==== IPv6 防火墙规则(后缀匹配放行)====
# ==========================================
# Nginx HTTPS (443) - 后缀匹配
config rule
option name 'Allow-Nginx-HTTPS-IPv6'
option family 'ipv6'
option src 'wan'
option dest 'lan'
# 匹配容器 MAC 02:42:ac:10:00:35 生成的 EUI-64 后缀
option dest_ip '::0242:acff:fe10:0035/::ffff:ffff:ffff:ffff'
option dest_port '443'
option proto 'tcp'
option target 'ACCEPT'
# Jellyfin (8096) - 可选,如果不需要直接 IPv6 访问 Jellyfin 可以省略
config rule
option name 'Allow-Jellyfin-IPv6'
option family 'ipv6'
option src 'wan'
option dest 'lan'
option dest_ip '::0242:acff:fe10:0032/::ffff:ffff:ffff:ffff'
option dest_port '8096'
option proto 'tcp'
option target 'ACCEPT'
# ICMPv6 (ping) - 可选,方便外网测试连通性
config rule
option name 'Allow-ICMPv6'
option family 'ipv6'
option src 'wan'
option dest 'lan'
option proto 'icmp'
option target 'ACCEPT'
# ==========================================
# ==== 安全加固:禁止 WAN 侧访问管理 ====
# ==========================================
config rule
option name 'Drop-WAN-SSH'
option family 'any'
option src 'wan'
option dest_port '22'
option proto 'tcp'
option target 'REJECT'
config rule
option name 'Drop-WAN-LuCI'
option family 'any'
option src 'wan'
option dest_port '80'
option proto 'tcp'
option target 'REJECT'
config rule
option name 'Drop-WAN-LuCI-HTTPS'
option family 'any'
option src 'wan'
option dest_port '443'
option proto 'tcp'
option target 'REJECT'
6.8 重启所有服务
/etc/init.d/network restart
/etc/init.d/dnsmasq restart
/etc/init.d/odhcpd restart
/etc/init.d/firewall restart
# 验证防火墙规则
uci show firewall | grep -E "redirect|rule" | head -20
6.9 验证 OpenWrt 网络状态
# 查看所有接口 IP
ifconfig
# 重点检查:
# - eth1 (WAN) 应有 IPv4 192.168.1.x 和 IPv6 240e:xxx::xxx
# - eth0 (LAN) 应有 IPv4 192.168.10.1,IPv6 可能是 fe80 开头(中继模式正常)
# 查看 IPv6 邻居表(应看到内网设备的 IPv6 地址)
ip -6 neigh show | grep -v fe80
6.10 访问 OpenWrt LuCI 管理界面
浏览器访问 http://192.168.10.1
- 默认用户名:
root - 默认密码:
password(不同镜像可能不同)
登录后立即执行:
- 系统 → 管理权:修改 root 密码
- 网络 → 接口:确认 WAN 口获取到 IPv4 和 IPv6
- 网络 → 防火墙:确认规则已生效
七、其他服务容器部署
7.1 创建项目目录
cd ~/docker-services
mkdir -p jellyfin_media nextcloud_data nextcloud_db transmission_downloads
7.2 完整 docker-compose.yml
vim docker-compose.yml
完整内容:
version: '3.8'
services:
# ============================================
# 1. OpenWrt 软路由
# ============================================
openwrt:
image: sulinggg/openwrt:x86_64
container_name: openwrt
restart: always
privileged: true
command: /sbin/init
networks:
macvlan_lan:
ipv4_address: 192.168.10.1
macvlan_wan:
# WAN 口不指定 IP,由 DHCP 分配
volumes:
- ./openwrt_config:/etc
# ============================================
# 2. Nginx Proxy Manager(反向代理网关)
# ============================================
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:latest
container_name: nginx-proxy-manager
restart: unless-stopped
networks:
macvlan_services:
ipv4_address: 192.168.10.53
mac_address: 02:42:ac:10:00:35
volumes:
- ./nginx_data:/data
- ./nginx_letsencrypt:/etc/letsencrypt
# ============================================
# 3. Jellyfin 媒体服务器
# ============================================
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
networks:
macvlan_services:
ipv4_address: 192.168.10.50
mac_address: 02:42:ac:10:00:32
volumes:
- ./jellyfin_config:/config
- ./jellyfin_media:/media
environment:
- TZ=Asia/Shanghai
# ============================================
# 4. Nextcloud 私有云
# ============================================
nextcloud-db:
image: mariadb:latest
container_name: nextcloud-db
restart: unless-stopped
networks:
macvlan_services:
ipv4_address: 192.168.10.61
volumes:
- ./nextcloud_db:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=your_root_password
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=your_db_password
command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
nextcloud:
image: nextcloud:stable
container_name: nextcloud
restart: unless-stopped
networks:
macvlan_services:
ipv4_address: 192.168.10.60
mac_address: 02:42:ac:10:00:3c
volumes:
- ./nextcloud_data:/var/www/html
environment:
- MYSQL_HOST=192.168.10.61
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=your_db_password
- NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.你的域名.com localhost 192.168.10.60
depends_on:
- nextcloud-db
# ============================================
# 5. Transmission 下载工具
# ============================================
transmission:
image: linuxserver/transmission:latest
container_name: transmission
restart: unless-stopped
networks:
macvlan_services:
ipv4_address: 192.168.10.51
mac_address: 02:42:ac:10:00:33
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
volumes:
- ./transmission_config:/config
- ./transmission_downloads:/downloads
# ============================================
# 6. Portainer 容器管理
# ============================================
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
restart: unless-stopped
networks:
macvlan_services:
ipv4_address: 192.168.10.52
mac_address: 02:42:ac:10:00:34
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./portainer_data:/data
# ============================================
# 7. DDNS 动态域名(双栈)
# ============================================
ddns-go:
image: jeessy/ddns-go:latest
container_name: ddns-go
restart: unless-stopped
networks:
macvlan_services:
ipv4_address: 192.168.10.54
mac_address: 02:42:ac:10:00:36
volumes:
- ./ddns_data:/root
environment:
- TZ=Asia/Shanghai
networks:
macvlan_lan:
external: true
macvlan_wan:
external: true
macvlan_services:
external: true
7.3 启动所有服务
cd ~/docker-services
docker compose up -d
# 查看所有容器状态
docker compose ps
八、光猫(主路由)端口转发配置
这是双重端口转发的第一重。登录光猫管理界面(通常为 http://192.168.1.1)。
8.1 获取 OpenWrt WAN 口 IPv4 地址
docker exec -it openwrt ifconfig eth1 | grep "inet addr"
# 输出示例: inet addr:192.168.1.100 Bcast:192.168.1.255 Mask:255.255.255.0
8.2 配置端口转发规则
在光猫的 端口转发 或 虚拟服务器 功能中添加:
| 规则名 | 外网端口 | 内网 IP | 内网端口 | 协议 |
|---|---|---|---|---|
| HTTPS | 443 | 192.168.1.100 | 443 | TCP |
| HTTP | 80 | 192.168.1.100 | 80 | TCP |
不要使用 DMZ! DMZ 会把所有端口暴露给 OpenWrt,增加攻击面。只开放 443 和 80 两个端口。
九、DDNS(ddns-go)双栈配置
9.1 访问 DDNS 管理界面
浏览器打开 http://192.168.10.54:9876
9.2 配置 DNS 服务商
- DNS 服务商:选择
阿里云 - AccessKey ID:填入你的 AccessKey ID
- AccessKey Secret:填入你的 AccessKey Secret
安全建议:使用 RAM(资源访问管理)子账号,仅授予
alidns:UpdateDomainRecord权限。
9.3 配置 IPv4
| 配置项 | 值 |
|---|---|
| 启用 | ✅ 勾选 |
| 获取方式 | 通过接口获取 |
| 域名 | ddns.你的域名.com(已存在的 A 记录) |
9.4 配置 IPv6(关键!)
| 配置项 | 值 |
|---|---|
| 启用 | ✅ 勾选 |
| 获取方式 | 通过网卡获取 |
| 网卡名称 | br-lan |
| 禁用临时地址 | ✅ 必须勾选(确保绑定固定的 EUI-64 地址) |
| 域名 | ddns.你的域名.com(已存在的 AAAA 记录) |
9.5 保存并验证
点击 Save,查看日志:
docker logs ddns-go --tail 20
正常输出应显示:
IPv4 地址: xxx.xxx.xxx.xxx
IPv6 地址: 240e:390:1234:5678:0242:acff:fe10:0036
更新成功
十、Nginx Proxy Manager 反向代理配置
10.1 访问管理界面
浏览器打开 http://192.168.10.53:81
- 默认账号:
admin@example.com - 默认密码:
changeme - 立即修改默认密码!
10.2 添加 Jellyfin 代理
点击 Hosts → Proxy Hosts → Add Proxy Host:
| 标签页 | 字段 | 值 |
|---|---|---|
| Details | Domain Names | jellyfin.你的域名.com |
| Forward Hostname/IP | 192.168.10.50 | |
| Forward Port | 8096 | |
| Cache Assets | 勾选 | |
| SSL | Force SSL | 勾选 |
| HTTP/2 Support | 勾选 | |
| SSL Certificate | Request a new SSL Certificate | |
| 邮箱 | 你的邮箱 | |
| Agree to Terms | 勾选 |
10.3 添加 Nextcloud 代理
| 标签页 | 字段 | 值 |
|---|---|---|
| Details | Domain Names | nextcloud.你的域名.com |
| Forward Hostname/IP | 192.168.10.60 | |
| Forward Port | 80 | |
| Cache Assets | 勾选 | |
| SSL | Force SSL | 勾选 |
| HTTP/2 Support | 勾选 | |
| SSL Certificate | Request a new SSL Certificate | |
| 邮箱 | 你的邮箱 |
10.4 添加 OpenWrt 管理界面代理(可选)
| 标签页 | 字段 | 值 |
|---|---|---|
| Details | Domain Names | openwrt.你的域名.com |
| Forward Hostname/IP | 192.168.10.1 | |
| Forward Port | 80 | |
| SSL | Force SSL | 勾选 |
| SSL Certificate | Request a new SSL Certificate | |
| 邮箱 | 你的邮箱 |
安全提醒:如需暴露 OpenWrt 管理界面到公网,建议在 OpenWrt 防火墙中额外限制允许访问的 IP 范围。
十一、完整验证流程
11.1 验证 IPv4 网络
# 宿主机到 OpenWrt
ping -c 3 192.168.10.1
# 宿主机到外网
ping -c 3 114.114.114.114
# OpenWrt 到外网
docker exec -it openwrt ping -c 3 114.114.114.114
11.2 验证 IPv6 网络
# 宿主机 IPv6 地址(应显示 240e 开头的公网地址)
ip -6 addr show br-lan | grep global
# 宿主机到外网 IPv6
ping6 -c 3 2400:3200::1
# OpenWrt IPv6 地址
docker exec -it openwrt ifconfig eth1 | grep inet6
# OpenWrt 到外网 IPv6
docker exec -it openwrt ping6 -c 3 2400:3200::1
11.3 验证容器 IPv6
# 查看 Jellyfin 容器的 IPv6 地址
docker exec -it jellyfin ip -6 addr show | grep global
# 应该看到类似:
# inet6 240e:390:1234:5678:0242:acff:fe10:0032/64 scope global dynamic
# 注意后缀 0242:acff:fe10:0032 与 MAC 02:42:ac:10:00:32 对应
11.4 验证 IPv6 中继(内网设备)
在连接到 br-lan 网桥的电脑上执行:
# Windows: ipconfig
# Linux/Mac: ifconfig
# 应该看到一个 240e:xxx 开头的公网 IPv6 地址
# 网关应该是光猫的 IPv6 地址(而不是 192.168.10.1)
11.5 验证公网访问
# 在手机(关闭 WiFi,使用流量)上访问
https://jellyfin.你的域名.com
https://nextcloud.你的域名.com
# 或使用 curl 测试
curl -6 https://jellyfin.你的域名.com
十二、故障排查完整清单
12.1 宿主机无 IPv6
| 检查项 | 命令 | 期望结果 |
|---|---|---|
| 内核转发 | sysctl net.ipv6.conf.all.forwarding | = 1 |
| RA 接收 | sysctl net.ipv6.conf.br-lan.accept_ra | = 2 |
| 网卡 IP | ip -6 addr show br-lan | 有 240e: 开头的地址 |
| 邻居表 | ip -6 neigh show | 有光猫的 IPv6 |
12.2 OpenWrt WAN6 无 IPv6
| 检查项 | 命令(在 OpenWrt 容器内) | 期望结果 |
|---|---|---|
| 接口状态 | ifconfig eth1 | 有 inet6 地址 |
| DHCPv6 客户端 | `ps | grep odhcp6c` |
| 日志 | `logread | grep -i dhcp6` |
修复:
docker exec -it openwrt /etc/init.d/odhcpd restart
docker exec -it openwrt /etc/init.d/network restart
12.3 容器无公网 IPv6
| 检查项 | 说明 |
|---|---|
| 容器 MAC 固定 | 检查 docker-compose.yml 中是否有 mac_address |
| 网络模式 | 容器必须使用 macvlan_services 网络 |
| 父接口 IPv6 | br-lan 必须有公网 IPv6 |
12.4 外网 IPv6 不通
| 检查项 | 命令(在 OpenWrt 容器内) | 期望结果 |
|---|---|---|
| 防火墙规则 | uci show firewall | grep ipv6 | 有对应的 rule |
| 规则是否启用 | uci show firewall.@rule[x].target | = ACCEPT |
| 端口监听 | docker exec nginx-proxy-manager netstat -tlnp | grep 443 | 监听中 |
修复:
docker exec -it openwrt /etc/init.d/firewall restart
12.5 DDNS 更新失败
| 检查项 | 命令 | 期望结果 |
|---|---|---|
| 容器日志 | docker logs ddns-go --tail 20 | 无报错 |
| 阿里云权限 | — | AccessKey 有 DNS 更新权限 |
| 域名存在 | nslookup ddns.你的域名.com | 返回 IP |
| 临时地址禁用 | sysctl net.ipv6.conf.br-lan.use_tempaddr | = 0 |
12.6 Nginx 代理 502
| 检查项 | 命令 | 期望结果 |
|---|---|---|
| 目标容器运行 | docker ps | grep jellyfin | Up |
| 目标 IP 正确 | Nginx 配置中 IP 是否一致 | 192.168.10.50 |
| 目标端口开放 | docker exec jellyfin netstat -tlnp | 8096 监听中 |
十三、持久化配置与日常维护
13.1 持久化配置清单
| 路径 | 内容 | 用途 |
|---|---|---|
/etc/netplan/01-netcfg.yaml | 网桥配置 | 持久化 br-lan |
/etc/sysctl.d/99-ipv6.conf | IPv6 内核参数 | 持久化 IPv6 设置 |
/etc/systemd/system/promisc.service | 混杂模式 | 开机自动开启 |
/etc/hostapd/hostapd.conf | WiFi AP 配置 | 持久化无线热点 |
~/docker-services/openwrt_config/ | OpenWrt 配置 | 持久化路由设置 |
~/docker-services/nginx_data/ | Nginx 代理规则 | 持久化代理配置 |
~/docker-services/nginx_letsencrypt/ | SSL 证书 | 持久化证书 |
~/docker-services/ddns_data/ | DDNS 配置 | 持久化 DDNS 设置 |
13.2 日常维护命令
# 查看所有容器状态
cd ~/docker-services && docker compose ps
# 查看容器日志
docker logs -f --tail 100 openwrt
docker logs -f --tail 100 nginx-proxy-manager
# 更新所有容器镜像
docker compose pull
docker compose up -d
# 重启所有服务
docker compose restart
# 进入 OpenWrt 容器
docker exec -it openwrt /bin/sh
# 备份配置
tar -czf backup_$(date +%Y%m%d).tar.gz ~/docker-services/
13.3 定期安全检查
| 检查项 | 频率 | 命令 |
|---|---|---|
| 检查日志 | 每周 | docker logs --tail 50 openwrt | grep -i error |
| 检查 SSL 证书 | 每月 | 访问域名确认证书有效 |
| 检查 IPv6 连通性 | 每月 | ping6 -c 3 2400:3200::1 |
| 检查容器磁盘 | 每月 | docker system df |
| 更新系统 | 每月 | sudo apt update && sudo apt upgrade -y |
附录:完整命令速查表
| 操作 | 命令 |
|---|---|
| 查看容器 IP | docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 容器名 |
| 查看容器 MAC | docker inspect -f '{{.Config.MacAddress}}' 容器名 |
| 查看容器 IPv6 | docker exec 容器名 ip -6 addr show | grep global |
| 进入 OpenWrt | docker exec -it openwrt /bin/sh |
| 重启 OpenWrt 网络 | docker exec -it openwrt /etc/init.d/network restart |
| 重启 OpenWrt 防火墙 | docker exec -it openwrt /etc/init.d/firewall restart |
| 查看 OpenWrt 防火墙规则 | docker exec -it openwrt uci show firewall |
| 查看 Nginx 日志 | docker logs -f --tail 50 nginx-proxy-manager |
| 查看 DDNS 日志 | docker logs -f --tail 50 ddns-go |