K8S多集群管理实践(一):Karmada架构解析与部署入门


预计阅读时间:12 分钟

1. 为什么需要多集群?

1.1 单集群管理规模有上限

etcd瓶颈

etcd对空间大小的要求很高,默认限制为2GB,但根据官方文档的建议最大值为8GB,这意味着集群能存储的对象数量和大小有限。

内存占用

API Server作为K8S集群的入口,为提高查询效率,API Server会对集群的所有对象做缓存,当集群越大,缓存需要的内存空间就越大。

K8S中其他控制器也需要对侦听的对象构建客户端缓存,也会导致占用系统内存,意味着内存需求对系统的规模也会有一定限制。

控制器复杂度

K8S的一个业务流程是由多个对象和控制器联动完成的,就算控制器遵循了设计原则,随着对象数量的增长,控制器的处理耗时也会越来越长。

1.2 单个计算节点资源上限

虽然CPU、内存等资源可以扩容,但在系统层面所支持的端口、进程数量等是有限的,例如:Linux支持的 TCP 端口上限是 65535,去除常用端口和程序源端口后,剩下Service nodePort 的端口数量是有限的。

1.3 故障域控制

集群规模越大,控制平面组件出现故障时的影响范围就越大,为了更好地控制故障域,需要将大规模的数据中心切分成多个规模相对较小的集群,每个集群控制在一定规模。

1.4 多中心高可用

生产应用通常会采用跨地域多数据中心部署实现高可用,当其中一个数据中心出现故障或出现灾难,或者集群升级时,其他数据中心可以继续提供服务。

1.5 混合云

私有云加公有云的混合云模式逐渐成为企业的主流架构。

2. Karmada

Karmada是华为云开源的一个K8S多集群编排管理系统,现在是CNCF孵化的一个项目。该项目是在Federation v1和v2的基础上继续开发的,一些基本概念也是继承自这两个版本。Federation已经社区不再维护,因此学习Karmada是有必要的。

2.1 组件

2.1.1 控制平面组件

karmada-apiserver

apiserver是Karmada控制平面的入口或网关,它暴露Karamada API及K8S原生API。karmada-apiserver是通过kube-apiserver实现,因此它与K8S原生API天然兼容,用户可以使用kubectl操作Karmada。

karmada-aggregated-apiserver

aggregated-apiserver是通过K8S API聚合层技术实现的扩展API服务,它提供集群API及相关的子资源,例如:cluster/status and cluster/proxy,实现聚合 Kubernetes API Endpoint 等可以通过 karmada-apiserver 访问成员集群的高级功能。

kube-controller-manager

kube-controller-manager由一组控制器组成,Karmada仅继承了来自K8S官方镜像的部分控制器以保持一致和用户体验和行为。值得注意的是不是所有的控制器都是Karmada需要的,推荐的控制器请参阅 推荐的控制器

注意:当用户向 Karmada API 服务器提交 Deployment 或其他 Kubernetes 标准资源时,它们只记录在 Karmada 控制平面的 etcd 中。 随后,这些资源会向成员集群同步。然而,这些部署资源不会在 Karmada 控制平面集群中进行 reconcile 过程(例如创建Pod)。

karmada-controller-manager

Karmada控制器管理器运行了各种自定义控制器进程。控制器负责监视Karmada对象,并与底层集群的API服务器通信,以创建原生的 Kubernetes 资源。所有的控制器列举在 Karmada 控制器

karmada-scheduler

karmada-scheduler负责将K8S原生API资源对象(以及CRD资源)调度到成员集群。scheduler根据策略约束和可用资源来决定调度队列中的资源可以调度到哪些集群,然后为每个集群进行打分排名并绑定资源到最佳的成员集群。

karmada-webhook

Karmada Webhook 是基于 Kubernetes Admission Webhook 机制实现的一种扩展能力,用于在资源对象提交到 Karmada API Server 后,对请求进行自定义处理。 需要注意的是,Webhook 并不是客户端请求进入 Karmada 的第一站。用户通过 kubectl 或其他客户端发送 API 请求后,请求首先到达 Karmada API Server,随后在 API Server 的准入控制(Admission Control)阶段,API Server 会根据配置调用对应的 Webhook 服务。 Karmada Webhook 主要支持两种类型: - Mutating Webhook(修改类型) Mutating Webhook 用于修改资源对象内容,例如为资源对象自动添加默认字段、注入额外配置等。该类型 Webhook 会优先执行,在对象完成修改后,API Server 会继续进行后续校验流程。 - Validating Webhook(验证类型) Validating Webhook 用于对资源对象进行策略校验,只负责判断请求是否允许通过,不会修改对象内容。当对象经过所有 Mutating Webhook 修改,并且 API Server 完成基础校验后,Validating Webhook 会被调用。如果对象不满足自定义策略,Webhook 可以拒绝该请求。

