天天看点

如何更“优雅”地部署Kubernetes集群

前言:Kubernet es的出现使得容器系统的调度管理变得简便易行,但是它自身所带的“部署难”又另千万开发者望而却步,因此该技术的推广也在一定程度上受到了限制。10月21日,在七牛云&K8S技术社区联合举办的架构师实践日上,七牛云容器云团队开发工程师 赵梓旗,由浅入深,从kubernetes本身架构出发,为大家讲解了如何从零开始部署一个完整的kubernetes工作集群,并对官方部署工具kubeadm的基本原理进行解析。结合部署时所遇到的难题,分享了解决心得。最后还分享了集群搭建经验,帮助大家更快更好的实现集群的高可用以及一键升级。以下是对他演讲内容的整理。

今天分享的主题是《如何更“优雅”地部 署Kubernetes集群》,我主要针对这些方面给大家介绍:

1、介绍Kubernetes部署方案的发展历史;

2、Kubeadm部署工具如何部署Kubernetes集群;

3、解析Kubeadm背后做的事情;

4、我们在使用Kubeadm部署集群的时候遇到的常见问题;

5、使用Kubeadm的技巧。

我们主要是针对Kubeadm为大家做介绍,后面会给大家介绍我们为什么要选择这个工具。

K8S是非常出名的容器调度平台,很多人都在想使用它,对新手 来说想要搭建一个完整的K8S集群还是比较难的,至少我们在使用过程中还是比较难的,这也是K8S的历史遗留问题。追溯K8S历史,它的第一个部署方案是现在在它的官方 repo上还存在的kube-up.sh脚本文件,它 声称能够一键部署一个K8S集群,但是在使用过程中 我们发现还是有很多问题。比如对它进行初始化的流程就很复杂,而且对用户不友好。另外它的功能随着K8S不断迭代变得很庞杂。最重要的一点是 通过shell进行编写,对shell不太熟的小朋友,在部署的时候会非常麻烦,出现问题定位也很困难。正是由于这个问题,社区当中也涌现出来非常多自研的部署方案。

图 1

这是我截的K8S官方的文档收录自研部署方案的数量,可以看到它部署难的现状。 当然经过这么长时间的迭代,还是涌现出来一些比较成熟的方案,直到现在我也是非常推荐给大家使用的,这里给大家列了比较出名的三个:

https://github.com/coreos/tectonic-installer

https://github.com/kubernetes-incubator/kubespray (Ex Kargo)

https://github.com/apprenda/kismatic

即便是这些方案经过了时间的考验,他们也有各自的问题。我认为他们有三点问题:一是有些部署方案的使用学习曲线高。二是有些部署方案的灵活性非常有限,如果想针对我的集群,有些特殊的地方想自定义的配置,他们可能无法实现。三是社区力量有限, 对于新功能的支持,他们开发的速度和支持的成熟度都不是特别可观。

在这里为大家介绍 Kubeadm 这样一个工具。主要基于以下几点原因推荐:

1、它是K8S官方推荐的下一代部署管理工具,通过K8S官方对这个工具的定位可以看出整个社区对K8S的部署有些新的思考。K8S集群对底层物理机的提供方式不做任何假设,只要满足我们的要求,无论是物理机还是虚拟机都是可以的。 所以K8S的部署工具应该更加关注K8S组件的部署,而不是所有的事情都涵盖进去,包括怎么提供主机。Kubeadm出现的目标是只专注于K8S组件的部署。所以官方也说Kubeadm不是kube-up.sh的2.0版本,而是更 专注于K8S组件的部署。正是由于它定位更加清晰,所以它的发展更加健康。

2、正是由于它功能更清晰才使使用方式更加友好,至于有多友好,一会儿给大家介绍。

3、配置的灵活性很高,可以针对不同的集群需求,无论测试集群、生产集群还是虚拟机、云主机,都能提供非常丰富的配置。一套部署方案适用于你任何一个环境。

4、Kubeadm背后的社区力量非常雄厚,我们都知道K8S社区是分为很多的兴趣小组,叫SIG,每个兴趣小组都有自己针对研究的主题,Kubeadm背后也有一个专门的兴趣小组是专门针对这个 工具进行维护和开发,他们有非常严格的开发流程和开发规范,而且这个兴趣小组的开发人员都是K8S的核心人员,所以他们对于整个K8S的底层、架构、机制都是非常了解的,由他们开发出来的部署工具在很大程度是非常可靠的。

