Homelab 手记

最近更新了家里的网关设备,也碰到了一堆问题,但也借由这个机会重新整理了网络拓扑,
并计划好好的利用 Kubernetes 部署内网的一堆应用与自动化工具。

网络拓扑

首先理清内网所有的主要网络设备,当前如图。

Network Topology

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 之上的集群,可以做很多事情。

参考

View Comments