etcd

etcd是用来存储Karmada/k8s的所有资源对象,它是一个一致性且高可用的键值对数据库。

karmada-agent(可选)

Karmada支持push和pull两种模式注册k8s集群,karmada-agent会被安装在每一个pull模式的k8s集群,它负责将当前集群注册到Karmada控制平面中,并且同步Karmada中的资源清单到当前集群,此外,它还能同步自己资源的状态给Karmada。

2.1.2 插件(可选)

karmada-scheduler-estimator

karmada-scheduler-estimator负责评估成员集群的资源情况,帮助karmada-scheduler 做出更合理的跨集群调度决策。 简单理解: * karmada-scheduler:负责决定应用应该放哪个集群 * karmada-scheduler-estimator:负责告诉 scheduler 某个集群大概还能不能放得下

karmada-descheduler

karmada-descheduler是一个重新调度组件,用于周期性检测(默认2分钟)成员集群中应用副本状态变化,当成员集群资源状态或实例分布发生变化,导致当前调度结果不再满足动态调度策略时,descheduler会触发重新调度流程,实现跨集群资源的动态平衡。

它会启动一个聚合服务,在多云环境中提供全局搜索和资源代理的能力。 * 全局搜索能力:是用来跨多个集群缓存资源对象和事件,以及通过搜索 API 对外提供图形化的检索服务。 * 资源代理能力:使用户既可以访问 Karmada 控制平面所有资源,又可以访问成员集群中的所有资源。

2.1.3 命令行工具

karmadactl

karmadactl是Karmada提供的一个命令行工具,用于和karamda-apiserver进行交互。

kubectl karmada

kubectl的一个插件,等价于karmadactl。

3. 部署入门

3.1 准备K8S集群

可以使用kind、kubeadm等工具初始化多个集群,kind可以快速创建多个集群,Karmada仓库中hack目录下也提供了现成脚本。

hack/create-cluster.sh host $HOME/.kube/host.config

但个人觉得,kind里面坑还是挺多的,如果不想花太多精力在kind上面,可以使用kubeadm,毕竟生产又不用kind,所以我启动了两台虚拟机,使用kubeadm跑两个只有master节点的k8s集群并将master节点设置为允许调度,可以根据自身环境配置来决定加入worker节点。

3.2 安装Karmada

Karmada支持多种安装方式,可参考教程,我采用的是helm安装方式,karmada-chart下载地址

helm install karmada -n karmada-system --create-namespace -f values.yaml .

检查安装好的组件

# kubectl get pods -n karmada-system 
NAME                                               READY   STATUS    RESTARTS   AGE
etcd-0                                             1/1     Running   0          9m29s
karmada-aggregated-apiserver-555fdf4687-f67bt      1/1     Running   0          9m29s
karmada-apiserver-565cbb7f58-4kc8c                 1/1     Running   0          9m29s
karmada-controller-manager-c8fbf689f-4d8f2         1/1     Running   0          9m29s
karmada-kube-controller-manager-5446545b65-xsbl7   1/1     Running   0          9m29s
karmada-scheduler-54f75997bd-mkqc8                 1/1     Running   0          9m29s
karmada-webhook-7bc88f6874-mvp6x                   1/1     Running   0          9m29s

导出karmada的kubeconfig文件

kubectl get secret -n karmada-system karmada-kubeconfig -o jsonpath={.data.kubeconfig} | base64 -d > /etc/kubernetes/karmada.conf

3.3 注册集群

3.3.1 安装karmada命令行工具

curl -s https://raw.githubusercontent.com/karmada-io/karmada/master/hack/install-cli.sh | sudo INSTALL_CLI_VERSION=1.18.2 bash

3.3.2 push模式注册集群

push模式适合在低延迟的网络中karmada控制平台可以直接访问到k8s集群的apiserver,或通过代理间接的方式,例如:管理同一个数据中心的集群。

karmadactl join sz --kubeconfig=/etc/kubernetes/karmada.conf --cluster-kubeconfig=$HOME/.kube/config

输出

cluster(sz) is joined successfully

将karmada.conf配置文件拷贝到其他集群的master节点。

