Kubernetesg官网中有这么一张图,使用 kubeadm 引导的 Kubernetes 集群,使用负载均衡器将kube-apiserver 暴露给工作节点。

如果你使用过rkerke2等 Kubernetes 发行版,你会发现,他们并没有使用负载均衡器也能实现 kube-apiserver 的高可用。

你有没有思考过 rkerke2等 Kubernetes 发行版是如何做 kube-apiserver 的高可用的呢?

要搞清楚这个问题,我们看一下 kubelet 是如何连接 kube-apiserver 的就有答案了。

rke 是如何实现 kube-apiserver 高可用的?

我找一个工作节点,查看 kubelet 容器配置

docker inspect kubelet

可以看到以下启动参数,主要看 --kubeconfig 参数:

        "Args": [
            "kubelet",
            <省略很多内容>
            "--kubeconfig=/etc/kubernetes/ssl/kubecfg-kube-node.yaml",
            <省略很多内容>
        ]

--kubeconfig=/etc/kubernetes/ssl/kubecfg-kube-node.yaml 参数说明 kubelet 连接 kube-apiserver 使用了 /etc/kubernetes/ssl/kubecfg-kube-node.yaml 这个配置文件连接 kube-apiserver,看下这个文件的内容。

cat /etc/kubernetes/ssl/kubecfg-kube-node.yaml

apiVersion: v1
kind: Config
clusters:
- cluster:
    api-version: v1
    certificate-authority: /etc/kubernetes/ssl/kube-ca.pem
    server: "https://127.0.0.1:6443"
  name: "local"
<省略很多内容>

可以看到,kube-apiserver 的地址是 https://127.0.0.1:6443,那么为什么是这个地址呢?继续查看本机有没有监听6443端口。

netstat -ntlp | grep 6443

tcp        0      0 0.0.0.0:6443            0.0.0.0:*               LISTEN      31002/nginx: master

可以看到有个nginx服务监听了 6443,我们看下这个nginx是怎么配置的。

docker exec -it nginx-proxy /bin/sh
sh-5.1# cat /etc/nginx/nginx.conf

<省略很多内容>

stream {
        upstream kube_apiserver {
            server 172.31.1.1:6443;
            server 172.31.1.2:6443;
            server 172.31.1.3:6443;
        }
        server {
            listen        6443;
            proxy_pass    kube_apiserver;
            proxy_timeout 10m;
            proxy_connect_timeout 2s;
        }
}

看到这里,你就明白了,rke 在每个工作节点都运行了一个 nginx-proxy 的容器,负责 kube-apiserver 的反向代理。

控制节点是不需要运行这个 nginx-proxy 的,因为控制节点已然部署了 kube-apiserver,监听了 6443 端口。https://127.0.0.1:6443 就是能访问到本机的 kube-apiserver 的。

rke2 是如何实现 kube-apiserver 高可用的?

同样的逻辑,找一个控制节点查看 kubelet 的启动参数。

ps aux | grep kubelet

<省略很多内容> kubelet <省略很多内容> --kubeconfig=/var/lib/rancher/rke2/agent/kubelet.kubeconfig

rke2kubelet 不是使用容器运行的,是 rke2 程序拉起的,所以在本机用 ps 命令即可查看到。rke2 中 kubelet 用的是这个配置文件,还是一样的配方,看下文件内容。

cat /var/lib/rancher/rke2/agent/kubelet.kubeconfig

apiVersion: v1
clusters:
- cluster:
    server: https://127.0.0.1:6443
    certificate-authority: /var/lib/rancher/rke2/agent/server-ca.crt
  name: local
<省略很多内容>

还是 https://127.0.0.1:6443,继续看。

netstat -ntlp | grep 6443

tcp        0      0 127.0.0.1:6443          0.0.0.0:*               LISTEN      13844/rke2 agent

rke2 agent 监听了 6443 端口。虽然可以猜测就是代理到 kube-apiserver 的,怎么能说服你呢?难道要翻代码找证据吗?好吧,简单翻一下看看。

以下代码在 release-1.26 分支:

// pkg/cli/cmds/agent.go L92
func AgentRun(clx *cli.Context) error {
    validateCloudProviderName(clx, Agent)
    validateProfile(clx, Agent)
    if err := windows.StartService(); err != nil {
        return err
    }
    return rke2.Agent(clx, config)
}

// rke2.Agent(clx, config) 跟进去

// pkg/rke2/rke2.go L128
func Agent(clx *cli.Context, cfg Config) error {
    if err := setup(clx, cfg, false); err != nil {
        return err
    }
    return agent.Run(clx)
}

// agent.Run(clx) 跟进去

// pkg/cli/agent/agent.go L98
func Run(ctx *cli.Context) error {
  // <省略很多内容>
    return agent.Run(contextCtx, cfg)
}

// agent.Run(contextCtx, cfg) 跟进去

// pkg/agent/run.go L270
func Run(ctx context.Context, cfg cmds.Agent) error {
  // <省略很多内容>
    proxy, err := createProxyAndValidateToken(ctx, &cfg)
}

代码跟到这里,就大概能看到了,rke2 agent 会起一个代理,将 6443 代理至 kube-apiserver

总结

不管是 kubeadm 还是 rkerke2 引导的集群,kube-apiserver 都是要做高可用的,只是 kubeadm 搭建的集群没有内置的反向代理机制帮我们简化工作,需要你在搭建时考虑高可用,一般是使用负载均衡器来做反向代理。

rkerke2 等发行版,他们内置了反向代理机制,我们在搭建集群时就不需要考虑集群内 kube-apiserver 的高可用了。他是在每一个工作节点上启动一个反向代理程序,rke 是用 nginx 容器跑的一个反向代理,rke2rke2 agent 起一个反向代理。但是如果你的 kube-apiserver 需要暴露给外部访问,且需要做高可用,你还是得考虑用一个负载均衡器才行。

画一个拓朴图,更直观些。

RKE
rke-apiserver高可用原理-rke.png

RKE2
rke-apiserver高可用原理-rke2.png

标签: none

添加新评论