Back
Featured image of post TKE托管收费之替代方案

TKE托管收费之替代方案

最近腾讯云要对托管的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的自动证书和负载均衡,比较完美的达成了集群迁移目标。收工!