5、Kubeadm被K8S官方全面的e2e测试涵盖进去,这个工具 会经过严格的测试。

6、Kubeadm 的话语权,Kubeadm是K8S官方 推荐,也就是他的亲儿子,它可以针对自己的要求向K8S提要求,K8S社区也会讨论他的需求是否合理,也会针对这些需求对自己的特性进行改变或者研发新的特性。这种 话语权,也是它领先其他社区方案的重要一点。

正是由于这些原因,基本上所有的第三方知名的社区方案都已经承认了Kubeadm的重要性,也想办法以Kubeadm作为自己的基础部署工具,进行上层的二次开发。 另外,我个人认为Kubeadm的代码逻辑非常清楚,我在使用它的过程中,通过它的代码了解了很多K8S部署和K8S内部机制的知识,既能够提升我们对K8S的了解,也方便我们出现问题的时候定位是什么问题。

说了很多我选择这个工具的理由,下面介绍一下它是如何部署K8S的。在介绍部署流程之前先为大家简述下K8S的架构,K8S的集群大家都知道是有两种角色,Master节点和Workers节点。Master节点主要是负责集群的调度、集群的管理,Workers负责启动各种各样的容器。无论你是什么样的节点,你只要想启动容器,都必须安装Kubelet组件,当然你在底层要启动容器,还要安装Docker这个组件。

图 2

在Master的节点上还要运行集群调度的组件,主要是这四个(上图蓝色的四个部分),他们分别是APIServer、Etcd、Scheduler、Controller-Manager。APIServer的功能就是作为整个集群的服务器,接收来自两方面的请求,一方面是来自集群内部的,包括这些组件,Scheduler、Controller-manager发送给它的请求。另一方面也接收来自外部用户通过Kubectl向它发出的请求。这些请求都是对K8S里面的对象的增删改查。最后这些对象的信息都是存储在ETCD当中的。还有一个组件是Controller-Manager,Controller-Manager主要是用来管理K8S当中各种各样的对象。下一个 组件是Scheduler,它负责对Pod进行调度,当创建一个新的Pod的时候,Scheduler会根据集群资源消耗的状态选择最合适的物理节点把它调度上去,调度上去之后,就由那个节点上的Kubelet来启动这个Pod。这些是跟Master相关的组件。另外还有一个组件非常关键,是 Kube-proxy,它实现的是K8S的service的概念,service是对功能类似的Pod的封装,我想要访问功能类似的三个Pod,我可以管理他们的service就可以了。如果想要实现这个,我们的 Kube-proxy会在每个物理节点上都部署一个,为每个物理节点书写一系列的iptables规则,这个节点上的Pod如果想要访问service的IP,他的请求会被这些规则修改,把请求的目的地从service的IP修改Pod的IP。还有一个比较关键的组件是网络组件,为了实现集群Pod和Pod为之间的网络通信,网络之间的选择方案有很多,相信大家了解过。这样就构成了整个K8S的集群。

介绍完架构之后,我们看一下Kubeadm这个工具到底如何部署可用的K8S集群的。它大概分为五个步骤:

1、准备正确配置的机器,K8S对底层机器只有三点要求:一是正确的操作系统,Ubuntu 16.04 / Debian9/CentOS 7/RHEL7/Fedora 25 / HypriotOS v1.0.1 其中一个都可以。比较常见的是Ubuntu或者CentOs。二是硬件配置要求你至少有1GB的内存。三是网络上要求集群里的节点之间网络互通。只要满足这三个要求,无论是虚拟机、物理机还是云主机都是可以的。

2、准备好机器之后要安装必要的软件,这里主要都是通过deb包或者rpm包来进行安装的。主要包括这几个比较重要的软件:一是Docker,K8S官方给出的推荐,如果集群是1.8版本之前的,建议你采用1.12.06 Docker版本,如果K8S集群是1.8版本或者1.8版本之后的就采用比较新的1.17.03这个版本。安装Docker之后还要安装Kubelet组件,安装之后,它通过操作系统的systemd进程管理软件来进行管理的,并且设计成开机自启动,在一定程度上保证 Kubelet组件的可用性。自然,要用Kubeadm来部署,在每个机器上还要安装Kubeadm,Kubectl也是,我们也会把它进行安装。这步完成之后就可以保证每个节点安装这几个组件。

