Docker 中部署 OpenWrt 实现宿主机网络转发

本文将详细介绍如何在 Docker 中部署 OpenWrt 并配置宿主机网络转发,使用 172.16.0.0/16 的内网环境。

docker 容器默认使用 172.17.0.0/16 以及之后的网段, 注意可能冲突

架构概述

Docker 容器中运行 OpenWrt 作为透明网关,使宿主机和局域网设备可以通过 OpenWrt 容器转发流量。架构特点包括:

  • 使用 Docker 运行完整的 OpenWrt 系统
  • Docker macvlan 网络:容器直接接入物理网络
  • 双 IP 设计:宿主机同时拥有主 IP 和 macvlan IP
  • OpenClash 使用 Fake-IP & TUN 模式

网络拓扑

[路由器]
│    
└─ LAN 口 (172.16.0.0/16)
    │
    ├─[宿主机 Debian]
    │  ├─ enp1s0 172.16.1.202 (主 IP)
    │  ├─ local-macvlan 172.16.0.248 (虚拟网卡, 桥接 enp1s0)    
    │  └─ Docker 服务
    │        └────┐
    ├─[Debian > Docker > OpenWrt]
    │  └─ eth0 172.16.0.254
    │
    └─[其他设备] 
       └─ 172.16.x.x

准备工作

系统要求

  • Linux 宿主机(本文以 Debian 为例)
  • DockerDocker Compose 已安装
  • 物理网卡 enp1s0 (根据实际情况调整)
  • Debian 网络使用 networking 配置 systemctl status networking

文件结构准备

创建以下目录结构:

~/openwrt-docker/
├── docker-compose.yml
├── openwrt/
│   ├── config/ # 持久化 openwrt 配置文件
│   └── core/   # 放置 openclash meta 内核文件 clash_meta

详细配置步骤

1. Docker Compose 配置

创建 docker-compose.yml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
services:

openwrt:
image: sulinggg/openwrt:amd64
container_name: openwrt
restart: always # 自动重启
privileged: true # 授予特权模式
networks:
openwrt_macnet:
ipv4_address: 172.16.0.254 # 固定容器 IP
command: /sbin/init # 初始化系统
cap_add: # 添加必要的能力
- NET_ADMIN # 网络管理
- SYS_MODULE # 加载内核模块
volumes: # 持久化存储
- /etc/localtime:/etc/localtime:ro # 时区同步
- /etc/timezone:/etc/timezone:ro
- ./openwrt/config:/etc/config # OpenWrt 配置
- ./openwrt/core:/etc/openclash/core # OpenClash 核心
environment:
TZ: Asia/Shanghai # 时区设置

networks:

openwrt_macnet:
driver: macvlan # 使用 macvlan 驱动
driver_opts:
parent: enp1s0 # 绑定物理网卡
macvlan_mode: bridge # 桥接模式
ipam: # IP地址管理
config:
- subnet: "172.16.0.0/16" # 子网
gateway: "172.16.0.1" # 网关指向路由器
ip_range: "172.16.0.254/32" # IP范围 (给 Openwrt 使用)

关键参数说明:

privileged: truecap_add 是容器获得完整网络功能所必需
macvlan 将物理网卡拆分成多个虚拟网卡的实现方案
macvlan_mode: bridge 使容器获得与宿主机同级的网络地位
固定 IP 172.16.0.254 作为网关地址(OpenWrt 的 IP)
使用 bridge 模式确保容器可以与其他设备通信

2. 宿主机网络配置

主网络接口配置

创建 /etc/network/interfaces.d/enp1s0.network

1
2
3
4
5
6
7
8
9
10
11
12
13
# 基本网络配置
allow-hotplug enp1s0 # 热插拔支持
auto enp1s0 # 自动启用接口

# 静态IP配置
iface enp1s0 inet static
address 172.16.1.202 # 宿主机主 IP
netmask 255.255.0.0 # 子网掩码
gateway 172.16.0.254 # 如果需要让宿主机自身流量经过 openwrt 则 gateway 设置为 openwrt 的 ip

# 启用混杂模式(允许接收所有流量)
up ip link set enp1s0 promisc on
down ip link set enp1s0 promisc off

macvlan 接口配置

创建 /etc/network/interfaces.d/local-macvlan.network

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# macvlan 接口配置
auto local-macvlan
iface local-macvlan inet static
address 172.16.0.248 # 宿主机在容器网络的IP
netmask 255.255.0.0

