跳至正文
老丹的足迹 —— 代码写给机器,游记写给自己,感悟写给时间
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面

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 方案设计思路

协议分配方式路由方式防火墙
IPv4OpenWrt DHCP 分配 192.168.10.xOpenWrt 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 规划表

层级设备/接口IPv4IPv6获取方式
0光猫192.168.1.1240e:xxx::1/64静态 + ISP 分配
1OpenWrt WAN192.168.1.100240e:xxx::100/64DHCPv4 + DHCPv6
1OpenWrt LAN192.168.10.1中继(不分配)静态
2宿主机 br-lan192.168.10.2240e:xxx::xxxx/64静态 + SLAAC
3Nginx192.168.10.53240e:xxx::0242:acff:fe10:0035/64静态 + SLAAC
3Jellyfin192.168.10.50240e:xxx::0242:acff:fe10:0032/64静态 + SLAAC
3Nextcloud192.168.10.60240e:xxx::0242:acff:fe10:003c/64静态 + SLAAC
3ddns-go192.168.10.54240e:xxx::0242:acff:fe10:0036/64静态 + SLAAC
3内网设备192.168.10.100-200240e:xxx::xxxx/64DHCPv4 + 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(不同镜像可能不同)

登录后立即执行:

  1. 系统 → 管理权:修改 root 密码
  2. 网络 → 接口:确认 WAN 口获取到 IPv4 和 IPv6
  3. 网络 → 防火墙:确认规则已生效

七、其他服务容器部署

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内网端口协议
HTTPS443192.168.1.100443TCP
HTTP80192.168.1.10080TCP

不要使用 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:

标签页字段值
DetailsDomain Namesjellyfin.你的域名.com
Forward Hostname/IP192.168.10.50
Forward Port8096
Cache Assets勾选
SSLForce SSL勾选
HTTP/2 Support勾选
SSL CertificateRequest a new SSL Certificate
邮箱你的邮箱
Agree to Terms勾选

10.3 添加 Nextcloud 代理

标签页字段值
DetailsDomain Namesnextcloud.你的域名.com
Forward Hostname/IP192.168.10.60
Forward Port80
Cache Assets勾选
SSLForce SSL勾选
HTTP/2 Support勾选
SSL CertificateRequest a new SSL Certificate
邮箱你的邮箱

10.4 添加 OpenWrt 管理界面代理(可选)

标签页字段值
DetailsDomain Namesopenwrt.你的域名.com
Forward Hostname/IP192.168.10.1
Forward Port80
SSLForce SSL勾选
SSL CertificateRequest 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
网卡 IPip -6 addr show br-lan有 240e: 开头的地址
邻居表ip -6 neigh show有光猫的 IPv6

12.2 OpenWrt WAN6 无 IPv6

检查项命令(在 OpenWrt 容器内)期望结果
接口状态ifconfig eth1有 inet6 地址
DHCPv6 客户端`psgrep odhcp6c`
日志`logreadgrep -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 网络
父接口 IPv6br-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 jellyfinUp
目标 IP 正确Nginx 配置中 IP 是否一致192.168.10.50
目标端口开放docker exec jellyfin netstat -tlnp8096 监听中

十三、持久化配置与日常维护

13.1 持久化配置清单

路径内容用途
/etc/netplan/01-netcfg.yaml网桥配置持久化 br-lan
/etc/sysctl.d/99-ipv6.confIPv6 内核参数持久化 IPv6 设置
/etc/systemd/system/promisc.service混杂模式开机自动开启
/etc/hostapd/hostapd.confWiFi 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

附录:完整命令速查表

操作命令
查看容器 IPdocker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 容器名
查看容器 MACdocker inspect -f '{{.Config.MacAddress}}' 容器名
查看容器 IPv6docker exec 容器名 ip -6 addr show | grep global
进入 OpenWrtdocker 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
作者

老丹

关注我
其他文章
上一个

深入理解 Docker Macvlan:让容器“插上”物理网线

下一个

Ubuntu 日志管家 logrotate 完全指南:安装、原理与深度配置

关于博主

    老丹是一名C/C++后台开发工程师,信奉“无抽象不设计,无性能不生产”。

  • 技术栈:Modern C++、Linux环境编程、多线程/并发、网络编程等。
  • 信条:能用constexpr解决的问题绝不拖到运行时,能靠RAII避免的泄漏绝不写析构。
  • 正在填坑:从解封装到渲染的C++全链路实现,正在驯服FFmpeg与H.264/H.265。
  • 输出原则:这里的每一段代码都经过-Wall -Wextra -Werror -O2的洗礼。

近期文章

  • Ubuntu 防火墙迁移指南:从 UFW 到 firewalld 的完整实践 2026年9月12日
  • Nano 编辑器完全操作指南:从入门到熟练 2026年9月12日
  • SSCG:让自签名证书不再“危险”的生成工具 2026年9月12日
  • Ubuntu Samba 服务安装与配置完全指南 2026年9月12日
  • 从零开始:用 Docker 部署 Jellyfin 并启用英特尔核显硬件加速 2026年9月11日

文章分类

  • C/C++开发 (22)
  • Docker容器 (5)
  • Linux工具包 (17)
  • Linux服务配置 (50)
  • Linux系统 (16)
  • OpenWrt路由 (3)
  • Shell脚本 (3)
  • 代码管理 (1)
  • 安防技术 (4)
  • 数据安全 (36)
  • 未分类 (1)
  • 网络协议 (25)
  • 计算机理论 (23)
  • 音视频技术 (5)
联系我们:📍 地址:中国·广东省深圳市   |   ✉️ 邮箱:support@tanglinux.com   |   💬 QQ:870866607
版权所有:老丹的足迹粤ICP备2026061170号-1       公安备案图标 粤公网安备44030002013274号