3、启动Master节点, 在Master节点的机器上运行这样一条命令,kubeadm init,这个命令帮助你启动跟Master相关的组件,还有刚才介绍的四个APIServer、Etcd、Scheduler、Controller-Manager,而且还会顺便启动Kube-proxy这个组件,还有Kube-DNS,它不是工作集群的必备组件,但是有这个 组件就可以让集群具备DNS的服务。这步完成之后,标准输出会输出这样一条命令,叫做kubeadm join -- token xxxxxx master _ip;master _port,这个token比较关键,是我们后面为了安全的添加一个节点到集群当中必要的信息,而且会提示你这个信息一定要保存好,只有信任的人才能拿到。它会告诉你,如果你想加一个新的机器在集群当中,只要在这个机器上运行这条命令就好。

4、我们的下一步就是到worker节点上去运行这样一条命令,这样就构成了基础的集群。但是这个集群现在是残废的集群,因为没有网络组件,你是无法真正工作的。这时候你看它的状态,所有的节点都是处于没有准备好的状态。

5、下一步我们要部署整个网络。这是K ubeadm比较局限性的地方,它不关心用户到底采用什么样的网络方案,这是经过思考做出的决定。 社区认为关于网络方案的选择,以及网络组件的部署应该交由用户决定,所以kubeadm不会做任何支持。比较幸运的是这些比较知名的网络方案都已经能够跑在K8S上,跑在K8S上就代表可以把他们写成一个yaml文件部署到集群中。比较典型的是calico这样的方案,官方已经提供了完全不用修改的配置,可以直接放到集群当中,最后构成完整的集群。

这是Kubeadm部署K8S集群的大致流程。它整个只需要五步,而且这些命令和他要做的事情都是相对应的,比如我要初始化一个集群那就是kubeadm init,所以我说这个工具是非常友好工具非常重要的原因。

部署好的这个集群具备什么样的特性呢?1、这个集群工作在安全模式下,安全模式含义是所有组件之间的通讯都是通过TLS加密,任何想和这个集群通讯的用户或者组件都必须配置一个kubeconfig认证才可以。2、这个集群所有的重要组件,包括Master相关的组件都是容器化部署的。3、集群只有单个Master节点。4、自带Kube-dns组件。

上面为大家介绍了如何通过Kubeadm来部署一个集群。下面为大家介绍的是 Kubeadm背后到底做了什么?

我们看Kubeadm搭建整个集群好像很容易,但是使用过程中可能还是会遇到一些问题,有些问题是因为我们在中国,有些问题是我们集群的配置有些不太对。当时我也是研究了Kubeadm到底背后做了什么,这让我收获很多,对排查问题很有帮助。

主要是两个步骤:一是kubeadm init,还有一个是k ubeadm join。init是初始化一个集群,首先它会对机器进行前期检查,比如我们要启动K8S的组件,它的端口是否被其他进程占用,有些cgroups特性是否正确配置等等。有些错误会导致Kubeadm无法继续执行,需要手动修复才能继续运行。检查通过之后就会生成token,token是说后来加入的新节点要输入的token,它到底怎么完成呢?工作原理是怎么样呢?这个后面为大家介绍。

图 3

到底如何实现安全的K8S环境?这是很多社区方案没有为大家很好解决的事情。K8S工作的模式有点类似于CS的模式,可以看到图中打领带的小人代表为整个APIServer,它是作为整个集群的服务器来为不同的客户端服务的,包括左下角的集群内部的组件,比如Kubelet、Control-manage r这种组件,对他们提供服务,还有集群外部使用Kubectl的用户来提供服务。如果想实现K8S的安全集群,首先客户端访问服务端的时候,要对服务端要进行认证,所采用的方案是最经典的服务器认证机制,要求我们的服务端要去一个比较知名的证书中心颁发证书给我,这些客户端才用这些证书中心的根证书来验证服务器证书,才知道服务器是否是真实可信的服务器。这种方案从成本上肯定不可行,公司肯定有几个集群,而且我笔记本还有一个集群,不可能给每个集群买证书。Kubeadm也考虑到这个问题,它采用的方案是自签名的证书中心,它创造虚拟的证书中心,只在集群内部可用,这个证书中心会给APIServer签发证书,并且把根证书通过kubeconfig文件的方式分发到想和 APIServer通讯的客户端这里。这个客户端如果想要和Server通讯的时候,他会通过手中握有的证书来验证APIServer的证书,验证通过,就代表APIServer是真实可信的,大概是这样的流程。建立好认证的过程,我们后续就会采用比较经典对称加密的机制来加密他们之间的数据传输。这步要做的事情是建立客户端对服务端的信任,安全包括两方面,建立客户端到服务端的信任。