scp /etc/kubernetes/karmada.conf root@192.168.3.13:/etc/kubernetes/karmada.conf 

3.3.3 pull模式注册集群

pull模式适合在特定的网络环境,例如Karmada控制平台无法直接访问到成员集群,例如NAT环境,相比push模式,它的性能会更好,因为分担了一部分Karmada控制平面的压力。

karmadactl token create --print-register-command --kubeconfig /etc/kubernetes/karmada.conf

执行输出命令

karmadactl register 192.168.3.11:31008 --token 51u9hx.dt1yulin2zdg1cbs --discovery-token-ca-cert-hash sha256:d96a14ac5da95df979607eddb80f27d669a91a28dab06eb33a1926f36792a027

执行输出:

[preflight] Running pre-flight checks
[preflight] All pre-flight checks were passed
[karmada-agent-start] Waiting to perform the TLS Bootstrap
[karmada-agent-start] Waiting to check cluster exists
[karmada-agent-start] Assign the necessary RBAC permissions to the agent
[karmada-agent-start] Waiting to construct karmada-agent kubeconfig
[karmada-agent-start] Waiting the necessary secret and RBAC
[karmada-agent-start] Waiting karmada-agent Deployment

cluster(fz) is joined successfully

自动安装karmada-agent

kubectl get pods -n karmada-system 
NAME                             READY   STATUS    RESTARTS   AGE
karmada-agent-59bd6fd665-6qvdb   1/1     Running   0          2m37s

查看当前注册的集群

 karmadactl get clusters --kubeconfig=/etc/kubernetes/karmada.conf
NAME   CLUSTER   VERSION   MODE   READY   AGE   ADOPTION
fz     Karmada   v1.34.2   Pull   True    5s    -
sz     Karmada   v1.34.2   Push   True    46s   -

注意:pull模式下证书验证的坑

[preflight] Running pre-flight checks [preflight] All pre-flight checks were passed [karmada-agent-start] Waiting to perform the TLS Bootstrap error: couldn't validate the identity of the API Server: Get "https://192.168.3.11:31008/api/v1/namespaces/kube-public/configmaps/cluster-info?timeout=10s": tls: failed to verify certificate: x509: certificate is valid for 127.0.0.1, not 192.168.3.11

原因是karmada-apiserver生成证书时IP地址只包含了127.0.0.1。

kubectl -n karmada-system get secret karmada-cert -o jsonpath='{.data.karmada\.crt}' | base64 -d | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
            X509v3 Subject Alternative Name: 
                DNS:kubernetes.default.svc, DNS:*.etcd.karmada-system.svc.cluster.local, DNS:*.karmada-system.svc.cluster.local, DNS:*.karmada-system.svc, DNS:localhost, IP Address:127.0.0.1

因为我是helm安装,所以需要更改一下helm values.yaml,但更改完之后并不会重新生成证书,所以卸载重新安装。

certs:
  mode: auto
  auto:
    expiry: 43800h
    rootCAExpiryDays: 3650
    hosts: [
      "kubernetes.default.svc",
      "*.etcd.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }}",
      "*.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }}",
      "*.{{ .Release.Namespace }}.svc",
      "localhost",
      "127.0.0.1",
      "192.168.3.11"    # 添加karmada-apiserver控制平面的nodePort地址
    ]
    rsaSize: 3072

3.4 使用karmada分发Deployment

创建nginx deployment

karmadactl --kubeconfig /etc/kubernetes/karmada.conf apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

创建PropagationPolicy

karmadactl --kubeconfig /etc/kubernetes/karmada.conf apply -f - <<EOF
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: nginx-propagation
spec:
  resourceSelectors:
    - apiVersion: apps/v1
      kind: Deployment
      name: nginx
  placement:
    clusterAffinity:
      clusterNames:
        - sz
        - fz
    replicaScheduling:
      replicaDivisionPreference: Weighted
      replicaSchedulingType: Divided
      weightPreference:
        staticWeightList:
          - targetCluster:
              clusterNames:
                - sz
            weight: 1
          - targetCluster:
              clusterNames:
                - fz
            weight: 1
EOF

验证

karmadactl --kubeconfig /etc/kubernetes/karmada.conf get deploy
NAME    CLUSTER   READY   UP-TO-DATE   AVAILABLE   AGE   ADOPTION
nginx   Karmada   2/2     2            2           19m   -

本文由 leojamin 原创,转载请注明出处。

📖相关推荐