Liangwu's Notes

Just note anything

19年的时候搞了块J3455的板子自己拼了个NAS,可能机箱空间太小,硬盘数据线有时接触不佳,老掉盘。搞了2个2T,1个3T的SATA硬盘,当时图便宜都是SMR的盘,硬盘数据老真的是老慢慢,现在准备是用来搞冷备使用了。系统盘当时用的一个Thinkpad T400淘汰下来2.5寸的机械硬盘,老盘时常出问题,一出问题就只能把机箱搬到电视机前(家里没有HDMI显示器),然后再调试。搞过几次吧,很烦,于是搞了个HPE Microserver Gen8,主要是有iLO,解决显示器的问题。然后后面又搞了一些二手的东西,把家里的网络终于搞得可用了。

硬件

GPON: Nokia G-010S-A,原来用的是运营商光猫桥接,然后有个Buffalo WZR-HP-AG300H刷了OpenWrt来拨号。后来买了个弱三层的交换机,就全部网络设备动了动。

主路由:EdgeRouter X-SFP,当时在小黄鱼蹲守的时候,主要看中的是这个和Mirotik hEX S,两个价格差不多,但前者存储比后者大,所以就入了X-SFP。入手后发现还是X-SFP比较适合我吧,系统基于Debian,配置相对ROS也简单。但确实ROS要配置更丰富一些。

核心交换机:Huarong S5735S-L8T4S-QA2,当时某项目现场用到了这个型号,PDD入了一个,感觉应该是弱三层里非常非常便宜的了。

接入交换机:Linksys SLM2008,简单网管交换机,最早为了测试链路聚合,二手入的。

无线APHuawei AX6,刚开始用的是Tenda AC6。两个都是纯AP模式,后面新买的效果真的是秒杀。

配置

目的

以前用的是OpenWrt通过MAC配合防火墙来实现设备是否可以上网,解决未经许可连接WIFI的人蹭网,主要是常常有人用Android的什么万能钥匙,具体设置

现在用弱三层交换机当核心,实现DHCP Server,ACL控制,路由器与交换机通过OSPF动态路由,这样交换机及路由器功能分开,坏一个,其他能正常工作。不同网段通过ACL进行限速。

然后用GPON可以省掉光猫。用AX6无线路由器当AP,再配合交换机的MAC-VLAN功能,实现无线设备的网络隔离。

具体现有的大致网络示意图如下。

networking

GPON

最初想直接把GPON插在交换机上使用,发现可以识别,但无法使用,后面才买的X-SFP路由器。但GPON最开始的时候LAN IP是192.168.1.10,和现有的网络冲突,记得好像是用fwsetenv的命令改了IP地址,只修改配置文件不行,稍微研究了下,应该是配置文件写到变量里面去了,启动的时候读取mtd分区来设置环境变量,所以单纯修改配置文件无效。

主路由

路由器用的是X-SFP,基本满足了我的需求,但是IPv6这块有点小问题,好像无法用DHCPv6 Server来分配从ISP获得的IPv6 PD进行网络划分,但Mikrotik可以实现。OSPF动态路由,主网10.0.0.0/24(路由器LAN),子网192.168.100.0/24(GPON LAN)。

现在主路由的功能主要是:

  1. PPPoE拨号
  2. FRP内网穿透(IPv4 SSH)
  3. Wireguard,外网可连接路由器
  4. 脚本实现IPv6 DDNS,DigitalOcean可以通过curl来更新DNS记录,方便用IPv6的时候进行连接SSH控制内网。
  1. DNS查询转至内网Pihole,解决部分流氓软件或广告,以及实现内外网同一域名访问服务。同时运行一个小脚本,当内网Pihole无法正常提供服务器,放行外网DNS查询。

其他上Github上找了一个自动备份的脚本,原本是可以在commit的时候自动进行备份,后面发现在频繁,于是改动每天自动备份。

其中各接口用途:

  • Eth0: 之前用于接光猫桥接进行PPPoE拨号的,后空着,后续如果ISP禁了GPON的话,可以直接连光猫使用。
  • Eth1、Eth2: Switch0(10.0.0.1/24),VIF 1000(IPv6),接内网。
  • Eth3: 192.168.200.1,PoE 24v供电给Mikrotik 750Gr3,开启DHCP Server,主要是测试Ros使用,不测试的时候PoE取消。
  • Eth4: VIF 8(192.168.8.x),连接交换机管理VLAN,可通过路由器直接管理交换机。
  • Eth5: 192.168.100.1,PPPoE拨号。

路由器具体配置,其中部分就是防火墙部分需要开启对应的规则,LOCAL、IN等端口。

路由器IPv6设置

最开始向导设置的时候就启用IPv6,这样系统会自动创建IPv6的防火墙规则,省不少事。IPv6我主要是用来外网直接访问内网服务和SSH,以及PT下载。AKA:之前是可以直接访问443端口的,后来封掉,只改用其他端口了。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
firewall {
options {
mss-clamp {
interface-type pppoe
mss 1452
}
mss-clamp6 {
interface-type pppoe
mss 1432 #解决IPv6上网慢的问题
}
}
}
interfaces {
ethernet eth5 {
address 192.168.100.1/24
description "Internet SFP"
pppoe 1 {
default-route auto
dhcpv6-pd {
no-dns
pd 1 {
interface switch0.1000 {
host-address ::1
no-dns
prefix-id :1
service slaac
}
prefix-length /60
}
rapid-commit enable
}
ipv6 {
address {
autoconf
}
dup-addr-detect-transmits 1
enable {
}
}
}
}
switch switch0 {
vif 1000 {
description IPv6_HOME_LAN
ipv6 {
router-advert {
default-lifetime 1800
max-interval 600
min-interval 60
prefix ::/64 {
autonomous-flag true
on-link-flag true
preferred-lifetime 86400
valid-lifetime 86400
}
reachable-time 0
retrans-timer 0
send-advert true
}
}
}
}
wireguard wg0 {
address 10.8.2.x/32
description Wireguard
mtu 1420
peer xxx {
allowed-ips 10.8.2.0/24
endpoint xxxx:52550
persistent-keepalive 60
}
private-key ****************
route-allowed-ips true
}
}
service {
# 转发DNS请求至pihole
nat {
rule 10 {
description DNS
destination {
address !192.168.1.199
port 53,5353,5354,853
}
inbound-interface switch0
inside-address {
address 192.168.1.199
port 53
}
protocol tcp_udp
source {
address !192.168.1.199
}
type destination
}
rule 5002 {
description "Maqquerade for DNS"
destination {
address 192.168.1.199
port 53
}
outbound-interface switch0
protocol tcp_udp
source {
address 192.168.0.0/16
}
type masquerade
}
}
}

核心交换机

当时想搞个三层交换机的原因就是想家里用VLAN进行网络隔离,并对不同网段进行上网限制,但内网访问速度不进行限。最早的时候用X-SFP当单臂路由进行VLAN隔离的,网间流量需要经过路由器,且路由器firewall规则多了之后会影响速度,最后买了个弱三层交换机。

