跨城区局域网的搭建(基于Docker)
概述:
当时家里和公司都有服务器,想统一管理,但两边的内网是隔离的。传统的端口映射太麻烦,而且有些服务不想暴露在公网。试过一些方案,最后觉得用 Docker OpenVPN 最省事。
选择 Docker OpenVPN 而不是传统 VPN,主要是因为容器化部署特别方便,证书管理也简单得多。最后的效果很好,通过域名就能访问远程内网的各种服务,省去了很多配置麻烦。
网络拓扑:
graph TD
classDef gateway fill:#e67e22,stroke:#d35400,color:#fff,stroke-width:2px
classDef lb fill:#2980b9,stroke:#2471a3,color:#fff,stroke-width:2px
classDef proxy fill:#5dade2,stroke:#2e86c1,color:#fff
classDef vpn fill:#5dade2,stroke:#2e86c1,color:#fff
classDef arm fill:#85c1e9,stroke:#2e86c1,color:#333
classDef isp fill:#76d7c4,stroke:#1abc9c,color:#333
classDef user fill:#bdc3c7,stroke:#7f8c8d,color:#333
subgraph overseas["海外服务区域"]
UserOut["外网用户"]:::user
OS["海外服务"]
CDN["CDN"]
SS1["SS Server 1"]:::proxy
SS2["SS Server 2"]:::proxy
UserOut -->|用户访问| OS
OS -->|用户访问| CDN
CDN -->|用户访问| SS2
OS -->|Shadowsocks| SS1
OS -->|Shadowsocks| SS2
end
GFW["GFW 防火墙"]:::gateway
LB["负载均衡"]:::lb
subgraph internal["内网核心区域"]
subgraph proxyVpn["接入代理 & VPN"]
UserIn["内网用户"]:::user
Nginx["Nginx"]:::proxy
Haproxy["Haproxy"]:::proxy
OpenVPN["OpenVPN"]:::vpn
UserIn -->|用户访问| Nginx
UserIn -->|用户访问| Haproxy
Nginx <-->|DockerNet| Haproxy
Haproxy -->|DockerNet| OpenVPN
end
subgraph armCluster["多线出口 ARM 集群"]
ARM1["ARM1"]:::arm --> Telecom["电信AD"]:::isp
ARM2["ARM2"]:::arm --> Mobile["移动AD"]:::isp
ARM3["ARM3"]:::arm --> Unicom["联通AD"]:::isp
end
Mickey["Mickey"]:::user
end
SS1 -->|Shadowsocks| GFW
SS2 -->|Shadowsocks| GFW
GFW -->|Shadowsocks| LB
LB -->|SS| ARM1
LB -->|SS| ARM2
LB -->|SS| ARM3
SS1 -.->|TCP BBR| GFW
GFW -.->|BBR| ARM1
GFW -.->|BBR| ARM2
GFW -.->|BBR| ARM3
OpenVPN -->|VPN 隧道| Mickey
Telecom -->|VPN| Mickey
Mobile -->|VPN| Mickey
Unicom -->|VPN| Mickey整体架构分三个区域:海外服务区通过 Shadowsocks 穿墙,经过 GFW 和负载均衡后分发到内网的 ARM 服务器集群,ARM 板子分别接电信、移动、联通三条线路做出口。内网通过 Nginx/Haproxy 做代理接入,OpenVPN 提供 VPN 隧道让个人用户远程访问。
代理流程:
graph TD
classDef client fill:#27ae60,stroke:#1e8449,color:#fff,stroke-width:2px
classDef proxy7 fill:#5dade2,stroke:#2471a3,color:#fff,stroke-width:1px
classDef streamSelect fill:#e67e22,stroke:#d35400,color:#fff,stroke-width:1px
classDef stream fill:#76d7c4,stroke:#1abc9c,color:#333,stroke-width:1px
classDef cache fill:#27ae60,stroke:#1e8449,color:#fff,stroke-width:2px
classDef lb7 fill:#e67e22,stroke:#d35400,color:#fff,stroke-width:1px
classDef backend fill:#27ae60,stroke:#1e8449,color:#fff,stroke-width:2px
classDef moduleBox fill:#ecf0f1,stroke:#7f8c8d,stroke-width:1px
subgraph L1_7["第一层代理 · 七层协议"]
direction TB
C["客户端"]:::client
A1["第一层代理"]:::proxy7
S1["指向第一层的 stream"]:::streamSelect
Cache["缓存"]:::cache
C --> A1
A1 --> S1
S1 --> Cache
end
subgraph L1_4["第一层代理 · 四层协议"]
direction TB
ST1["第一层 stream"]:::stream
SE1["选择下一层 stream"]:::streamSelect
S1 --> ST1
ST1 --> SE1
end
subgraph L2_4["第二层代理 · 四层协议"]
direction TB
ST2["第二层 stream"]:::stream
SE2["选择下一层 stream"]:::streamSelect
SE1 --> ST2
ST2 --> SE2
end
subgraph L3_7["第三层代理 · 七层协议"]
direction TB
A3["第三层代理"]:::proxy7
LB7["七层协议负载均衡"]:::lb7
Backend["后端系统"]:::backend
SE2 --> A3
A3 --> LB7
LB7 --> Backend
end具体流程:
购买云服务器部署docker
需要一台公网 VPS 作为中转,因为 OpenVPN 需要一个公网入口点让各个客户端连接。建议购买支持 systemctl 的Linux系统,比较好管理,并部署docker:
- 外挂存储格式化为xfs分区;
1 2# mkfs.xfs /dev/vdb5 # echo "/dev/vdb5 /mnt/data xfs defaults 1 1" |tee -a /etc/fstab- 调整docker的目录,两种方法;
- 挂载/var/lib/docker目录:
1 2 3 4 5 6# systemctl stop docker # mkdir -p /mnt/data/docker # rsync -aXS /var/lib/docker/. /mnt/data/docker/ # echo "/mnt/data/docker /var/lib/docker none bind 0 0"|tee -a /etc/fstab # mount -a # systemctl start docker- 指定具体目录: 在/etc/systemd/system/multi-user.target.wants/docker.service里面修改如下:
1ExecStart=/usr/bin/dockerd --storage-driver=overlay2 -g /mnt/hhd/docker安装docker-openvpn服务:
- Pick a name for the $OVPN_DATA data volume container, it will be created automatically. 给配置数据卷起个名字
1# OVPN_DATA="ovpn-data"- Initialize the $OVPN_DATA container that will hold the configuration files and certificates 初始化 PKI,生成证书体系
1 2 3 4 5 6# docker volume create --name $OVPN_DATA # docker run -v $OVPN_DATA:/etc/openvpn \ --rm kylemanna/openvpn ovpn_genconfig \ -u udp://VPN.SERVERNAME.COM # docker run -v $OVPN_DATA:/etc/openvpn \ --rm -it kylemanna/openvpn ovpn_initpki如果使用tcp
1 2 3# docker run -v $OVPN_DATA:/etc/openvpn \ --rm kylemanna/openvpn ovpn_genconfig \ -u tcp://VPN.SERVERNAME.COM:1443- Start OpenVPN server process 启动 OpenVPN 服务端
1 2 3# docker run -v $OVPN_DATA:/etc/openvpn -d \ -p 1194:1194/udp \ --cap-add=NET_ADMIN kylemanna/openvpnOR
1 2 3# docker run -v $OVPN_DATA:/etc/openvpn -d \ -p 1443:1194/tcp \ --cap-add=NET_ADMIN kylemanna/openvpnRunning a Second Fallback TCP Container
1 2 3 4# docker run -v $OVPN_DATA:/etc/openvpn \ --rm -p 1443:1194/tcp \ --privileged kylemanna/openvpn ovpn_run \ --proto tcp- Generate a client certificate without a passphrase . 生成客户端证书,不需要密码 Retrieve the client configuration with embedded certificates, “CLIENTNAME"可自定义;
1 2 3 4# docker run -v $OVPN_DATA:/etc/openvpn \ --rm -it kylemanna/openvpn easyrsa build-client-full CLIENTNAME nopass # docker run -v $OVPN_DATA:/etc/openvpn \ --rm kylemanna/openvpn ovpn_getclient CLIENTNAME > CLIENTNAME.ovpn- 增加路由规则,让 Docker 内部网络能访问到 VPN 客户端的节点 在docker主机上增加一条路由规则,目的是使其他容器可以通过默认的网络来访问到openvpn客户端的节点:
1# ip route add 192.168.255.0/24 via $DOCKER_OPENVPN_IP- 给客户端配置静态内外IP,确保每次连接 IP 不变,方便代理配置
1 2# cat ccd/CLIENTNAME ifconfig-push 192.168.255.10 192.168.255.9部署前端代理
- 选择DOCKER-CADDY做反向代理,主要原因是自动 HTTPS 和配置简单
| |
- CADDY的配置参考:
| |
部署TCP代理
- 选择DOCKER-HAPROXY并部署,适合处理数据库连接、SSH 等长连接服务
| |
映射后端端口
在 HAProxy 配置文件中添加后端服务映射,指定具体的 IP 地址和端口号,支持 TCP 和 HTTP 代理模式。配置完成后重启 HAProxy 服务使配置生效。