最近腾讯云要对托管的TKE集群开始收费了,紧接着TKE Mesh也开始收费了,各种费用认真一算还不低。于是乎我开始寻找其它替代方案:最后落地方案是基于k3s集群,替代部分旧的基础设施,将原来的服务迁移到k3s集群内。
当前现状
盘点了一下手上的机器,这个集群我取名叫tesla
,所以机器都有model3、modely、modelx这样。
机器列表为:
- modely 2C2G 172.17.32.8
- model3 1C2G 172.17.48.8
- modelX 2C2G 172.17.96.88
应用列表:
目前在TKE集群有如下自己安装的应用:
- postgresql 提供评论的存储,有持久化到cbs。同时定期导出DB到cos上。
- bitwarden 提供了密码管理服务,数据持久化到云mysql中
- myblog 博客,基于nginx。数据在COS桶
bucket: "blog-xxx"
中,以PV挂载 - mycomments 博客评论系统,基于waline。数据使用集群内自建的
postgres
- pgsql-backup 将自建的postgresql备份到COS平台保存的工具
- cert-manager 基于ACME的证书自动申请续期
- busybox 工具集
- istio-ingressgatway 接入服务
- cls-provisioner CLS日志采集
- csi-coslauncher COS组件
- prometheus
- grafana
创建k3s集群
安装master
在安装前最好把机器加速一下,可以看之前我写过的国内CVM访问github加速
一文。
因为已经使用了外部高可用mysql作为数据库,所以我本次只使用一台master机器。
在需要作为master机器上执行如下命令:
1
2
3
4
5
| SECRET=tesla-k3s
curl -sfL https://get.k3s.io | sh -s - server \
--tls-san=[此master机器的外网IP] \
--token=$SECRET \
--datastore-endpoint="mysql://[用户名]:[密码]@tcp(172.17.0.2:3306)/k3s"
|
安装node
查看master上的node-token信息。
1
2
| ubuntu@modelx:~$ sudo cat /var/lib/rancher/k3s/server/node-token
[一串token]::server:tesla-k3s
|
使用如下命令部署node:
1
2
3
| curl -sfL https://get.k3s.io | sh -s - agent \
--server=https://172.17.96.88:6443 \
--token="[上面的一串token]::server:tesla-k3s"
|
注意修改server为master的内网IP,修改token为上面获得的node-token。
安装基础组件
安装COS组件,提供COS挂载能力
参考TencentCloud
期间部署腾讯云COS组件时遇到报错。
1
2
| ❯ kubectl create -f deploy/cosfs/kubernetes/coscsidriver-new.yaml
error: unable to recognize "deploy/cosfs/kubernetes/coscsidriver-new.yaml": no matches for kind "CSIDriver" in version "storage.k8s.io/v1beta1"
|
原因是CSIDriver已经不再是beta版本了,修改为storage.k8s.io/v1
可以正常部署。
整个部署是:
1
2
3
4
5
6
7
| git clone https://github.com/TencentCloud/kubernetes-csi-tencentcloud.git
cd kubernetes-csi-tencentcloud
# 注意这步要修改一个此文件的kind
kubectl create -f deploy/cosfs/kubernetes/coscsidriver-new.yaml
kubectl create -f deploy/cosfs/kubernetes/coslauncher-new.yaml
kubectl create -f deploy/cosfs/kubernetes/rbac.yaml
kubectl create -f deploy/cosfs/kubernetes/cosplugin-new.yaml
|
安装CBS组件,提供CBS挂载能力
参考TencentCloud CBS文档
按文档顺序操作即可。注意不要随便修改名字,比如secret名cbs-csi-api-key
在相关部署里就是指定这个的。如果过程中secret相关密钥写错要修改,记得要重启相关csi的pod。
否则可能会收获如下报错:
1
| Warning ProvisioningFailed 24s com.tencent.cloud.csi.cbs_csi-cbs-controller-dcf9fd55f-97mtz_9acf7cca-2584-4aa1-a197-e58dc045345a failed to provision volume with StorageClass "cbs": rpc error: code = InvalidArgument desc = [TencentCloudSDKError] Code=AuthFailure.SignatureFailure, Message=请求签名验证失败,请检查您的签名计算是否正确。, RequestId=b5d420f4-86ce-470c-be6d-f03c15a7ccff。
|
安装服务
除了通过正常部署我的上述服务外,原本用istio通过Ingress统一对外入口的,现在我选择使用k3s
默认的traefik
来代替。同时它还有类似原cert-manager
功能,可通过ACME获取证书(包括泛域名证书),后面会讲如何配置。
示例:安装博客
我把blog、comments等服务都部署了,其中像blog我是使用nginx
作为基础,将博客的静态网页上传到腾讯云对象存储(COS)平台,然后把对象存储挂载到nginx容器中。这样当博客更新只要同步到COS,后续就会自动同步给容器不用我们操作任何集群内东西。博客的部署yaml大概是这样:
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
| # Source: blog/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: myblog
labels:
helm.sh/chart: blog-0.1.0
app.kubernetes.io/name: blog
app.kubernetes.io/instance: myblog
app.kubernetes.io/version: "1.16.0"
app.kubernetes.io/managed-by: Helm
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: blog
app.kubernetes.io/instance: myblog
---
# Source: blog/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myblog
labels:
helm.sh/chart: blog-0.1.0
app.kubernetes.io/name: blog
app.kubernetes.io/instance: myblog
app.kubernetes.io/version: "1.16.0"
app.kubernetes.io/managed-by: Helm
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: blog
app.kubernetes.io/instance: myblog
template:
metadata:
labels:
app.kubernetes.io/name: blog
app.kubernetes.io/instance: myblog
spec:
securityContext:
{}
containers:
- name: blog
securityContext:
{}
image: "nginx:1.21"
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /usr/share/nginx/html
name: cos
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{}
volumes:
- name: cos
persistentVolumeClaim:
claimName: myblog
---
# Source: blog/templates/cos-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myblog
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: ""
# PV还要你主动定义指向你指定的桶,这里就不再细说了,当然还有相关的secret,看官方文档。
|
示例:通过Traefik暴露服务
同时定义ingressroute
资源定义路由规则。比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
| apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: to-blog
spec:
entryPoints:
- websecure
routes:
- match: Host(`gameapp.club`) || Host(`blog.gameapp.club`)
kind: Rule
services:
- name: myblog
port: 80
|
建议此资源和对应service,pod在同一个namespace下。
自动获取证书
这个集群除了博客,还有一系列其它服务都在其中。之前在TKE中通过cert-manager
获得一个泛域名证书。当然用traefik也可以做到,只要如下配置(感觉更简单了)。
建议给证书持久化本地PV
因为证书有获取次数限制,我建议还是将获取的证书持久化在本地。在k3s中有Local-path作为pv。先定义一个PVC:
1
2
3
4
5
6
7
8
9
10
11
12
| apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pv-traefik-acme
namespace: kube-system
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 50Mi
|
在k3s中内置的pv是local-storge,可见/var/lib/rancher/k3s/server/manifests/local-storage.yaml
中定义的。
相关的PV保存在本地的/var/lib/rancher/k3s/storage/
目录下。
设置Traefik自动获取证书
然后,我们修改traefik启动的一些参数,让它使用ACME获取证书,并且将local path挂载在/data
目录(这是默认的,所以下面参数没有写明)。
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
| # sudo cat /var/lib/rancher/k3s/server/manifests/traefik-config.yaml
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
ports:
websecure:
tls:
enabled: true
certResolver: "le"
additionalArguments:
- "--entrypoints.websecure.http.tls.domains[0].main=*.[你的网址]"
- "--entrypoints.websecure.http.tls.domains[0].sans=[你的网址]"
- "--certificatesresolvers.le.acme.email=[你的邮箱]"
- "--certificatesresolvers.le.acme.storage=/data/acme.json"
- "--certificatesresolvers.le.acme.dnschallenge=true"
- "--certificatesresolvers.le.acme.dnschallenge.provider=dnspod"
# 在测试阶段建议取消下面注释,看整个流程有无问题,因为LE证书有频率限制,防止被禁。
#- "--certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.le.acme.dnschallenge.delaybeforecheck=0"
env:
- name: "DNSPOD_API_KEY"
value: "[你在DnsPod的API密钥中的ID,对应KEY]"
- name: "DNSPOD_HTTP_TIMEOUT"
value: "10"
- name: "DNSPOD_POLLING_INTERVAL"
value: "5"
- name: "DNSPOD_PROPAGATION_TIMEOUT"
value: "300"
persistence:
enabled: true
existingClaim: "pv-traefik-acme"
logs:
general:
level: DEBUG
|
因为我用的腾讯云的DnsPod,是直接被支持的。安全起见env中参数可以从secret中获取,但自己集群裸写也问题不大。观察traefik的日志,正常的话一会它会获取到证书并且保存到/data/acme.json
文件中了,迟迟没有不妨重启一下pod。
使用免费的负载均衡器svclb
原来我们使用弹性公网IP绑定到istio-ingressgateway并且魔改让它作为statefulset的方式已经不需要了。在k3s中默认的svclb通过一系列操作,可以使相关节点组成一个负载均衡器。
1
2
3
4
5
| ❯ k get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
...
kube-system traefik LoadBalancer 10.43.140.96 172.17.48.8,172.17.96.88 80:31528/TCP,443:31667/TCP 37h
...
|
可看到external ip
为我的几台节点IP,这些机器的80/443端口流量会转发给traefik的Service,完美符合需求!
总结
到这里基本尾声了。我们在完全自建的集群中,重新安置了我们的服务。放弃了CLB和EIP等,也放弃了原来托管的istio和TKE。不过继续用了腾讯云开源的CSI组件,这样可以用上可靠的存储。用上了k3s的自动证书和负载均衡,比较完美的达成了集群迁移目标。收工!