有线部分的网段划分比较简单,然后AX6不支持WIFI VLAN隔离,最后查文档的时候想到VLAN划分还有MAC划分,然后想着就WIFI过来靠MAC进行VLAN划分。

VLAN划分如下:

  • 8: 管理VLAN
  • 100: 用于上行链路,连路由器
  • 200: 服务器及需要大带宽的设备,不限速,其他网段无法访问此网段,除NAS,Pihole
  • 210: 一般设备接入,默认WIFI接入VLAN,下行限100,上行限20
  • 220: 用于IoT,摄像头、天猫精灵、火火兔等,下行限10,上行限10
  • 230: 用于不让连外网的设备,电视机、无线路由器AX6、空调、天猫精灵(不让小孩听的时候)等
  • 1000: 用于IPv6接入

每VLAN配置:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
vlan 8
description lan for mgmt
name MGMT_LAN
management-vlan
vlan 100
description to route
vlan 200
description lan for home
name HOME_LAN
mac-vlan mac-address e45e-3732-xxxx priority 0
dhcp snooping enable
ipv4 source check user-bind enable
ipv6 source check user-bind enable
vlan 210
description lan for guest
name GUEST_LAN
dhcp snooping enable
ipv4 source check user-bind enable
ipv6 source check user-bind enable
traffic-policy tp_vlan210-to-lan inbound
vlan 220
description lan for IoT
name IoT_LAN
mac-vlan mac-address ec3d-fdbe-xxxx priority 0
dhcp snooping enable
ipv4 source check user-bind enable
ipv6 source check user-bind enable
traffic-policy tp_vlan210-to-lan inbound
vlan 230
description intranet
name Intra_LAN
mac-vlan mac-address 345b-bb8f-xxxx priority 0
dhcp snooping enable
ipv4 source check user-bind enable
ipv6 source check user-bind enable
traffic-policy tp_permit-199 inbound
vlan 1000
description ipv6 for lan
interface Vlanif8
ip address 192.168.8.x 255.255.255.0

interface Vlanif100
ip address 10.0.0.2 255.255.255.0
interface Vlanif200
ip address 192.168.1.1 255.255.255.0
dhcp select interface
dhcp server ip-range 192.168.1.x 192.168.1.x
dhcp server gateway-list 192.168.1.1
dhcp server dns-list 192.168.1.199 223.5.5.5 114.114.114.114
dhcp server logging allocation-fail allocation-success renew-success
interface Vlanif210
ip address 192.168.21.1 255.255.255.0
dhcp select interface
dhcp server ip-range 192.168.21.x 192.168.21.x
dhcp server gateway-list 192.168.21.1
dhcp server dns-list 192.168.1.199 223.5.5.5 114.114.114.114
interface Vlanif220
ip address 192.168.22.1 255.255.255.0
dhcp select interface
dhcp server ip-range 192.168.22.x 192.168.22.x
dhcp server gateway-list 192.168.22.1
dhcp server dns-list 192.168.1.199 223.5.5.5 114.114.114.114
dhcp server logging allocation-fail allocation-success renew-success
interface Vlanif230
ip address 192.168.23.1 255.255.255.0
dhcp select interface
dhcp server ip-range 192.168.23.x 192.168.23.x
dhcp server gateway-list 192.168.23.1
dhcp server dns-list 192.168.23.1
dhcp server logging allocation-fail allocation-success renew-success

各接口设置:

  • G0/0/2: 上行链接

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    interface GigabitEthernet0/0/2
    port link-type hybrid
    port hybrid pvid vlan 100
    undo port hybrid vlan 1
    port hybrid tagged vlan 1000
    port hybrid untagged vlan 100
    loopback-detect enable
    traffic-policy tp_dest-vlan210 inbound
    traffic-policy tp_source-vlan210 outbound
    dhcp snooping enable
  • G0/0/6,G0/0/8: Eth-Trunk,连NAS链路聚合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    interface GigabitEthernet0/0/6
    eth-trunk 1
    interface GigabitEthernet0/0/8
    eth-trunk 1
    interface Eth-Trunk1
    port link-type trunk
    undo port trunk allow-pass vlan 1
    port trunk allow-pass vlan 8 200 210 220 230 1000
    loopback-detect enable
    stp edged-port enable
    dhcp snooping enable
  • G0/0/1,G0/0/3,G0/0/4,G0/0/5,G0/0/7,G0/0/9-G0/0/12: 下行链接

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    interface G0/0/x
    energy-efficient-ethernet enable #SFP无
    port-auto-sleep enable #SFP无
    port link-type hybrid
    port hybrid pvid vlan 210
    undo port hybrid vlan 1
    port hybrid tagged vlan 8
    port hybrid untagged vlan 200 210 220 230
    loopback-detect enable
    stp edged-port enable
    mac-vlan enable
    dhcp snooping enable

OSPF及ACL策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ospf 1 router-id 10.0.0.2
area 0.0.0.0
network 10.0.0.0 0.0.0.255
area 0.0.0.1
network 192.168.1.0 0.0.0.255
network 192.168.21.0 0.0.0.255
network 192.168.22.0 0.0.0.255

traffic behavior tb_dest-vlan210
car cir 51200 pir 102400 cbs 6400000 pbs 12800000 mode color-blind green pass yellow pass red discard
traffic behavior tb_dest-vlan220
car cir 5120 pir 10240 cbs 640000 pbs 1280000 mode color-blind green pass yellow pass red discard
traffic behavior tb_source-vlan210
car cir 5120 pir 10240 cbs 640000 pbs 1280000 mode color-blind green pass yellow pass red discard
traffic behavior tb_source-vlan220
car cir 5120 pir 10240 cbs 640000 pbs 1280000 mode color-blind green pass yellow pass red discard

SLM2008

交换机没有华为系的Hybrid接口,把连AP的口和上链口都设成同一个untagged vlan ID,这样可以靠上链交换机的MAC-VLAN功能进行VLAN划分。其他口我也没再划分其他VLAN的想法,都设成同一个untagged,只是上链口还有一个tagged vlan 8,这样可对其进行简单管理。

Gen8

哈,刚买Gen8的时候,少几个螺丝,跟客服沟通,我说买的G8(J8)少几个螺丝,那边哥们狂笑,连忙说Gen8(根8),哈哈哈。

硬件稍微升级了下,CPU/E3-1265L V2,内存/HPE SmartMemory 8Gx2,RAID卡/Smart Array P222 Controller,硬盘/SAS 4Tx4/SSD 512G/SD 8G。P222卡的温度还有点高,买了个4cm风扇,接主板里面的USB供电,从原来的70度降到47度左右,不知道夏天温度会怎样。

Host OS: HPE-ESXi-6.7.0-Update3-19195723-iso-Gen9plus-670.U3.10.9.0.8 (Hewlett Packard Enterprise),安装在内置的8G SD卡,为了减少SD卡的写操作,将系统的log日志改写到其他Datasource里。

