一、前言

自从上次折腾k3s已经过去很久了,当初在部署完成后也尝试将常用服务容器化统一管理,但是无奈于部署文件编写工作量巨大、硬件平台性能捉襟见肘且部署的应用数量很少以至于看上去完全像是为了这碟醋包了顿饺子。快进到现在的2025年,一切都不同于往日了。硬件方面首先焕然一新,有独立120+T的存储服务器,有独立的应用服务器,也有独立的防火墙及万兆交换机。所以在这一切的加持下,一切的复杂系统都变得可能。

目前应用完全使用docker在跑,不完全统计已经有50个以上的容器在运行。带来的问题也日益凸显,管理不便、配置等文件分散、监控困难、无高可用迁移能力等等。所以在这个时间点,又想起来老朋友----k3s。

所以本次的计划也是进行一个完备的集群部署。首先是实现多服务器节点,将节点分布在3台物理服务器上(PVE作为底层),数据统一由集中的存储负责;其次对于集群,需要有负载均衡器,以支持故障转移,需要有分布式存储卷;针对编解码需求,建立vGPU节点。

k3s所有节点均以虚拟机形式部署,版本就选择最新的。其他配置如下:

  • Cilium:作为网络插件CNI,启用Ingress功能,代替kube proxy且启用l2announcements作为loadbalancer
  • cert-manager:自动化TLS证书申请、管理
  • JuiceFS:分布式存储(暂定)
  • vGPU Node:两个Intel vGPU节点

二、部署前准备

除去升级系统等常规操作,尤其要注意当前网络环境下quay.io等镜像地址能否访问以及速度如何,如果无法访问会一直卡在拉取镜像从而导致节点状态一直为NotReady

如需使用Harbor作为镜像代理,参考如下配置:

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
# /etc/rancher/k3s/registries.yaml
mirrors:
docker.io:
endpoint:
- https://harbor.xxxx.local
rewrite:
"(^.+$)": "dio/$1"
quay.io:
endpoint:
- https://harbor.xxxx.local
rewrite:
"(^.+$)": "quay/$1"
ghcr.io:
endpoint:
- https://harbor.xxxx.local
rewrite:
"(^.+$)": "ghcr/$1"
gcr.io:
endpoint:
- https://harbor.xxxx.local
rewrite:
"(^.+$)": "gcr/$1"
registry.k8s.io:
endpoint:
- https://harbor.xxxx.local
rewrite:
"(^.+$)": "k8s/$1"
k8s.gcr.io:
endpoint:
- https://harbor.xxxx.local
rewrite:
"(^.+$)": "k8s/$1"
configs:
"harbor.xxxx.local":
auth:
username: <BASIC AUTH USERNAME>
password: <BASIC AUTH PASSWORD>

需要强调,如果Harbor里各个仓库设置为非公开,configs."harbor.xxxx.local".auth里需要写的地址是Harbor的地址,而不是被代理的镜像库的地址,官方文档提到可以用"*"通配符表示任意地址但是实际测试失败。rewrite作用就是将Harbor中各个仓库名字附加到原始镜像名之前,如果harbor支持仓库子域名就没这么麻烦了。

三、集群部署

1. 控制平面部署

1) 单节点部署

安装命令如下:

1
2
3
4
5
6
7
8
9
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
INSTALL_K3S_MIRROR="cn" \
K3S_KUBECONFIG_MODE="644" \
INSTALL_K3S_EXEC="--flannel-backend=none \
--disable-network-policy \
--disable-kube-proxy \
--prefer-bundled-bin \
--disable=traefik,servicelb,local-storage" \
sh -s -

主要有以下几点:

  • --flannel-backend=none --disable-network-policy:取消默认网络插件flannel
  • --disable-kube-proxy:禁用kube proxy
  • --disable=traefik,servicelb,local-storage:禁用无关组件(servicelb禁用大概和上两项有关)

这里部署好了之后Node状态会一直为NotReady,这是因为没有CNI,等Cilium部署完成后就没问题了。

2) 高可用部署

  • 第一个Master节点
1
2
3
4
5
6
7
8
9
10
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
INSTALL_K3S_MIRROR="cn" \
K3S_KUBECONFIG_MODE="644" \
INSTALL_K3S_EXEC="--cluster-init \
--flannel-backend=none \
--disable-network-policy \
--disable-kube-proxy \
--prefer-bundled-bin \
--disable=traefik,servicelb,local-storage" \
sh -s -
  • 其余Master节点
1
2
3
4
5
6
7
8
9
10
11
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
INSTALL_K3S_MIRROR="cn" \
K3S_URL="https://<server_ip_or_dns>:6443" \
K3S_TOKEN="<your_node_token>" \
INSTALL_K3S_EXEC="server \
--flannel-backend=none \
--disable-network-policy \
--disable-kube-proxy \
--prefer-bundled-bin \
--disable=traefik,servicelb,local-storage" \
sh -s -

这边INSTALL_K3S_EXEC里的server必须要有,否则默认初始化为Agent节点。

2. 工作节点部署

这个不用着急部署,最好等Cilium部署完成再部署,命令内容也很简单不再赘述:

1
2
3
4
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
INSTALL_K3S_MIRROR="cn" \
K3S_URL="https://<server_ip_or_dns>:6443" \
K3S_TOKEN="<your_node_token>" sh -s -