图 4

客户端信任服务端,服务端也要信任客户端。这要给当前想给APIServer通讯的组件都颁发一个Kubeconfig文件,这就涉及K8S认证鉴权机制,我不知道大家对这个机制是否有深入的了解,你要真正搭建起生产上能用的集群,这是你必须考虑的一部分。K8S认证鉴权机制是分为三个步骤,可以在上图中看到,这三个步骤有列出来,任何客户端想发送请求在APIServer的时候,在这之前它一定会经过三重检查,这重检查分别是:认证、 鉴权和访问控制。这三个检查做的事情我分别用一句话为它总结一下,相当于这个客户端想要发送请求就要回答这三个问题,一是客户端是谁,是否是我认识的人;二是你这个人是否有在请求当中想要做的事情的权利,是否有权利做你想要请求的事情;三是你要请求做的这件事情肯定要设置一些参数,设置的参数是否合理, Kubeadm 在第四步要给每个客户端签发kubeconfig就是为了客户端回答你是谁的问题。这一步和上一步的机制很像,我们上一步构造虚拟的证书中心,不仅给APIServer颁发服务器的证书,还给每个客户端颁发客户端的证书,这个证书可以让 APIServer认证你这个人是谁,这建立起从服务端到客户端的信任。这块需要颁发证书的客户端,包括Kubelet、Controller-Manager、Scheduler ,还包括我们后面自己要用Kubectl创建集群,我们还会给管理员去创建kubeconfig的文件。

启动和Master相关的组件,图中看到的Etcd、APIServer、Controller-Manager、Scheduler。它启动的方式是基于static pod的方式,这种方式是把这些组件的yaml文件写到 /etc/kubernetes/manifests目录下,Kubelet会周期性轮巡这个目录下面的文件,如果这个目录下面创建新文件 ,就根据文件的要求在这个节点上启动一个Pod。跟Master相关的组件都是通过static pod来启动的。它启动完这个文件之后,Kubeadm就会不停的轮巡api server的健康接口,直到健康接口返回200才会认为这个集群的重要组件都已经启动了。这是我们在使用过程中经常出现问题的一个地方,大家运行到这个地方就卡住了,一直在等。我们后面会具体探讨这块经常出现哪些问题。

图 5

上一步如果通过的话,我们就会对集群进行后续的初始化工作,包括给Master节点加上一些Label和Taint,保证和Master相关的pod 才会部署到这个节点上。

创建Kube-proxy和Kube-dns这两个组件。

为了后续添加新 的节点,并且是以一种更安全的方式进行添加。 Kubeadm希望建立一种双向的信任,像图中表示的一样,左边表示的是现在已有的集群,右边代表的是新添加的节点,建立双向的信任代表已有的集群要信任新添加的节点,是可信的、真实的。另外新添加的节点也要相信自己加入的集群是可信的集群,这种构建双向信任的基础是我们刚才的token,所以token一定要保存好, 保证只有可信的人才能知道token,因为它是构建双向信任的基础。由于它和后面Kube adm join的命令非常相关,所以我一起为大家介绍。

建立第一个方向的信任, 即新添加的节点如何相信自己加入的集群是可信的集群?这个方向的建立是通过Kubeadm建立的Cluster-info的公共信息,Cluster-info其实就是一个configmap的对象,这里面主要存放 三个信息。第一个信息是虚拟证书中心的根证书,第二个是和集群相关的信息,比如APIServer的地址在哪里。 第三个信息需要我们把这两个信息放在一起,通过只有我们信任的人知道的token对这个信息做一下哈希,把这三个放在一起构成Cluster -info,Cluster-info是完全对外暴露的,也就是说任何人不用提供任何认证信息就可以拿到这个东西。与之相应的,Kubeadm join命令是先获取 Cluster-info信息,只有我们相信的人知道token,它可以用token验证Cluster -info是否可信,这样构建从新添加的节点到集群方向的信任。