最开始的时候买了个PCIe转NVMe的卡,然后Datasource就放在这个盘里面。后来看到小黄鱼上面有4个4T的SAS盘,买回来后感觉还是超值的,2021年产,通电时间2年,写入只有30多G。为了这4个盘,所以买了P222卡(改成HBA模式),然后PCIe转NVMe的卡只能下岗。后面把SATA 5位置(ODD)升级为512G的SSD盘了,之前是一个12年买的Intel 40G :(,虽然接口速度只有SATA1,但用来安装Guest OS影响不大。然后Guest OS备份的话,可以直接挂载Guest OS的NFS成Datasource,将备份文件存到NFS里。

Guest OS

  1. Alpine: Docker Image里经常看到的OS。这里最主要的作用是用来跑Docker aList,用来挂载中国移动的网盘,然后Debian挂载,备份文件存到网盘。最开始的时候是在Debian里面直接容器跑的,但是不能一启动就挂载,所以改到Alpine里面跑,然后把Alpine设置成随Host OS第一个启动,Debian延迟启动,这样就不用去搞脚本在Debian里延迟挂载,总感觉优雅,所以单独用Alpine提供aList网盘服务,Debian启动直接WebDav挂载。
  2. Debian: 主力NAS系统。直通P222卡,挂载4个SAS盘。三张网卡,分别对应VLAN 200/8/1000,不限速网段/管理网段/IPv6网段。
    2.1 提供SMB服务,TV Kodi给小孩看影片,通过Siri可远程开关服务
    2.2 提供NFS服务,TV Kodi给大人看影片,呵呵,Kodi可设置多个profile,这样小孩默认profile默认是用SMB,而切换profile后默认是用NFS。 :)
    2.3 Nextcloud容器,方便手机照片备份以及其他一些小资料存储
    2.4 Navidrome容器,提供音频服务,硬盘里有一直存放的音乐,可以用手机播放,还能用CarPlay,很不错,解决了车上听歌的问题,不用再烦各种音乐播放软件
    2.5 Pihole容器,提供内网DNS服务,解决部分广告及隐私收集问题,还有内网域名解析。
    2.6 qBittorrent容器,主要用于PT下载。之前用的Transmission,几个CUI停更后,换成qBittorrent了。
    2.7 其他,FRP内网穿透,Netty-Protobuf内网穿透(自己写的),Wireguard内网穿透,CURL脚本更新域名DNS IPv6地址,Nginx反代各二级域名访问内网以上各服务及iLO、ESXi等(同步VPS的SSL证书,并加简单认证),BorgBackup备份Debian系统、Nextcloud及个人资料并定时上传到网盘。
  3. Win7: QQ或Wechat使用,远程给小孩打印作业 :),并增加各网段网卡测试使用。
  4. DSM7: 当时本来打印用作主力NAS的,因无法使用现有硬盘资料,且挂载的NFS无法使用各服务(当时主要想用Audio Station)。现在主要用来下载一些其他网盘的资料,比如OneDrive,GoogleDrive等。
  5. FreeBSD: 想着用ZFS,但没法用Docker,又不想再去折腾Jail,放弃作主力NAS,当作测试使用。
  6. Gentoo: 一直非常喜欢的一个发行版本,当时在上家单独,一直用它当主力开发系统。留着测试使用。
  7. pfSense: 尝尝其他系统 :)

访问内网服务

域名DNS设置:

  • A Record: 设置成VPS的IPv4地址,然后NAS里设置FRP将Nginx端口映射到VPS,再在VPS的Nginx设置反代到FRP映射的端口,NAS里的Nginx通过二级域名访问各服务。Internet --> VPS Nginx --> FRP --> NAS Nginx --> 各服务
  • AAAA Record: 因某国内最轻的VPS没有IPv6地址,NAS脚本每隔1小时更新此记录。这样可以直接SSH连到内网,或访问各服务。Internet --> NAS Nginx --> 各服务
  • CNAME Record: 设置各服务的二级域名alias到前面A和AAAA记录上,这样只需改A和AAAA记录,服务的二级域名无需修改。

主要参考:

远程控制NAS开关Samba

之前通过一些方法,可以用手机控制家里的NAS,开始的时候手动开关Samba服务,后来写了一个小Shell脚本,可以设置定时关闭Samba,当时只考虑半个小时,后来呢可能其他需求,时间有变化,就在脚本后面加参数,控制关闭时间。后来有一天开车,无法用手机远程开Samba服务,就想到了NAS提供一个服务,外面调用就OK,然后手机用Siri语音远程调用。

修改Shell脚本

在原来的基础上,增加参数,可以调节时间。

g.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash

