2.1 利用kubeadm部署Kubernetes集群
Kubernetes系统可运行于多种平台之上,包括虚拟机、裸服务器或PC主机等,例如本地物理服务器、独立运行的虚拟机或托管的云端虚拟机等。若仅用于快速了解或开发的目的,读者可直接在单个主机之上部署伪分布式的Kubernetes集群,将集群所有组件部署运行于单台主机,著名的minikube项目可帮助用户快速构建此类环境。若要学习使用Kubernetes集群的完整功能,则应该构建真正的分布式集群环境,将Master和Node的相关组件分别部署在多台主机上,主机的具体数量按实际需求确定。
作为一个典型的分布式项目,Kubernetes集群的部署一直是初学者难以逾越的“天堑”。幸运的是,如今已然有多样化的集群部署工具可供选择,例如kubeadm、kops、kubespray和kind等,也有用户会选择使用Kubernetes的二次发行版,常见的如Rancher、Tectonic和OpenShift等。较简单的方式是,基于kubeadm一类的部署工具运行两条命令即可拉起一个真正的分布式集群;而基于二进制程序包从零开始手动构建集群环境则是相对困难的选择,它一般是熟悉使用Kubernetes系统以后的另一种常见选择。本节主要介绍如何利用kubeadm部署Kubernetes集群。
2.1.1 kubeadm部署工具
kubeadm是Kubernetes社区提供的集群构建工具,它负责构建一个最小化可用集群并执行启动等必要的基本步骤,简单来讲,kubeadm是Kubernetes集群全生命周期管理工具,可用于实现集群的部署、升级/降级及卸载等,如图2-1所示。在部署操作中,kubeadm仅关心如何初始化并拉起一个集群,其职责仅限于图2-1中的Layer2和Layer3中的部分附件,至于准备基础设施的Layer1和包含其他附件的Layer3则不在其职责之内。
图2-1 kubeadm功能示意图[1]
换句话说,kubeadm专注于在现有基础架构上引导Kubernetes集群启动并执行一系列基本的维护任务,其功能未涉及底层基础环境的构建,也不会管理如图2-1中的每一个附件,而仅仅是为集群添加最为要紧的核心附件CoreDNS和kube-proxy。余下的其他附件,例如Kubernetes Dashboard、监控系统和日志系统等必要的附加组件则不在kubeadm考虑范围内,这些附加组件由管理员按需自行部署。
kubeadm的核心工具是kubeadm init和kubeadm join,前者用于创建新的控制平面节点(见图2-2),后者则用于将节点快速连接到指定的控制平面,它们是创建Kubernetes集群最佳实践的“快速路径”。
此外,kubeadm token命令负责可管理集群构建后节点加入集群时使用的认证令牌,以供新节点基于预共享密钥在首次联系API Server时进行身份认证。而删除集群构建过程中生成的文件并重置回初始状态,则是kubeadm reset命令的功能。其他几个可用的工具包括kubeadm config和kubeadm upgrade等。这些工具随Kubernetes 1.13版发布时已正式进入GA阶段,其命令接口的使用、底层实现、配置文件模式、次要版本升级以及etcd存储系统部署设定等已成熟到可创建一致的Kubernetes集群,并能够支持多种多样的部署选项。
概括来说,使用kubeadm部署Kubernetes集群有以下几个方面的优势。
▪简单易用:kubeadm可完成集群的部署、升级和卸载操作,并且对新手用户非常友好。
▪适用领域广泛:支持将集群部署于裸机、VMware、AWS、Azure、GCE及更多环境的主机上,且部署过程基本一致。
▪富有弹性:支持阶段式部署,管理员可分多个独立步骤完成部署操作。
▪生产环境可用:kubeadm遵循最佳实践的方式部署Kubernetes集群,它强制启用RBAC,设定Master的各组件间以及API Server与kubelet之间进行认证和安全通信,并锁定了kubelet API等。
kubeadm并不仅仅是一键安装类的解决方案,它还有着更宏大的目标,旨在成为一个更大解决方案的一部分,试图为集群创建和运营构建一个声明式的API驱动模型,它把集群本身视为不可变组件,而升级操作等同于全新部署或就地更新。目前,kubeadm已经成为越来越多的组织或企业部署测试或生产环境时的选择。
2.1.2 集群组件运行模式
第1章中曾经讲到,Kubernetes集群主要由Master和Node两类节点组成:Master节点主要运行etcd、kube-apiserver、kube-controller-manager和kube-scheduler这4个组件,而各Node节点则分别运行kubelet和kube-proxy等组件,以及容器的运行时引擎。事实上,这些组件自身也是应用程序,除了kubelet和Docker等容器引擎之外,其他几个组件同样可以运行为容器。原因在于,kubelet运行在各Node之上,负责操作Docker这类运行时引擎,并管理容器网络和存储卷等宿主机级别的功能,而将kubelet运行于容器中显然难以完成其中的部分功能,例如管理宿主机的文件系统或Pod的存储卷等。
以集群的组件是否以容器方式运行,以及是否运行为自托管模式为标准,Kubernetes集群可部署为3种运行模式。
1)独立组件模式:Master各组件和Node各组件直接以守护进程方式运行于节点之上,以二进制程序部署的集群隶属于此种类型,如图2-3所示。
2)静态Pod模式:控制平面的各组件以静态Pod对象形式运行在Master主机之上,而Node主机上的kubelet和Docker运行为系统级守护进程,kube-proxy托管于集群上的DaemonSet控制器,如图2-4所示。
3)自托管(self-hosted)模式:类似于第二种模式,但控制平面的各组件运行为Pod对象(非静态),并且这些Pod对象同样托管运行在集群之上,且同样受控于DaemonSet类型的控制器。
使用kubeadm部署的Kubernetes集群可运行为第二种或第三种模式,默认为静态Pod对象模式,当需要使用自托管模式时,可使用kubeadm init命令的--features-gates=selfHosting选项激活。在独立组件模式进行集群的构建时,需要把各组件运行在系统的独立守护进程中,其间需要用到的证书及Token等认证信息也都需要手动生成,过程烦琐且极易出错;若有必要,建议使用GitHub上的适当的项目辅助进行,例如通过Ansible Playbook进行自动部署等。
2.1.3 kubeadm init工作流程
kubeadm拉起一个Kubernetes集群主要需要两个步骤:先在第一个Master主机上运行kubeadm init命令初始化控制平面,待其完成后,在其他主机上运行kubeadm join命令逐一加入控制平面,进而成为集群成员。
初始化一个控制平面需要一系列复杂的过程,kubeadm init命令将其分割成多个阶段逐一实现,例如环境预检(preflight)、启动kubelet(kubelet-start)、为集群生成所需CA及数字证书(cert)等10多个阶段,各阶段的描述可参考kubeadm init --help命令提供的配置信息。各阶段的简要说明如表2-1所示。
表2-1中的各阶段还可由kubeadm init phase [PHASE]命令来分步执行,或在必要时仅运行指定的阶段,例如kubeadm init phase preflight仅负责执行环境预检操作。此外,kubeadm init命令还支持众多选项设置其工作特性。
具体来说,针对表2-1中的各个阶段,kubeadm init命令通过以下过程拉起Kubernetes的控制平面节点。
1)执行由众多步骤组成的环境预检操作,以确保节点配置能够满足运行为Master主机的条件。其中,有些错误仅触发警告,有些错误则被判定为严重问题且必须纠正后才能继续。这些预检操作可能包括如下内容:
▪探测并确定可用的CRI套接字以确定容器运行时环境;
▪校验Kubernetes和kubeadm的版本;
▪检查TCP端口6443、10259和10257的可用性;
▪检查/etc/kubernetes/manifests目录下是否存在kube-apiserver.yaml、kube-controller-manager.yaml、kube-scheduler.yaml和etcd.yaml配置清单;
▪检查crictl是否存在且可执行;
▪校验内核参数net.bridge.bridge-nf-call-iptables和net.ipv4.ip_forward的值是否满足需求;
▪检查Swap设备是否处于启用状态;
▪检查ip、iptables、mount、nsenter、ebtables、ethtool、socat、tc和touch等可执行程序是否存在;
▪验证kubelet的版本号以及服务是否处于启用(enable)和活动(active)状态;
▪验证TCP端口10250、2379和2380是否可用,以及/var/lib/etcd目录是否存在与是否为空。
以上步骤出现任何一个严重错误都会导致kubeadm init命令执行过程提前中止,较为常见的严重错误有未禁用Swap设备、内核参数设定不当、kubeadm和kubelet版本不匹配等。希望忽略特定类型的错误时,可以为该命令使用--ignore-preflight-errors选项进行指定。
2)生成自签名的CA,并为各组件生成必要的数字证书和私钥,相关文件存储在由--cert-dir选项指定的目录下,默认路径为/etc/kubernetes/pki,相关的CA和证书如图2-5所示。指定目录路径下事先存在CA证书和私钥时,表示用户期望使用现有的CA,因而命令将不再执行相关的设定,而直接使用该目录下的CA。
3)Kubernetes集群的各个组件以API Server为中心完成彼此间的协作,这些组件间通信时的身份认证和安全通信一般借助第2步中生成的数字证书完成,但它们需要将相关的认证信息转换格式,并保存于kubeconfig配置文件中以便组件调用。kubeadm在kubeconfig步骤中生成如下4个kubeconfig文件。
▪admin.conf:由命令行客户端kubectl使用的配置文件,相应的用户(CN)会被识别为集群管理员。
▪controller-manager.conf:kube-controller-manager专用的kubeconfig配置文件。
▪kubelet.conf:当前节点上kubelet专用的kubeconfig配置文件。
▪scheduler.conf:kube-scheduler专用的kubeconfig配置文件。
4)control-plane阶段用于为API Server、Controller Manager和Scheduler生成静态Pod配置清单,而etcd阶段则为本地etcd存储生成静态Pod配置清单,它们都会保存于/etc/kubernetes/manifests目录中。当前主机上的kubelet服务会监视该目录中的配置清单的创建、变动和删除等状态变动,并根据变动完成Pod创建、更新或删除操作。因此,这两个阶段创建生成的各配置清单将会启动Master组件的相关Pod,且kubeadm成功对localhost:6443/healthz这个URL进行健康状态检查后才会进行后续的步骤。
5)等到控制平面的各组件成功启动后,会进入upload-config阶段。这个阶段会将kubeadm和kubeconfig的配置存储为kube-system名称空间中的ConfigMap资源对象。而upload-certs阶段需要显式地由--upload-certs选项激活,它会将kubeadm的证书存储在Secret资源对象中,以供后加入的其他Master节点使用。
6)mark-control-plane阶段负责为当前主机打上node-role.kubernetes.io/master=,将其标记为Master,并为该主机设置node-role.kubernetes.io/master:NoSchedule污点,以防止其他工作负载Pod运行在当前主机上。
7)bootstrap-token阶段会生成引导令牌,其他Node主机需要使用该令牌在加入集群时与控制平面建立双向信任。换句话说,只要持有Bootstrap Token,任何节点都可使用kubeadm join命令加入该集群。该令牌也可由kubeadm token命令进行查看、创建和删除等管理操作。
8)kubelet-finalize阶段进行一些必要的配置,以允许节点通过Bootstrap Token或TLS Bootstrap机制加入集群中。这类配置包括:生成ConfigMap,设置RBAC以允许节点加入集群时能获取到必要的信息,允许Bootstrap Token访问CSR签署API,以及能够自动签署CSR请求等。
9)最后一个阶段则是为集群添加必要的基本附件,如CoreDNS和kube-proxy,它们都以Pod形式托管运行在当前集群之上。
上述过程的简要执行流程如图2-6所示。基于二进制程序包以手动方式部署Kubernetes集群时,这些步骤和流程都需要逐步调试实现,使得许多人或望而生畏或浅尝辄止。
2.1.4 kubeadm join工作流程
由kubeadm init命令为构建的目标集群初始化第一个控制平面节点之后即可在其他各主机上使用kubeadm join命令将其加入集群中。根据用户的设定,新加入的主机可以成为新的Master主机(Master HA模式中的其他节点),也可以成为Worker主机(工作节点)。
kubeadm join命令也需要进行环境预检操作,以确定所在节点满足可加入集群中的前提条件,这类关键检测的步骤包括:
▪探测并确定可用的CRI套接字以确定容器运行时环境;
▪检查/etc/kubernetes/manifests目录下是否存在且是否为空;
▪检查/etc/kubernetes目录下kubelet.conf和bootstrap-kubelet.conf文件是否存在;
▪检查crictl是否存在且可执行;
▪校验内核参数net.bridge.bridge-nf-call-iptables和net.ipv4.ip_forward的值是否满足需求;
▪检查Swap设备是否处于启用状态;
▪检查ip、iptables、mount、nsenter、ebtables、ethtool、socat、tc和touch等可执行程序是否存在;
▪验证kubelet的版本号以及服务是否处于启用和活动状态;
▪检查TCP端口10250的可用性;
▪检查文件/etc/kubernetes/pki/ca.crt是否存在。
以上步骤出现任何一个严重错误都会导致kubeadm join命令执行过程的中止,较为常见的严重错误有未禁用Swap设备和内核参数设定不当等,不过--ignore-preflight-errors选项允许用户指定要忽略的错误选项。
随后,同控制平面建立双向信任关系是新节点加入集群的关键一步,这种双向信任可分为发现和TLS引导程序两个阶段。发现阶段是让新节点通过共享令牌(Bootstrap Token)向指定的API Server发送请求以获取集群信息,Bootstrap Token可让节点确定API Server的身份,但无法确保信息传输过程的安全,因此还需要借助CA密钥哈希进行数据真实性验证。TLS引导程序阶段是让控制平面借助TLS Bootstrap机制为新节点签发数字证书以信任请求加入的节点,具体过程是kubelet通过TLS Bootstrap使用共享令牌向API Server进行身份验证后提交证书并签署请求(CSR),随后在控制平面上自动签署该请求从而生成数字证书,如图2-7所示。
提示
发现阶段也可使用kubeconfig格式的引导配置文件进行,该配置文件可位于本地文件系统,也可通过URL获取。
接下来的过程会因新加入节点的不同角色而有所区别。kubeadm将为加入集群主机的工作节点基于接收到的数字证书生成kubelet.conf配置文件,这样kubelet程序便能够以该配置文件接入到集群当中并进行安全通信,再由kube-proxy的DaemonSet控制器为此节点启动kube-proxy Pod,至此,该过程完成。
若加入的是其他Master主机,kubeadm join则会进行以下步骤。
1)从集群下载控制平面节点之间共享的数字证书,该证书在配置第一个Master的kubeadm init命令中的upload-certs阶段生成。
2)运行kubeadm init命令类型的预检操作,而后为新的控制平面各组件生成静态Pod配置清单、证书和kubeconfig后,再由kubelet启动相应的静态Pod。
3)更新kubelet的配置信息并由其完成TLS Bootstrap。
4)为本地运行的etcd生成配置清单,由kubelet启动相应的静态Pod并将其添加为现有的etcd集群成员。
5)将该节点的相关信息上传至kube-system名称空间中的ConfigMap对象的kubeadm-config中。
6)为该节点添加控制平面专用标签node-role.kubernetes.io/master=''和污点node-role.kubernetes.io/master:NoSchedule。
在不同的节点上重复运行本节中描述的步骤,即可多次新增Master或Node主机到集群中。
2.1.5 kubeadm配置文件
初始化集群的kubeadm inti命令也可通过--config选项让配置文件接收配置信息,它支持InitConfiguration、ClusterConfiguration、KubeProxyConfiguration和KubeletConfiguration这4种配置类型,而且仅InitConfiguration或ClusterConfiguration其中之一为强制要求提供的配置信息。
▪InitConfiguration提供运行时配置,用于配置Bootstrap Token及节点自身特有的设置,例如节点名称等。
▪ClusterConfiguration定义集群配置,主要包括etcd、networking、kubernetesVersion、controlPlaneEndpoint、apiserer、controllerManager、scheduler、imageRepository和clusterName等。
▪KubeProxyConfiguration定义要传递给kube-proxy的自定义配置。
▪KubeletConfiguration指定要传递给kubelet的自定义配置。
下面给出了一个完整的ClusterConfiguration的配置框架及各字段的简要说明,为了节省篇幅,省略了controllerManager和scheduler中也可以额外配置的存储卷。
apiVersion: kubeadm.k8s.io/v1beta2 kind: ClusterConfiguration etcd: local: # 堆叠式etcd拓扑,与external字段互斥 imageRepository: "k8s.gcr.io" # 获取etcd容器镜像仓库 imageTag: "3.4.3" # etcd容器镜像的标签 dataDir: "/var/lib/etcd" # 数据目录 extraArgs: # 额外参数,例如监听的URL和端口,可省略 listen-client-urls: https://172.29.9.1:2379 serverCertSANs: # 服务器证书的Subject列表 - "k8s-master01.ilinux.io" peerCertSANs: # 集群成员内部通信的对等证书Subject列表 - "172.29.9.1" # external: # 外部etcd集群拓扑,与local互斥 # endpoints: # 外部etcd集群的成员端点列表 # - "10.100.0.1:2379" # - "10.100.0.2:2379" # caFile: "/etcd/kubernetes/pki/etcd/etcd-ca.crt" # CA的证书 # certFile: "/etcd/kubernetes/pki/etcd/etcd.crt" # 客户端证书 # keyFile: "/etcd/kubernetes/pki/etcd/etcd.key" # 证书配对的私钥 networking: # 网络定义 serviceSubnet: "10.96.0.0/12" # Service网络地址 podSubnet: "10.100.0.0/24" # Pod网络地址 dnsDomain: "cluster.local" # 集群域名后缀 kubernetesVersion: "v1.19.0" # Kubernetes自身的版本 controlPlaneEndpoint: "k8s-api.ilinux.io:6443" # 控制平面端点 apiServer: # 配置API Server extraArgs: # 额外指定的参数 authorization-mode: "Node,RBAC"# 支持的授权机制 cluster-signing-cert-file: /etc/kubernetes/pki/ca.crt # 激活内置签名者 cluster-signing-key-file: /etc/kubernetes/pki/ca.key extraVolumes: # 用到的存储卷,可省略 - name: "some-volume" hostPath: "/etc/some-path" mountPath: "/etc/some-pod-path" readOnly: false pathType: File certSANs: # API Server服务器端证书的Subject列表 - "k8s-master01.ilinux.io" timeoutForControlPlane: 4m0s controllerManager: # 控制器管理器相关的配置 extraArgs: "node-cidr-mask-size": "24" # 为节点分别配置podCIDR时使用的掩码长度 scheduler: # 调度器相关的配置 extraArgs: address: "127.0.0.1" # 监听的地址 certificatesDir: "/etc/kubernetes/pki" # 证书文件目录 imageRepository: "k8s.gcr.io" # k8s集群组件镜像文件仓库 useHyperKubeImage: false # 是否使用HyperKubeImage clusterName: "example-cluster" # 集群名称 : pod-with-
若默认值符合配置期望,上面这些ClusterConfiguration的配置参数中的大多数字段都可以省略,而且通过kubeadm config print init-defaults命令也能打印出默认使用的配置信息。
[1] 图片来源:https://asksendai.com/