最近更新了家里的网关设备,也碰到了一堆问题,但也借由这个机会重新整理了网络拓扑,
并计划好好的利用 Kubernetes 部署内网的一堆应用与自动化工具。
网络拓扑
首先理清内网所有的主要网络设备,当前如图。
ESXi 以及 NAS,Printer 都是之前就配置好的,这里不再赘述。
主要涉及的是这次的新设备 ROS(RB450Gx4) 主网关以及 OpenWRT 虚拟路由,FreeGateway 用于科学上网,EdgeGateway 使用 Zerotier 作为边界网关,主要用于内网对等及穿透。
主网关
主网关使用 RB450Gx4,该设备属于有线路由,性能强劲,支持硬件转发,我在测速时可以跑满 200M,峰值270M,旧的路由器 RB951G 2HND 则只能跑到 170M。
ROS 主要配置的是策略路由,国外 IP 将 TCP 包路由到 FreeGateway,对等子网路由到 EdgeGateway,并配置 DNS 转发到 FreeGateway。
这里 ROS 配置时碰到了一个困扰很久的问题,路由转发到 FreeGateway 时,出现了大量丢包,导致无法完成请求。
使用 Wireshark 抓包分析发现有 ICMP Redirect 请求,之前的旧路由是同样的工作模式,没有出现问题,经过大量搜索与排查,没有在网上找到解决方案。
最后在配置防火墙时,无意发现禁用某条默认规则后再无丢包。
该条规则如下:
1/ip firewall filter
2add action=drop chain=forward connection-state=invalid
作用是丢掉无效连接状态的包,但是由于做了 marking-route 转发时产生了 ICMP Redirect,导致 tcp 状态失效,因此该规则就丢弃了部分正常包,下次记住配置 ROS 一定要默认的防火墙规则都删掉。
DNS
一个合格的 Homelab 网络理应有 DNS 提供内网的主机名映射,这里我使用的是 FreeGateway 提供的 chinadns 作为主 DNS 解析,并配置了内网 DNS 转发,内网主机应统一配置域名后缀 lan,或者由 DHCP 下发。
同时 ROS 配置了三条巧妙的 DNS 转发规则:
1/ip firewall nat
2add action=netmap chain=dstnat comment=DNS dst-address=10.0.0.1 dst-port=53 \
3 protocol=udp src-address=!10.0.1.1 to-addresses=10.0.1.1 to-ports=53
4add action=masquerade chain=srcnat dst-address=10.0.1.1 dst-port=53 protocol=\
5 udp src-address=!10.0.1.1
6add action=netmap chain=dstnat comment=HijackDNS dst-address=8.8.8.8 dst-port=\
7 53 protocol=udp src-address=!10.0.1.1 to-addresses=10.0.1.1 to-ports=53
主DNS为 ROS 10.0.0.1
,也是 DHCP 下发的 DNS,这两条规则的作用是,当 DNS 请求源 IP 为 FreeGateway 10.0.1.1
时,DNS解析由 ROS 负责,当请求源 IP 为其他主机时,DNS 解析请求 NAT 转发到 10.0.1.1
,而由于 FreeGateway 配置了内网域 lan
转发到 ROS,内网主机的解析就重新由 ROS 来解析。
最后劫持了对 8.8.8.8 的 DNS 请求,转发到 10.0.1.1
,这样内网主机使用时,DHCP DNS 下发为 10.0.0.1,
手动配置 DNS 则为 8.8.8.8,都能达到同样的效果,并且对应用和配置毫无侵入。
而 ROS 则配置了一个脚本 schedule 来定时获取 DHCP 主机名并更新静态 DNS,以完成内网的解析,脚本如下。
Gist: dhcpHostToDNS.script
1:local ttl "00:05:00"
2:local zone "lan"
3:local hostname
4:local ip
5:local dnsip
6:local dhcpip
7:local dnsnode
8:local dhcpnode
9
10/ip dns static;
11:foreach i in=[find where name ~ (".*\\.".$zone) ] do={
12 :local nodettl
13 :set hostname [ get $i name ];
14 :set hostname [ :pick $hostname 0 ( [ :len $hostname ] - ( [ :len $zone ] + 1 ) ) ];
15 :set nodettl [get $i ttl];
16 /ip dhcp-server lease;
17 :set dhcpnode [ find where host-name=$hostname ];
18 :if ( [ :len $dhcpnode ] > 0) do={
19 :log debug ("Lease for ".$hostname." still exists. Not deleting.");
20 } else={
21# there's no lease by that name. Maybe this mac has a static name.
22 :local found false
23 /system script environment
24 :foreach n in=[ find where name ~ "shost[0-9A-F]+" ] do={
25 :if ( [ get $n value ] = $hostname ) do={
26 :set found true;
27 }
28 }
29 :if ( found ) do={
30 :log debug ("Hostname ".$hostname." is static");
31 } else={
32 :if ( $nodettl = $ttl ) do={
33 :log info ("Lease expired for ".$hostname.", deleting DNS entry.");
34 /ip dns static remove $i;
35 }
36 }
37 }
38}
39
40/ip dhcp-server lease;
41:foreach i in=[find] do={
42 :set hostname ""
43 :local mac
44 :set dhcpip [ get $i address ];
45 :set mac [ get $i mac-address ];
46 :while ($mac ~ ":") do={
47 :local pos [ :find $mac ":" ];
48 :set mac ( [ :pick $mac 0 $pos ] . [ :pick $mac ($pos + 1) 999 ] );
49 };
50 :foreach n in=[ /system script environment find where name=("shost" . $mac) ] do={
51 :set hostname [ /system script environment get $n value ];
52 }
53 :if ( [ :len $hostname ] = 0) do={
54 :set hostname [ get $i host-name ];
55 }
56 :if ( [ :len $hostname ] > 0) do={
57 :set hostname ( $hostname . "." . $zone );
58 /ip dns static;
59 :set dnsnode [ find where name=$hostname ];
60 :if ( [ :len $dnsnode ] > 0 ) do={
61# it exists. Is its IP the same
62 :set dnsip [ get $dnsnode address ];
63 :if ( $dnsip = $dhcpip ) do={
64 :log debug ("DNS entry for " . $hostname . " does not need updating.");
65 } else={
66 :log info ("Replacing DNS entry for " . $hostname);
67 /ip dns static remove $dnsnode;
68 /ip dns static add name=$hostname address=$dhcpip ttl=$ttl;
69 }
70 } else={
71# it doesn't exist. Add it
72 :log info ("Adding new DNS entry for " . $hostname);
73 /ip dns static add name=$hostname address=$dhcpip ttl=$ttl;
74 }
75 }
76}
Kubernetes 集群
完成了以上基础网络和环境配置后,才有基本的条件建立 Kubernetes 集群,也是之后的主要目的,这里我使用了 kubeadmin。
由于搭建集群和配置过于复杂,应另起一文详叙。
集群运行的组件:
- Gitlab Runner
Gitlab Runner 的主要目的是提供一个特殊的 runner 供 gitlab-ci 使用,Gitlab 提供的 runner 虽然可以以 privlileged 模式运行,但是没有加载 nbd 内核模块,缺少我需要的构建条件,因此运行此 runner 来进行依赖 nbd 的特殊构建。 - Bitcoin Daemon
比特币全节点,主要用于开发和测试 API。 - Geth
以太坊全节点,同上。 - go-filecoin
Filecoin 节点,同上。
结语
网络知识是一个架构师所需的基本素质,无论是架构还是应用都需要,了解这些对于提升技术水平甚至架构水平很有帮助。
搭建这个实验环境的目的也是为了更好的学习和提升,未来 Kubernetes 也许是云上的应用操作系统,需要更多的了解和深入学习它,在云端的 Kubernetes 和本机搭建的集群始终有所限制,通过这个 Homelab 之上的集群,可以做很多事情。