家庭网络优化: 利用 FakeMesh + 802.11k/v/r 漫游做到全屋 WiFi 无死角覆盖
前言博主最近在思考如何优化自己的家庭网络环境时, 突然想到对家里的 WiFi 进行一个优化. 由于博主自己是一个人使用一台 AP 的, 所以对 WiFi 信号一直都觉得挺好的. 但是最近家里人和博主反映说家里的 WiFi 信号不是很好, 且走到某个房间里时就基本没信号了. 博主自己使用的是一台小米 AX3600, 但是家里其他人使用的却是一台很普通的 WiFi5 路由器. 所以博主打算给全屋 WiFi 进行一个更新.现状目前的 WiFi 环境是这样的: 博主自己使用 AX3600 拥有一个单独的 SSID, 发射功率和穿墙性能都还可以. 家里人使用的一台 ASUS WiFi5 路由器的功率和穿墙性能都不太行. 对于博主当前家里的这个户型来说很不友好.优化思路目前打算购置两台能刷 OpenWrt 软路由系统的硬路由, 然后配置 FakeMesh, 统一由主路由管理 SSID 和密码之类的配置. 然后两台路由器运行 802.11kvr (WiFi 漫游), 以此来实现自动分配信号更好的 AP. 在网上看了一圈, 发现小米 AX1800 刚好能满足这个需求, 且价格很便宜 (目前二手平台 40 左右就能买到), 所以买了两台 AX3600, 刷了 X-wrt, 准备来做这么一个配置. 当然博主自己使用的 AX3600 因为有特殊需求, 所以不参与这个网络的构建.注: 本文的方案适用于纯 OpenWrt 系列操作系统环境. 如果你想将你的 OpenWrt 路由器和其他硬路由 / AP 等设备进行 WiFi 漫游, 则可以参考 家庭网络更新 - 猫棒 + 802.11k/v/r 漫游 - Kagura iYoRoy开整1. 统一内网网段博主的规划是整个内网使用同一个网段, 因此所有路由器必须处于 AP 模式, 不再承担 DHCP Server 和 NAT 工作, 由主路由负责, 而不是 AP.启动 AP, 接入内网, 打开 WebUI.进入 Network -> Interface 页面, 找到并编辑 lan 接口. 将其 IPv4 地址设置为处于主网段但不与其他设备冲突的地址, 这里博主设置为 192.168.1.253. 然后将网关设置为主路由.切换到 DHCP Server 选项卡, 勾选 Ignore Interface 选项以完全禁用其 DHCP Server, 然后点击 Save 保存删除 wan 和 wan6 接口切换到 Devices 选项卡, 找到 br-lan 接口的 Configure 按钮并点击进行配置勾选所有物理接口和 AP 并单击保存如果你的情况和我一样, 这里列出的接口是 Switch VLAN 类型而不是 Ethernet Adapter, 可以通过这个方式来查找接口.切换到 Network -> Switch 页面从这里我们可以看出, LAN1, LAN2, LAN3 处于 VLAN 1 中, 模式为 Access, 由 CPU (eth0) 负责流量转发, WAN 处于 VLAN 2 中, 由 CPU (eth1) 负责流量转发. 所以上图中的 Switch VLAN: "eth0.1" 就是所有 LAN 口, Switch VLAN: "eth1.2" 就是 WAN 口.同时这种情况还可能导致下游设备没法拿到 IPv6 地址. 这里我们检查 eth0.1, eth1.2 的配置页面, 将 IPv6 Enabled 选项手动改为 enabled.做好所有配置后, 单击 Save&Apply 保存并应用.等待一段时间后, 尝试连接 WiFi, 查看 WiFi 属性可以看到这里已经成功拿到公网 IPv6 地址和 192.168.1.0/24 网段的 IPv4 地址.这样的配置在两台路由器上都需要进行2. 启用 FakeMeshFakeMesh 是 OpenWrt 提供的一个轻量级, 软件模拟的 Mesh 组网功能. 它能够做到在多台 AP 设备中进行统一的配置管理, 实现无缝漫游, 并且简化多路由网络管理.2.1 主路由配置进入 Mesh -> FakeMesh 页面, 勾选 Enable 启用功能在 Mesh ID 处创建当前 Mesh 会话的标识符. 这里建议不要填写英文字母, 下划线, 数字以外的特殊字符, 以免出现编码错误.在 Key 中创建当前 Mesh 会话的加密密钥在 Band 中选择 5G在 Role 处切换至 Controller (AC) 模式模式解释Wired AP 表示该设备为被控制有线 APAgent 表示该设备用于无线 Mesh 组网勾选 Sync Configuration 同步配置在 Band Steering Helper 中选择 usteerusteer: OpenWrt 官方的轻量级漫游辅助工具, 简单稳定DAWN: 一个功能更强大的第三方漫游辅助工具, 算法更复杂. 这里考虑到路由器的性能, 不做选择.None: 关闭漫游辅助功能关于 Controller Access IP: 这个地址是 FakeMesh 控制器设置的一个固定 IP, 方便你在局域网内访问它的 WebUI. 通常情况下不需要修改, 保持默认值 10.10.10.1 即可. 这个 IP 是 FakeMesh 在内部网络中用于服务发现的, 不会和你的主路由 LAN 口 IP 产生冲突. 平时管理主路由还是使用 LAN 口的 IP 去管理. 当然, 如果你的网络环境中 10.10.10.1 这个地址存在冲突, 那你当然可以换掉它. 但请记住在 AP 中配置时也要填写相应的 IP.关于 Disable Fronthaul: 该功能用于开关当前节点的 SSID 广播功能. 如果你不希望当前节点发射 SSID 信号, 则可以勾选它. 在博主的配置场景中, 主路由不具备 WiFi 信号发射功能, 所以这里可以勾选配置完成后点击 Save&Apply. 应用成功后, 下方的 Wireless Management 板块会出现一个 WiFi SSID. 这里我们点击编辑在弹出的对话框中配置你想要的 SSID, 加密方式, 频段和密码. 填写完成后再次点击保存并应用.到这里为止, 主路由 FakeMesh 的配置就结束了2.2 AP 配置进入 Mesh -> FakeMesh 页面, 勾选 Enable 启用功能在下面的信息中填入你刚才在主路由中配置的信息, 并将 Role 切换为 Wired AP (Ethernet backhaul) 模式信息填写完成后单击保存并应用.届时, AP 设备原有的 IP 地址将会失效, 此时需要进入主路由的 Mesh -> Usteer 页面查看可以看到这里有一台 AP 已经上线. 我们使用页面中所给的 192.168.1.182 访问 AP 的后台登录后台, 切换到 Network -> Interface 页面点击 meshx0 接口的编辑按钮, 将其从 DHCP Client 切换到 Static Address 协议输入你之前设定的 IP 地址信息, 然后点击保存并提交. 之后使用你设置的新 IP 访问 AP 后台.切换到 Network -> Wireless 查看 WiFi 发射信息. 如果看到刚才在主路由上配置的 SSID 有正确发送, 则证明配置成功.如果你配置完成后, AP 侧已经同步了相关配置信息, 但是就是没有发送 WiFi 信号. 别急, 这不是你的问题.经过分析, 这是由于主路由 FakeMesh 控制器的 wifim 配置中缺少 band 选项. 在 AP 侧, 生成 WiFi 信号的主要逻辑在 agent_sync_config 函数中: while uci get fakemeshac.@wifim[$J] &>/dev/null; do ssid=$(uci get fakemeshac.@wifim[$J].ssid) encryption=$(uci get fakemeshac.@wifim[$J].encryption) key=$(uci get fakemeshac.@wifim[$J].key) bands=$(uci get fakemeshac.@wifim[$J].band) # ← 关键！ enabled=$(uci get fakemeshac.@wifim[$J].enabled || echo 1) [ "$bands" = "2g5g" ] && bands="2g 5g" [ "$enabled" = "1" ] && { for band in $bands; do # ← band 为空则循环不执行！ for wbandstr in $(uci show wireless | grep .band=\'${band}\'$); do radio=$(echo $wbandstr | cut -d\. -f2) uci set wireless.wifinet$K=wifi-iface uci set wireless.wifinet$K.device="${radio}" # ... 设置 SSID、加密、802.11r/k/v 等 K=$((K+1)) done done } J=$((J+1)) done控制器的 wifim 配置没有 band 选项, 导致 AP 同步配置后无法匹配任何 radio, 客户端 WiFi 接口从未被创建. 初步判断这可能是 OpenWrt 系统的 Bug. 现在来进行修复登录主路由 SSH, 执行以下指令uci set fakemesh.@wifim[0].band="2g5g" uci commit fakemesh登录 AP SSH, 执行以下指令uci delete fakemesh.default.wifim_md5 uci commit fakemesh /etc/init.d/fakemesh restart等待 20s 左右, 让 FakeMesh 完成同步. 届时你就应该能看到 WiFi 信号正确创建.至此, AP 侧配置完成.相应的配置在两台 AP 上都需要执行.3. 测试在两台 AP 上都配置完成后, 再次打开主路由的 Network -> Usteer 页面. 应当能看到两台 AP 接入此时打开手机的 WiFi 页面检查, 理应能看到 4 个 AP 接入点. (双 2.5G, 双 5G)连接上网络后, 四处走动观察首选 AP 连接状况变化AP 1AP 2可以看到, AP 确实有在按照信号强度切换.最后使用 SpeedTest 对网络速率进行测试博主家里是 2000Mbps 宽带, 路由器的接口速率为 1Gbps, 这里局限于路由器性能, 跑到 700Mbps 属正常水平, 故测试正常!至此, 配置全部结束后记本人非专业人士, 如有错误或疏漏请直接指出! 感谢您的阅读!
整活向：使用 OSPFv3 + WireGuard 隧道让一个没有 IPv6 的网络环境也能使用 IPv6 上网
前言博主大学的校园网环境中没有公网 IPv6 出口. 对于博主这种玩网络的人来说十分的不便. 但是博主在学校和在家的主路由都是 OpenWrt, 且之间有一条 VPN 骨干网可以互相通信. 于是乎博主想到了一个方法: 使用类似 Overlay Network 的方式, 通过 OSPFv3 将学校主路由的 IPv6 默认路由下一跳改为家里的主路由, 将家里的 IPv6 网络给校园网用.配置需求对端网关可以互相通信 (可通过 EasyTier, ZeroTier, TailScale 等 VPN)双方网关为软路由系统或可玩性较高的硬路由基础配置思路由于没有 IPv6 环境, 但是 OSPFv3 又需要使用 IPv6 来进行通信, 所以这里我们借助 WireGuard 来建立一条 6in4 通道. 然后使用 Bird v2 来运行 OSPFv3, 将家庭宽带的 ::/0 路由引入该 OSPF 会话, 从而达到校园网网关使用这条线路进行 IPv6 上网的目的.由于该文章编写于暑期阶段, 因此这里将使用 Debian 13 虚拟机作为校园网网关. 该虚拟机已经在内网接口关闭了 IPv6 功能, 因此无法直接通过内网连接公网 IPv6 业务.开整1. 安装并配置 Bird v2由于新版 OpenWrt 系统内置 WireGuard, 因此我们这里只需要另外安装 Bird 即可.进入 WebUI, 打开 System -> Software 页面, 搜索 Bird 并安装 bird2, bird2c安装完成后打开 SSH, 查询 Bird 安装状态root@X-wrt-Home:~# bird --version BIRD version 2.19.1移动配置文件位置并更改配置文件存储位置随着配置愈发复杂, 单个配置文件是无法满足我们的配置要求的. 所以这里我们使用 /etc/bird 来作为配置文件的存储文件夹.实际上, 除了 OpenWrt, 常规 Linux 安装 Bird 时都是采用 /etc/bird 作为配置文件目录mkdir /etc/bird mv /etc/bird.conf /etc/bird/bird.conf vim /etc/init.d/bird将其中的 BIRD_CONF 赋值为 /etc/bird/bird.conf更改完后保存并重启 Bird 进程/etc/init.d/bird restart2. 配置 WireGuard 接口OpenWrt 默认安装 WireGuard, 但没有默认的私钥和公钥, 所以这里我们自己生成进入 SSH, 生成公钥和私钥cd /etc/wireguard wg genkey | tee privatekey | wg pubkey > publickeyOpenWrt 原生支持在 WebUI 中配置和管理 WireGuard 接口, 所以我们这里直接在 WebUI 上进行配置.打开 WebUI, 切换至 Network -> Interfaces, 单击 Add new interface...在 Name 中输入该 WireGuard 接口的名称, 注意不要太长. 然后将 Protocol 切换至 WireGuard VPN在 Private Key 和 Public Key 中填入我们刚刚生成的密钥在 Listen Port 中填入可供 WireGuard 监听的空闲端口在 IP Addresses 中填入一个CIDR 格式的链路本地 IPv6 地址和一个符合 ULA 规范的 IPv6 地址我们这里因为接入的设备不多, 所以即使在考虑冗余的情况下, 给一个 /112 的网段也是绰绰有余了.示例配置切换至 Peers 页面, 点击 Add peer在 Description 中填入对端设备名称在 Public Key 中填入对端设备公钥在 Allowed IPs 中填入对端 WireGuard 内网 IPv6 地址和 ::/0在 Endpoint Host 中填入校园网网关的 VPN 内网地址在 Endpoint Port 中填入校园网网关的 WireGuard 监听地址因为我们还未配置校园网网关接口, 所以这里直接随便写一个端口即可.在 Persistent Keep Alive 中填入 25示例配置配置完成后保存提交相同的配置需要在校园网网关上也配置一遍双方都配置好后, 检查接口状态. 如果接口的 TX 和 RX 都有流量, 那就证明配置成功了. 此时可以进行测试.root@Debian:/etc/wireguard# ping fe80:1::1%tunnel_ipip6 PING fe80::1%tunnel_ipip6 (fe80:1::1%tunnel_ipip6) 56 data bytes 64 bytes from fe80:1::1%tunnel_ipip6: icmp_seq=1 ttl=64 time=0.812 ms 64 bytes from fe80:1::1%tunnel_ipip6: icmp_seq=2 ttl=64 time=0.614 ms 64 bytes from fe80:1::1%tunnel_ipip6: icmp_seq=3 ttl=64 time=0.750 ms 64 bytes from fe80:1::1%tunnel_ipip6: icmp_seq=4 ttl=64 time=1.25 ms ^C --- fe80:1::1%tunnel_ipip6 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3052ms rtt min/avg/max/mdev = 0.614/0.857/1.254/0.239 ms可以看到连接是没有问题的, 至此 WireGuard 侧配置完成.3. 配置 OSPF在这两台设备上创建 bird.confvim /etc/bird/bird.conf按照该模板填写log syslog all; define ROUTER_ID = <ROUTER_ID> protocol device { scan time 10; }; protocol direct { ipv4; ipv6; }; include "kernel.conf"; include "ospf.conf";ROUTER_ID 当前路由器的 Router ID. 需要双方不同且唯一. 值形式与 IPv4 类似, 均为 xxx.xxx.xxx.xxx. 这里可以直接填写本机的内网 IPv4 地址.分别创建 kernel.confprotocol kernel kernel_v4{ ipv4 { import none; export filter { if source = RTS_STATIC then reject; accept; }; }; } protocol kernel kernel_v6 { ipv6 { import none; export filter { if source = RTS_STATIC then reject; accept; }; }; }该配置文件旨在让 Bird 将动态路由协议 (如 BGP, OSPF) 学习到的路由写入系统内核, 但拒绝将自己配置文件中写死的静态路由写入内核.分别创建 ospf.conf校园网网关protocol ospf v3 ospf_4to6 { router id ROUTER_ID; ipv6 { import all; export none; }; include "/etc/bird/ospf/0.conf"; }该配置文件旨在让 Bird 从该 OSPF 会话中引入所有学习到的路由, 但不导出任何路由到该 OSPF 会话中.家庭网关filter export_default { if net ~ [ ::/0 ] then { accept; } else { reject; } } protocol ospf v3 ospf_4to6_test { router id ROUTER_ID; ipv6 { import none; export filter export_default; }; include "/etc/bird/ospf/0.conf"; }该配置文件旨在让 Bird 将 ::/0 的默认路由导入 OSPF 中, 但不导出任何学习到的路由到系统内核中.分别创建 OSPF 会话配置文件校园网网关mkdir ospf vim ospf/0.conf配置文件area 0.0.0.0 { interface "tunnel_ipip6" { type ptmp; ttl security tx only; neighbors { fd00:1::1; }; }; };家庭网关mkdir ospf vim ospf/0.conf配置文件area 0.0.0.0 { interface "tunnel_ipip6" { type ptmp; ttl security tx only; neighbors { fd00:1::2; }; }; };Q. 为什么使用 ULA 地址建立连接却还需要 Link-Local 地址?A. 因为 OSPFv3 邻居发现依赖链路本地地址 (Link-Local）, 协议报文以 Link-Local 为源/目的进行邻接建立; 全局或 ULA 地址可以用于配置, 管理或路由条目. 但不能替代接口上的 Link-Local 地址参与邻接建立, 但是公网会话却需要使用符合 ULA 规范的地址. 因此虽然 OSPF 配置文件中使用 ULA 地址, 但实际连接时 Bird 会检查该接口是否拥有 Link-Local 地址. 如果没有, OSPF 连接则会失败.分别应用配置birdc c检查会话状态birdc s p如果 OSPF 已经处于 Running 状态, 则配置完成root@Debian:/etc/bird# birdc s p BIRD 2.19.1 ready. Name Proto Table State Since Info device1 Device --- up 14:40:45.408 direct1 Direct --- up 14:40:45.408 kernel_v4 Kernel master4 up 14:40:45.408 kernel_v6 Kernel master6 up 14:40:45.408 ospf_4to6 OSPF master6 up 14:46:00.698 Running3. 配置防火墙到达这一步, 我们已经拥有了 IPv6 默认路由, ULA 地址, Link-Local 地址. 但我们仍然无法上网. 为什么呢? 这是因为在家庭网关侧我们需要启用 nat6 才可以让本地数据包正确的发往公网, 同时公网返回的数据包也可以正确的到达本地设备. 因为 OpenWrt 可以使用 WebUI 配置, 所以这里我们全程使用 WebUI.打开 Network -> Firewall 页面, 在下方的 Zones 区域点击 wan 区域的编辑按钮在 Advanced Setting 中勾选 IPv6 Masquerading, 然后保存提交点击左下角 Add 按钮, 命名为 WireGuard, 将所有规则都选择 accept, Covered networks 选择之前创建的 WireGuard 接口, Allowed forwared to destination zones 和 Allowed forwared to source zones 全部勾选. 之后同样切换到 Advanced Settings 中打开 IPv6 Masquerading, 保存提交切换到 NAT Rules 选项卡, 点击 Add 按钮新建规则规则名称命名为 nat6_ipip6Tunnel, Restrict to address family 选择 IPv6 Only. Outbound Zone 选择 WireGuard. Source address 填入 fd00:1::/112 (即为刚才填写的 ULA 地址段), Action 选择 MASQUERADE, 保存提交.至此, 防火墙配置完成4. 验证到了这里, 我们已经完成了所有配置. 现在我们来验证校园网网关是否可以 IPv6 上网.IPv6 地址查询root@Debian:/etc/bird# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host noprefixroute valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether bc:24:11:41:d0:1c brd ff:ff:ff:ff:ff:ff altname enp0s18 altname enxbc241141d01c inet 192.168.1.21/24 brd 192.168.1.255 scope global eth0 valid_lft forever preferred_lft forever 3: tun0: <POINTOPOINT,UP,LOWER_UP> mtu 1360 qdisc fq_codel state UNKNOWN group default qlen 500 link/none inet 10.0.64.21/24 brd 10.0.64.255 scope global tun0 valid_lft forever preferred_lft forever inet6 fe80::617b:570b:ef5a:a679/64 scope link stable-privacy proto kernel_ll valid_lft forever preferred_lft forever 5: tunnel_ipip6: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000 link/none inet6 fd00:1::2/112 scope global valid_lft forever preferred_lft forever inet6 fe80:1::2/112 scope link valid_lft forever preferred_lft forever可以看到这里 eth0 接口是没有公网 IPv6 地址的连通性测试root@Debian:/etc/bird# ping www.baidu.com -6 PING www.baidu.com (2409:8c20:6:123c:0:ff:b0f6:b2d) 56 data bytes 64 bytes from 2409:8c20:6:123c:0:ff:b0f6:b2d: icmp_seq=1 ttl=52 time=14.3 ms 64 bytes from 2409:8c20:6:123c:0:ff:b0f6:b2d: icmp_seq=2 ttl=52 time=13.8 ms 64 bytes from 2409:8c20:6:123c:0:ff:b0f6:b2d: icmp_seq=3 ttl=52 time=13.7 ms 64 bytes from 2409:8c20:6:123c:0:ff:b0f6:b2d: icmp_seq=4 ttl=52 time=14.0 ms 64 bytes from 2409:8c20:6:123c:0:ff:b0f6:b2d: icmp_seq=5 ttl=52 time=14.0 ms ^C --- www.baidu.com ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4005ms rtt min/avg/max/mdev = 13.671/13.957/14.308/0.208 ms可以看到这里成功访问了百度的 IPv6 地址, 连通性正常!网络路径查询root@Debian:/etc/bird# traceroute www.baidu.com -6 traceroute to www.baidu.com (2409:8c20:6:123c:0:ff:b0f6:b2d), 30 hops max, 80 byte packets 1 _gateway (fd00:1::1) 0.873 ms 0.856 ms 0.825 ms 2 ah-ipv6 (2409:***) 2.385 ms 2.360 ms 2.312 ms 3 * ah-ipv6 (2409:***) 3.636 ms * 4 2409:*** (2409:***) 5.922 ms * * 5 2409:*** (2409:***) 12.053 ms * 12.465 ms 6 2409:*** (2409:***) 14.650 ms * * 7 2409:*** (2409:***) 10.908 ms 2409:*** (2409:***) 10.122 ms 2409:*** (2409:***) 8.499 ms ...可以看到, 这里的第一跳就是我们刚才通过 OSPF 配置的 Peer! 至此, 配置全部结束!后记本文为整活性质想法, 如果你真的有类似需求, 你或许应该寻找更加体面的方法. 本人非专业人员, 如有错误或疏漏欢迎指出! 感谢阅读.
HomeLab 实战：使用 LACP 聚合双千兆网口，实现 2Gbps 网络带宽
前言博主的 HomeLab 网络环境是支持全链路 10 Gbps 的. 但是博主的 SubSerer 的单个端口却只有 1 Gbps. 由于该服务器负责的是业务流量转发, 其中包含网络代理. 在入户带宽 2000 Mbps 的情况下, 1 Gbps 的单口带宽就显得有些差强人意. 但好在这台服务器有两个这样的 1 Gbps 端口, 博主所使用的交换机也原生支持 LACP. 所以这篇文章就记录了博主尝试通过 LACP 对两个 1 Gbps 接口做链路聚合的过程.什么是链路聚合?简单来说, 链路聚合就是把两台设备之间的多条物理链路聚合在一起, 当做一条逻辑链路来使用. 通过这种技术, 我们不仅可以提升链路总带宽, 我们还可以增强业务 SLA, 随便坏其中任意一条链路都不影响业务可用性.什么是 LACP?LACP 是一种 基于 IEEE 802.3ad 标准的实现链路动态聚合与解聚合的协议. 通过这种协议, 我们可以很轻松的在终端和网络设备之间进行链路聚合, 且能够获得极高的稳定性. 但这种方法也有弊端: 只有终端和网络设备均支持 802.3ad 协议的情况下才可以使用, 这就造成了某些使用廉价 Layer 2 交换机或非网管交换机的用户群体无法使用该协议. 在本篇文章中, 我们只在双方都支持 LACP 的情况下进行配置.开整1. 配置交换机 LACP博主这里使用的是 D-Link 品牌的交换机, 直接使用 Web UI 进行配置. 虽然配置过程可能不同, 但 LACP 协议的基础原理都是通用的. 使用其他品牌交换机的读者也可以进行参考.启动交换机 Web UI, 进入 Link Arrangement 页面的 Port Trunking Settings 子页面.我们这里使用 40 和 48 号端口连接服务器, 所以这里勾选 40, 48 号端口.Group ID 填写的是该 LACP 会话的组 ID. 该参数的作用是标识不同的 LACP 会话, 从而达到一台交换机负责多个 LACP 连接的目的. 这里我们填写 1.Type 标识的是该 Trunk 端口的类型. 有两种可供选择, 分别为 Static 和 LACP, 这里我们选择 LACP.Master Port 标识的是该 LACP 会话的主要端口. 我们需要选择我们做聚合的其中一个端口, 这里我们选择 40 号端口.State 标识的是 LACP 会话的状态. 这里我们选择 Enabled.Trap 标识的是当前 LACP 会话是否开启 SNMP Trap 监听. 如果您的网络环境中存在 SNMP 网关服务器, 且进行了相关配置, 您可以选择 Enable.配置完成后我们单击 Add, 然后切换至 LACP Port Settings 子页面.在这里我们需要将 40 和 48 号端口的 Activity 配置为 Active, 即激活状态.配置完成后, 交换机侧的配置就彻底结束了.2. 配置服务器 LACP博主这里使用的是基于 Debian 13 的 Proxmox 9.2.3 系统.安装管理绑定的工具 ifenslavesudo apt update sudo apt install ifenslave加载指定内核模块, 让内核支持网卡绑定功能sudo modprobe bonding打开 /etc/network/interfaces 文件并按照指示编辑将你的物理网卡 (如 eth0, eth1) 设置为手动模式, 并指定它们属于哪个逻辑 bond 接口.auto eth0 iface eth0 inet manual bond-master bond0 auto eth1 iface eth1 inet manual bond-master bond0配置逻辑接口 bond0, 包括 bonding 的各项参数.auto bond0 iface bond0 inet manual bond-slaves eno1 eno2 bond-mode 802.3ad # 等价于 4 bond-miimon 100 bond-xmit-hash-policy layer3+4如果您使用的是常规 Linux 而非 Proxmox VE, 则这么配置auto bond0 iface bond0 inet static address 192.168.1.100 # 你的服务器IP netmask 255.255.255.0 # 你的网络子网掩码 gateway 192.168.1.1 # 你的网关地址 bond-slaves eth0 eth1 # 指定绑定的物理网卡 bond-mode 4 # 指定 bonding 模式 bond-miimon 100 # 链路检查间隔(毫秒) bond-xmit-hash-policy layer3+4 # 负载均衡的哈希策略关于 bond-mode模式名称效果与说明适用场景mode=4802.3ad (LACP)专业级链路聚合, 能够叠加速率并提供负载均衡和容错. 需要交换机也支持 LACP.服务器与交换机均支持的理想生产环境mode=0balance-rr (轮询)简单负载均衡, 能叠加速率, 不依赖交换机. 但可能导致数据包乱序.希望无需调整交换机就能获得较好性能的场景. 理论上无需 LACP, 但通常仍建议交换机支持静态聚合, 否则兼容性有限mode=6balance-alb (自适应)无需交换机支持, 即可实现发送和接收的负载均衡, 理论上能叠加速率希望无需调整交换机就能获得较好性能的场景mode=1active-backup (主备)仅提供冗余, 不叠加带宽. 同一时间只有一个网卡工作.只希望提高网络 SLA, 不追求提升速率配置 vmbr0 网卡如果您配置的是普通 Linux 而非 Proxmox VE, 则可以不用配置这一步auto vmbr0 iface vmbr0 inet static address 192.168.1.100/24 # 服务器 IP 地址 gateway 192.168.1.1 # 服务器网关 bridge-ports bond0 # 关键：网桥连接至 bond0 bridge-stp off bridge-fd 0完整配置示例# network interface settings; autogenerated # Please do NOT modify this file directly, unless you know what # you're doing. # # If you want to manage parts of the network configuration manually, # please utilize the 'source' or 'source-directory' directives to do # so. # PVE will preserve these directives, but will NOT read its network # configuration from sourced files, so do not attempt to move any of # the PVE managed interfaces into external files! source /etc/network/interfaces.d/* auto lo iface lo inet loopback auto eno1 iface eno1 inet manual bond-master bond0 auto eno2 iface eno2 inet manual bond-master bond0 auto bond0 iface bond0 inet manual bond-slaves eno1 eno2 bond-mode 802.3ad bond-miimon 100 bond-xmit-hash-policy layer3+4 auto vmbr0 iface vmbr0 inet static address 192.168.1.5/24 gateway 192.168.1.1 bridge-ports bond0 bridge-stp off bridge-fd 0 配置完成后, 对网络进行热重载ifreload -a如果您正在配置普通 Linux, 则使用该指令systemctl restart networking重载完成后, 检查 Web UI 上的配置. 如果没问题, 则配置完成3. 测试因为 LACP 是基于流量哈希计算的链路聚合, 所以在单线程下, 带宽仍然是单条链路的带宽. 因此我们这里使用多线程的 iperf3 进行测试iperf3 -c 192.168.1.5 -P 2这里可以看到, 总带宽已经达到 2 Gbps 的标准. 由此可以得出, LACP 聚合的配置已经成功!如果您使用的是企业交换机, 不要忘了保存配置后记本人非专业人员, 如有错误或疏漏请指出! 感谢您的阅读!
工程实践教程：基于 IX 前置与 OSPF 动态路由的 IEPL 专线网络搭建方案
前言博主拥有自己的境外服务器, 用于完成自己的业务: Samba 文件备份恢复, 自建网站 CDN, DN42 路由等. 但在这之中, 某些 VPS 的线路并没有对国内有特殊优化, 这导致国内直连的体验非常的糟糕, 同时业务搭建的体验也非常的不好. 这使得博主打算通过购买 IEPL/IPLC 之类的专线业务来提升自己的业务使用体验. 这篇文章将会描述博主搭建专线业务网络的整个过程, 来给各位打算购买专线服务的做一个参考.配置需求可接入 IX 网络的国内前置服务器境外专线 VPS境外普通 VPS善于思考的大脑专有名词解释IEPL (International Ethernet Private Line) / IPLC (International Private Leased Circuit)国际以太网专线, 跨越国家或地区的端到端数字专有带宽连接的租用服务. 不同于普通线路的国际出口, IEPL 和 IPLC 通常拥有非常高的 SLA 保障, 可以做到无论网络是否处于高峰期, 都能够跑满线路的签约速率, 且丢包率通常极低, 甚至在日常业务的使用中可以忽略不记. 这两类线路的延迟也可以做到非常低, 在我们比较熟悉的沪日专线中, 延迟可以做到 ~30ms; 广港因为物理距离近, 延迟更是低到只有 ~3ms. 同时, 由于这两类线路的特殊性质, 其网络通常不受长城防火墙 (Great Fire Wall) 限制, 而是由监管更加严格的傲盾来进行流量监控. 因此, 要想在这样的线路上搭建梯子等违法业务, 是一件很困难的事.IEPL / IPLC 有两种接入方式, 一种为通过 IX 前置接入, 一种为直接接入. 前者无法直接被普通家用/商用宽带访问, 需要可以接入 IX 的云服务器作为跳板机使用. 后者则可以直接在任意网络环境中直接接入.IX (Internet Exchange Point)IX (Internet Exchange Point), 可以理解为一个大型的网络枢纽, 各大云厂商都接入其中, 彼此可以直接且高速地进行数据交换. 同时, 因为 IEPL/IPLC 线路的特殊性, 在国内通常不直接提供入口, 而是接入 IX, 仅由有资质的云厂商提供服务. 所以, 我们普通的家庭网络或者企业网络, 要想使用 IEPL/IPLC 提供的服务, 就必须需要 IX 前置来作为跳板机使用.法律声明本文仅作技术分享, 不教学任何违法违规的技术, 也请勿将本文中提到的技术用于违法违规业务的搭建与使用! 本文读者所造成的一切行为与本文, 本站以及博主无关!开整1. 购买 IX 前置目前, 国内可以接入 IX 的云厂商总共有以下这些阿里云 (Alibaba Cloud)腾讯云 (Tencent Cloud)华为云 (Huawei Cloud)火山引擎 (Volcano Engine)百度智能云 (Baidu Cloud)UCloud (UCloud)下面是它们的优势与劣势云厂商核心优势主要劣势 / 注意事项火山引擎延迟低流量费用高昂阿里云 / 腾讯云成本低, 常有高性价比套餐带宽可能受限, 存在稳定性风险, 且审查更为严格华为云 / 百度云 / UCloud作为备选方案, 可以覆盖更多场景信息相对较少, 具体性能和特定 IX 节点的兼容性需要自行验证数据仅供参考, 具体请以实际体验为准本篇文章选用阿里云的上海轻量服务器和 Debian 系统作为演示2. 搭建 WireGuard VPN 网络这一步的目的是为了让我们自己的设备和境外 VPS 建立直接连接, 使得我们可以直接访问到境外 VPS.2.1 安装 WireGuard我们需要安装 wireguard-tools 软件包, 使用这条指令安装apt install wireguard wireguard-tools -y使用这条指令确认 WireGuard 是否安装成功wg --version示例输出wireguard-tools v1.0.20210914 - https://git.zx2c4.com/wireguard-tools/安装完成后, 生成 WireGuard 密钥对cd /etc/wireguard wg genkey | tee privatekey | wg pubkey > publickey这样的操作需要在你的 IX 前置, 本地服务器, 境外 IEPL VPS 上都执行一遍2.2 规划 VPN 网络在本文的使用环境中, 博主需要建立一共包含多台设备的 WireGuard VPN 网络. 主要设备分别为 IX 前置, IEPL 服务器*, 个人内网网关, 此外还需要包含一些其他境外普通服务器. 所以这里我们直接给 /24 的一整个段.VPN 网络的地址段必须基于内网地址, 且不应与系统路由表冲突. 这里我们给与 10.0.1.0/24 这一段的地址.由于博主在建立这条 WireGuard VPN 网络前, 还早已建立了一条 EasyTier 骨干网. 所以这里 IP 地址的最后 8 bit 也按照 EasyTier 骨干网段的地址给. 因此, IEPL 的地址就是 10.0.1.106, IX 前置的地址为 10.0.1.1, 个人内网网关的地址为 10.0.1.254.设计完成后, 正式开始建立 Peer.2.3 建立 WireGuard Peer2.3.1 建立专线 Peer因为 IX 类型 IEPL / IPLC 的特殊性, 这里我们需要现在专线机器上创建 WireGuard 服务端, 来让 IX 前置作为客户端接入在专线设备上建立 WireGuard 接口cd /etc/wireguard vim infra-ixpeer.confinfra-ixpeer 作为 WireGuard 接口名称, 您可以自行定义, 但注意不要太长.在配置文件中按照这个模板进行创建[Interface] Address = <ipAddr> ListenPort = <port> PrivateKey = <privKey> MTU = 1420 [Peer] PublicKey = <ix_pubKey> AllowedIPs = <vpn_net_CIDR> PersistentKeepalive = 25ipAddr 专线服务器 VPN 内网地址port 专线服务器监听端口. 注意: 专线服务器的 IX 入口通常是 NAT 类型, 且只有 ~1000 个端口可以直接连接. 这里的监听端口一定要在这 ~1000 个可以直接连接的端口中选用privKey 专线服务器 WireGuard 私钥ix_pubKey IX 前置的 WireGuard 公钥vpn_net_CIDR CIDR 格式的整个 VPN 内网地址段示例配置[Interface] Address = 10.0.1.106/32 ListenPort = 16601 PrivateKey = *** MTU = 1420 [Peer] PublicKey = *** AllowedIPs = 10.0.1.0/24 PersistentKeepalive = 25配置开机自动启动并启动端口systemctl enable --now wg-quick@infra-ixpeer创建完成后可以输入 wg 来查看接口是否生效root@NB-JPIEPL01:/etc/wireguard# wg interface: infra-aliyun public key: (hidden) private key: (hidden) listening port: 16601 peer: (hidden) endpoint: 172.16.1.2:51132 allowed ips: 10.0.1.0/24 latest handshake: 57 seconds ago transfer: 430.17 KiB received, 119.27 KiB sent persistent keepalive: every 25 seconds如果可以正常显示接口, 则证明已经生效. 至此, 专线服务器部分配置完成.2.3.2 建立个人网关 Peer我们自己的服务器也需要和 IX 前置进行 Peer. 但由于处于家庭宽带/普通企业宽带中的设备通常处于 NAT 环境, 因此这里作为客户端建立单向 WireGuard 连接在个人服务器上创建 WireGuard 端口cd /etc/wireguard vim infra-ixpeer.conf在配置文件中按照这个模板进行创建[Interface] PrivateKey = <privKey> Address = <ipAddr> MTU = 1420 [Peer] PublicKey = <ix_pubKey> Endpoint = <ix_endpoint> AllowedIPs = <allowedIPs> PersistentKeepalive = 25privKey 当前服务器 WireGuard 私钥ipAddr 当前服务器 VPN 内网地址ix_pubKey IX 前置 WireGuard 公钥ix_endpoint IX 前置 WireGuard 监听地址allowedIPs 允许通过 WireGuard 接入的 IP 地址段示例配置[Interface] PrivateKey = *** Address = 10.0.1.254/32 MTU = 1420 [Peer] PublicKey = *** Endpoint = 10.0.64.1:51132 AllowedIPs = 10.10.64.0/24, 10.0.1.0/24 PersistentKeepalive = 25配置开机自动启动并启动端口systemctl enable --now wg-quick@infra-ixpeer创建完成后可以输入 wg 来查看接口是否生效root@X-wrt-Home:/etc/wireguard# wg interface: jp_iepl01 public key: (hidden) private key: (hidden) listening port: 46997 peer: (hidden) endpoint: 8.***.***.***:51132 allowed ips: 10.10.64.0/24, 10.0.1.0/24 latest handshake: 1 minute, 14 seconds ago transfer: 402.16 KiB received, 149.66 KiB sent persistent keepalive: every 25 seconds至此, 个人网关 Peer 就配置完成了.2.3.3 建立 IX Peer我们的目的是要让 IX 前置作为一跳 (hop) 去为我们自己的机器提供路由, 所以这里我们使用 IX 前置作为控制平面, 来与其他设备建立 Peer.在 IX 前置中创建 WireGuard 接口cd /etc/wireguard vim infra-ixpeer.conf在配置文件中按照这个模板进行创建[Interface] PrivateKey = <privKey> Address = <ipAddr>/32 ListenPort = <port> MTU = 1420 PostUp = iptables -I FORWARD -i %i -j ACCEPT PostUp = iptables -I FORWARD -o %i -j ACCEPT PostDown = iptables -D FORWARD -i %i -j ACCEPT 2>/dev/null || true PostDown = iptables -D FORWARD -o %i -j ACCEPT 2>/dev/null || true [Peer] PublicKey = <iepl_pubKey> Endpoint = <iepl_endpoint> AllowedIPs = <iepl_allowedIPs> PersistentKeepalive = 25 [Peer] PublicKey = <pubKey> AllowedIPs = <allowedIPs> PersistentKeepalive = 25privkey 本机 WireGuard 私钥ipAddr 本机 WireGuard 私有地址port 本机 WireGuard 流量监听地址iepl_pubkey IEPL 服务器 WireGuard 公钥iepl_endpoint IEPL 服务器的 WireGuard 监听地址iepl_allowedIPs 本机所允许的专线服务器输入 IP 地址段. 如果您只想和 IEPL 服务器做通信, 则填入专线服务器的 WireGuard 私有地址即可, 如果想让专线服务器作为路由设备, 则需要填写对应的网段. 所有 IP 地址都应符合 CIDR 格式. 本文对应的情况为后者.pubkey 您的个人境内私有服务器/网关设备 WireGuard 公钥allowedIPs 您的个人境内私有服务器/网关设备 WireGuard 地址示例配置[Interface] PrivateKey = *** Address = 10.0.1.1/32 ListenPort = 51132 MTU = 1420 PostUp = iptables -I FORWARD -i %i -j ACCEPT PostUp = iptables -I FORWARD -o %i -j ACCEPT PostDown = iptables -D FORWARD -i %i -j ACCEPT 2>/dev/null || true PostDown = iptables -D FORWARD -o %i -j ACCEPT 2>/dev/null || true [Peer] PublicKey = *** Endpoint = 114.***.***.***:16601 AllowedIPs = 10.10.64.0/24, 192.168.0.0/16, 10.0.1.106/32 PersistentKeepalive = 25 [Peer] PublicKey = *** AllowedIPs = 10.0.1.254/32 PersistentKeepalive = 25配置开机自动启动并启动端口systemctl enable --now wg-quick@infra-ixpeer创建完成后可以输入 wg 来查看接口是否生效root@Aliyun-SH:/etc/wireguard# wg interface: bridge_iepl public key: (hidden) private key: (hidden) listening port: 51132 peer: (hidden) endpoint: 114.***.***.***:16601 allowed ips: 10.10.64.0/24, 192.168.0.0/16, 10.0.1.106/32 latest handshake: 18 seconds ago transfer: 120.07 KiB received, 433.36 KiB sent persistent keepalive: every 25 seconds peer: (hidden) endpoint: 112.***.***.***:40849 allowed ips: 10.0.1.254/32 latest handshake: 35 seconds ago transfer: 151.40 KiB received, 402.07 KiB sent persistent keepalive: every 25 seconds如果两个接口的 transfer 字段中 received 前有流量, 则证明通信正常. 此时可以通过 ping 命令验证通信root@Aliyun-SH:/etc/wireguard# ping 10.0.1.106 PING 10.0.1.106 (10.0.1.106) 56(84) bytes of data. 64 bytes from 10.0.1.106: icmp_seq=1 ttl=64 time=4.53 ms 64 bytes from 10.0.1.106: icmp_seq=2 ttl=64 time=4.19 ms 64 bytes from 10.0.1.106: icmp_seq=3 ttl=64 time=4.22 ms 64 bytes from 10.0.1.106: icmp_seq=4 ttl=64 time=4.30 ms ^C --- 10.0.1.106 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3003ms rtt min/avg/max/mdev = 4.190/4.309/4.527/0.131 ms root@Aliyun-SH:/etc/wireguard# ping 10.0.1.4 PING 10.0.1.4 (10.0.1.4) 56(84) bytes of data. 64 bytes from 10.0.1.4: icmp_seq=1 ttl=64 time=17.5 ms 64 bytes from 10.0.1.4: icmp_seq=2 ttl=64 time=17.3 ms 64 bytes from 10.0.1.4: icmp_seq=3 ttl=64 time=17.6 ms 64 bytes from 10.0.1.4: icmp_seq=4 ttl=64 time=17.4 ms ^C --- 10.0.1.4 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3005ms rtt min/avg/max/mdev = 17.339/17.471/17.647/0.120 ms可以看到, 两个 Peer 已经成功建立并正常通信. 因为博主这里是建立在网关上的, 所以可以直接使用下级设备尝试与专线服务器进行通信一切正常!2.3.4 建立境外服务器间 WireGuard Peer为了使境外服务器之间能够联通, 且不受系统路由表干扰, 这里我们需要将每个境外服务器都加入到当前的 WireGuard 网络中.在境外其他普通服务器上创建接口cd /etc/wireguard vim intra-iepl.conf按照该模板创建配置[Interface] PrivateKey = <privKey> Address = <ipAddr> ListenPort = <port> MTU = 1450 [Peer] PublicKey = <pubKey_iepl> AllowedIPs = <wgNet/CIDR>, <domesticNet/CIDR> PersistentKeepalive = 25privKey 当前设备的 WireGuard 私钥ipAddr 当前设备的 WireGuard 内网地址port 当前设备的 WireGuard 监听端口pubKey_iepl 专线服务器的 WireGuard 公钥wgNet/CIDR CIDR 格式的 WireGuard 网段domesticNet/CIDR CIDR 格式的境内代理网段示例配置[Interface] PrivateKey = ... Address = 10.0.1.103 ListenPort = 51132 MTU = 1450 [Peer] PublicKey = ... AllowedIPs = 10.0.1.0/24, 192.168.1.0/24 PersistentKeepalive = 25启动该端口systemctl enable --now wg-quick@intra-iepl如果没有报错则启动完成在专线服务器的 intra-ixpeers.conf 中添加配置[Peer] PublicKey = <pubKey_global> Endpoint = <ipAddr_global>:<port> AllowedIPs = <ipAddr_wg>/32 PersistentKeepalive = 25pubKey_global 先前配置的境外服务器的 WireGuard 公钥ipAddr_global 先前配置的境外服务器的公网 IP 地址, 可以是域名port 先前配置的境外服务器的 WireGuard 监听地址ipAddr_wg 先前配置的境外服务器的 WireGuard 内网地址示例配置[Peer] PublicKey = ... Endpoint = v4.sb1.jp.nanami.tech:51132 AllowedIPs = 10.0.1.103/32 PersistentKeepalive = 25重启接口systemctl restart wg-quick@intra-ixpeers重启后检查连通性root@NB-JPIEPL01:/etc/wireguard# ping 10.0.1.103 PING 10.0.1.103 (10.0.1.103) 56(84) bytes of data. 64 bytes from 10.0.1.103: icmp_seq=1 ttl=64 time=29.0 ms 64 bytes from 10.0.1.103: icmp_seq=2 ttl=64 time=28.8 ms 64 bytes from 10.0.1.103: icmp_seq=3 ttl=64 time=28.9 ms 64 bytes from 10.0.1.103: icmp_seq=4 ttl=64 time=28.8 ms 64 bytes from 10.0.1.103: icmp_seq=5 ttl=64 time=29.1 ms ^C --- 10.0.1.103 ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4007ms rtt min/avg/max/mdev = 28.780/28.904/29.060/0.098 ms至此, 设备连接完成.注意: 这样的操作在添加的每一台设备添加时都需要执行.3. 建立 OSPF Peer3.1 安装 Bird既然我们的目的是将专线服务器作为中转来连接我们境外的其他服务器, 那么我们就需要将这台专线服务器配置成一台路由器, 来帮我们转发业务流量. 这里我们使用 Bird v2 来将其配置为路由器.您需要在您的私人服务器/网关和专线服务器上安装 Bird使用这几条指令安装最新版本 Bird v2sudo apt update && sudo apt -y install apt-transport-https ca-certificates wget lsb-release sudo wget -O /usr/share/keyrings/cznic-labs-pkg.gpg https://pkg.labs.nic.cz/gpg echo "deb [signed-by=/usr/share/keyrings/cznic-labs-pkg.gpg] https://pkg.labs.nic.cz/bird2 $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/cznic-labs-bird2.list sudo apt update && sudo apt install bird2 -y指令转载至 DN42探究日记 - Ep.2 通过OSPF搭建内部网络并启用iBGP - iYoRoy Blog输入这条指令验证安装bird --version示例输出root@NB-JPIEPL01:/etc/bird# bird --version BIRD version 2.19.1如果没有报错, 则安装结束3.2 配置专线服务器备份核心配置文件并创建新配置cd /etc/bird mv bird.conf bird.conf.bak vim bird.conf按照如下示例创建配置log syslog all; define ROUTER_ID = <routerID>; define INTRA_OWNAS = <ownAS>; define INTRA_OWNIP = <ownIP>; define INTRA_OWNNET = <ownNet/CIDR>; define INTRA_OWNSET = [ <ownSet/CIDR+> ]; protocol device { scan time 10; }; protocol direct { ipv4; ipv6; }; include "kernel.conf"; include "ospf.conf";routerID 该服务器运行在所有协议中的 Router IDownAS 该服务器的 AS Number. 因为此处我们配置的是内网而非 IANA/DN42, 所以我们这里需要填写私有 ASN. 私有 ASN 的范围是 64512-65534, 在这里取用即可.ownIP 该服务器在您想代理的网段中的 IP. 在本文的环境中, 我们填写这台服务器在境外 VPN 骨干网的 IP 地址.ownNet/CIDR 您所填写的 IP 地址的整个网段. 在本文的环境中, 我们填写整个境外 VPN 骨干网的 IP 地址段.ownSet/CIDR+ 您所想引入的所有网段. 这里需要填写您未来会引入的所有地址段信息.在 Bird 配置文件中, define 用于声明全局变量, 故这里的 INTRA_OWNAS, INTRA_OWNIP 等字段均为全局变量, 在其他的配置文件中均可调用bird.conf 为 Bird 主要配置文件, Bird 启动时只会读取这里的配置信息. include 则用于在该文件中引入其他配置文件. 因此 kernel.conf, ospf.conf 均为引入的文件.示例配置log syslog all; define ROUTER_ID = 10.10.64.106; define INTRA_OWNAS = 65534; define INTRA_OWNIP = 10.10.64.106; define INTRA_OWNNET = 10.10.64.0/24; define INTRA_OWNSET = [ 192.168.1.0/24+, 192.168.20.0/24+, 10.0.64.0/24+, 10.10.64.0/24+ ]; protocol device { scan time 10; }; protocol direct { ipv4; ipv6; }; include "defs.conf"; include "ospf.conf";保存后, 在相同目录下创建 defs.conf, 写入如下内容function is_intra_net4() { return net ~ INTRA_OWNSET; }保存后, 在相同目录下创建 ospf.conf, 写入如下内容protocol ospf v2 ospf_intra { router id ROUTER_ID; ipv4 { import where is_intra_net4(); export filter { if source = RTS_DEVICE && net ~ <ipAddr/CIDR> then accept; reject; }; }; include "/etc/bird/ospf/*"; };ipAddr/CIDR 境内私人服务器/网关所代理的网段创建 OSPF 配置文件夹和 Area 0 的配置文件mkdir ospf vim ospf/0.conf按照以下模板写入配置area 0.0.0.0 { interface "intra-ixpeers" { type ptmp; ttl security tx only; neighbors { <ipAddr_domestic>; }; }; }ipAddr_domestic 境内私人服务器/网关的 WireGuard 地址应用配置birdc c如果得到如下输出, 则应用成功BIRD 2.19.1 ready. Reading configuration from /etc/bird/bird.conf Reconfiguration in progress此时查看具体协议状态birdc s p得到如下输出BIRD 2.19.1 ready. Name Proto Table State Since Info device1 Device --- up 2026-07-07 direct1 Direct --- up 2026-07-07 kernel_v4 Kernel master4 up 2026-07-07 kernel_v6 Kernel master6 up 2026-07-07 ospf_intra OSPF master4 up 2026-07-16 Alone可以看到此时 OSPF 的状态为 Alone, 这是因为目前只有这一台设备在运行 OSPF, 还没有其他 Peer. 所以下一步我们来配置境内服务器/网关的 OSPF.3.3 配置境内服务器/网关备份核心配置文件并创建新配置cd /etc/bird mv bird.conf bird.conf.bak vim bird.conf按照如下示例创建配置log syslog all; define ROUTER_ID = <routerID>; define INTRA_OWNAS = <ownAS>; define INTRA_OWNIP = <ownIP>; define INTRA_OWNNET = <ownNet/CIDR>; define INTRA_OWNSET = [ <ownSet/CIDR+> ]; protocol device { scan time 10; }; protocol direct { ipv4; ipv6; }; include "kernel.conf"; include "ospf.conf";示例配置log syslog all; define ROUTER_ID = 10.0.64.254; define INTRA_OWNAS = 65534; define INTRA_OWNIP = 192.168.1.1; define INTRA_OWNNET = 192.168.1.0/24; define INTRA_OWNSET = [ 192.168.1.0/24+, 192.168.20.0/24+, 10.0.64.0/24+, 10.10.64.0/24+ ]; protocol device { scan time 10; }; protocol direct { ipv4; ipv6; }; include "kernel.conf"; include "defs.conf"; include "ospf.conf";保存后, 在相同目录下创建 defs.conf, 写入如下内容function is_intra_net4() { return net ~ INTRA_OWNSET; }保存后, 在相同目录下创建 ospf.conf, 写入如下内容protocol ospf v2 ospf_intra { router id ROUTER_ID; ipv4 { import where is_intra_net4(); export filter { if source = RTS_DEVICE && net ~ <ipAddr/CIDR> then accept; reject; }; }; include "/etc/bird/ospf/*"; };ipAddr/CIDR 境内私人服务器/网关所代理的网段创建 OSPF 配置文件夹和 Area 0 的配置文件mkdir ospf vim ospf/0.conf按照以下模板写入配置area 0.0.0.0 { interface "intra-ixpeers" { type ptmp; ttl security tx only; neighbors { <ipAddr_domestic>; }; }; }ipAddr_domestic 境内私人服务器/网关的 WireGuard 地址应用配置birdc c如果得到如下输出, 则应用成功BIRD 2.19.1 ready. Reading configuration from /etc/bird/bird.conf Reconfiguration in progress此时查看具体协议状态birdc s p得到如下输出BIRD 2.19.1 ready. Name Proto Table State Since Info device1 Device --- up 2026-07-07 direct1 Direct --- up 2026-07-07 kernel_v4 Kernel master4 up 2026-07-07 kernel_v6 Kernel master6 up 2026-07-07 ospf_intra OSPF master4 up 2026-07-16 Running可以看到, 此时 OSPF 协议状态为 Running, 则表明成功与境外专线服务器建立 Peer. 此时双方的配置均已完成.4. 测试在境外专线服务器对境内设备进行联通测试root@NB-JPIEPL01:~# ping 192.168.1.4 PING 192.168.1.4 (192.168.1.4) 56(84) bytes of data. 64 bytes from 192.168.1.4: icmp_seq=1 ttl=62 time=24.5 ms 64 bytes from 192.168.1.4: icmp_seq=2 ttl=62 time=24.1 ms 64 bytes from 192.168.1.4: icmp_seq=3 ttl=62 time=24.4 ms 64 bytes from 192.168.1.4: icmp_seq=4 ttl=62 time=24.4 ms 64 bytes from 192.168.1.4: icmp_seq=5 ttl=62 time=24.7 ms ^C --- 192.168.1.4 ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4006ms rtt min/avg/max/mdev = 24.087/24.400/24.713/0.199 ms在境内服务器对境外设备进行联通测试root@X-wrt-Home:/etc/wireguard# ping 10.0.1.103 PING 10.0.1.103 (10.0.1.103): 56 data bytes 64 bytes from 10.0.1.103: seq=0 ttl=62 time=53.015 ms 64 bytes from 10.0.1.103: seq=1 ttl=62 time=53.352 ms 64 bytes from 10.0.1.103: seq=2 ttl=62 time=53.052 ms 64 bytes from 10.0.1.103: seq=3 ttl=62 time=53.209 ms --- 10.0.1.103 ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 53.015/53.157/53.352 ms可以看到, 两边都成功联通并稳定. 至此, 配置全部结束后记本人非专业人员, 如有疏漏或错误请指出! 感谢您的阅读!
一个 EasyTier VPN 参数导致的 iBGP 全面崩溃 - BIRD iBGP 排坑记录
前言博主某天在查看自己 DN42 的 Looking Glass 时, 发现几个用于 ASN 内传输路由的 iBGP 协议全部处于 Connection closed 状态, 导致了 ASN 内部无法正常传输路由. 在检查了各项配置且尝试重启无果之后, 意识到这貌似并不是什么简单的问题, 遂尝试解决.网络状态各节点 BIRD: 正常iBGP 节点间通信: 正常iBGP Peer 协商: 不正常网络架构描述iBGP Peer 一共有五台设备, 一台设备处于境内, 与其他四台设备建立 Peer, 其他四台设备处于境外, 这四台设备之间互相不 Peer. 且每台设备都与若干 eBGP 节点进行了 Peer.已确认的部分iBGP 节点间 ASN 相同iBGP Peer 的 IP 地址配置无误节点间防火墙配置无误问题分析iBGP Peer 不成立通常可能是以下几个原因导致的Router ID 冲突Peer IP 地址不匹配TCP MD5 密码不匹配使用 LoopBack 建立连接时, BGP 多跳未配置监听地址与端口不匹配或配置错误某一方 BGP 会话未启动Hold Time 协商问题下面我们一个一个进行分析由于这些 iBGP Peer 的情况都大致相同, 且配置也大致相同. 因此每一步的验证仅取其中一对 Peer 进行查看Router ID 冲突对双方 Router ID 进行检查VM-Subroot@VM-Sub:/etc/bird/ibgp# birdc show status | grep "Router ID" Router ID is 172.23.66.97JPSB01root@JPSB01:~# birdc show status | grep "Router ID" Router ID is 45.xxx.xxx.xxx可以明显看出, 双方 Router ID 不同, 故不是此问题Peer IP 地址不匹配现在对双方在配置文件中的 Peer IP 进行检查VM-Subroot@VM-Sub:/etc/bird/ibgp# cat sb1-jp.conf protocol bgp 'ibgp_sb1-jp' from ibgpeers{ neighbor 10.0.64.103 as DN42_OWNAS; };JPSB01root@ACCK-JPSB01:/etc/bird/ibgp# cat homelab-subserver.conf protocol bgp 'ibgp_homelab-subserver' from ibgpeers{ neighbor 10.0.64.4 as DN42_OWNAS; };双方的 IP 地址均为 WireGuard VPN 内网中的 IP 地址, 因此互相连接时, 源 IP 地址肯定是 VPN 网络中的 IP 地址, 因此不是这个问题.TCP MD5 密码不匹配iBGP Peer 间未单独配置密码, 故不是该问题使用 LoopBack 建立连接时, BGP 多跳未配置iBGP 使用直连连接, 未使用 LoopBack 接口, 连接只有一跳, 故不是该问题监听地址与端口不匹配或配置错误监听地址和端口均为进行修改, 所有配置为 BIRD 默认, 故不存在该问题某一方 BGP 会话未启动双方 BGP 会话均启动, 故不是该问题深入排查现在, 所有可能的原因都排查完了, 但问题还未解决. 还剩怎么排查呢? 我们可以抓包查看具体通信报文是什么. 这里使用 tcpdump 对 179 端口进行监听, 并输出 pcap 文件tcpdump -i any port 179 -vvv -s0 -w bgp.pcap等待一段时间后, 使用 WireShark 打开该文件查看报文这就是具体抓到的数据包. 现在我们过滤所有 eBGP 报文, 仅查看需要检查的两个 iBGP Peer 之间的报文. 使用这个过滤器ip.addr == 10.0.64.4 && ip.addr == 10.0.64.103在这份数据包中我们可以看到, 10.0.64.4 多次尝试向 10.0.64.103 发送 OPEN 报文, 但最终会被 10.0.64.4 发送 RST 报文, 整个连接被重置, 最终导致 BIRD 报错 Connection closed.现在, 我们重新在 10.0.64.103 上进行抓包, 结果如下可以看到, 无论是 10.0.64.4 还是 10.0.64.60, 均只对对方的 TCP 报文进行了处理, 没有任何 BGP 层的处理. 这很奇怪, 如果问题出现在 BGP 层, 那么应该由其中一方发送 BGP Notification 报文来终止连接, 而不是 TCP Reset.鉴于这个问题是突然出现的, 所以我们来审视一下问题出现的前后, 我都对网络做了什么改动. 回想了一下, 在这期间我对网络做的唯一改动就是 将 Easytier VPN 的部署从 直接在 systemd 服务中将具体配置参数输入给 Easytier 二进制程序 改为了 通过将具体配置写进配置文件再送给 Easytier 处理. 在配置文件中, 我多加上了这么几条参数[flags] enable_kcp_proxy = true enable_quic_proxy = true latency_first = true private_mode = true我们逐个检查这些 flag 会实现什么功能enable_kcp_proxy 启用 KCP 代理. 将 TCP 连接使用 KCP 协议进行代理该功能虽然会改变 TCP 流量的传输方式, 但不改变原始 TCP 报文, 所以不会造成影响.enable_quic_proxy 启用 QUIC 代理. 将 TCP 连接使用 QUIC 协议进行代理该功能虽然会改变 TCP 流量的传输方式, 但不改变原始 TCP 报文, 所以不会造成影响.laterncy_first 延迟优先. 无视 TTL 跳数, 始终优先使用延迟最短的路径转发流量该功能虽然达到了最小延迟的目的, 但可能会增加 TTL 跳数. 从上面的配置文件可以看出, 配置里并没有单独配置 max-hop, 这可能造成连接失败.我们来测试一下双方互通的跳数10.0.64.4traceroute to 10.0.64.60 (10.0.64.60), 30 hops max, 60 byte packets 1 10.0.64.60 (10.0.64.60) 14.478 ms 14.476 ms 14.463 ms10.0.64.60traceroute to 10.0.64.4 (10.0.64.4), 30 hops max, 60 byte packets 1 10.0.64.4 (10.0.64.4) 13.845 ms 14.444 ms 14.378 ms这里可以看到, 两台机器虽然均开启了 laterncy_first, 但双方的跳数仍为 1, 不会造成 iBGP 故障. 但保险起见, 我们还是把这项功能关掉.private_mode 隐私模式. 不允许任何非相同网络的节点使用该节点建立连接或转发流量顾名思义, 这项功能是为了防止外部未知设备在未经允许的情况下借助这个节点进行 VPN 网络搭建或流量转发, 但在我查询 AI 后, 它告诉我Root cause: EasyTier's private_mode with KCP/QUIC proxy rewrites the source IP of incoming BGP connections to 10.0.64.60 (this machine's own tunnel IP). BIRD rejects these because it only knows about neighbor 10.0.64.4. The same issue likely affects 10.0.64.4 in reverse, so both directions fail.底层原因: EasyTier 的 private_mode 通过 KCP/QUIC 代理时会 重写连入 BGP 连接的源 IP 地址 到 10.0.64.60 (这台机器的私有隧道 IP). BIRD 拒绝了这些连接, 因为它只知道邻居的地址 10.0.64.4. 相同的问题也影响了 10.0.64.4, 故两边均失败了.由此我们可以得知, 当某一方的 BGP 连接传入时, Easytier 会先将 IP 数据包中的 源 IP 地址 改为本机隧道地址, 然后再被 BIRD 接收.为了验证是不是为这个问题, 我们在 10.0.64.60 中查看 BIRD 的日志root@SchoolNode-Main:/home/nanami114/PersonalFiles/easytier/configs# journalctl -xeu bird Jun 04 17:50:58 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 39138) Jun 04 17:51:03 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 56654) Jun 04 17:51:07 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 56670) Jun 04 17:51:11 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 56676) Jun 04 17:51:15 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 44306) Jun 04 17:51:19 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 44320) Jun 04 17:51:23 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 38916) Jun 04 17:51:27 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 38924) Jun 04 17:51:31 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 38930) Jun 04 17:51:35 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 37640) Jun 04 17:51:40 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 37656) Jun 04 17:51:44 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 48124) Jun 04 17:51:49 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 48128) Jun 04 17:51:53 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 55374) Jun 04 17:51:58 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 55388) Jun 04 17:52:02 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 39252) Jun 04 17:52:07 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 39266) Jun 04 17:52:11 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 39270) Jun 04 17:52:15 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 33950) Jun 04 17:52:20 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 33964) Jun 04 17:52:24 SchoolNode-Main bird[2383741]: BGP: Unexpected connect from unknown address 10.0.64.60 (port 59404) ...可以从日志中看到, BIRD 对 10.0.64.60 传入的连接进行了大量的报错, 由此我们得知, 问题就出在这里!现在我们将这项功能关掉, 然后再次查看连接状态root@SchoolNode-Main:/home/nanami114/PersonalFiles/easytier/configs# birdc s p BIRD 2.18.1 ready. Name Proto Table State Since Info device1 Device --- up 17:15:38.915 kernel_v4 Kernel master4 up 17:15:38.915 kernel_v6 Kernel master6 up 17:15:38.915 dn42_static_v4 Static master4 up 17:15:38.915 dn42_static_v6 Static master6 up 17:15:38.915 dn42_rpki_akix RPKI --- up 17:15:39.962 Established ospf_dn42 OSPF master4 up 17:15:38.915 Alone ospf_dn42_v6 OSPF master6 up 17:15:38.915 Alone ibgp_homelab-subserver BGP --- up 17:54:48.504 Established可以看到, 此时 iBGP 已经处于了 Established 状态. 至此, 问题全部解决.结语本人非专业网络运维, 如有错误或疏漏还请指出! 如果觉得文章不错, 还请分享给有需要的人, 感谢!
当 Easytier 遇上 mwan3: 一次 VPN 回程路由被劫持的修复实录
前言博主使用了 Easytier 在家, 学校, 境内外公网服务器之间通过 WireGuard 协议建立了一个三层 VPN 网络. 平日里在学校或出门在外都会通过这个 VPN 网络连接家里的设备, 访问家里搭建的业务. 在博主今天尝试在学校连接家里服务器上的 SMB 时, 发现全部连接失败, 故着手研究原因.网络拓扑架构在 HomeLab 中, 有三台设备代理了 192.168.1.0/24 网段, 即其他设备可以走这三个节点访问该网段. 且对于该网段的访问没有专门的 ACL 限制, 所有设备均可访问在校园节点中, 网关代理了 192.168.20.0/24 网段, 同样可以被所有设备访问链路状态相关节点状态: HomeLab (在线), Router-School (在线)链路通信状态校园节点在网关上与 192.168.1.0/24 段的设备通信正常, 但下游设备与该网段的通信时不正常HomeLab 中所有节点的网络通信状态与子网代理状态都是正常的疑点: 下游设备与 192.168.1.14/32 通信时正常, 但与该网段中其他所有设备通信都不正常. 表现为 ICMP Ping 无回包, 业务数据访问不通问题分析造成该故障最常见的原因可能有以下几点回程路由缺失反向路径过滤 (rp_filter) 导致回包被丢弃IP 转发未开启Easytier 的出口策略 / 策略路由冲突我们这边逐个来排查1. 回程路由缺失请求: 192.168.20.0/24 → 网关 → tun0 → VPN → 192.168.1.0/24回包: 192.168.1.0/24 → VPN → 网关 tun0 → ??? → 192.168.20.0/24网关自身访问时, 源地址通常是 tun0 的 IP (10.0.64.253), 这个地址天然在 VPN 路由域内, 对端可以直接回包.而下游设备访问时, 源地址保留为 192.168.20.x, 如果对端节点没有去往 192.168.20.0/24 的路由, 回包就会丢失或走默认路由被丢弃.这里检查校园网关的路由表root@X-wrt-School:~# ip rou default via 10.131.0.1 dev pppoe-wan proto static metric 40 default via 10.151.0.1 dev pppoe-xwan01 proto static metric 41 default via 10.141.0.1 dev pppoe-xwan02 proto static metric 42 default via 10.141.0.1 dev pppoe-xwan03 proto static metric 43 default via 10.151.0.1 dev pppoe-xwan04 proto static metric 44 10.0.64.0/24 dev tun0 proto kernel scope link src 10.0.64.253 10.0.64.0/24 dev tun0 proto static metric 65535 10.10.64.0/24 dev tun0 proto static metric 65535 10.131.0.1 dev pppoe-wan proto kernel scope link src 10.131.15.37 10.141.0.1 dev pppoe-xwan02 proto kernel scope link src 10.141.102.228 10.141.0.1 dev pppoe-xwan03 proto kernel scope link src 10.141.40.183 10.151.0.1 dev pppoe-xwan01 proto kernel scope link src 10.151.10.205 10.151.0.1 dev pppoe-xwan04 proto kernel scope link src 10.151.32.226 169.254.0.0/16 dev br-lan proto kernel scope link src 169.254.254.254 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 192.168.1.0/24 dev tun0 proto static metric 65535 192.168.3.0/24 dev tun0 proto static metric 65535 192.168.20.0/24 dev br-lan proto kernel scope link src 192.168.20.1 192.168.20.0/24 dev br-lan proto bird scope link metric 32 192.168.100.0/24 dev tun0 proto static metric 65535可以看到, 192.168.1.0/24 正常从 tun0 出口然后检查 HomeLab 节点中设备的路由表nanami114@NanamiWork-Server:~$ ip rou default via 192.168.1.1 dev vmbr0 proto kernel onlink 10.0.64.0/24 dev tun0 proto static metric 65535 10.10.64.0/24 dev tun0 proto static metric 65535 192.168.1.0/24 dev vmbr0 proto kernel scope link src 192.168.1.8 192.168.3.0/24 dev tun0 proto static metric 65535 192.168.20.0/24 dev tun0 proto static metric 65535 192.168.100.0/24 dev tun0 proto static metric 65535可以看到这里也是正常的, 因此该原因被排除2. 反向路径过滤 (rp_filter) 导致回包被丢弃这里直接检查校园网关的 rp_filter 策略root@X-wrt-School:~# sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.all.rp_filter = 0 root@X-wrt-School:~# sysctl net.ipv4.conf.tun0.rp_filter net.ipv4.conf.tun0.rp_filter = 0 root@X-wrt-School:~# sysctl net.ipv4.conf.br-lan.rp_filter net.ipv4.conf.br-lan.rp_filter = 0可以看到, 所有相关网络接口的 rp_filter 均为 0, 证明这里没有问题, 故排除该原因3. IP 转发未开启首先网关本身使用的系统为 OpenWrt, 该系统为专业的软路由系统, 因此可以直接排除该原因. 但为了严谨, 这里还是检查一下root@X-wrt-School:~# sysctl net.ipv4.ip_forward net.ipv4.ip_forward = 14. Easytier 的出口策略 / 策略路由冲突在第一项检查中我们可以看到, 路由表中存在多个 pppoe-wan, 说明这是一个多拨的网络环境. 因此可能造成下游流量在转发时被路由策略规则劫持, 导致回包走到了错误的物理接口, 而非回到 tun0.root@X-wrt-School:~# ip rule show 0: from all lookup local 1001: from all iif pppoe-wan lookup 1 1002: from all iif pppoe-xwan01 lookup 2 1003: from all iif pppoe-xwan02 lookup 3 1004: from all iif pppoe-xwan03 lookup 4 1005: from all iif pppoe-xwan04 lookup 5 2001: from all fwmark 0x100/0x3f00 lookup 1 2002: from all fwmark 0x200/0x3f00 lookup 2 2003: from all fwmark 0x300/0x3f00 lookup 3 2004: from all fwmark 0x400/0x3f00 lookup 4 2005: from all fwmark 0x500/0x3f00 lookup 5 2061: from all fwmark 0x3d00/0x3f00 blackhole 2062: from all fwmark 0x3e00/0x3f00 unreachable 32766: from all lookup main 32767: from all lookup default通过这条输出, 我们可以注意到:下游设备 192.168.20.x → 网关 → 匹配 main 路由表走 tun0 → VPN → 192.168.1.y (正常发出)回包到达网关 tun0 后, 内核会进行路由查找. 此时, 如果回包被打上了某个 fwmark (例如 0x100), 就会命中规则 2001: from all fwmark 0x100/0x3f00 lookup 1, 从而使用路由表 1 进行转发.这里我们查看路由表 1 的具体路由信息root@X-wrt-School:~# ip route show table 1 default via 10.131.0.1 dev pppoe-wan metric 40 10.0.64.0/24 dev tun0 proto kernel scope link src 10.0.64.253 metric 256 10.0.64.0/24 dev tun0 proto static scope link metric 65535 10.10.64.0/24 dev tun0 proto static scope link metric 65535 10.131.0.1 dev pppoe-wan proto kernel scope link src 10.131.15.37 10.141.0.1 dev pppoe-xwan02 proto kernel scope link src 10.141.102.228 metric 3 10.141.0.1 dev pppoe-xwan03 proto kernel scope link src 10.141.40.183 metric 4 10.151.0.1 dev pppoe-xwan01 proto kernel scope link src 10.151.10.205 metric 2 10.151.0.1 dev pppoe-xwan04 proto kernel scope link src 10.151.32.226 metric 5 169.254.0.0/16 dev br-lan proto kernel scope link src 169.254.254.254 metric 256 192.168.1.0/24 dev tun0 proto static scope link metric 65535 192.168.3.0/24 dev tun0 proto static scope link metric 65535 192.168.20.0/24 dev br-lan proto bird scope link metric 32 192.168.20.0/24 dev br-lan proto kernel scope link src 192.168.20.1 metric 256 192.168.100.0/24 dev tun0 proto static scope link metric 65535从输出的路由表信息来看, 已经存在去往 192.168.20.0/24 的正确路由, 这说明单纯的回程缺失已经被排除. 但下游设备无法通信, 此时问题大概率出现在连接跟踪表记 (conntrack) 与防火墙过滤的联合作用上.首先我们查看 OpenWrt 的防火墙配置可以看到, Easytier 接口的出入配置都是正确且有效的, 那么接下来我们对问题进行深入分析与排查深入分析mwan3 的标记机制mwan3 会在 mangle 表的 PREROUTING 和 OUTPUT 中挂载 mwan3_hook. 核心逻辑如下CONNMARK restore: 恢复连接上保存的标记 (fwmark).如果恢复后标记为 0, 则根据入接口 (mwan3_ifaces_in) 或策略 (mwan3_rules) 打上新标记, 最后 CONNMARK save, 将标记保存到连接跟踪里.此后所有属于该连接的包都会自动被还原相同标记, 并通过 ip rule 的 fwmark 匹配, 强制走对应的路由表 (如 table 1~5).当前链路存在的问题当 192.168.20.x 首次访问 192.168.1.x 时出站 (br-lan → 网关 → tun0):该包在 PREROUTING 或 OUTPUT 链被 mwan3 打上某个标记 (例如 0x100), 并 CONNMARK save 保存.回包 (tun0 → 网关 → 应回到 br-lan):回包进入 PREROUTING → mwan3_hook → CONNMARK restore 直接将标记恢复为 0x100.由于标记非零, 后续 mwan3_ifaces_in 等链都被跳过 (它们要求 mark match 0x0/0x3f00).回包带着 0x100 进入路由查找, 命中 ip rule 中的 2001: from all fwmark 0x100/0x3f00 lookup 1, 使用 table 1 转发.尽管 table 1 有去往 192.168.20.0/24 的直连路由, 理论上可以通, 但实际环境中常被以下两种隐形过滤阻断:filter 表 FORWARD 链的状态跟踪丢弃大多数 OpenWrt 默认防火墙 FORWARD 链允许 ESTABLISHED ,RELATED 状态, 丢弃 INVALID.当回包带着被强制改变的路由标记走过路由后, 连接跟踪可能将其状态标记为 INVALID (因为出方向走的是 main 表或不同的路由表, 回包却走 table 1, 属于非对称路由, conntrack 可能视其为无效), 随后被 FORWARD 链丢弃.反向路径校验 *(即使 rp_filter=0 仍有其他机制)某些内核版本在策略路由下仍会做额外的路径校验, 或者 secure_redirects 等参数造成丢弃. 但更常见的是第 1 点.因此, 即使路由存在, 包也可能在送到 br-lan 之前就被 filter 规则或 conntrack 剔除.故障修复快速恢复最快且最可靠的办法是让回包不再经过 FORWARD 转发路径, 而是直接进网关本机. 将下游设备的源地址伪装为网关 VPN 接口的地址即可.iptables -t nat -A POSTROUTING -s 192.168.20.0/24 -o tun0 -j MASQUERADE执行完后看效果:C:\Users\Nanami114>ping 192.168.1.4 Pinging 192.168.1.4 with 32 bytes of data: Reply from 192.168.1.4: bytes=32 time=11ms TTL=63 Reply from 192.168.1.4: bytes=32 time=11ms TTL=63 Reply from 192.168.1.4: bytes=32 time=11ms TTL=63 Reply from 192.168.1.4: bytes=32 time=12ms TTL=63 Ping statistics for 192.168.1.4: Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 11ms, Maximum = 12ms, Average = 11ms C:\Users\Nanami114>ping 192.168.1.8 Pinging 192.168.1.8 with 32 bytes of data: Reply from 192.168.1.8: bytes=32 time=7ms TTL=63 Reply from 192.168.1.8: bytes=32 time=7ms TTL=63 Reply from 192.168.1.8: bytes=32 time=7ms TTL=63 Reply from 192.168.1.8: bytes=32 time=7ms TTL=63 Ping statistics for 192.168.1.8: Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 7ms, Maximum = 7ms, Average = 7ms可以看到, 此时下游设备已经可以正常收到对端设备 ICMP Ping 的回包. 接下来查看业务流量状态:这里可以看到, 此时 SMB 流量已经可以正常通讯, 说明整个网络链路已经恢复, 下游设备恢复了与 192.168.1.0/24 段的正常通讯.当然, 这个方法虽然恢复快速但也存在不妥:所有下游设备去往 192.168.1.0/24 的通讯, 都会被目标设备识别为 10.0.64.253 在与其通信, 也就是校园网关在与其通信. 为了解决这个问题, 下面我们进行深度修复.根因修复: 调整 mangle 规则, 避免 VPN 回程路由被标记精准修复命令在 mwan3_hook 中 CONNMARK save 之前插入一条规则: 如果包是从 tun0 进入且目标是 192.168.20.0/24, 则将其标记清零, 并让 conntrack 保存清零后的标记. 这样该连接后续流量将不再受多线策略影响.首先查看 mwan3_hook 接口编号root@X-wrt-School:~# iptables -t mangle -L mwan3_hook --line-numbers Chain mwan3_hook (2 references) num target prot opt source destination 1 CONNMARK all -- anywhere anywhere mark match 0x0/0x3f00 CONNMARK restore mask 0x3f00 2 mwan3_ifaces_in all -- anywhere anywhere mark match 0x0/0x3f00 3 mwan3_local all -- anywhere anywhere mark match 0x0/0x3f00 4 mwan3_rules all -- anywhere anywhere mark match 0x0/0x3f00 5 mwan3_connected all -- anywhere anywhere 6 CONNMARK all -- anywhere anywhere CONNMARK save mask 0x3f00可以看到 mwan3_connected 是第五条, CONNMARK save 是第六条. 我们需要在这两条之间插入标记清零的规则iptables -t mangle -I mwan3_hook 6 -i tun0 -d 192.168.20.0/24 -j MARK --set-mark 0这里可以看到, 刚才插入的规则已经生效. 此时看下游设备效果.这里使用下游设备连接对端物理设备的 SSH, 连接两次即可在终端内看到上一次连接的 IP 地址.Linux NanamiWork-Server 7.0.0-3-pve #1 SMP PREEMPT_DYNAMIC PMX 7.0.0-3 (2026-04-21T22:56Z) x86_64 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Wed May 27 16:19:00 2026 from 192.168.20.2 nanami114@NanamiWork-Server:~$可以看到, 这里显示 Last login: Wed May 27 16:19:00 2026 from 192.168.20.2, 证明改动已经生效, 并已经恢复了与目标设备的连接. 至此, 整个 VPN 网络的修复就已完成.后记本人非专业网络运维, 如有疏漏或错误, 还请指出! 如果您觉得文章不错, 还请分享给有需要的
记录一则处理 systemd/journald 日志过多导致磁盘 I/O 爆炸及 systemd 处理缓慢的问题
前言博主在使用自己开发的 super-frpc 项目时, 注意到每次程序启动的时候, 磁盘 I/O 都会被占满. 一开始博主以为是被攻击, 后面使用 htop 查询发现, 占用量最大的进程分别为 systemctl status superfrpc_* 和 journalctl -u superfrpc_* -n 50 --no-pager. 这两者的相同点是都会读取最近日志. 因此引出了本篇文章所需要解决的问题.故障分析为什么 systemctl status 也会造成大量 I/O 开销首先, systemctl status * 并不会直接造成大量磁盘 I/O, 但是它会执行下面这些隐藏动作:查询 unit 状态读取最近日志 (journalctl)显示最近几十行 stdout / stderr解析 cgroup / PID / 状态信息也就是说, 实际上 systemctl status * 隐式执行了journalctl -u superfrpc_*.service -n 10为什么 journalctl 查询日志会造成大量 I/O 开销众所周知, 正常情况下查询 systemd 服务日志应当是一瞬间的事情. 但如果你的日志符合以下任意一条特征:该服务日志量巨大日志没有被 rotatejournal 文件非常大journal 已损坏日志开启了压缩日志存储在机械盘btrfs / zfs 上有校验/压缩那么, journald 就会疯狂读磁盘, 造成巨大 I/O 开销.super-frpc 本身出了什么问题主进程行为在 super-frpc 的执行框架中, 如果用户开启了 watchdog 监听, 那么当主进程启动时, 会一并拉起 watchdog 进程, 然后检测现在已经开启的所有隶属于 super-frpc 的 frpc 服务进程, 并将已经启动的服务进程托管至 watchdog 进行监听.watchdog 行为watchdog 在收到来自主进程的监听任务时, 会启动一个 runMonitor 的 goroutine. 在该函数中调用了一个 checkServiceLogs 的函数, 负责监听程序日志中的报错行为. 以下为修改前的 checkServiceLogs 函数代码func checkServiceLogs(serviceName string) (bool, error) { cmd := exec.Command("journalctl", "-u", serviceName, "-n", "50", "--no-pager") output, err := cmd.Output() if err != nil { return false, fmt.Errorf("failed to execute journalctl: %w", err) } logContent := string(output) errorKeywords := []string{"error", "fatal", "failed", "critical", "exception"} for _, keyword := range errorKeywords { if strings.Contains(strings.ToLower(logContent), keyword) { return false, nil } } return true, nil }可以看出, 这里调用了 journalctl 对服务进行了日志的查询. 而这样的查询任务会在每一个新服务被添加到监听列表时都会执行一遍, 且是持续监听. 这也就导致了磁盘 I/O 爆炸.故障排查查询整个 journald 日志所占据的磁盘空间nanami114@VM-Sub:~/PersonalFiles/super-frpc$ journalctl --disk-usage Archived and active journals take up 4.0G in the file system.可以看到, 光是日志就已经占据了 4.0G 的巨大空间, 这也就导致了 journald 读取日志时的大量磁盘开销. 同时也说明了本次博主所碰到的问题属于 服务日志量巨大故障修复临时删除不需要的日志nanami114@VM-Sub:~/PersonalFiles/super-frpc$ sudo journalctl --vacuum-size=500M # 将除最新的 500MB 日志以外的日志全部删除 Deleted archived journal /var/log/journal/e2bc53213dbb447bab4d9898e22776fa/system@cda8f901ce914c2ba110f7e17205bc58-0000000000f6d8ba-000650eb1f09e7db.journal (77.7M). Deleted archived journal /var/log/journal/e2bc53213dbb447bab4d9898e22776fa/user-1000@cda8f901ce914c2ba110f7e17205bc58-0000000000f6daa3-000650eb2316285c.journal (3.6M). Deleted archived journal /var/log/journal/e2bc53213dbb447bab4d9898e22776fa/system@cda8f901ce914c2ba110f7e17205bc58-0000000000f94f56-000650f2434e1d58.journal (80.5M). Deleted archived journal /var/log/journal/e2bc53213dbb447bab4d9898e22776fa/system@cda8f901ce914c2ba110f7e17205bc58-0000000000fbc5fb-000650fa9502ddda.journal (78.6M). Deleted archived journal /var/log/journal/e2bc53213dbb447bab4d9898e22776fa/user-1000@cda8f901ce914c2ba110f7e17205bc58-0000000000fdae6c-000650ff56ec6396.journal (3.6M). Deleted archived journal /var/log/journal/e2bc53213dbb447bab4d9898e22776fa/system@cda8f901ce914c2ba110f7e17205bc58-0000000000fe3e79-00065100649d2f0e.journal (79.3M). Deleted archived journal /var/log/journal/e2bc53213dbb447bab4d9898e22776fa/system@cda8f901ce914c2ba110f7e17205bc58-000000000100af6d-00065109684030a0.journal (78.8M). Deleted archived journal /var/log/journal/e2bc53213dbb447bab4d9898e22776fa/user-1000@cda8f901ce914c2ba110f7e17205bc58-000000000100b5c6-000651097e126843.journal (3.7M). Deleted archived journal /var/log/journal/e2bc53213dbb447bab4d9898e22776fa/system@cda8f901ce914c2ba110f7e17205bc58-0000000001032883-0006511015b880a5.journal (81.9M). Deleted archived journal /var/log/journal/e2bc53213dbb447bab4d9898e22776fa/system@cda8f901ce914c2ba110f7e17205bc58-000000000105a875-0006511658dceb30.journal (78.1M). Deleted archived journal /var/log/journal/e2bc53213dbb447bab4d9898e22776fa/system@cda8f901ce914c2ba110f7e17205bc58-0000000001081e15-0006511f823b08c7.journal (83.9M). Deleted archived journal /var/log/journal/e2bc53213dbb447bab4d9898e22776fa/user-1000@cda8f901ce914c2ba110f7e17205bc58-000000000108b2c0-00065121421f3acb.journal (3.6M). ... Vacuuming done, freed 3.5G of archived journals from /var/log/journal/e2bc53213dbb447bab4d9898e22776fa. Vacuuming done, freed 0B of archived journals from /var/log/journal. Vacuuming done, freed 0B of archived journals from /run/log/journal.限制 journald 日志创建大小编辑 /etc/journal/journald.conf将 SystemMaxUse, SystemMaxFileSize, RuntimeMaxUse 这三条参数取消注释, 并按照自己的需求设置. 这里博主分别设置为 500M, 50M, 100M. 完整的配置文件如下[Journal] #Storage=auto #Compress=yes #Seal=yes #SplitMode=uid #SyncIntervalSec=5m #RateLimitIntervalSec=30s #RateLimitBurst=10000 SystemMaxUse=500M #SystemKeepFree= SystemMaxFileSize=50M #SystemMaxFiles=100 RuntimeMaxUse=100M #RuntimeKeepFree= #RuntimeMaxFileSize= #RuntimeMaxFiles=100 #MaxRetentionSec= #MaxFileSec=1month #ForwardToSyslog=no #ForwardToKMsg=no #ForwardToConsole=no #ForwardToWall=yes #TTYPath=/dev/console #MaxLevelStore=debug #MaxLevelSyslog=debug #MaxLevelKMsg=notice #MaxLevelConsole=info #MaxLevelWall=emerg #LineMax=48K #ReadKMsg=yes #Audit=yes保存并重启 journaldsystemctl restart systemd-journald程序代码修复systemd 配置文件问题造成问题的核心原因是, super-frpc 在创建 frpc 服务时, 并没有对日志方面做出任何配置. 在询问 AI 后, 得到的答复是这里我们先来看一下原来的代码是怎么写的func createSystemdService(instanceID int, configPath, runUser string) error { frpcPath, err := GetFrpcPath() if err != nil { return err } serviceName, err := database.GetServiceNameByInstanceID(instanceID) if err != nil { return err } serviceContent := fmt.Sprintf(`[Unit] Description=superfrpc_%d After=network.target [Service] Type=simple ExecStart=%s -c %s User=%s Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target `, instanceID, frpcPath, configPath, runUser) servicePath := filepath.Join("/etc/systemd/system", serviceName+".service") if err := os.WriteFile(servicePath, []byte(serviceContent), 0644); err != nil { return fmt.Errorf("failed to create systemd service file: %w", err) } cmd := exec.Command("systemctl", "enable", serviceName) if output, err := cmd.CombinedOutput(); err != nil { os.Remove(servicePath) return fmt.Errorf("failed to enable service: %s, output: %s", err, output) } cmd = exec.Command("systemctl", "daemon-reload") if output, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("failed to reload daemon: %s, output: %s", err, output) } return nil }在这段代码中, systemd 服务的配置文件模板为[Unit] Description=superfrpc_%d After=network.target [Service] Type=simple ExecStart=%s -c %s User=%s Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target可以看到, 没有对日志做出任何配置. 这里我们添加上如下两条配置StandardOutput=append:%s/superfrpc_%d.log StandardError=append:%s/superfrpc_%d.log并在程序配置文件中新增了 logsPath 这条参数, 用于单独存放 frpc 产生的日志.日志无限增长问题将 frpc 日志重定向到文件输出之后, 还有一个问题需要解决: 日志无限增长.systemd 自己并不会主动清理日志文件, 所以我们需要借助 logrotate 工具来对日志进行约束. 详细解决流程如下在主进程创建首个 systemd 服务后, 创建 logrotate 配置/etc/logrotate.d/superfrpc_frpclog在配置中填写以下字段/path/to/logs/superfrpc_*.log { daily rotate 7 compress delaycompress missingok notifempty copytruncate }/path/to/logs/superfrpc_*.log: 匹配符合 /path/to/logs/superfrpc_*.log 的所有文件daily: 每天进行一次 rotaterotate 7: 保留 7 份历史日志compress: 旧日志自动进行 gzip 压缩delaycompress: 延迟一轮再压缩missingok: 日志不存在也不报错nofityempty: 空日志不归档copytruncate: 将日志文件 inode 属性进行迁移进行了 logrotate 配置后, 整个日志的 rotate 过程如下:假设当前日志文件 superfrpc_8.log 的大小达到了 500MB, 则执行 rotate:Day 1:superfrpc_8.log # 新日志文件 superfrpc_8.log.1到了第二天, 自动 rotate 一遍:Day 2:superfrpc_8.log # 新日志文件 superfrpc_8.log.1 # 延迟一轮再压缩 superfrpc_8.log.2.gz # 被 gzip 压缩到了第八天, 删除最开始的一条日志: superfrpc_8.log.7.gz修改后的代码如下创建 CreateLogrotatePolicy 函数func CreateLogrotatePolicy() error { logrotatePath := "/etc/logrotate.d/superfrpc_frpclog" policyContent := fmt.Sprintf(`%s/superfrpc_*.log { daily rotate 7 compress delaycompress missingok notifempty } `, global.CurrentConfig.LogsPath) if err := os.WriteFile(logrotatePath, []byte(policyContent), 0644); err != nil { return fmt.Errorf("failed to create logrotate policy file: %w", err) } if err := exec.Command("logrotate", "-f", logrotatePath).Run(); err != nil { return fmt.Errorf("failed to apply logrotate policy: %w", err) } return nil }然后在程序初始化过程中调用该函数if global.CurrentConfig.LogsPath != "" && !global.CurrentConfig.Status.IsLogrotateConfigured { // Set up logrotate policy if LogsPath is configured if err := sys.CreateLogrotatePolicy(); err != nil { postLog.Fatal(fmt.Sprintf("Failed to create logrotate policy: %v", err)) } else { global.CurrentConfig.Status.IsLogrotateConfigured = true config.SaveConfig() postLog.Info("Logrotate policy has been configured successfully.") } }测试修复效果root@VM-Sub:/home/nanami114/PersonalFiles/super-frpc# ./super-frpc 2026/05/22 17:15:16 [frpLogger] Init.d log streamer initialized 2026/05/22 17:15:16 [frpLogger] Systemd log streamer initialized [2026-05-22 17:15:16.541 - INFO] Super-frpc 0.0.1 (Build 1.pre-release) by Madobi Nanami [2026-05-22 17:15:16.549 - INFO] Logs database initialized successfully [2026-05-22 17:15:16.558 - INFO] Database initialized successfully [2026-05-22 17:15:16.565 - INFO] Routes setup successfully [2026-05-22 17:15:16.588 - INFO] Watchdog process watchdog_systemd is not running, starting it... [2026-05-22 17:15:16.797 - DEBUG] [GetServiceNameByInstanceID] instanceID: 8, serviceName: superfrpc_8 [2026-05-22 17:15:16.864 - INFO] [IsInstanceRunning] Systemd service superfrpc_8 is running [2026-05-22 17:15:16.870 - DEBUG] [GetServiceNameByInstanceID] instanceID: 8, serviceName: superfrpc_8 [2026-05-22 17:15:16.878 - DEBUG] [watchdog] Add service monitor: superfrpc_8 [2026-05-22 17:15:16.885 - DEBUG] [GetServiceNameByInstanceID] instanceID: 9, serviceName: superfrpc_9 [2026-05-22 17:15:16.903 - INFO] [IsInstanceRunning] Systemd service superfrpc_9 is running [2026-05-22 17:15:16.909 - DEBUG] [GetServiceNameByInstanceID] instanceID: 9, serviceName: superfrpc_9 [2026-05-22 17:15:16.916 - DEBUG] [watchdog] Add service monitor: superfrpc_9 [2026-05-22 17:15:16.923 - DEBUG] [GetServiceNameByInstanceID] instanceID: 10, serviceName: superfrpc_10 [2026-05-22 17:15:16.940 - INFO] [IsInstanceRunning] Systemd service superfrpc_10 is running [2026-05-22 17:15:16.946 - DEBUG] [GetServiceNameByInstanceID] instanceID: 10, serviceName: superfrpc_10 [2026-05-22 17:15:16.952 - DEBUG] [watchdog] Add service monitor: superfrpc_10 [2026-05-22 17:15:16.959 - DEBUG] [GetServiceNameByInstanceID] instanceID: 11, serviceName: superfrpc_11 [2026-05-22 17:15:16.976 - INFO] [IsInstanceRunning] Systemd service superfrpc_11 is running [2026-05-22 17:15:16.982 - DEBUG] [GetServiceNameByInstanceID] instanceID: 11, serviceName: superfrpc_11 [2026-05-22 17:15:16.988 - DEBUG] [watchdog] Add service monitor: superfrpc_11 [2026-05-22 17:15:16.995 - DEBUG] [GetServiceNameByInstanceID] instanceID: 12, serviceName: superfrpc_12 [2026-05-22 17:15:17.012 - INFO] [IsInstanceRunning] Systemd service superfrpc_12 is running [2026-05-22 17:15:17.018 - DEBUG] [GetServiceNameByInstanceID] instanceID: 12, serviceName: superfrpc_12 [2026-05-22 17:15:17.024 - DEBUG] [watchdog] Add service monitor: superfrpc_12 [2026-05-22 17:15:17.031 - INFO] [watchdog] synced 5 running instance(s) to monitor list [2026-05-22 17:15:17.037 - INFO] Connected to Watchdog successfully [2026-05-22 17:15:17.045 - INFO] Server starting on 0.0.0.0:7000从日志中可以看出, 此时查询 systemctl status 的速度已经很快了, 然后查询磁盘 I/O 占用可以看到, 磁盘 I/O 几乎没有占用.至此, 问题全部解决结语通过此次故障的发生, 我们需要加强对服务日志的管控以及对程序潜在 bug 的审查. 感谢您的阅读, 如果觉得不错, 还请分享给有需要的人, 感谢! 如有错误或疏漏, 还请指出, 感谢!
一篇文章搞定 PXE Server 网络启动, 从此装机抛弃物理存储设备 - iVentoy 部署
前言在做网络运维的过程中, 常常会碰到需要给各种物理机或虚拟机安装系统的情况. 以往装系统都是通过 U 盘等物理存储介质进行安装. 这种安装方式不仅效率低, 还可能因为 U 盘速率不达标而导致安装过程缓慢. 本篇文章将会给大家介绍一种效率极高且速率极快的系统安装方式 - PXE 网络启动. 使用 PXE Server 安装系统不仅可以做到几百台设备同时安装, 还可以做到通过一个通道, 自由安装不同种类, 不同平台的操作系统.需要注意的是, PXE 技术不仅可以用于安装系统, 还可以托管操作系统, 使下游设备做到无盘启动. 本文只介绍前者的解决方案.配置需求一台网络接口 1Gbps 及以上, 硬盘速率足够的物理机/虚拟机支持配置 TFTP Server 的硬/软路由本篇文章使用基于 OpenWrt 的 X-wrt 作为演示开整安装 iVentoy在待配置为 PXE Server 的设备上安装 Debian/Ubuntu 操作系统 (略).在官方发布页 中根据设备架构下载 iVentoy 的软件包截至本文编写时, 官方最新版本为 1.0.30. 如果是 x86_64 设备, 需要下载的文件为 https://github.com/ventoy/PXE/releases/download/v1.0.30/iventoy-1.0.30-linux-x86_64-free.tar.gz解压下载的软件包tar -xzvf https://github.com/ventoy/PXE/releases/download/v1.0.30/iventoy-1.0.30-linux-x86_64-free.tar.gz启动 iVentoycd iventoy-1.0.30 chmod +x ./iventoy.sh ./iventoy.sh start脚本运行后, 默认运行在 26000 端口上. 以下是输出示例iventoy start SUCCESS PID=2040 Please open your browser and visit http://127.0.0.1:26000 or http://x.x.x.x:26000 (x.x.x.x is any valid IP address)使用浏览器输入 http://<Server IP>:26000, 即可进入 iVentoy 的配置页面.在主页中按照您的需求进行配置Select Server IP - 选择服务器 IP. 这里列出的是当前服务器的 IP 地址NIC Name - 服务器网卡名称.Subnet Mask - 子网掩码.Gateway - 网关.IP Pool (begin) - PXE Boot IP 地址池起始. 注意: 免费版的 iVentoy 一次性支持 20 个客户端同时启动, 故此处设置 20 个 IP 即可IP Pool (end) - PXE Boot IP 地址池末尾.DNS Server - DNS 服务器地址. 置空即可进入 Configuration 页面, 按照需求对 iVentoy 的启动方式进行配置DHCP Server Mode - DHCP 服务器模式. 这里推荐保持不变Boot Background Mode - 启动时背景模式. 这里推荐选择 GUI, 即图形化用户界面Boot Menu Resolution - 启动菜单分辨率. 可以不用设置太高, 合适即可EFI Boot File - EFI 引导文件. 按需选择, 如果没有特殊要求, 保持默认即可System Boot Password - 系统引导密码. 选择 PXE 引导时是否需要输入密码, 如不需要置空即可System Default Image - 系统默认映像. 选择 iVentoy 默认引导的系统映像, 如无特殊需求置空即可Page Default Language - 页面默认语言.导入 ISO 映像在软件包解压后的目录中有一个 iso 文件夹, 将您的所有 ISO 映像文件放入该文件夹即可.注: 您也可以使用 NFS/SMB 将存有映像文件的网络目录挂载到 iso 目录, 当然使用硬链接也是可以的配置 TFTP Server本文使用基于 OpenWrt 的 X-Wrt 进行演示进入设备 WebUI, 依次选择 Network (网络) --> DHCP --> dnsmasq --> PXE/TFTP勾选 Enable TFTP Server (启用 TFTP 服务器)在下方的 Special PXE boot options for Dnsmasq. 表格中点击 Add (添加) 按钮在对话框中填入您的相关配置.Filename - 引导文件文件名. 填入 iVentoy 设置中的 EFI Boot FileServer name - 引导服务器名称. 随意设置DHCP Options - DHCP 附加选项. 推荐置空Match this Tag - 仅携带该 Tag 的 DHCP 客户端才会被引导至 PXE Server. 如无特殊需求推荐置空Force - 总是发送选中的 DHCP 选项. 推荐置空Instance - dnsmasq 实例. 如果网络环境为普通的单广播域家庭网络, 直接选择实例 0 即可. 其他情况请按需选择.配置完成后单击 Save (保存)在当前页面的 Network boot image (网络启动映像) 中填入刚才设置的 Filename. 如果您添加了不止一个 Host, 那么请填入您需要的.全部修改完成后单击 Save & Apply (保存并应用)测试进入 iVentoy WebUI, 在主页中单击绿色三角按钮启动服务器将需要安装系统的设备接入 iVentoy 所在的内网启动设备注: iVentoy 同时支持传统 MBR 启动和新型 UEFI 启动. 如果您选择 UEFI 启动, 请务必关闭 Secure Boot, 否则可能启动失败如果设备中显示了 iVentoy 的启动页面, 则配置成功至此, iVentoy 的配置就完成了结语本人非专业运维, 如有错误或疏漏还请指出! 如果觉得文章有用, 请分享给有需要的人, 感谢!
Super-frpc让 frp 客户端集群管理变得可视化, 自动化, 可靠化.项目简介将您的所有 frp 客户端实例置于一个 WebUI 中进行管理. 集中化, 标准化, 自动化. 增强您的 frp 链路鲁棒性, 让您的 frp 实例管理更方便, 更快捷该项目目前处于预发布阶段, 可能会遇到各种不确定的 Bug 和有待完善的功能. 还请各路大佬高抬贵手, 有能力的帮忙完善项目项目特色集中管理: 使用一个后端服务, 一个 WebUI, 就可以一次性对所有 frp 实例进行管理跨平台: 同时支持 Windows, GNU/Linux 平台 (GNU/Linux 平台目前支持 systemd, initd)基于角色的访问控制: 系统内置三种用户角色 (visitor, admin, superuser), 每个角色权限明确, 使得您可以安排您信任的人共同维护您的 frp 客户端集群轻松部署: 整个项目使用二进制方式部署, 方便快捷, 部署难度低操作简单: Web 面板内各个功能模块分区明确, 操作简单易上手自动化运维: 内置 Watchdog 程序, 可以对您的 frp 客户端实例进行监控. 一旦检测到程序报错, 会先自行尝试恢复, 如无法恢复, 可以通过您自行设定的通知渠道来通知您技术栈开发语言: Go, Vue3后端模块: BurntSushi/toml, gorilla/websocket, modernc.org/sqlite数据库: SQLite支持平台GNU/Linux (systemd, initd)Windows 10 or Up其中, 主程序的测试已在 Linux 中完成, 但 watchdog 在目前的版本中只支持 systemd 管理器的 Linux 平台项目地址Gitea截图部署Linux 平台在 后端发布页面 中下载最新发行版的二进制程序, 并将其放入新建文件夹中给予可执行权限chmod +x super-frpc在相同目录中新建 watchdog 文件夹mkdir watchdog在 Watchdog 发布页 中下载最新发行版的二进制程序, 并将其放入 watchdog 文件夹中赋予可执行权限chmod +x watchdog_systemd退回上一级, 并以 root 用户执行主程序如果程序执行没有报错, 则证明正常运行. 程序会默认监听 7000 端口, 可以通过该端口进入内嵌的 WebUI进入 frp 官方发布页 下载最新 frp 二进制文件发行压缩包解压到 super-frpc 目录tar -xzvf frp_0.69.0_darwin_amd64.tar.gz赋予 frpc 可执行权限chmod +x frp_0.69.0_darwin_amd64/frpc进入程序 WebUI, 按照初始化提示进入设置页面, 填入 frpc 二进制文件的绝对路径, 并按照要求和需求完成剩余设置后应用并重启如果一切正常, 那么您已经成功部署了!在初次进入程序时, 程序会要求您进行初始化设置.
前言随着业务量的上涨与集群节点的增加, 我们使用 kubectl 命令行工具进行运维会逐渐变得困难, 并且也无法实时监测到每个节点的实时状态. 这时, 一套好用的图形化监控与运维工具就成为了我们的刚需. 这篇文章将会详细描述 Kubernets 图形化运维工具 Rancher 的搭建与配置过程本文章同样适用于 K8s配置需求可运行的 K3s/K8s Master 节点与至少一个 Worker 节点开整安装 HelmHelm 是一个为 Kubernets 开发的包管理器, 它简化了应用的部署与管理. 它使用 Helm charts, 这是一个定义了 Kubernets 资源分配的 YAML 文件, 为自动化的安装, 升级, 管理 Kubernets 集群应用程序而生.个人翻译, 有错见谅, 原文如下:Helm is a package manager for Kubernetes that simplifies the deployment and management of applications. It uses Helm charts, which are collections of YAML files that define Kubernetes resources, to automate the process of installing, upgrading, and managing applications in a Kubernetes cluster.安装 Helm 软件源与 GPG Keysudo apt-get install curl gpg apt-transport-https --yes curl -fsSL https://packages.buildkite.com/helm-linux/helm-debian/gpgkey | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/helm.gpg] https://packages.buildkite.com/helm-linux/helm-debian/any/ any main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list更新软件源并安装 Helm可能需要科学上网sudo apt update sudo apt install helm安装 cert-manager可以在 这里 看到 Rancher 的支持平台与配置需求添加 Rancher 软件源helm repo add rancher-latest https://releases.rancher.com/server-charts/latest在 Kubernets 中创建 Rancher 的 namespacekubectl create namespace cattle-system添加 cert-manager 清单文件kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/<VERSION>/cert-manager.crds.yamlVERSION Github 上的版本 Tag, 需要您自己填写添加 jetstack 软件源helm repo add jetstack https://charts.jetstack.io更新软件源helm repo update安装 cert-managerhelm install cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace注: 如果您在安装过程中碰到报错 Error: INSTALLATION FAILED: Kubernetes cluster unreachable: Get "http://localhost:8080/version": dial tcp [::1]:8080: connect: connection refused, 则您需要按照如下步骤进行配置Q. 为什么会报错?A. helm v3 版本不再需要 Tiller, 而是直接访问 ApiServer 来与 k8s 交互，通过环境变量 KUBECONFIG 来读取存有 ApiServre 的地址与 token 的配置文件地址, 默认地址为 ~/.kube/config *转自 CSDN修改 /etc/profilevim /etc/profile在开头处写入内容 export KUBECONFIG=/etc/rancher/k3s/k3s.yaml保存并执行souce /etc/profile执行完成后, 再次尝试运行安装命令, 这次应当没有报错.如果先前有安装失败的记录, 那么重新安装可能会遇到报错 Error: INSTALLATION FAILED: cannot re-use a name that is still in use, 此时删除对应的 Secret 即可helm -n {namespace} delete cert-manager kubectl delete secrets -n cert-manager -l owner=helm,name=cert-manager如果在安装时遇到了错误Error: INSTALLATION FAILED: failed post-install: 1 error occurred: * timed out waiting for the condition则是因为 cert-manager 在安装时会创建一个 Job (startupapicheck) 来校验 webhook 是否正常工作. 该校验会向 API Server 发送一个测试请求, 触发 webhook 调用, 但此时的 Webhook 配置中 caBundle 还没有被 cert-manager 的 controller 及时填上 (或者使用的是旧的/无效的 CA), 导致 API Server 连接 webhook 时证书不信任, TLS 握手失败, Job 一直重试直至 Helm 超时.修复方法如下:获取 CA 证书kubectl get secret cert-manager-webhook-ca -n cert-manager -o jsonpath='{.data.ca\.crt}' | base64 -d给两个 Webhook 注入证书CA_BUNDLE=$(kubectl get secret cert-manager-webhook-ca -n cert-manager -o jsonpath='{.data.ca\.crt}') kubectl patch mutatingwebhookconfiguration cert-manager-webhook --type='json' \ -p="[{'op': 'replace', 'path': '/webhooks/0/clientConfig/caBundle', 'value':'${CA_BUNDLE}'}]" kubectl patch validatingwebhookconfiguration cert-manager-webhook --type='json' \ -p="[{'op': 'replace', 'path': '/webhooks/0/clientConfig/caBundle', 'value':'${CA_BUNDLE}'}]"如果有多个 Webhook, 还可以使用循环来依次注入for wh in mutatingwebhookconfiguration cert-manager-webhook validatingwebhookconfiguration cert-manager-webhook; do kubectl get $wh -o json | jq '.webhooks[].clientConfig.caBundle = "'${CA_BUNDLE}'"' | kubectl apply -f - done此时再次进行安装, 就不会再次出现该问题了安装 Rancher使用以下命令安装 Rancherhelm install rancher rancher-latest/rancher \ --namespace cattle-system \ --set hostname=<hostname> \ --set replicas=1 \ --set bootstrapPassword=<PASSWORD_FOR_RANCHER_ADMIN>hostname 是 Ingress 监听的目标地址. 这条配置对应的是 Ingress 配置中的 host 如果这是部署在生产环境中的, 请务必将其设置为服务器域名或反向代理的目标域名.PASSWORD_FOR_RANCHER_ADMIN 是 Rancher 管理员的密码, 需要您自行设置, 长度至少为 12 个字符如果您的 Pod 状态处于 ImagePullBackOff, 则很可能是镜像拉取失败了. 您可以按照以下操作为 Rancher 配置镜像Master:cat <<EOF > /etc/rancher/k3s/registries.yaml mirrors: docker.io: endpoint: - "https://docker.m.daocloud.io" quay.io: endpoint: - "https://quay.m.daocloud.io" EOF systemctl restart k3sWorker:mkdir /etc/rancher/k3s cat <<EOF > /etc/rancher/k3s/registries.yaml mirrors: docker.io: endpoint: - "https://docker.m.daocloud.io" quay.io: endpoint: - "https://quay.m.daocloud.io" EOF systemctl restart k3s-agent执行完成后, 镜像拉取应该会正常如果没有报错, 并且使用绑定的域名可以访问到 Rancher 控制台, 那么配置到此结束. 如果您不想让 Ingress 去管理反向代理, 而想使用外部的反向代理工具 (Nginx, openresty 等) 去负责反代和负载均衡, 或者您想直接使用 IP 地址: 端口的形式去进行访问, 则可以接着看下去.暴露相关端口Rancher 安装后默认的策略是 ClusterIP, 这种策略是无法被外部所访问的, 只能在集群内访问. 如果想使用外部的反向代理工具, 则需要将策略改为 NodePort, 并配置持久化导出配置清单helm get values rancher -n cattle-system -o yaml > rancher-values.yaml进入清单文件, 在文件末尾添加这些参数service: type: NodePort ports: https: nodePort: <httpsPort> http: nodePort: <httpPort>httpsPort https 服务器对外暴露的端口httpPort http 服务器对外暴露的端口保存后, 升级 Rancher, 应用新配置helm upgrade rancher rancher-latest/rancher \ -n cattle-system \ -f rancher-values.yaml配置完成后, 使用您设置的地址在浏览器中访问, 如果能顺利进入到 Rancher 的初始化页面, 并输入您设置的默认密码进入主页, 则配置完成结语本人非专业 Kubernets 运维人员, 如有疏漏或错误, 还请指出!如果觉得文章有帮助, 还请分享给有需要的人, 感谢!