1. Kubernetes 部署本质
部署的本质,其实就是将 Kubernetes 各个组件编译成二进制文件,并且为这些二进制文件编写对应的配置文件、配置自启动脚本,以及为 kube-apiserver 配置授权文件等等诸多运维工作。(可继续参考张磊老师:k8s 的本质一节的内容)
kubernetes 安装用到的二进制
- kubeadm
- kubeadm.yaml
- kubectl
- kubelet
- kubelet.service(如在 /etc/systemd/system/ 中)
- kubelet.service.d/10-kubeadm.conf (如在 /etc/systemd/system/ 中)
- /var/lib/kubelet/config.yaml
kubernetes 所需的相关组件
- etcd(相关组件)
- api-server
- controller
- scheduler
- DNS
网络相关-CNI 插件
- bridge 等
containerd 容器运行时
- containerd、container-shim、runc 等二进制
- /etc/containerd/config.toml
- containerd.service(如:/etc/systemd/system/containerd.service)
- /var/run/containerd/containerd.sock
- /var/lib/containerd
命令行工具
- crictl
- crictl.yaml
Docker 容器运行时
- dockerd、containerd、containerd-shim 等二进制
- /etc/systemd/system/docker.service
- /var/run/docker.sock、/var/run/dockershim.sock
- /var/lib/docker
2. Kubernetes 部署发展历程
Kubernetes 项目发布初期,Kubernetes 的部署完全依靠一堆由社区维护的脚本。
2018 年的时候,各大云厂商最常用的部署方法,是使用 SaltStack、Ansible 等运维工具自动化地执行上述部署过程。但是,由于 SaltStack 这类专业的运维工具本身的学习成本,就可能比 Kubernetes 要高,所以部署过程还是相当繁琐。
SaltStack 这样的运维工具或者由社区维护的脚本的功能,就是把各个组件编译成的二进制传输到指定的机器当中,然后编写控制脚本来启停这些组件。
而为了解决 Kubernetes 部署繁琐的问题,2017 年,在志愿者的推动下,社区发起了一个独立的部署工具:kubeadm:https://github.com/kubernetes/kubeadm。kubeadm 项目的目的就是要让用户能够通过下面这两条指令完成一个 kubernetes 集群的部署:
# 创建一个Master节点
$ kubeadm init
# 将一个Node节点加入到当前集群中
$ kubeadm join <Master节点的IP和端口>
3. kubeadm 部署
3.1. kubeadm init 的流程
kubelet 以二进制的方式直接运行在宿主机上,然后使用容器部署其他的 kubernetes 组件。
-
所以第一步是在机器上手动安装 kubeadm、kubelet、kubectl 这几个二进制文件。
apt-get install kubeadm
-
接下去就可以使用 kubeadm init 来部署 master 节点了
-
kubeadm init 执行之后,首先要做的,是一系列的检查工作,以确定这台机器可以用来部署 Kubernetes,称为“Preflight Checks”。主要包含以下几个方面:
- Linux 内核的版本必须是否是 3.10 以上
- Linux Cgroups 模块是否可用
- 机器的 hostname 是否标准?在 Kubernetes 项目里,机器的名字以及一切存储在 Etcd 中的 API 对象,都必须使用标准的 DNS 命名(RFC 1123)
- 用户安装的 kubeadm 和 kubelet 的版本是否匹配
- 机器上是不是已经安装了 Kubernetes 的二进制文件
- Kubernetes 的工作端口 10250/10251/10252 端口是不是已经被占用
- ip、mount 等 Linux 指令是否存在
- Docker 是否已经安装
- ......
-
通过 preflight checks 之后,kubeadm 要做的就是生成 kubernetes 对外提供服务所需要的各种证书和对应的目录
- Kubernetes 对外提供服务时,除非专门开启“不安全模式”,否则都要通过 HTTPS 才能访问 kube-apiserver。这就需要为 Kubernetes 集群配置好证书文件。kubeadm 为 Kubernetes 项目生成的证书文件都放在 Master 节点的 /etc/kubernetes/pki 目录下。在这个目录下,最主要的证书文件是 ca.crt 和对应的私钥 ca.key。
- 此外,用户使用 kubectl 获取容器日志等 streaming 操作时,需要通过 kube-apiserver 向 kubelet 发起请求,这个连接也必须是安全的。kubeadm 为这一步生成的是 apiserver-kubelet-client.crt 文件,对应的私钥是 apiserver-kubelet-client.key。
- 除此之外,Kubernetes 集群中还有 Aggregate APIServer 等特性,也需要用到专门的证书,具体可以看相应的目录。
需要注意的是,可以选择不让 kubeadm 为你生成这些证书,而是拷贝现有的证书到如下证书的目录里:
/etc/kubernetes/pki/ca.{crt,key}
,这个时候 kubeadm 就会跳过证书生成的步骤,把它完全交给用户处理。 -
证书生成后,kubeadm 接下来会为其他组件生成访问 kube-apiserver 所需的配置文件。这些文件的路径是:/etc/kubernetes/xxx.conf:
ls /etc/kubernetes/ admin.conf controller-manager.conf kubelet.conf scheduler.conf
这些文件里面记录的是,当前这个 Master 节点的服务器地址、监听端口、证书目录等信息。这样,对应的客户端(比如 scheduler,kubelet 等),可以直接加载相应的文件,使用里面的信息与 kube-apiserver 建立安全连接。
-
kubeadm 会为 Master 上所需要的组件(kube-apiserver、kube-controller-manager、kube-scheduler)生成 Pod YAML 文件。假如没有提供一个外部的 eted 服务的话,也会再生成一个 Etcd 的 Pod YAML 文件。由于这个时候 kubernetes 集群还没有起来,此时将采用 static pod 方式来启动这些 Pod。Static Pod 是把要部署的 Pod 的 YAML 文件放在一个指定的目录里,那么当这台机器上的 kubelet 启动时,它会自动检查这个目录,加载所有的 Pod YAML 文件,然后在这台机器上启动它们。
在 kubeadm 中,Master 组件的 YAML 文件会被生成在 /etc/kubernetes/manifests 路径下,一旦相应的 YAML 文件出现在这个目录下,kubelet 就会自动创建这些 YAML 文件中定义的 Pod,即 Master 组件的容器。最后 Master 组件的 Pod YAML 文件如下所示:
$ ls /etc/kubernetes/manifests/ etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
kubelet 在 Kubernetes 项目中的地位非常高,在设计上它就是一个完全独立的组件,而其他 Master 组件,则更像是辅助性的系统容器。
-
对控制平面节点应用标签和污点标记,以便不会在它上面运行其他的工作负载。
-
Master 容器启动后,kubeadm 会通过检查 localhost:6443/healthz 这个 Master 组件的健康检查 URL,等待 Master 组件完全运行起来。
-
然后,kubeadm 就会为集群生成一个 bootstrap token。在后面,只要持有这个 token,任何一个安装了 kubelet 和 kubadm 的节点,都可以通过 kubeadm join 加入到这个集群当中。这个 token 的值和使用方法,会在 kubeadm init 结束后被打印出来。
在 token 生成之后,kubeadm 会将 ca.crt 等 Master 节点的重要信息,通过 ConfigMap 的方式保存在 Etcd 当中,供后续部署 Node 节点使用。这个 ConfigMap 的名字是 cluster-info。
-
kubeadm init 的最后一步,就是安装默认插件。Kubernetes 默认 kube-proxy 和 DNS 这两个插件是必须安装的。它们分别用来提供整个集群的服务发现和 DNS 功能。其实,这两个插件也只是两个容器镜像而已,所以 kubeadm 只要用 Kubernetes 客户端创建两个 Pod 就可以了。
-
https://kubernetes.io/zh/docs/reference/setup-tools/kubeadm/kubeadm-init/
3.2. kubeadm join 的流程
kubeadm init 生成 bootstrap token 之后,你就可以在任意一台安装了 kubelet 和 kubeadm 的机器上执行 kubeadm join 了。
需要 bootstrap token 的原因
任何一台机器想要成为 kubernetes 集群中的一个节点,就必须在集群的 kube-apiserver 上注册。可是,要想跟 apiserver 打交道,这台机器就必须要获取到相应的证书文件(CA 文件)。可是,为了能够一键安装,我们就不能让用户去 Master 节点上手动拷贝这些文件。
保存在 ConfigMap 中的 cluster info。cluster info 保存了 APIServer 的授权信息,比如 kube-apiserver 的地址、端口、证书等,有了这些信息之后,。
所以这个时候 kubeadm 至少需要向 kube-apiserver 发起一次 “不安全模式”的访问,从而拿到保存在 ConfigMap 中的 cluster info,进而拿到相关的证书。而 bootstrap token,扮演的就是这个过程中的安全验证的角色,从而确保这次不安全的访问是安全的。
得到了 kube-apiserver 的地址、端口、证书之后,kubelet 就可以以安全模式连接到 apiserver 上。这样,一个新的节点就部署完成了。
3.3. 配置 kubeadm 的部署参数
默认情况下,我们使用 kubeadm init
和 kubeadm join
的默认参数就可以完成一个集群的搭建了,但是有时候我们希望可以配置集群组件,比如 apiserver 的启动参数。这个时候也可以指定使用一个 YAML 来部署集群,
kubeadm init --config kubeadm.yaml
apiVersion: kubeadm.k8s.io/v1alpha2
kind: MasterConfiguration
kubernetesVersion: v1.11.0
api:
advertiseAddress: 192.168.0.102
bindPort: 6443
...
etcd:
local:
dataDir: /var/lib/etcd
image: ""
imageRepository: k8s.gcr.io
kubeProxy:
config:
bindAddress: 0.0.0.0
...
kubeletConfiguration:
baseConfig:
address: 0.0.0.0
...
networking:
dnsDomain: cluster.local
podSubnet: ""
serviceSubnet: 10.96.0.0/12
nodeRegistration:
criSocket: /var/run/dockershim.sock
...
YAML 文件是部署参数配置文件,你可以在 YAML 文件里填写各种自定义的部署参数。比如要指定 kube-apiserver 的参数,那么在文件里头加上如下内容即可。这样,kubeadm 就会用这些内容来替换 /etc/kubernetes /manifests/kube-apiserver.yaml 里的 command 字段里的参数了。
...
apiServerExtraArgs:
advertise-address: 192.168.0.103
anonymous-auth: false
enable-admission-plugins: AlwaysPullImages,DefaultStorageClass
audit-log-path: /home/johndoe/audit.log
除此之外,你还可以通过 YAML 文件
- 修改 kubelet 和 kube-proxy 的配置,
- 修改 kubernetes 使用的基础镜像的 URL(默认的 k8s.gcr.io/xxx 镜像在国内访问是有困难的)
- 指定自己的证书文件
- 指定特殊的容器运行时
3.4. 为什么 kubelet 不用容器方式部署
kubelet 是 Kubernetes 项目用来操作 Docker 等容器运行时的核心组件。可是除了跟容器运行时打交道外,kubelet 在配置网络、管理容器数据卷时,都需要操作宿主机。
而如果 kubelet 本身就运行在一个容器里,那么直接操作宿主机就会变得很麻烦。对于网络配置来说还好,kubelet 容器可以通过不开启 Network Namespace(即 Docker 的 host network 模式)的方式,直接共享宿主机的网络栈。可是,要让 kubelet 隔着容器的 Mount Namespace 和文件系统,操作宿主机的文件系统,就有点儿困难了。
比如,如果用户想要使用 NFS 做容器的持久化数据卷,那么 kubelet 就需要在容器进行绑定挂载前,在宿主机的指定目录上,先挂载 NFS 的远程目录。可是,这时候问题来了。由于现在 kubelet 是运行在容器里的,这就意味着它要做的这个“mount -F nfs”命令,被隔离在了一个单独的 Mount Namespace 中。即,kubelet 做的挂载操作,不能被“传播”到宿主机上。
对于这个问题,有人说可以使用 setns() 系统调用,在宿主机的 Mount Namespace 中执行这些挂载操作;也有人说,应该让 Docker 支持一个–mnt=host 的参数。但是,到目前为止,在容器里运行 kubelet,依然没有很好的解决办法,也不推荐你用容器去部署 Kubernetes 项目。
所以 kubeadm 采用的方式是将 kubelet 这个组件的二进制直接部署在宿主机上,而其他 kubernetes 组件则使用容器的方式部署起来。
3.5. 存在的问题
从上面我们可以看到,kubeadm 更多是将一个节点部署成 master 节点。所以说 kubeadm 欠缺的是一键部署一个高可用的 kubernetes 集群的能力,即 Etcd、Master 组件都应该是多节点集群,而不是单点的。
4. 巨人的肩膀
- https://kubernetes.io/zh/docs/reference/setup-tools/kubeadm/kubeadm-init/
- 极客时间,张磊,《深入剖析 kubernetes》
5. 实操-安装和使用
-
首先是替换 apt-get 源,可以给 apt-get 添加一个源;
-
下载 Docker 和 kubeadm(使用 apt-get 下载 kubeadm 的时候,kubeadm 往往已经包含了 kubelet 和 kubectl,假如没有这两个也需要下载);
-
关闭 swap;
-
获取镜像列表,预下载先,因为默认使用的镜像很难下载;
-
kubeadm init/kubeadm join;
-
配置授权信息,以便 kubectl 可以访问 kube-apiserver;
-
单节点,设置 master 节点也可以运行 Pod;
-
假如提示没有网络,那么需要额外安装网络插件,比如 CNI 插件安装包
具体的安装步骤可以参考下面这几个网址:
-
https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/
-
极客时间,《Serverless入门课》
6. 附: kubeadm/kubectl 使用
6.1. kubeadm 命令
# 创建一个Master节点
$ kubeadm init
$ kubeadm init --config kubeadm.yaml
# 将一个Node节点加入到当前集群中
$ kubeadm join <Master节点的IP和端口>
kubeadm join 10.168.0.2:6443 --token 00bwbx.uvnaa2ewjflwu1ry --discovery-token-ca-cert-hash sha256:00eb62a2a6020f94132e3fe1ab721349bbcd3e9b94da9654cfe15f2985ebd711
6.2. kubectl 命令
kubectl config get-contexts # 查看当前选中的集群
kubectl config use-context docker-desktop # 切换集群为 docker-desktop
kubectl describe node master
kubectl describe pod nginx-deployment-67594d6bf6-9gdvr
kubectl get nodes
kubectl get pods
kubectl get pods -l app=nginx
kubectl get pods -n kube-system
kubectl get all # 查看当前 k8s 环境的整体情况
kubectl get all -n kube-system # 安装好的 k8s 系统运行状况
kubectl get secret regcred
kubectl create secret docker-registry regcred --docker-server=registry.cn-shanghai.aliyuncs.com --docker-username=你的容器镜像仓库用户名 --docker-password=你的容器镜像仓库密码
kubectl exec -it nginx-deployment-5c678cfb6d-lg9lw -- /bin/bash
kubectl create -f nginx-deployment.yaml
kubectl apply -f nginx-deployment.yaml
kubectl delete -f nginx-deployment.yaml
kubectl delete pod/你的 pod 名字
kubectl label nodes 1.1.1.1 role=nginx
kubectl label nodes 1.1.1.1 role- # 删除一个Label,只需在命令行最后指定Label的key名并与一个减号相连即可
kubectl label nodes 1.1.1.1 role=apache --overwrite # 修改一个Label的值,需要加上 --overwrite 参数
6.3. 集群容器运行时清理
Containerd:
-
/var/lib/containerd — 清空
-
/var/run/containerd/containerd.sock — 清空
-
Containerd 二进制文件(相关的二进制文件)— 清空/覆盖
-
/etc/systemd/system/containerd.service — 清空/覆盖
-
/etc/containerd/config.toml — 覆盖
Docker
-
/var/lib/docker — 清空
-
/var/run/docker.sock — 清空
-
/var/run/dockershim.sock — 清空
-
Docker 相关二进制(也会包含 containerd 相关) — 清空/覆盖
-
/etc/systemd/system/docker.service — 清空/覆盖
巨人的肩膀
- 极客时间.《深入剖析Kubernetes》.张磊
- 《Kubernetes 权威指南》