
构建最小化的 Kubernetes 集群
作者 |阳明
来源 | k8s技术圈(ID:kube100)
Kubernetes 号称云原生操作系统,可想而知其复杂程度也是非常大的,由许多组件组成,我们很难去追踪到所有的组件信息。
上图中至少列出了七八个组件,我们这里会忽略其中大部分组件,要运行一个最小级别的 Kubernetes 至少要包括如下三个基本组件:
- kubelet:在集群中每个节点上运行的代理,负责容器真正运行的核心组件
- kube-apiserver:Kubernetes 控制平面的组件,提供资源操作的唯一入口
- 容器运行时(Docker)
这里我们来尝试配置一个最小级别的 Kubernetes,这对于我们加速对集群的理解也是非常有帮助的。
安装
首先需要在节点上安装 Docker 容器运行时,我们这里使用的是操作系统为 CentOS7 版本,在 root 用户下面执行相关操作。执行如下所示命令直接安装即可:
接下来,我们需要获取 Kubernetes 二进制文件。实际上,我们只需要使用 kubelet 组件来引导我们的“集群”,因为我们可以使用 kubelet 来运行其他组件,一旦集群启动了,我们就可以使用 kubectl 来进行操作了。
由于 kubelet 配置太多,但是这里我们只需要几个设置参数即可:
我们这里需要使用的是 --pod-manifest-path 这个参数,该参数用于指定要运行的静态 Pod 文件的目录,静态 Pod 不受 Kubernetes API 管理,虽然平时我们在使用 Kubernetes 的时候比较少使用静态 Pod,但是对于引导集群却是非常有用,对 Kubeadm 熟悉的应该知道,该方案就是利用静态 Pod 将 Kubernetes 控制面板容器化的。下面我们来尝试下是否可以使用 kubelet 来运行 Pod。
首先我们创建一个静态 Pod 目录来运行 kubelet:
然后重新打开一个终端,创建如下所示的 Pod 资源清单文件:
上面资源清单出现在 pods 目录后,就可以在 kubelet 日志中看到如下所示的错误信息:
这是因为 Kubernetes 的 Pod 默认情况下会优先启动一个 k8s.gcr.io/pause:3.2的 pause 镜像,而该镜像由于某些原因获取不到,我们可以 --pod-infra-container-image 参数重新指定一个可以访问到的镜像:
现在我们检查下 Docker 容器是否有新的容器启动:
kubelet 通过我们指定的静态 Pod 目录,读取其中的 YAML 文件来创建 Pod。由于我们这里执行的就是 echo 命令,所以会不断的重启,验证完成后删除该 YAML 文件即可。
当然这还不够,我们还需要运行 APIServer,要做到这一点,我们需要首先运行 etcd,同样的我们也可以使用静态 Pod 来运行 etcd,创建如下所示的 etcd 资源清单文件:
这就是一个非常普通的 Pod 资源清单文件,大家应该都非常熟悉,不过还是需要注意两件事:
- 我们将宿主机的 /var/lib/etcd 目录挂载到 Pod 容器中,这样可以保证 etcd 在重新启动以后数据依然存在。
- 另外我们设置了 hostNetwork=true,这样可以使容器和宿主机共享网络命名空间,可以让 APIServer 更容易和 etcd 通信。
我们可以使用如下所示的命令来检查 etcd 是否启动成功:
现在 etcd 启动成功了,就可以来启动 APIServer 了,我们这里只需要通过参数 --etcd-servers 传递 etcd 地址即可,同样在静态 pods 目录下面创建如下所示的资源清单:
创建完成后正常 APIServer 就会正常启动,可以通过如下所示的命令来验证:
而且 kubectl 也不需要额外的配置就可以直接使用了:
这是因为 kubectl 默认是通过 localhost:8080 和 APIServer 进行通信的。
配置
但是当我们去获取刚刚创建的静态 Pod 的时候却发现没有对应的记录:
而且运行 kubelet 的节点也根本没有显示:
这其实是因为 kubelet 不知道如何与 APIServer 进行通信并更新状态造成的,我们可以通过 kubelet 的 --kubeconfig 参数来指定 KUBECONFIG 文件的路径,可以通过该文件来指定如何连接到 APIServer。由于我们这里就是启动一个最新的 Kubernetes,没有身份验证或者证书之类的麻烦事情,所以非常简单,创建名为 kubeconfig.yaml 的如下所示文件:
然后杀掉 kubelet 进程,添加上 --kubeconfig 参数重新运行:
隔一会儿后我们再次使用 kubectl 来查看上面我们运行的静态 Pod 就正常了:
这样我们就运行了一个最小功能集的 Kubernetes 集群了。下面我们来尝试运行一个普通的 Pod 看能否正常运行。
同样尝试来创建一个 nginx 的 Pod:
然后使用 kubectl 来创建上面的资源对象:
可以看到有错误信息,这是因为我们上面部署的最小级别的 Kubernetes 环境完整性还是不够,没有自动生成默认的 default 这个 ServiceAccount,我们来手动创建再来验证一次:
我们手动创建了 ServiceAccount,但是却并没有创建对应的身份验证的 Token,我们可以看到以前很多自动完成的操作现在都没有了。
不过我们可以通过 automountServiceAccountToken 参数在 ServiceAccount 上来规避这个特定问题,因为实际上我们这里并不需要使用 ServiceAccount:
现在我们可以看到 Pod 出现了,但是处于 pending 状态,这是因为我们并没有部署 kube-scheduler 这个负责调度的组件,自然是不能被调度的,当然我们也可以不需要调度程序,直接使用 nodeName 属性将 Pod 手动固定到节点上即可:
现在将之前部署的 Pod 删除重新来部署,正常就可以运行了:
为了验证 Pod 与 Pod 之间是可以正常通信的,我们可以使用如下的 Pod 来验证:
可以看到可以正常通信。这样我们就完成了一个最小的 Kubernetes 集群部署。当然这也仅仅是为了简化我们对 Kubernetes 的理解而已,在实际的生产环境是绝对不能这样去部署使用的。
参考
- https://eevans.co/blog/minimum-viable-kubernetes/
- https://commons.wikimedia.org/w/index.php?curid=53571935
- https://kubernetes.io/docs/concepts/overview/components/
- https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/
