变更缘由
早期系统选型时为了减少大周期系统版本更迭带来的维护成本,同时期望有强大的社区能支撑最新软件版本的安装,选择了使用Manjaro做NAS系统。使用初期确实能通过获取大量所需的最新版本软件,同时隔段时间进行滚动更新也保证了系统无需在大周期重装系统。但同时也会发现滚动更新时,一些服务进行了无法向下兼容的升级,导致业务不可用,这需要花费时间去修复,并尝试恢复数据。一次是caddy从1到2的大版本升级,他的配置文件格式完全变更了,这导致网页服务直接崩溃;另一个是transmisson升级,无法进入服务,最后发现他的权限和配置路径有了一定变化。这两次软件变化都拖了好久才去解决。另外还有系统级别的问题,似乎是我升级了内核,但没完全升级和卸载,很长一段时间中启动时grub首选项有问题,重启需要连接显示器。尝试解决好几次没解决,隔了好几个月终于尝试find内核版本,发现有残留文件。同时也在考虑更换CPU的事,目前使用 ,发布到现在已经十年了,性能太差。因此也期望能尽量将现有服务无损迁移,这也要求我对服务配置文件有一定把控,而不是继续AUR社区脚本设计的配置路径,这可能会导致数据丢失。
本文用于指导如何将 linux 服务器中运行的服务容器化。对外部资料仅作必要的摘选,旨在讲清楚我们做的目的。不建议0基础看本文,当然我会尽量在一些名词首次出现时加上描述链接。
容器化前提
你得连上互联网,仅处于大型局域网的情况下很多容器或配置将会无法下载。你玩过K8S,了解基本知识,这里推荐《Kubernetes in Action》,还有,官网文档真的很强大,在容器化过程中有大量不懂的知识,都是通过查看官网文档解决的。
K3S
是的,本文容器化使用了轻量化kubernetes版本 ,此举是由于我期望能使用到k8s成熟的配套软件以及考虑未来可能的扩展性。同时我对k8s的配置比docker熟悉。当然也曾使用过,之前的CCTV系统就是使用compose管理的。
资源分析
J1900怎么也比Pi4B厉害,消耗不大。
组件 | 处理器 | 最小 CPU | Kine/SQLite 的最小 RAM | 嵌入式 etcd 的最小 RAM |
---|---|---|---|---|
具有工作负载的 K3s Server | Intel(R) Xeon(R) Platinum 8124M CPU, 3.00 GHz | 核的 10% | 768M | 896M |
具有单个 Agent 的 K3s 集群 | Intel(R) Xeon(R) Platinum 8124M CPU, 3.00 GHz | 核的 10% | 512M | 768M |
K3s agent | Intel(R) Xeon(R) Platinum 8124M CPU, 3.00 GHz | 核的 5% | 256M | 256M |
具有工作负载的 K3s Server | Pi4B BCM2711, 1.50 GHz | 核的 20% | 768M | 896M |
具有单个 Agent 的 K3s 集群 | Pi4B BCM2711, 1.50 GHz | 核的 20% | 512M | 768M |
K3s agent | Pi4B BCM2711, 1.50 GHz | 核的 10% | 256M | 256M |
架构
- Server 节点指的是运行 k3s server 命令的主机,control plane 和数据存储组件由 K3s 管理。
- Agent 节点指的是运行 k3s agent 命令的主机,不具有任何数据存储或 control plane 组件。
- Server 和 Agent 都运行 kubelet、容器运行时和 CNI。
最大程度减轻了外部依赖性,K3s 仅需要现代内核和 cgroup 挂载。K3s 打包了所需的依赖,包括:
- containerd
- Flannel (CNI)
- CoreDNS
- Traefik (Ingress)
- Klipper-lb (Service LB)
- 嵌入式网络策略控制器
- 嵌入式 local-path-provisioner
- 主机实用程序(iptables、socat 等)
在这个架构图中,我们可以发现他和k8s的Master/Worker架构设计不同(Master当然可以清除污点,但这不是最佳实践),他的Server拥有了全部的功能。因此在NAS这种单节点集群中只需要下图这样带有嵌入式数据库的单服务器即可。
安装
我们使用最便捷的安装方式,不考虑其他配置参数。
# 互联网用户快速安装方式
curl -sfL https://get.k3s.io | sh -
# 中国用户快速安装方式
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh -
单节点 Server 安装是一个功能齐全的 Kubernetes 集群,它包括了托管工作负载 pod 所需的所有数据存储、control plane、kubelet 和容器运行时组件。除非你希望向集群添加容量或冗余,否则没有必要添加额外的 Server 或 Agent 节点。
使用方式
k3s的kubeconfig位于 /etc/rancher/k3s/k3s.yaml
,直接拷贝到 ~/.kube/config
使用即可。
我更期望在macOS上连接管理,使用homebrew安装kubectl后,将NAS的/etc/rancher/k3s/k3s.yaml
,直接拷贝到macOS上的 ~/.kube/config
,并修改其中的 _server _为NAS地址即可。
查看节点
[morningtzh@MBP-yeshigelaji:~/Downloads]
% kubectl get node
NAME STATUS ROLES AGE VERSION
morningserver Ready control-plane,master 4d1h v1.26.3+k3s1
k3s本体位于 /usr/local/bin/k3s
,同时还有其他几个文件。
➜ ~ ls /usr/local/bin/
crictl k3s k3s-killall.sh k3s-uninstall.sh kubectl
k3s使用systemctl进行管理,systemctl的使用不再赘述:
➜ ~ systemctl status k3s
● k3s.service - Lightweight Kubernetes
Loaded: loaded (/etc/systemd/system/k3s.service; enabled; preset: disabled)
Active: active (running) since Sat 2023-04-15 13:52:08 CST; 1 day 7h ago
Docs: https://k3s.io
Main PID: 659814 (k3s-server)
Tasks: 238
Memory: 592.5M
CPU: 6h 56min 32.326s
CGroup: /system.slice/k3s.service
├─345834 /var/lib/rancher/k3s/data/c26e7571d760c5f199d18efd197114f1ca4ab1e6ffe494f96feb65c87fcb8cf0/bin/containerd-shim-runc-v2 -namespace k8s.io -id 6ed5ea0bd559fbeb3f06b4fee1982cfa468165ea>
├─346112 /var/lib/rancher/k3s/data/c26e7571d760c5f199d18efd197114f1ca4ab1e6ffe494f96feb65c87fcb8cf0/bin/containerd-shim-runc-v2 -namespace k8s.io -id eb21ad1e07411017725be9dc932098608b2e61ff>
├─347299 /var/lib/rancher/k3s/data/c26e7571d760c5f199d18efd197114f1ca4ab1e6ffe494f96feb65c87fcb8cf0/bin/containerd-shim-runc-v2 -namespace k8s.io -id b650f3b749d888d78e3fae4f246d129337f9bd6a>
├─347404 /var/lib/rancher/k3s/data/c26e7571d760c5f199d18efd197114f1ca4ab1e6ffe494f96feb65c87fcb8cf0/bin/containerd-shim-runc-v2 -namespace k8s.io -id 58ba3c304da6b5318ee3e2c819f6875d2a58daf2>
├─349116 /var/lib/rancher/k3s/data/c26e7571d760c5f199d18efd197114f1ca4ab1e6ffe494f96feb65c87fcb8cf0/bin/containerd-shim-runc-v2 -namespace k8s.io -id 87659380a2a91d0e5ceffca87185e211e44fd9a2>
├─373127 /var/lib/rancher/k3s/data/c26e7571d760c5f199d18efd197114f1ca4ab1e6ffe494f96feb65c87fcb8cf0/bin/containerd-shim-runc-v2 -namespace k8s.io -id 9d8d3ff6de94d96af28b181e5ef3c86682d428d7>
├─568174 /var/lib/rancher/k3s/data/8b0b55f0927ab996ab2898cb565b8117869e87444ed2867e1528e6b92c62fcf9/bin/containerd-shim-runc-v2 -namespace k8s.io -id a2ca6806ba0ba245996fef3ad29c911d3e880d65>
├─646797 /var/lib/rancher/k3s/data/8b0b55f0927ab996ab2898cb565b8117869e87444ed2867e1528e6b92c62fcf9/bin/containerd-shim-runc-v2 -namespace k8s.io -id ad3228c2fc8da3feb6ff35162b86251e8590d7a4>
├─659814 "/usr/local/bin/k3s server"
├─659848 containerd -c /var/lib/rancher/k3s/agent/etc/containerd/config.toml -a /run/k3s/containerd/containerd.sock --state /run/k3s/containerd --root /var/lib/rancher/k3s/agent/containerd
├─660439 /var/lib/rancher/k3s/data/c26e7571d760c5f199d18efd197114f1ca4ab1e6ffe494f96feb65c87fcb8cf0/bin/containerd-shim-runc-v2 -namespace k8s.io -id 82222f545c978704440c7b2eb9fc12e98d33eacc>
├─662649 /var/lib/rancher/k3s/data/c26e7571d760c5f199d18efd197114f1ca4ab1e6ffe494f96feb65c87fcb8cf0/bin/containerd-shim-runc-v2 -namespace k8s.io -id e926ed390c0aa1ec9b314aecc192e70d384d172c>
├─666162 /var/lib/rancher/k3s/data/c26e7571d760c5f199d18efd197114f1ca4ab1e6ffe494f96feb65c87fcb8cf0/bin/containerd-shim-runc-v2 -namespace k8s.io -id e429079caedae8969c62f93591a202614f82e235>
├─667641 /var/lib/rancher/k3s/data/c26e7571d760c5f199d18efd197114f1ca4ab1e6ffe494f96feb65c87fcb8cf0/bin/containerd-shim-runc-v2 -namespace k8s.io -id 95498d64d949c28d379efae5f861b90da831a778>
└─696331 /var/lib/rancher/k3s/data/c26e7571d760c5f199d18efd197114f1ca4ab1e6ffe494f96feb65c87fcb8cf0/bin/containerd-shim-runc-v2 -namespace k8s.io -id 8562b2dc9bf86369032cbe137f857c173cd9cd16>
其他
升级直接下载 替换,然后重启k3s即可。其他升级方式见官网文档。卸载使用 /usr/local/bin/k3s-uninstall.sh
脚本备份:
- /var/lib/rancher/k3s/server/token
- /var/lib/rancher/k3s/server/db
- 当然也可以全部导出yaml进行备份
DevOps方式
希望你了解K8S基本的使用。
Portainer
是一个非常棒的图形化配置界面,可以在几分钟内对Kubernetes、Docker、Swarm和Nomad的容器进行部署、配置、故障排除和保护。我主要使用他创建,能通过简单的点点点很快部署一个服务。
如果你像我一样简单的安装k3s,就能直接安装portainer到集群中。在这边使用安装到 的命令。如果使用 将会使用宿主机端口,不太建议。
kubectl apply -n portainer -f https://downloads.portainer.io/ce2-17/portainer-lb.yaml
使用 http://{host ip}:9000
进行访问。
Portainer有很多问题,导致无法彻底部署复杂服务,目前遇到了以下问题:
- 无法通过页面创建 ,yaml创建完后倒是可以查看到;
- 无法在创建服务时反复使用同一个持久卷,第二个服务会选择不到;
- 无法创建超过默认范围的NodePort;
- 无法给服务提供命令行参数;
- 其他太多东西无法使用了。
因此创建完后需要借助其他工具修改。
Lens
是电脑端远程访问的IDE,可以连上后直接查看集群的属性,以及编辑集群yaml。下载安装即可。
只要将k3s的kubeconfig放在电脑上的.kube/config
即可,或者在Clusters的右下角选择相应kubeconfig文件。详见:
方便起见,使用了Lens提供的,用于监控设备。在Lens集群的配置中的 Builtin Metrics Provider里将选项全部勾选后Apply即可,然后在Metrics中Prometheus选择Lens即可。内建的Prometheus版本有些老,是半年前的。
VS Code
安装,就能在vscode中查看和修改集群配置了。特别方便,同时还能进行yaml补全。
业务部署基础指导
业务数据准备
查找业务已有数据
- systemctl管理的服务
- 使用
systemctl status transmission
查找其启动时的目录和启动命令,查找配置文件。
- 使用
- docker管理的服务
- 直接看docker配置即可,容器镜像一般会需要外部配置文件目录
- 还是找不到
- 没办法了,用find命令查找吧
sudo find / -name "*transmission*"
。就这样找到了/var/lib/transmission/.config/transmission-daemon
文件夹。
- 没办法了,用find命令查找吧
将数据放到期望的地方
例如:/storage/wwwroot/transmission
数据持久化
本文为专属场景的指导,假设NAS中的硬盘已有数据,并进行了简单的挂载使用。容器化后不改变磁盘的使用方式,数据依旧存在原位,或者在我们方便管理的地方。因此完全使用hostPath类型的持久化方式。
(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。持久卷申领(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载,参见访问模式)。
StorageClass在部署Portainer的文档中可以看到,但在本文中由于没有SC的问题,已经直接跳过了。K3S中默认有一个local-path的存储类。
创建PersistentVolume
我们在Lens Cluster下方使用**+**按钮Create resource,写入以下信息:
apiVersion: v1
kind: PersistentVolume
metadata:
name: storage # PV名称,自己取
spec:
capacity:
storage: 1000000Gi # 此处可以填入磁盘大小
hostPath:
path: /storage # 你的磁盘路径,可以使用较大的路径,也可以使用较小的
accessModes:
- ReadWriteOnce # 允许单节点中所有pod进行读写
persistentVolumeReclaimPolicy: Retain
storageClassName: local-path # 使用k3s的local-path即可
volumeMode: Filesystem # 表明是文件系统
点击Save后,会在相关资源中看到 ttttt状态已经是 Available
创建PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: storage # PVC名称
spec:
storageClassName: local-path # 使用k3s的local-path,和PV相同
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
volumeName: storage # 表明你绑定的PV名称
volumeMode: Filesystem # 和PV相同
保存完PVC后可以看到 PVC和PV的状态都为 Bound
创建临时PV及PVC
创建临时PV及PVC tmp绑定到任意空目录。以便使用Protainer配置后再修改卷。
两者绑定问题处理
一旦发现搞错了,然后删除了PV或者PVC导致两者状态不再 Bound时,可以参照这篇文章处理:
创建服务
这边以NAS常用的下载工具 为例,这是一个配置较为复杂的服务。先在软件官网或者 中找到相应容器镜像。我们可以发现 transmission 的容器镜像是 https://hub.docker.com/r/linuxserver/transmission_ _。同时我们可以在相关页面找到镜像的相关配置,例如transmission的:
Parameter | Function | 备注 |
---|---|---|
-p 9091 | WebUI | 使用Ingress |
-p 51413 | Torrent Port TCP | 使用NodePort |
-p 51413/udp | Torrent Port UDP | 使用NodePort |
-e PUID=1000 | for UserID - see below for explanation | |
-e PGID=1000 | for GroupID - see below for explanation | |
-e TZ=Etc/UTC | specify a timezone to use, see this list. | |
-e TRANSMISSION_WEB_HOME= | Specify the path to an alternative UI folder. | 默认即可,想换Web也行 |
-e USER= | Specify an optional username for the interface | |
-e PASS= | Specify an optional password for the interface | |
-e WHITELIST= | Specify an optional list of comma separated ip whitelist. Fills rpc-whitelist setting. | |
-e PEERPORT= | Specify an optional port for torrent TCP/UDP connections. Fills peer-port setting. | |
-e HOST_WHITELIST= | Specify an optional list of comma separated dns name whitelist. Fills rpc-host-whitelist setting. | |
-v /config | Where transmission should store config files and logs. | 需要找一下当前的配置文件 |
-v /downloads | Local path for downloads. | 这个可以叫其他名字,甚至得挂好几个文件夹 |
-v /watch | Watch folder for torrent files. |
使用Portainer部署,登录后点击 Applocations – Add with form
- 暂时不区分 ,全部放置在Default中;
- image填入后缀 linuxserver/transmission_ _即可
- Environment加入上方 -e 的参数,好多不用配置;
- Persisting data 是磁盘信息,加入刚才填写临时的PVC tmp。如果PVC已经被其他服务绑定,这边将看不到,这时候就需要手动修改yaml
- Publishing the application
- 选择 Cluster IP,点击 Create service,加入 9091,这个将会在后续使用 ingress 暴露出来
- 选择 NodePort,点击 Create service,都填入TCP 51413,再 publish a new port,都填入UDP 51413,此时NodePort那一栏会报错,超出范围了,那就随便填一个,后面修改yaml。
这样就配置好一个服务了,转到 Lens 中,查看 deployment及pod资源,主要查看pod是否建立,pod显示为Running基本就好了,还可以再点击最右边看看log
修改卷挂载
由于Protainer挂载的功能太弱,不支持subPath,所以得手工修改。同时上文使用tmp持久卷,以免脏数据污染磁盘。
Lens中找到相应deployment。打开,点击修改图标(笔)修改以下内容(仅关注添加注释的地方):
spec:
volumes:
- name: lvm # 可新增卷用于下载存盘
persistentVolumeClaim:
claimName: lvm
- name: storage # 原本是tmp的改为上文的storage
persistentVolumeClaim:
claimName: storage # 原本是tmp的改为上文的storage
containers:
- name: transmission
image: docker.io/linuxserver/transmission:latest
env:
- name: TZ
value: Asia/Shanghai
resources: {}
volumeMounts:
- name: lvm # 可以新增卷挂载
mountPath: /lvm
- name: storage # 原本是tmp的改为上文的storage
mountPath: /config # transmission在容器中的配置文件夹
subPath: wwwroot/transmission # 新增本行,将相对 /storage 的路径填入
保存配置,pod将会新建后删除原有的。此时tmp持久卷挂载的目录下会多出些文件,都是业务生成的,已经不需要了,可以丢弃。
设置网络
NodePort
在刚才的配置中,我们可以看到 51413 被设置为了NodePort,这个并不会直接将端口绑定到宿主机上,而在宿主机的 iptables 中设置端口转发。trasmission作为bt下载器,上传时需要外网可访问到 51413,因此需要四层的转发,无法使用ingress。
➜ ~ sudo iptables -S -t nat | grep 51413
[sudo] morningtzh 的密码:
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/transmission-2:port-0" -m tcp --dport 51413 -j KUBE-EXT-FJXM6KG4S3PBI7XG
-A KUBE-NODEPORTS -p udp -m comment --comment "default/transmission-2:port-1" -m udp --dport 51413 -j KUBE-EXT-LCJX7EGXMCW2QCXG
-A KUBE-SEP-ETADUV7IOFZWSN4M -p udp -m comment --comment "default/transmission-2:port-1" -m udp -j DNAT --to-destination 10.42.0.40:51413
-A KUBE-SEP-M3PYMBKBUQGF3NWS -p tcp -m comment --comment "default/transmission-2:port-0" -m tcp -j DNAT --to-destination 10.42.0.40:51413
-A KUBE-SERVICES -d 10.43.184.84/32 -p tcp -m comment --comment "default/transmission-2:port-0 cluster IP" -m tcp --dport 51413 -j KUBE-SVC-FJXM6KG4S3PBI7XG
-A KUBE-SERVICES -d 10.43.184.84/32 -p udp -m comment --comment "default/transmission-2:port-1 cluster IP" -m udp --dport 51413 -j KUBE-SVC-LCJX7EGXMCW2QCXG
-A KUBE-SVC-FJXM6KG4S3PBI7XG ! -s 10.42.0.0/16 -d 10.43.184.84/32 -p tcp -m comment --comment "default/transmission-2:port-0 cluster IP" -m tcp --dport 51413 -j KUBE-MARK-MASQ
-A KUBE-SVC-FJXM6KG4S3PBI7XG -m comment --comment "default/transmission-2:port-0 -> 10.42.0.40:51413" -j KUBE-SEP-M3PYMBKBUQGF3NWS
-A KUBE-SVC-LCJX7EGXMCW2QCXG ! -s 10.42.0.0/16 -d 10.43.184.84/32 -p udp -m comment --comment "default/transmission-2:port-1 cluster IP" -m udp --dport 51413 -j KUBE-MARK-MASQ
-A KUBE-SVC-LCJX7EGXMCW2QCXG -m comment --comment "default/transmission-2:port-1 -> 10.42.0.40:51413" -j KUBE-SEP-ETADUV7IOFZWSN4M
➜ ~
之前我们无法将端口绑定为 51413,他超出范围了,因此我们需要修改k3s的启动脚本 /etc/systemd/system/k3s.service
,最后修改为:
ExecStart=/usr/local/bin/k3s \
server \
--kube-apiserver-arg service-node-port-range=1-65535
然后通过systemctl重启
systemctl daemon-reload
systemctl restart k3s
接下来进入Lens或vscode,在Network–Service中查找叫做transmission的service,有两个,选择Type为NodePort的进行修改。点开,点击右上角笔的标志进行修改。将yaml中刚才瞎写的 nodePort 改为51413
Ingress
Ingress 公开从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。Ingress 是七层代理,不会公开任意端口或协议。下面是一个将所有流量都发送到同一 Service 的简单 Ingress 示例:
在 Portainer 的 Ingresses页面中 Add with form:
- Ingress class 选择 k3s自带的ingress:traefik
- Hostname 写入你想要的 url,例如 transmission.morningserver.local
- Paths中的path写为 / ,其他点点点即可
DNS
现在需要有 transmission.morningserver.local 域名的DNS,有几个方法:
- 修改hosts文件,但不支持通配符,也就是说十个URL需要写十条静态DNS;
- 修改路由器的DNS,这个看家庭网络环境,建议这条,方便家庭其他终端访问,又简单些;
- 自己搭建,加油!
接下来基本就能在自己主机上通过 http://transmission.morningserver.local 访问到 transmission了。
结果展示
对外服务
Lens监控
Grafana监控
可以在 Grafana Labs中找自己需要的模板导入
NAS整体监控
(可以把Cockpit卸载了
Pod监控
看电视和bt下载时的资源信息都能很清楚
参考链接:
上面该有的都有了。