if [ $# -gt 1 ]; then
echo 'Too many args' >&2
exit 1
fi

if [ $# == 0 ]; then
DEADTIME=$(date -d "+30 minutes" +'%H:%M')
fi

if [ $# == 1 ]; then
DEADTIME=$(date -d "+"$1" minutes" +'%H:%M')
fi

HOUR=$(echo $DEADTIME | cut -d ':' -f1)
MINUTE=$(echo $DEADTIME | cut -d ':' -f2)

sudo systemctl start smbd

crontab -l 2>/dev/null | head -n -1 > /tmp/crontemp
echo "$MINUTE $HOUR * * * sudo systemctl stop smbd" >> /tmp/crontemp
crontab /tmp/crontemp
rm /tmp/crontemp

内网穿透

现在访问家里的NAS有三个方式吧,一个是之前IPv6的方式,但是有的时候有些地方没有IPv6。另外一个方式就是Frp,VPS的一个端口访问NAS的SSH端口,另外一个方式就是NAS的SSH Remote Tunnel,再另外一个方式就是自己用Netty写了一个内网穿透的东东。

最终考虑的方式是Frp,简单,占用资源不多,可以SSH远程命令。

对外服务

有了内网穿透提供SSH,然后前面的Shell脚本,其实就比较简单了,ssh -p port -i id_rsa username@ip cmd,就可以直接调用NAS的Shell脚本了。

方式呢考虑了好几个:

  1. Nginx + Lua脚本,Nginx重新编译增加Lua支持,然后指定url运行脚本,os.execute('cmd'),Nginx增加Lua需要下一些东西, LuaJIT, ngx_devel_kit, ngx_lua, Nginx, lua-resty-core, lua-resty-lrucache,虽然本身Nginx是自己编译的,但是下这多东西,还是算了。
  2. PHP,嗯,最好的编程语言 :(,本身一个老的VPS上面架了个Wordpress,是有PHP的,但是为了一些安全考虑,php配置文件中已经禁用了shell调用,为了一个功能也懒得打开了。
  3. Java/Golang/C#/Python,写个小的Web服务,用Nginx反代,调用Shell脚本。Python资源比较大,C#/Java占用资源也比较大,就考虑用Golang写个简单的服务吧,直接调用Shell命令,后来查了下,可以连接SSH直接运行远程命令。

运行shell

main.go
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
func main() {
gin.SetMode(gin.ReleaseMode)
router := gin.Default()
router.GET("/tv/tv", openTV)
router.GET("/tv/tv/:min", openTV)

router.Run(":8888")
}

func openTV(c *gin.Context) {
minParam := c.Param("min")
x := int64(35)

if min, err := strconv.ParseInt(minParam, 10, 32); err == nil {
x = min
}

cmd := exec.Command("ssh", "-p", "10000", "-i", "/home/liangwu/.ssh/id_rsa", "liangwu@127.0.0.1", "~/g.sh", strconv.Itoa(int(x)))

result, err := cmd.Output()
if err != nil {
log.Println(err)
c.IndentedJSON(http.StatusInternalServerError, gin.H{"status": "wrong"})
return
}

resultString := string(result[:])
c.IndentedJSON(http.StatusOK, gin.H{"result": resultString})

连接SSH

Siri语音指令

Shortcuts新增指令,名字可以直接用Siri打开,比如Shortcuts命名为打开电视,直接用Hi Siri 打开电视就可以执行。

  • Ask for Input,’Ask for Number with How much time?
  • Text, ‘host/tv/tv/Provided Input
  • Safari Open URLs, ‘Open Text

OK,这样就可以Siri打开家里电视咯。。。

心酸之路

去年下半年左右开始搞一些与西门子PLC通信的事情,也补了一些PLC编程知识,相信有一些编程基础的人,学PLC编程应该会很快的,尤其有一些C基础。

但是呢原来的程序一直一直是用OPC DA的方式与PLC通信,看了下代码质量确实不高,近20年前的代码,反正能运行读取数据,也没有重构的动力。

怎么工作的呢?有一个配置程序进行地址配置

  1. 配置PLC地址串,连接方式
  2. 配置所有需要用到的变量,地址,数据类型,存在数据库一张表中
  3. 配置变量组,从上步中选取各变量,存在数据库一张表中
  4. 在程序中对应各变量组建立相应类,用于读写数据
  5. 配置连接,变量组等
  6. 变量组读到的数据通过for循环,每次用switch判断字符串,然后硬编码对应到对象的字段中去,还需要硬编码类型转换

总之这个过程非常非常不优雅,太多模版代码,还有太多硬编码,既然已经配置好了地址对应的数据类型,为什么还需要硬编码转类型呢?而且当时里面用的EF读写数据库,那种操作真的是快让人吐,正常一个查询搞定的事,代码硬生生读取十几次数据库,然后很多读写数据库,速度慢得吓人。怎么做的呢?先查表按过滤条件,把id全找出来,对,只有id,然后对id列表用for循环,再用id去查数据库,一次查一条数据,再组装数据到对象中去,感觉完全不像是在用ORM框架。想死的心都有,完完全全不想动这些代码。

尝试过的S7通信方式:

  1. HslCommunication,感觉很全面,但是好像要付费,pass,国人写的
  2. OPC UA Helper,用的OPC基金UA Client的包,然后西门子官网上有个Helper示例,试了试,还OK,但是需要PLC开启OPC UA功能,或用Kepware软件
  3. s7netplus,比较不错,自己包装包装可以用得比较顺手,后面几个小项目用的是它
  4. Eclipse Milo,OPC UA方式读写数据,未仔细研究,OPC UA方式暂时放弃
  5. s7connector, Java,想着C#还不是很精通,用Java来试试,蛮久没有更新,没找到批量读取的功能。
  6. plc4x,netty, SPI, opm, connection-pool, byte-buddy, 暂时发现还蛮合适的。

开整

最开始的时候学习官方的方式,OPM,类似ORM,读取的数据直接映射到对象。

新建对象,类加上注解,字段加上注解并填上地址、类型,创建连接,ByteBuddy创建动态对象,拦截get/set方法。get方法,判断是否在缓存时间内,如果在则直接返回缓存值,不在则从PLC中读取新值。set方法,直接写入值到PLC。这里有两个地方有坑,其它无参方法则会先读取所有PLC值,执行原无参方法,再将值写入PLC;其它一个参数方法,则会先读取PLC值,再执行原方法。这两点看源码能清楚,所以感觉Java这块比C#要好一些,随时能清楚代码干了什么事情,而不是包装得不清楚怎么实现的。

动态类

了解了规则后,自己按照类型方法开整。

读取yml文件,两大配置,一是PLC地址,包含需要读取的数据列表;二是数据说明,TagName,地址,数据类型,转换类型,比例等。根据“数据说明”配置,生成动态类,加类注解@PLCEntity,字段加注解@PLCField并地址和缓存时间填入,生成相应get/set方法,生成一个方法,带一个参数,用于从PLC读取所有字段值(从OPM源码了解有此功能)。

生成另一个动态类,和第一个动态类类似,但不增加注解,再增加另外2个字段,数据更新时间和PLC标识,用于写入数据库。另外如有比例变换,增加变换后的get方法,处理比例逻辑。现场间断生成,可能一些数值几个小时内都不会变化,如果采集数据都写入数据库,会有很多无效数据。后面新读的值与旧值比较,如果一样,则不写入数据库。从PLC读到值写入第一个动态生成的类对象,然后复制到第二个动态生成的类对象,此时后者多两个字段,是变化的,所以比较新旧值的时候,hashCodeequals方法将这两个字段值去掉。嗯,现场效果还可以。但是young GC比较多,可能与机制相关,频繁读取数据,生成新对象。

改变策略

前面的方法熟悉了bytebuddy的使用,以后写框架会有一些好处吧。

后面想了想,感觉没有必要生成动态类,直接用Map应该就可以了。嗯,MongoDB的bson里面有个Document很合适,写入数据后直接插入到MongoDB。新代码写完了,但是学没有到现场测试效果。不知道young GC会不会少一些。

嗯嗯,拖了好久没有把这个发出来了。自从移动可以用IPv6后,想办法可以在外面用IPv6访问自家NAS,主要是用transmission下载,然后Nextcloud保存手机上的照片。基本上这些功能已经实现,但是中间也碰到了一些问题。思路就是将域名AAAA记录改为当前NAS的IPv6地址。正好DigitalOcean的有这个功能,提供了API,可以用API修改域名记录。

  1. 网络结构

家里网络是一光猫,桥接,后面连一个Buffalo的路由器(刷了OpenWRT),然后一个CISCO千兆非网管的交换机,再接到各终端设备。无线用的是新的华为新的AX6路由器,当一个无线接入点AP。这里提一点是,光猫比较早了,大概是2016年办的,烽火HG266GU,有个大坑,后面LAN口只有LAN1口是千兆,其他是百兆,当时升级宽带的时候速率一直达不到要求,后来找移动才知道。还有一个无线的问题,当时用的是TP-LINK AC1200,用无线下NAS的东西,最大只有4MB/s,用Wireshark抓包,一直有丢包,速度上不去,换了它后一切都好了,笔记本Intel AX210网卡2402/2402Mbps,测试到NAS的网速,卡在1000Mbps,卡在有线网络上了,当然从NAS下东西速率还是卡在硬盘上面。

  1. 路由器设置

此时Buffalo的路由器成了上网关键设备,设置好IPv4防火墙,保证网络安全,禁止内网非法设备上网。暂时IPv6防火墙没有太多规则,考虑到现在IPv6可能被扫的机率比较低。

  1. DigitalOcean设置

可以在DO界面生成新的API Token,这样就可以利用API来远程修改DNS信息。具体方法可以参考官方文档。可以用cURL,Python等,当时可能正在用Python,所以后来我就用Python来修改DNS记录了。其中用到了python-digitalocean,主要是用到了Listing records of a domain/Update a domain record。在DigitalOcean的DNS管理界面,设置一条AAAA的记录,后面脚本会根据NAS实际IPv6的地址进行修改。

  1. 获取NASIPv6地址

这部分是修改最多的地方吧,最开始也是用Python来获取IPv6地址,但是不太理想,NAS的IPv6地址有很多,其中临时的IPv6大概是一段时间就会失效,然后会生成一个新的IPv6地址,这里查本机IPv6地址会有很多。

后来改成用SHELL脚本来搞。拿到网卡的所有IPv6地址,去掉link环路,过时地址,临时地址这时候大概会获取1到2个地址。基本可用,然后得到的地址传给Python脚本去修改DO的DNS记录。

1
IPs=`ip -6 addr show enp1s0 | grep inet6 | grep -v -E "link|host|deprecated|temporary" | awk '{print $2}' | awk -F'/' '{print $1}'`

后来发现一个问题,因为路由器重启的话,IPv6地址会有上次拨号的记录和这次拨号的记录,会导致AAAA记录错误。后来想了一些办法没成功,最后来了个最笨的办法,直接从路由器拿到最的IPv6地址,跟NAS的地址进行对比,这样DNS就可以更新成最新的记录了。

1
IPPre=`ssh -p PORT root@IPADDR ip -6 addr show br-lan | grep inet6 | grep -v -E "link|host" | awk '{print $2}' | awk -F'/' '{print $1}' | awk -F'::' '{print $1}'`

最近碰到一个问题,使用Keras,Tensorflow在某厂里进行机器学习,把现场的数据拷到自己电脑里面,然后学习了一段时间,实验了下下,发现还行吧,准备把东西放到内网里面,再加个FastAPI提供个服务给其它程序调用,本来想用GRPC的,想想算了,也没有太高的要求,能用就OK,懒得麻烦,HTTP省心一些。

但是但是,这鬼是个内网,不能下载对应的包,心中一万匹马跑过。自己电脑虚拟机搞个Windows,然后把环境搭好,到能上网的地方把依赖下载好,直接拷贝好像不行哦。

于是上网搜了搜,有一个包conda-pack可以直接用。

1
2
conda install -c conda-forge conda-pack
conda pack -n my_env -o my_env.tar.gz

my_env.tar.gz拷到内网机器里面,然后搞个批处理文件就可以工作了。

startup.bat
1
2
3
4
@echo off
cd my_env
call bin\activate.bat
python main.py

其实还有其他方式

1
2
3
4
5
6
7
8
9
10
11
# 本机
conda create --name snapshot --clone myenv

# spec list
conda list --explicit spec-list.txt

conda create --name python-course --file spec-list.txt

# Environment.yml
conda env export environment.yml
conda env create -f environment.yml

ref:

家里整了NAS,后来移动有了IPv6,通过修改DNS信息,自动更新域名对应的IPv6地址,这样可以在外面直接控制NAS。里面下了不少动画片给小孩看,家里设备上网需要在OpenWrt里进行设置才可以,电视机很早就屏蔽了上网功能,所以小孩打开电视也没有用。以前试过通过cron来定时开启和关闭SMB,让小孩定时看动画片,可是有时候上兴趣班之类的过了时间,还是需要手动打开,然后修改cron增加定时关闭SMB,操作多一些,后来想了下,应该还可以再简化,记录一下吧。

最早之前是有想法的,比如下午5点定时打开SMB,然后监测SMB连接情况,到达30分钟后,自动关闭SMB。当时搜了下,好像没有特别好的方案,才有了上面的手动方式。后来换了下思路,以开启SMB时间为准,30分钟后自动关闭,写一个shell脚本完成。

smb.sh
1
2
3
4
5
6
7
8
9
10
11
12
#/bin/bash

DEADTIME=$(date -d "+30 minutes" +'%H:%M')
HOUR=$(echo $DEADTIME | cut -d ':' -f1)
MINUTE=$(echo $DEADTIME | cut -d ':' -f2)

sudo systemctl start smbd

crontab -l 2>/dev/null | head -n -1 > /tmp/mycron
echo "$MINUTE $HOUR * * * sudo systemctl stop smbd" >> /tmp/mycron
crontab /tmp/mycron
rm /tmp/mycron

需要设置下用户权限,可以使用sudo systemctl不用输密码

/etc/sudoers
1
liangwu ALL=(ALL:ALL) NOPASSWD:/usr/bin/systemctl

现在就是小孩需要看电视的时候,用手机SSH登录到NAS,执行下脚本就不管了。后期考虑VPS暴露一个WEBAPI,输入一个地址加密钥完成脚本执行,嗯嗯 :)

这段时间整了一点东西,也有几点是重复查资料的事情,所以需要记录下,方便自己查找。

Windows端口转发

回本行后,基本服务器、客户端都是Windows的天下了,有的企业内部网络环境比较恶心,偶尔需要用到转口转发,这个在Linux下面是很好解决的,直接SSH就可以了。很早以前在医院也用Windows整过端口转发,后来忘掉,查了查文档,查命令的帮助还是能解决一部分问题的。相当于ssh -L

1
netsh interface portproxy add v4tov4 listenaddress=127.0.0.1 listenport=9000 connectaddress=192.168.0.10 connectport=80

Netty内网穿透

现在用的内网穿透是frp,还是很好用的,应该是因为安全的原因,有个我比较需要的功能没有,就是可以在服务端或另一客户端在线确定何种类型何种端口转发。某企业服务器已经内网连到公网服务器,而自己的电脑连上服务器后,可以在线确定本地、远程或动态端口转发。

前两个月断断续续地用Netty做了一部分工作,本地端口转发功能已经完成了,待完善后面两个转发。后面如果代码写得还OK的话,估计发github上面吧,好像github注册之后一直没有真正地往上面堆代码。

福建中兴F452光猫超级密码

之前在福建出差,用的是电信网络,一般都可以通过修改光猫的配置来启用IPv6,这样可以直接连家里的NAS拖电影看,很多光猫的超级密码比较好获得,但是有次碰了一个中兴F452,搞了几个小时才搞定,记录下吧。

看了一个帖子,可以用普通用户登录后下载一个文件,然后用工具把超级密码给读出来,后来换了一个房间这个工具不能用了。想了想,工具应该是没有问题的,估计是文件有问题,后面也验证了这个想法。放狗搜了搜,这个工具也是在一个论坛的兄弟整的,详细写了那个配置文件的解法

他是Delphi写的,16年前用过Delphi,看起来还好,文件如何解也有详细説明,正好现在在熟悉C#,不如直接开干吧。这里有个插曲,就是丫有个Nuget包的ZLib解包居然有问题,浪费了近两个小时。。。最后用的包是Zlib.Portable

写完后,验证了之前的想法,就是下载下来的配置文件有问题,不完整,所以真好解一部分就把这部分存起来。还好可以分段解包。

Samba链接共享问题

之前家里NAS是把整个BTDownloads的目录共享的,小孩认识几个字后,很快就能在电视机上操作Kodi,视频,BTDownloads,汪汪队,一顿操作很熟练啊。于是就想把小孩喜欢的几个动画片的英文版搞到一上目录下,但是这几个英文版还在做种中,不想再动。就想能不能新建目录,然后用symlinks,但是发现不能正常工作,后来查了下文档,解决了这个问题,可以让小孩只看英文版的动画片,而不影响原来所有的东西。

/etc/samba/smb.conf
1
2
3
4
5
6
[global]
allow insecure wide links = yes

[share]
follow symlinks = yes
wide links = yes

最后一个月

最后一个月了,今年的计划完成了吗?好像没有, :(

前段见时间有个项目,自己想尽可能查看现场各设备的运行参数,显示一些统计信息,之前同事用C#写了个画面的,但感觉不灵活,换个项目,要改的东西就很多,界面基本需要重画。后来想起之前用ECharts做过电子在屏的,应该展示效果会更好。之前考虑用React做前端的,后来想想就一个页面,直接用Vue干吧,正好Vue也升级到3了,然后用Electron做个包装,也不用再下浏览器。记录下碰到的问题吧。

ElementUI按需引入

之前Vue用的脚手架是vue-cli,统计时间是比较长的,升级到3之后,改用vite了,很方便。因为使用的组件不多,把ElementUI全部引入的话,JS文件会比较大,所以只引入使用的几个组件。参照官方文档

main.js
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
import 'element-plus/lib/theme-chalk/index.css'

import {
ElButton,
ElCol,
ElInputNumber,
ElProgress,
ElRow,
ElDescriptions,
ElDescriptionsItem,
} from 'element-plus';

const plugins = [
ElRow,
ElCol,
ElButton,
ElDescriptions,
ElDescriptionsItem,
ElInputNumber,
ElProgress
]

const app = createApp(App)

plugins.forEach(plugin => {
app.use(plugin)
})

app.config.globalProperties.$ELEMENT = { size: 'small', zIndex: 3000 }

app.mount('#app')

ECharts

按需引入

ECharts升级到5之后,与之前的引入有一点区别,具体可参考官方文档

linechart.vue
1
2
3
4
5
6
7
import * as echarts from 'echarts/core'
import { BarChart, LineChart } from 'echarts/charts'
import { CanvasRenderer } from 'echarts/renderers' // CanvasRenderer, SVGRenderer
import { GridComponent, TooltipComponent, TitleComponent } from 'echarts/components'
// require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'
import moment from 'moment'

VUE中引入ECharts

最开始在VUE中引入ECharts老是报错,后来查了下,应该是DOM没有准备好,具体参考的应该是vue-element-admin里面的配置来着。应该用nextTick(),待DOM准备好之后,ECharts绑定到DOM。

LineChart.vue
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
data() {
this.chart = null
return {
// chart: null
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
methods: {
initChart() {
echarts.use([BarChart, LineChart, GridComponent, TooltipComponent, TitleComponent, CanvasRenderer])
this.chart = echarts.init(this.$el) // 因为这个组件只有一个div展示图片,所以用this.$el,如果是其它,可使用this.refs..$el
this.chart.setOption(
{
title: {
text: 'title'
},
grid: [
{
left: 50,
right: 50,
bottom: 0,
containLabel: true
},
],
axisPointer: {
link: {xAxisIndex: 'all'}
},
xAxis: [
{
// id: 'x1', 解决内存泄漏
type: 'category',
boundaryGap: true,
axisPointer: {
type: 'shadow'
},
splitLine: {
show: true,
interval: 9,
lineStyle: {
type: "dashed",
color: "rgba(0, 0, 0, 1)",
shadowBlur: 2
}
}
},
{
//id: 'x2',
type: 'category',
boundaryGap: true,
position: 'top',
axisLabel: {
show: false
}
}
],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'none',
animation: false,
}
},
yAxis: [
{
//id: 'y1',
type: 'value',
name: 'y1',
max: 900,
min: 0,
interval: 100,
axisLabel: {
formatter: '{value} °C'
},
axisTick: {
show: true
},
axisLine: {
show: true
},
splitLine: {
show: true
}
},
{
//id: 'y2',
type: 'value',
name: 'y2',
axisLabel: {
formatter: '{value} min'
},
axisTick: {
show: true
},
axisLine: {
show: true
},
splitLine: {
show: false
}
},
{
//id: 'y3',
type: 'value',
name: 'y3',
inverse: true,
min: 0,
show: false
}
],
series: [
{
//id: 's1',
name: 's1',
type: 'bar',
},
{
//id: 's2',
name: 's2',
type: 'line',
yAxisIndex: 1,
},
{
//id: 's3',
name: 's3',
type: 'bar',
xAxisIndex: 1,
yAxisIndex: 2,
}]
}
)
this.chart.on('click', 'series', params => {
this.$emit('click', parseInt(params.name))
})
},
}

内存泄漏

参照ECharts的样例做好了一个动态更新的图表,每秒从后台拉下数据进行更新,还蛮好看的。可是过两天后发现浏览器或者Electron内存暴了,直接把系统快干死。当时不清楚哪里的问题,以为是每秒获取数据后处理有问题,检查了好久都没办法解决,最后只好排除法,页面上的东西一个个排除观察一段时间看内存是不是一直往上涨,最后定位到ECharts。上网查了下,发现有提到的echarts.clear()echarts.dispose(),感觉这个有点不靠谱,这样子那不是每次echarts都需要重新生成?反正也试了,确实效果不好,图表都是重新生成,不能连续变化。

没办法,查文档吧,最后还真发现了些什么。里面有个组件合并模式,就是每次数据变化后都会调用一次this.echarts.setOption(),因为设置不当,数据一直往图表里面填,没有释放旧数据。其实解决办法很简单,就是把有变动的地方设置好ID,每次更新就更新带ID的数据。

LineChart.vue
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
35
36
37
38
this.chart.setOption(
{
xAxis: [
{
id: 'x1',
data: xArray
},
{
id: 'x2',
data: xArray,
}
],
yAxis: [
{
id: 'y2',
max: Math.max(...stayTimes.filter(e => !isNaN(e))),
min: Math.min(...stayTimes.filter(e => !isNaN(e))),
},
{
id: 'y3',
max: Math.max(...stayTimesDelta.filter(e => !isNaN(e))) * 10,
}
],
series: [
{
id: 's1',
data: chargeTemps
},
{
id: 's2',
data: stayTimes
},
{
id: 's3',
data: stayTimesDelta
}]
}
)

想法

之前家里是光纤通过运营商的光猫桥接,然后一个路由器拨号,再连一个交换机。因为之前已经搞了台NAS,之前一直想整台HP Gen8的,有iLO管理口,再加上eSXi虚拟化,搞个软路由和NAS,这样比较完美。后面没买到便宜的Gen8,现在考虑功耗比较大,软Raid导致噪音比较大,就算了没整Gen8了。看看后面能不能换个大House,整个小的机柜,再搞个服务器玩玩吧。

现在路由器、交换机和NAS在一个小小的工具箱里面,夏天温度有点高,想想看把软路由整到NAS里面去吧。NAS用的OS是OMV5,基于Debian,比较熟悉,整个虚拟机安装OpenWrt应该是一个可行的方案。KVM一直不是太熟悉,直接用VirtualBox,之前VirtualBox没用过CLI,正好供机会学习下CLI控制。

这样的好处就是不用再单独开一个硬件路由器了,减少了一点点电费和热量,然后性能会强一些;缺点就是Host OS挂掉的话,就全屋不能上网了。

主要参考三篇官方文章OpenWrt on VirtualBox HowTo,VirtualBox Advanced,VirtualBox Manual

构架

NAS主板有一个千兆电口,然后自己备了一个PCI双网卡,但是因为主板的原因,PCI网卡的最高速率好像只有400M,所以想的就是一个PCI的口连ISP,板载千兆电口连交换机,多一个口备用。

Host OS Debian里面将板载网卡(enp1s0)设置成静态IP(192.168.1.2/32),PCI网卡(enp6s0/enp7s0)设置成启用(否则网卡为Down状态,无法使用)。

OpenWrt三网口,管理口(eth0),Hostonly,用于Host访问;WAN口(eth1),桥接物理网口,连接ISP网口;LAN口(eth2),桥接物理网口,连接交换机。大致示意图可参照OpenWrt说明

Networking Example

安装Virtual Box

官方文档安装VirtualBox和Extension Pack,另外需要安装编译工具和对应头文件,dkms,build-essential, linux-headers-$(uname -r),将用户加入用户组usermod -a -G vboxusers user

有一点就是如果内核有升级的话,VirtualBox就需要重新设置下/usr/sbin/vboxconfig,所以非必要情况下不升级内核,apt-mark hold linux-image-amd64 linux-headers-$(uname -r)

安装Extension Pack,VBoxManage extpack install ext.vbox-extpack

检查服务vboxautostart-service是否自动启动,这样Host启动时,会自动启动对应的vm。

安装OpenWrt

下载对应OpenWrt镜像,参考官方地址

创建vm

1
2
3
4
5
6
7
mkdir -p ~/vbox
cd ~/vbox
VBoxManage createvm openwrt --ostype Linux26_64 --registervm --default --basepath `pwd`
cd ~/vbox/openwrt
wget -c https://downloads.openwrt.org/releases/19.07.8/targets/x86/64/openwrt-19.07.8-x86-64-combined-ext4.img.gz
gzip -d openwrt-*x86-64-combined*.img.gz
VBoxManage convertfromraw --format VDI openwrt-*x86-64-combined*.img openwrt.vdi

VirtualBox创建一个Linux vm,设置基础目录为~/vbox/openwrt,下载对应镜像,并转换为VirtualBox可用的文件。

修改vm配置

1
2
3
4
5
6
VBoxManage hostonlyif create
VBoxManage hostonlyif ipconfig vboxnet0
VBoxManage hostonlyif ipconfig vboxnet0 --ip 192.168.56.1 --netmask 255.255.255.0
VBoxManage dhcpserver add --network=vboxnet0 --server-ip=192.168.56.1 --lower-ip=192.168.56.101 --upper-ip=192.168.56.254 --etmask=255.255.255.0 --enable

VBoxManage modifyvm openwrt --memory 128 --boot1 disk --boot2 none --boot3 none --nic1 hostonly --nictype1 virtio --hostonlyadapter1 vboxnet0 --nic2 bridged --nictype2 virtio --bridgeadapter2 enp6s0 --nic3 bridged --nictype3 virtio --bridgeadapter3 enp1s0 --mouse ps2 --keyboard ps2 --audio none --usb off --usbehci off --usbxhci off --vrde on --vrdeport 10001 --audostart-enabled of --autostart-delay 120

这里主要是创建一个hostonly网络,并设置好IP,DHCP等,Host的IP是192.168.56.1,等下Openwrt里面再设置管理口的IP。然后修改openwrt vm的内存为128,只从硬盘启动,网卡1为hostonly管理网卡,网卡2为bridged的WAN(enp6s0)口,网卡3为bridged的LAN(enp1s0)口。然后关闭声卡,USB。

VBoxManage Manual

挂载OpenWrt镜像

1
2
VBoxManage showvminfo openwrt | grep 'Storage Controller'
VBoxManage storageatt openwrt --storagectl 'SATA' --device 0 --port 0 --type hdd --medium ~/vbox/openwrt/openwrt.vdi

正常默认情况下有个IDE和SATA,IDE用于光驱,SATA用于硬盘使用。然后将openwrt的镜像挂载为硬盘。

虚拟机随host自动启动

参考Manual,AutoStart VirtualBox VMs on System Boot on Linux

1
VBoxManage modifyvm openwrt --autostart-enabled on --autostart-delay 10

远程控制vrde

参考Manual

1
VBoxManage modifyvm openwrt --vrde on --vrde-port 10001

这样就可以通过Host OS的10001端口进行远程操作,直接使用windows的mstsc可以连接,linux的rdesktop。

OpenWrt网络配置

前面配置好OpenWrt后,先将网卡设置成断开,然后启动OpenWrt,防止网络冲突啥的。

1
2
3
VBoxManage controlvm openwrt poweroff
VBoxManage modifyvm openwrt --cableconnected1 off --cableconnected2 off --cableconnected3 off
VBoxManage startvm --type headless

然后远程连上10001端口进行网络设置,将三张网卡分别设置好。

/etc/config/network
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
config interface 'loopback'
option ifname 'lo'
option proto 'static'
option ipaddr '127.0.0.1'
option netmask '255.0.0.0'

config globals 'globals'

config interface 'lan'
option ifname 'eth2'
option proto 'static'
option netmask '255.255.255.0'
option ip6assign '60'
option ipaddr '192.168.1.1'

config interface 'wan'
option ifname 'eth1'
option proto 'pppoe'
option password 'password'
option ipv6 'auto'
option username 'username'

config interface 'mng'
option proto 'static'
option netmask '255.255.255.0'
option ifname 'eth0'
option ipaddr '192.168.56.2'

配置DHCP,主要是为了使用IPv6,见lan配置处。

/etc/config/dhcp
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
config dnsmasq
option domainneeded '1'
option localise_queries '1'
option rebind_protection '1'
option rebind_localhost '1'
option local '/lan/'
option domain 'lan'
option expandhosts '1'
option authoritative '1'
option readethers '1'
option leasefile '/tmp/dhcp.leases'
option resolvfile '/tmp/resolv.conf.auto'
option localservice '1'
option confdir '/tmp/dnsmasq.d'

config dhcp 'lan'
option interface 'lan'
option start '100'
option limit '150'
option leasetime '12h'
option ra_management '1'
option ra 'server'
option dhcpv6 'server'
option ndp 'relay'

config dhcp 'wan'
option interface 'wan'
option ignore '1'

取消ipv6防火墙,禁止LAN口上网,需要授权才能使用。见OpenWrt限制设备连接Internet。如没有此需求,将forwarding段的enabled选项设置为1即可。

/etc/config/firewall
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
config defaults
option syn_flood '1'
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'REJECT'
option disable_ipv6 '1'
option drop_invalid '1'

config zone
option name 'lan'
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'ACCEPT'
list network 'lan'

config zone
option name 'wan'
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
option masq '1'
option mtu_fix '1'
list network 'wan'

config forwarding
option src 'lan'
option dest 'wan'
option enabled '0'

一切就绪后就可以将网卡连接上使用了。

1
2
3
VBoxManage controlvm openwrt poweroff
VBoxManage modifyvm openwrt --cableconnected1 on --cableconnected2 on --cableconnected3 on
VBoxManage startvm --type headless

团队去年将代码管理迁移到Gitlab后,尝试了CI/CD,用Gitlab-Runner来测试提交的代码是否有问题,用3台机器,kubeadm搭建了一个小的k8s运行环境。上个月25日突然反映runner不能正常运行,后台看了下是证书过期的原因,kubeadm v1.5后好像有证书更新的功能,之前用的好像是v1.2版本,证书更新起来不太方便。现在在做项目,没有时间去整理,还不如重新搭建算了。后来有时间的话,再尝试搭建k8s,the hard way,应该会对kubernetes更深一步的了解。

前段时间CentOS 8发布了,也试了下CentOS 8,但是各方软件兼容还存在一点问题,比如Docker,后来还是以CentOS 7为操作系统。vSphere里新建一台机器,各参数设置好后,再Clone两台,修改IP和hostname就OK,省一些系统安装的时间。

Docker准备

Docker安装比较简单,按官方指引来就OK,因其它原因国内下载Docker Image比较慢,需要一些简单配置,主要参考TUNAUSTC

1
2
3
4
5
6
wget -O /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo
sudo sed -i 's+download.docker.com+mirrors.tuna.tsinghua.edu.cn/docker-ce+' /etc/yum.repos.d/docker-ce.repo
yum install yum-utils device-mapper-persistent-data lvm2
yum install docker-ce
gpasswd -a user docker
systemctl enable --now docker
/etc/docker/daemon.json
1
2
3
4
5
6
7
8
9
10
11
12
{
"registry-mirrors": ["https://registry.docker-cn.com","https://docker.mirrors.ustc.edu.cn/"],
"insecure-registries": ["10.17.65.22:8088"],
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3",
"labels": "production_status",
"env": "os,customer"
},
"exec-opts": ["native.cgroupdriver=systemd"]
}

Kubeadm安装k8s

Kubeadm准备

Kubeadm安装参考官方文档。这次安装的版本是v1.16.2,版本不一样可能涉及到一些Docker Image不一样。可通过kubeadm config images list查看对应版本需要下载的镜像。

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
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
#gpgcheck=1
gpgcheck=0
EOF

# Set SELinux in permissive mode (effectively disabling it)
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

systemctl enable --now kubelet

cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system

systemctl stop firewalld
sudo systemctl disable firewalld

Docker Image准备

因为某些原因无法从外网下载镜像,需要手动处理下,这里要感谢下阿里,提供了一个镜像下载地址。

./k8s_mirror.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash

images=(kube-apiserver:v1.16.2
kube-controller-manager:v1.16.2
kube-scheduler:v1.16.2
kube-proxy:v1.16.2
pause:3.1
etcd:3.3.15-0
coredns:1.6.2)
#flannel:v0.11.0-amd64

# or bluersw/image:version
for imageName in ${images[@]} ; do
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName
#docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName 10.17.65.22:8088/$imageName
#docker push 10.17.65.22:8088/$imageName
done

写好脚本后,直接运行这个脚本,把需要的Image拉下来。

k8s节点

k8s的节点安装参考官方文档

1
2
3
4
5
sudo kubeadm init --kubernetes-version=v1.16.2 --apiserver-advertise-address=10.17.65.250 --pod-network-cidr=10.244.0.0/16 --service-cidr=10.1.0.0/16
mkdir -p ~/.kube
sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config
sudo chown liangwu:liangwu ~/.kube/config
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/2140ac876ef134e0ed5af15c65e414cf26827915/Documentation/kube-flannel.yml

其它节点根据kubeadm init后的提示直接kubeadm join就好了。前提也是需要把一些镜像拉下来

  • k8s.gcr.io/pause:3.1
  • k8s.gcr.io/kube-proxy:v1.16.2
  • quay.io/coreos/flannel:v0.11.0-amd64

Gitlab-Runner Helm安装

安装Helm

参照官方文档,作简要配置。

rbac-config.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system

然后运行kubectl create -f rbac-config.yaml。或者其它方式。主要是RBAC的问题。

1
2
3
kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'

Helm配置

Helm也涉及到一些被墙的东西,换下拉取地址。再次感谢阿里? :)

1
2
helm repo add gitlab https://charts.gitlab.io/
helm init -i registry.cn-hangzhou.aliyuncs.com/google_containers/tiller:v2.5.1 --stable-repo-url https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts --service-account tiller --history-max 200

Gitlab Runner安装

Gitlab Runner的文档确实好像不怎么样,好多东西都没写,或者写得不好。

1
2
3
4
5
#values.yaml需要根据自己实际情况进行修改,各项都有说明
wget -c https://gitlab.com/gitlab-org/charts/gitlab-runner/raw/master/values.yaml?inline=false
helm fetch gitlab/gitlab-runner
tar xvf gitlab-runner-${version}.tgz
helm install --namespace gitlab --name gitlab-runner -f values.yaml ./gitlab-runner-${version}

values.yaml有以下几点需要注意的。

values.yaml
1
2
3
4
5
6
7
8
9
imagePullPolicy: IfNotPresent
gitlabUrl: "http://10.17.65.22:5622/gitlab"
#注册新runner
runnerRegistrationToken: "xxxx"
#已有runner的token
runnerToken: "D1zsss2nS6Lgkx5M4_zx"
runners:
#从私有docker registry中拉取image时需要的认证信息,可参考原values.yaml中的k8s官方链接说明
imagePullSecrets: ["regcred"]

我们Gitlab CI/CD中有个验证就是利用maven编译代码是否可通过,其中需要拉取很多供事的jar包,每次都拉取比较耗时,虽然都从局域网的私服拉。可将.m2的缓存存在node本机,小的k8s就这么做吧。懒得去设置PV,PVC了。以后需要大的k8s,再去整PV吧。NFS啊,MinIO之类的。在helm包文件里修改templates/configmap.yaml,在start gitlab runner之前加入以下内容。已经在Gist中加入。

gitlab-runner/templates/configmap.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
### ...

cat >>/home/gitlab-runner/.gitlab-runner/config.toml <<EOF
[[runners.kubernetes.volumes.host_path]]
name = "m2"
mount_path = "/root/.m2"
host_path = "/m2"
[[runners.kubernetes.volumes.host_path]]
name = "cache"
mount_path = "/cache"
host_path = "/cache"
EOF

### ...

下面是配置文件详情。

0%