这里新加入的节点会自动创建Cilium需要的DaemonSet,等待启动完成即可。

四、Cilium部署

1. 部署

Cilium安装方式主要分为两种:

  • 安装Cilium命令行工具并进行部署
  • 使用helm部署

我这边就选择使用Cilium命令行工具部署,比较方便。这个命令行工具实际也是对helm的包装,所以其实在配置好kubeconfig可以运行在任意主机,只要保证可以访问到集群即可。安装方法见install-the-cilium-cli,很简单直接就是部署二进制文件。下面直接给出cilium install的参数:

1
2
3
4
5
6
7
8
9
10
cilium install \
--set k8sServiceHost=10.10.0.58 \
--set k8sServicePort=6443 \
--set kubeProxyReplacement=true \
--set l2announcements.enabled=true \
--set ingressController.enabled=true \
--set ingressController.loadbalancerMode=dedicated \
--set ipam.operator.clusterPoolIPv4PodCIDRList="10.42.0.0/16" \
--set ipv4NativeRoutingCIDR="10.42.0.0/16" \
--set ipam.operator.clusterPoolIPv4MaskSize=24

其中各个参数意义:

  • kubeProxyReplacement:使用Cilium代替kubeProxy,参考kubeproxy-free
  • l2announcementsL2 Layer负载均衡,启用此功能必须设置kubeProxyReplacement,参考l2-announcements-settings
  • ingressController;启用Ciliumingress控制器,参考ingress
  • 最后三个关于网络的参数,需要与部署集群时一致,给出的就是默认值

第一次尝试时候仅仅做了基础的部署,没有启用ingreel2announcements,想要再启用怎么都不行,只能卸载cilium再重新安装,应该是我的操作有问题,不再细究。

部署完成会在kube-system这个ns下创建一些资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
kubectl get pods -n kube-system

# output
NAME READY STATUS RESTARTS AGE
cilium-98bnr 1/1 Running 0 2d22h
cilium-envoy-dnr75 1/1 Running 0 3d
cilium-envoy-lz779 1/1 Running 0 3d
cilium-envoy-nztv5 1/1 Running 0 3d
cilium-envoy-q58r4 1/1 Running 0 2d22h
cilium-envoy-vkt4q 1/1 Running 0 3d
cilium-hs89j 1/1 Running 0 3d
cilium-lr26c 1/1 Running 0 3d
cilium-operator-7dff4dfc8b-jzdx7 1/1 Running 0 3d
cilium-vrcln 1/1 Running 0 3d
cilium-zh7jv 1/1 Running 0 3d
coredns-64fd4b4794-fbsml 1/1 Running 0 3d23h
metrics-server-7bfffcd44-pznjd 1/1 Running 0 3d23h

主要有ciliumcilium-envoycilium-operator这三种,其中前两个是每个Node都有一个,最后一个是集群中只有一个。我这边很多是因为加入了多个Node。再查看一下cilium的状态:

image-20250911205707516

还可以通过如下命令进行网络联通性测试,external相关参数用于测试互联网联通性,但是默认参数国内是不通的:

1
2
3
4
5
6
7
8
9
10
cilium connectivity test \
# --external-ip 223.6.6.6 \
# --external-other-ip 223.5.5.5 \
# --external-cidr 114.114.0.0/16 \
# --external-ipv6 2400:3200:baba::1 \
# --external-other-ipv6 2400:3200::1 \
# --external-cidrv6 2402:e480::/32 \
# --external-target baidu.com \
# --external-other-target dns.alidns.com \
--request-timeout 30s --connect-timeout 10s

还可以以kubectl -n kube-system exec ds/cilium -- cilium-dbg status查看部署参数:

image-20250911210136795

2. 负载均衡

cilium负载均衡实现方法非常类似metallbL2负载均衡,基本思路就是在本网段内劫持ARP请求进行欺骗从而使得特定流量发送到cilium节点。cilium使用operator节点进行ARP劫持,首先选择一段未被使用的地址段作为负载均衡的地址池,如果有一个loadbalancer类型的service就会从地址池中选择一个为其分配,外部服务访问这个分配的地址时首先会通过ARP查找这个地址主机的MAC地址,此时cilium operator就会以某个Node的MAC进行回复,从而使流量进入集群内部。

指定负载均衡器监控的网口:

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
name: policy1
spec:
nodeSelector:
matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
interfaces:
- ^eth[0-9]+
externalIPs: true
loadBalancerIPs: true

参考lb-ipam建立IP地址池:

1
2
3
4
5
6
7
8
9
10
apiVersion: "cilium.io/v2"
kind: CiliumLoadBalancerIPPool
metadata:
name: "blue-pool"
spec:
blocks:
# - cidr: "10.0.10.0/24"
# - cidr: "2004::0/112"
- start: "10.10.0.100"
stop: "10.10.0.200"

建立一个loadbalancer类型的service进行测试:

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
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: my-nginx
spec:
selector:
run: my-nginx
type: LoadBalancer
ports:
- port: 32283
targetPort: 80

看看是否分配到了IP:

image-20250911211912067

在其他可访问到的主机curl 10.10.0.201:32283可以测试。