# 创建macvlan接口
pre-up ip link add local-macvlan link enp1s0 type macvlan mode bridge

# 添加容器路由
up ip route add 172.16.0.254 dev local-macvlan

# 调整默认路由
up ip route del default || true # 删除原默认路由
up ip route add default via 172.16.0.254 dev local-macvlan # 新默认路由

# 清理接口
post-down ip link del local-macvlan

双IP设计原理

主 IP(172.16.1.202) 为宿主机IP, 用于常规网络通信
local-macvlan 的 IP(172.16.0.248) 用作宿主机无法直接与 openwrt 容器 IP(172.16.0.254) 通信的三层路由方案

解决 Docker macvlan 网络无法与宿主机直接通信的问题

3. OpenWrt 网络配置

OpenWrt 的主要网络配置文件位于 /etc/config/network,我们需要配置以下内容:

1
2
3
4
5
6
7
8
9
config interface 'lan'
option type 'bridge' # 桥接模式
option ifname 'eth0' # 容器内网卡
option proto 'static' # 静态IP
option netmask '255.255.0.0' # 子网掩码
option ip6assign '60' # IPv6分配
option ipaddr '172.16.0.254' # 容器IP
option gateway '172.16.0.1' # 上行网关(路由器)
option dns '172.16.0.1' # DNS服务器

4. DNS 配置

修改宿主机 /etc/resolv.conf 以使用 OpenWrt 的 DNS 服务:

1
nameserver 172.16.0.254  # 指向 OpenWrt 容器

1. 要防止网络管理器覆盖, 可以看看 /etc/resolv.conf 文件是不是个 NetworkManager 的软链

当然直接软链删了直接创建个 /etc/resolv.conf 文件就能解决

像在某些系统上有 systemd-resolve 的, 会被接管 dns 到127.0.0.53, 可以直接把 systemd-resolve 关掉

2. 为防止网络管理器覆盖此配置,执行:

1
sudo chattr +i /etc/resolv.conf  # 设置不可变属性

部署与验证

1. 启动 OpenWrt 容器

1
2
# 进入 docker-compose.yaml 目录执行
docker compose up openwrt -d

2. 应用宿主机网络配置

1
sudo systemctl restart networking

3. 验证配置

检查容器网络

1
2
ip r # 看地址
ip a # 看路由

ip r 输出应该类似于:

default via 172.16.0.1 dev br-lan proto static 
172.16.0.0/16 dev br-lan proto kernel scope link src 172.16.0.254 
198.18.0.0/30 dev utun proto kernel scope link src 198.18.0.1 

应显示 eth0 地址为 172.16.0.254

检查宿主机路由表

1
ip route

输出应该类似于:

default via 172.16.0.254 dev local-macvlan 
172.16.0.0/16 dev enp1s0 proto kernel scope link src 172.16.1.202 
172.16.0.0/16 dev local-macvlan proto kernel scope link src 172.16.0.248 
172.16.0.254 dev local-macvlan scope link 

测试宿主机连接

1
2
ping 172.16.0.254  # 测试容器连通性
traceroute 8.8.8.8 # 检查路由路径

正常配置并成功启动 OpenClash 后, 验证宿主机流量转发情况

1
curl -vL google.com

验证 DNS 解析

1
dig @172.16.0.254 google.com  # 测试 DNS 解析

google.com DNS 若正常解析即实现本文宿主机的网络转发, 输出应该类似于:

; <<>> DiG 9.18.28-1~deb12u2-Debian <<>> @172.16.0.254 google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 59826
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; MBZ: 0x0001, udp: 1232
; COOKIE: d8dfbdf3bfbf757e (echoed)
;; QUESTION SECTION:
;google.com.                    IN      A

;; ANSWER SECTION:
google.com.             1       IN      A       198.18.0.5

;; Query time: 0 msec
;; SERVER: 172.16.0.254#53(172.16.0.254) (UDP)
;; WHEN: Fri Apr 01 13:35:00 CST 2025
;; MSG SIZE  rcvd: 67

--- END ---

写在最后

未尽事宜待补充, 如有疑问请联系站点概述内邮箱

感谢 kairlec 提供技术指导
本文由 DeepSeek V3 编写, solitude,kairlec 校对