第二个方向,如何让集群信任我添加的节点是真实的节点? 也是基于刚才说的token机制。它实现方式是会把token通过一种方式保存到集群中,并且开启认证机制,最后的效果是任何客户端在发送请求给APIServer,如果请求的头部带了token信息,都会被 APIServer认证为有效用户,这样其实就构建了集群对于worker的信任。这种双向的信任建立成功之后,Master节点就会给新添加的节点颁发一个可用的kubeconfig文件,新添加的节点可以用这个文件把自己加入集群中了。

使用Kubeadm工具中遇到的问题

1、安装包无法翻墙的问题,个人用户可能都会遇到。刚才我们介绍的工具Kubeadm、Kubelet、Kubectl都是通过deb包和rpm包安装的,但是官网只提供了Google安装源来进行安装,在我们国内是无法安装到的。给大家推荐中科大镜像源,这个源里面提供了和Google官方同步的安装 源,所以可以在里面体验到最新的K8S版本。

2、Docker镜像源无法下载的问题,这也是我们国内用户会遇到的。在Kubeadm初始化集群过程当中,它会下载很多的镜像,如果你不对它进行任何配置,它都是对Google官方镜像源进行下载,我们国内用户又是无法体验的。所以我们需要做两件事:一是找到国内和官方保持同步的源,这里为大家推荐阿里云的源。二是告诉Kubeadm到这个源下载 。通过给Kubeadm配置环境变量 KUBE_REPO_PREFIX,可以让它在这个源里下载所有的镜像。但是有一个例外是pause镜像, 当K8S每启动一个pod,不仅启动用户指定的镜像,还会启动一个pause镜像, 为用户镜像做初始化工作,它默认的下载地址是Google官方的地址。而且这个地址即便配置了Kubeadm的环境变量也不会对它产生影响,还是在这里进行下载。因为它其实是Kubelet的一个参数,所以我们需要修改Kubelet的参数才能让它从国内的镜像源下载,这个参数就是 。如果大家想在国内体验,一定要注意这点。这点没有修改成功,会导致集群无法启动。

3、APIServer参数 --advertise-address判定的问题,这个参数是用来指定APIServer 和其他组件之间通讯的地址或者它的监听地址。它默认的判定方法会选取机器默认的网卡ip地址,这 一点有时并不合适,因为我们在自己使用过程中会有这样的场景,我们有些机器可能默认网卡是 公网网卡,我们希望集群内部组件相互通讯的东西走内网流量,所以这个时候,我们还是采用这种默认的判定方法,就会让APIServer 绑定公网网卡。如果你的环境是这样的配置,建议你用这样的方式绑定到内网网卡。Kubelet也有这样的参数,叫做--node-ip ,在这样的场景下也会默认绑定在公网网卡上,如果你的环境也是这样子的,建议你用命令行的方式把内网地址 配置进去。这个问题的发现源于我们自己的使用实际场景,我们想通过虚拟机管理 软件Vagrant来部署集群的时候会出现一些问题,因为它为每个虚拟机创造一个nat网卡作为默认网卡,它的ip地址固定,如果创建两个虚拟机,这两个虚拟机默认网卡都是一个地址 。如果这个时候还是采用这种方式来指定,采用默认策略就会导致所有pod地址都是那个nat 网卡地址。K8S官方也给出一个方案,如果大家用这个部署集群的需求,要注意这个问题。

4、DNS Service IP和Kubelet --cluster-dns参数不匹配的问题。Kubeadm默认创建两个service,一个service是用来暴露 APIServer服务的,另外一个service是用来暴露Kube DNS的服务的,这两个服务地址不是随机生成的,是有一定规则的。Kubeadm 首先会指定一个service ip的范围,默认值是10.96 .0.0/16,根据这个范围就可以按照一定的规则生成两个地址。APIServer 服务的地址会取这个网段的第一个地址,也就是10.96.0.1,Kube dns更加死板,会把最后0前面加一个1,就作为service ip, 即10.96.0.10,而且不能修改配置方式。所以这是一个默认的规则。Kubelet还有一个参数叫-- cluster-dns,每当Kubelet启动一个pod的时候,利用这个参数昨 为这个容器的nameserver。也就是说这个容器如果它想要解析域名 的时候,它的DNS服务的nameserver是 什么。如果你想采用集群里面DNS服务,必须指定为Kube DNS 的 server ip,默认的情况,它配置的就是10.96.0.10,如果采用默认配置不会发现任何问题,集群工作好好的。但是一旦在生产环境中,比如你的环境,需要更换service ip 的范围,它会新生成新的Kube- DNS的ip,如果不及时同步,会导致你启动的pod无法使用这个DNS 的服务。这是我们当时遇到的问题,希望大家之后避免。

最后介绍使用Kubeadm的技巧:

图 6

Kubeadm 带有一个参数—config参数,我们可以给它传递一个yaml文件,yaml文件中描述了K8S里面的一种对象,叫做MasterConfiguration。 上图中我只是截了这种对象的描述信息的一部分,它提供了对集群非常丰富的 自定义配置项,你可以针对这个集群,这个集群的情况对它进行自定义的配置,比如想安装的 K8S版本,service ip的范围等等。如果大家使用这个工具,要灵活使用这样的文件。 下面介绍两个常见的使用实例。

1、有时候用户想体验K8S的新特性,一般采用新特性需要修改像APIServer等这种Master组件的启动的命令行参数,但是用Kubeadm启动的 Master组件默认情况有可能不会加上 我们想要的参数,那么如果我想 用的话,可以在MasterConfiguration里面去找a piServerExtraArgs参数,可以把对APIServer 额外的参数写到里面,这样启动的集群就把这些参数会带进去。同理Controller –manager和Scheduler也提供你想要的参数。

2、通常情况下,我们的集群都是内网集群,只暴露一个公网ip,我希望通过本地访问这个集群,这时候只能通过公网ip访问。 这时候应该怎么配置呢?我们一步一步的思考,刚才我们说了,Kubeadm在初始化的时候会为管理员生成一个 Kubeconfig文件,把它下载下来 是不是就可以?事实证明这样不行, 因为这个集群是内网集群,Kubeconfig文件 中APIServer的地址是内网ip。所以无法访问到。那么我把内网ip改成 APIServer公网ip是不是就可以了 呢?经过实验发现也是不可以的,会报 出认证无法通过的错误。为什么认证无法通过?这要回顾我们刚刚讲过的服务端认证的流程,刚才说一个客户端想跟APIServer访问 时,要拿服务器证书,进行解析,去看APIServer都有哪些别名,再把客户端访问APIServer时所采用的ip 或者域名和别名相比较,看是否已经涵盖在别名里面了。如果涵盖进去,我就认为这个server是可认证的。由于我们在这个场景部署出来的集群Kubeadm生成 APIServer证书不会把公网ip写到证书里,所以 导致用公网ip访问不通过验证。解决方案很简单,把公网ip签到证书里面就可以,所以这个yaml文件也给我们提供了这样一个选项,叫apiServerCertSANs这个选项,只要把公网IP写到这里,再启动这个集群的时候,这个证书就可以有这个公网ip,我就可以使用刚才我说的流程,把文件下载下来把APIServer的地址改成公网ip,然后就可以访问了。这是我工作当中非常常见的需求,这样会让你的开发工作更加方便一些。

3、下面我们再考虑一个常见的需求,刚才我们说的Master组件是通过static pod形式来创建的,如果现在一个集群正在运行中,我不能重新部署集群。还希望给Master组件,比如APIServer添加一个命令行参数,怎么办?只能对它进行动态更新。我们尝试了很多种方式,发现只有这样一种流程才能成功更新:首先先把APIServer的yaml文件从/etc/kubernetes/manifests目录下移出去,然后改好再移回来,才能实现更新。我们调研了K8S官方的说法,这是由于Kubelet的代码逻辑导致的,但是K8S官方认为static pod并不是会长期存在的形式,以后会越来越少用这种特性,所以也不会针对这种问题修改代码。如果大家还使用static pod的方式管理你的容器,就只能使用这种方案,没有办法解决。

本文转自掘金-

如何更“优雅”地部署Kubernetes集群

继续阅读