超详细的腾讯云 Kubernetes 一键部署实践

阅读数:67 2019 年 10 月 29 日 15:35

超详细的腾讯云 Kubernetes 一键部署实践

很多人在实际工作中都使用过 Kubernetes,我们的容器服务在 2016 年年底开始提供全托管的 Kubernetes 服务,主要提供了四个方面的功能。首先是提供了一键部署的 Kubernetes,与其他容器服务的提供商不一样,我们的 Kubernetes 是完全隔离的,每个用户都会独享所有的计算节点和控制节点,集群网络也在用户自己的 VPC 中。

我们在这个基础上提供了集群的全生命周期管理,包括集群的创建、销毁,还有计算节点的添加、删除,还有一些类似 Kubernetes 原有组件的初始化以及证书的初始化工作。为了大家更方便地使用 Kubernetes,我们在控制台包装了一些界面,使大家可以通过可视化的方式创建一些负载来暴露自己的服务,避免了大家手工编码的烦琐。

第三,我们提供了周边的监控能力,包括集群本身 pod 内存的使用率以及一些 Kubernetes 事件。这些能力都与腾讯云的云监控产品进行了打通,大家可以直接在云监控产品界面使用这些能力。为了方便大家将自己的一些比较传统的应用部署到云上,我们在 Kubernetes 集群之外还提供了 Docker 镜像仓库、TencentHub、CICD 的功能,为大家提供了一站式应用的云解决方案。

我今天所讲的内容,一方面是介绍 Kubernetes 相关的知识,另一方面是如何将 Kubernetes 和腾讯云进行集成。首先带大家了解一下 Kubernetes 集群需要哪些组件,将 Kubernetes 部署好之后还要做什么工作使它正常运行。之后会介绍云上 Kubernetes 上的网络存储和日志与监控,这是将 Kubernetes 和腾讯云集成时进行的工作。

最后介绍 Kubernetes 的两种部署方案,一种是容器服务产品发展早期所采用的一种基于 CVM 的方案,另一种是最近逐渐部署的使用 Kubernetes 集群来管理 Kubernetes 集群组件的方案。随着容器服务产品以及 Kubernetes 社区的发展,在我们提供托管的 CCS 服务通过了 CNCF 的 K8s 一致性验证之后,为了大家更好地认知腾讯的容器服务产品,产品的名字从之前的 CCS 正式改名为 TKE。

超详细的腾讯云 Kubernetes 一键部署实践

Kubernetes 组件

接下来我们看一下想要让 Kubernetes 集群运行起来我们要做哪些工作。如果对 Kubernetes 有了解的同学,可能知道使 Kubernetes 跑起来,无非是初始化一些 Master 组件和 Node 组件。首先给大家介绍 Master 组件,一个最简单的部署要包括 Kube-apiserver、Kube-contioller-mannager 、kube-scheduler 这些组件。Kube-apiserver 可以理解为整个集群的大脑或者是集中存储,所有组件与 k8s 的交互都是通过 Kube-apiserver 来完成的。其中存储着我们定义的一些工作负载以及我们对于存储的一些需求,这些数据都存储在 Kube-apiserver 中。

第二个组件 Kube-contioller-mannager 主要负责将声明的一些工作负载在实际的集群中运行起来,举个最简单的例子,当我们在 Kubernetes 上创建了一个 deployment 之后,Kube-contioller-mannager 就会去创建相应的 replicaset、pod 这些。根据需求将 pod 创建出来,在创建 pod 后,Kube-scheduler 负责对这些 pod 进行调度,比如这个 pod 实际应该被运行在哪台机器,比如 GPU 的结点做这样的调度工作。当一个集群的 Master 组件被完全部署好之后,我们会部署一些 Node。

在它的上面部署两个组件,一个是 kubelet,负责在这些 Node 上创建出来我们需要的 pod。另一个是 kube-proxy,它在集群中负责的工作是:当一个 deployment 需要以服务的形式对外暴露的时候,Kude-proxy 会负责配置这些 iptables 规则,让集群内不管是宿主机上的程序还是容器里的程序,都能够按照 service 的名字去做一个自动的发现和访问。

超详细的腾讯云 Kubernetes 一键部署实践

在 Kubernetes 这些标准化的组件之外,我们还提供了额外的组件,主要给大家介绍一下。包括三个,一个是 hpa-metrics-server,它是我们为了使用 Kubernetes 提供本身的 pod 横向扩展控制器而去自研的一个组件,这个组件相比于 Kubernetes 社区方案的优点,是 Kubernetes 社区方案只提供了基于 CPU 和内存的扩展,而我们在这个基础上更加拓展了 Pod 的入带宽和出带宽的指标,方便大家去适应更多的扩缩容场景。第二个组件是 cbs-provisioner,这个组件提供了去让 Kubernetes 里面的 pod 去消费我们腾讯云一个叫做 Cbs 块存储服务,我们后面会详细讲到。第三是 Ccs-log-collector,这个组件主要是负责收集容器里 pod 运行的日志,后面也会讲到。

超详细的腾讯云 Kubernetes 一键部署实践

容器网络

当我们把 Kubernetes 和腾讯云进行集成的时候,网络方案是怎么做的呢?在你将一些控制组件搭建起来之后,Kubernetes 对于网络提出了三点的要求。第一是在不使用 NAT 的情况下,集群内所有容器都可以和其他的容器进行通讯。第二是所有的节点都可以和所有的容器进行通信,反向也可以通信,同样要求不能使用 NAT。第三是容器看到的自己的 IP 与其他人看到的 IP 是一样的。

总的来说,它实现了 Node 和 container 之间的扁平化网络。同时为了应对服务发现的需求,降低网络复杂度,还要求不能使用 NAT。Kubernetes 并没有内置的网络解决方案,所以在社区其实有很多不同的解决方案,例如 flannel,它是通过 Tun/tap 设备,再通过内核用户态的转化程序,实现一个类似于 overlay 的网络能力。Callco 不需要进行 overlay 封装,直接通过路由的方案可以完成 Kubernetes 的网络需求。

接下来为大家介绍一下腾讯云 Kubernetes 使用的方案,我们的网络方案主要是直接使用了 VPC 提供的路由能力,叫做 global route。简单介绍一下 Kubernetes 结点加入到一个集群中配置网络的过程,当我们将一个结点加到集群中的时候,K8s 的 Kube-controller-manager 会为这个结点分配一个网端。

例如,集群网络是 172.16.1.0/16 掩码,这时加进去一个结点。按照我们的逻辑,我们会给它赋 172.16.1.0/24 位掩码的 CIDR。也就是所有在这个主机上创建的 pod,它的 ID 都在这个 CIDR 的范围内。然后我们会去 VPC 那里做一个动作,将 172.16.1.0/24 掩码到目的地址的流量去注册一条路由规则,使所有的包都发往被赋予这个 pod CIDR 的主机。这样我们就可以完成之前提到的 pod 和 Node 之间的扁平化网络。

具体过程如下:假设现在有一个位于 172.16.1.0/24 网端机上的 pod,向另一个机器的 pod 发送了这样一个包,这个包首先从容器出来,到 cbr0 bridge 然后出了主机。这时就会进入 VPC 的路由表进行匹配,当它发现这个 pod 的目的 IP 在 172.16.2.0/24 时,这个包就会被 VPC 转发到 10.1.1.3,这样我们就可以完成一个 pod 的跨主机通信。pod 在本地的通信比较简单,这里就不多讲了。通过这种方式,我们实现了一个 pod 和 Node 之间的扁平化网络,这里 Docker 的网络模式是用的 bridge 模式,pod IP 直接由 cni 插件进行分配。

超详细的腾讯云 Kubernetes 一键部署实践

容器存储

接下来给大家介绍我们的 K8s 和腾讯云容器集成的实现。K8s 集群集成了腾讯云的 CBS 和 CFS 两个能力,一个是块存储,一个是基于 NFS 的文件系统存储。当我们在一个 pod 中声明需要一个 volume 时,K8s 如何将 volume 最终挂载到 pod 里面?其实是这样一个过程:首先,Kube-controller-manager 会去 provisnon 这样一个 volume。

也就是说,实际去创建一个云盘,当云盘创建好之后会做一个 Attach 的动作,相当于把刚刚创建好的云盘插到对应主机上,这时,主机上的 Kubelet 会做一个 mount 动作,也就是将插进来的这个设备去 mount 到一个 Kubernetes 指定的目录,Kubelet 在创建这个 pod 的时候,通过 mount 的形式把 mount 到的目录实际挂载到容器的 namespace 里面。然后当我们这个 pod 销毁,这个 volume 不再被需要的时候,它就反向去执行,先从主机上把对应的块设备先 mount 掉,再将它 detach 掉,相当于把这块磁盘从主机上拔下来,然后会由 Kube-controller-manager 根据对应的 plugin 设置销毁或者是保留。

超详细的腾讯云 Kubernetes 一键部署实践

K8s 目前通过与 cloud provider 进行 volume 集成的方面主要是三种。一种是比较早期,所有要和 kubernetes 进行集成的 Volume 代码都需要写在 kubernetes 自己的代码仓库中。这样的缺点在于,假设我是一个存储提供商,我写的代码有些 bug,这样不仅影响存储功能的正常使用,可能也会影响整个集群的稳定性。所以后来为了更好的扩展性和稳定性,Kubernetes 提供了一种叫做 Flex volume 的形式。具体是将 mount 和 umount 这两个形式由 Flex volume 实现,Flex volume 是一个实现了特定接口的二进制文件,实际上在需要 mount、amount 的时候,Kubelet 会执行这个二进制文件,做一个 mount、umount 的动作。

这种方式其实也有一个问题,在 kubernetes 的部署环境中,如果要往主机上放一个二进制的动作,看起来不是那么的容器化。另外二进制文件执行的环境也有一定的要求,所以后来 Kubernetes 提供了第三种方式,也就是利用社区的 CSI 接口实现了基于 CSI 的插件。之前由 Flex volume 这个二进制文件完成的工作全部放到了容器里面,然后通过 unix socket 的形式,使 Kubelet 和实现对应功能的插件做一个通信,最后一个 mount、umount 的动作。我们现在也与腾讯云的 CBS 块存储做了集成,使用第一种方式,这是因为我们在早期使用的就是这种方式。后期我们也会将这部分独立出来,计划通过 CSI 的方式去提供存储的能力。这部分代码我们将会开源,但目前这个工作还正在进行中。

容器日志与监控

接下来为大家介绍一下日志和监控的方案,首先介绍一下日志的方案。在 K8s 中没有提供默认的日志方案,Kubernetes 社区提供的这种方式可以运行一个 sidecar 容器,在你自己的容器中运行一个这样的进程并将它输出到标准输出,然后再用你的 sidecar 容器做一个收集。这样的问题在于,每跑一个 pod,就要跑一个类似的 sidecar 容器,对于资源的消耗不太可以接受。腾讯容器服务基于 Fluentd+Kubernetes custom resource definition,实现了日志收集的控制器。这个控制器可以支持收集容器的标准输出,也可以支持收集我的 pod 所在的 Node 上主机上文件路径的文件内容。

主要的实现原理是:我们会在 kube-apiserver 上注册一个 LogCollector 的 custom resource definition,里面声明了我要收集 namespaces 下面某一个 deployment 对应的 pod 资源。还有另外一个进程就是 LogCollector,去监听 Kubernetes apiserver 资源,然后去生成对应的 Fluentd 的配置文件,再通过 sighup 信号的形式去触发 Fluentd 的重载,然后去收集我们想要的日志文件。因为 Kubernetes pod 对应的日志文件是存储在主机的 /var/log/containers 的路径规则下来,直接配置 Fluentd 去收集这个规则,再根据我们的实际需要做一个日志的路由。这样就可以把不同的日志发往用户指定的不同后端,比如 Kafka 或腾讯云的 CIS 的日志服务。这是我们目前对于日志收集的方案实现。

超详细的腾讯云 Kubernetes 一键部署实践

在监控方面,我们将 Kubernetes 中 pod 的性能信息和云监控做了对接,主要的实现方法是在用户的每台 kubernete 结点上运行一个 agent,这个 agent 在 kubelet 中内置的 cadvisor 收集 pod 运行的性能信息,然后再到 apiserver 获取这些 pod 对应的元数据。

我们将这些性能信息和元数据进行打包,再将它上传到腾讯云的监控服务上。另外,基于腾讯云存储的监控指标,我们实现了 hpa-metrics-server。Kubernetes 提供的 HPA 能力,是它在 kube-controller-manager 里面实现了 horizontal pod autoscaler,它会定期请求 hpa metrics server 去获取这个 pod 目前的 CPU Usage 或者是入带宽、出带宽这些指标的数量,根据我们的定义,比如当 CPU 使用率超过 80% 的时候,我们要去进行扩容。它也会定期拉取数据,比对当前的 pod 负载,直接去修改 deployment 中的 replica 字段,实现自动扩缩容。目前日志和监控的方案,是将 Kubernetes 和腾讯云的基础设施进行对接时候所需要做的一部分工作,也是我们的主要工作。

超详细的腾讯云 Kubernetes 一键部署实践

在 CVM 上部署 Kubernetes

下面介绍一下我们早期在腾讯云上的部署方案。当时为了产品能够快速上线,也为了满足一个完全的隔离的全托管的 Kubernetes 服务,我们直接把 Kubernetes 的 Master 组件部署在了一台 CVM 上面。同时,为了实现隔离的方式,我们将 Master 放到用户的 VPC 中。在 Master 组件上运行了一些标准化的 Kubernetes 组件,用户的 Node 结点以用户的身份直接在 CVM 那边购买机器,我们在这个基础上做一些 kubelet 或者 kube proxy 参数数值化的工作。此外,我们会做一些集群的证书配置、默认拉取镜像的凭证初始化工作。

在这个方案中,我们所有的节点都处于用户的 VPC 里面,通过 Agent 初始化的方式将整个集群部署起来。缺点在于难以管理,因为我们早期是通过 SSH 直接登录到客户的 Master 结点上进行一些运维操作,对于客户的 Node 结点是没有办法访问的。当然,现在也没有办法访问。对于 Master 的一些运维工作比较困难,因为它没有办法去编程化。

超详细的腾讯云 Kubernetes 一键部署实践

将 Kubernetes 组件部署在 Kubernetes 集群中

虽然我们自己提供的是 Kubernetes 的服务,但对于整个容器的运维好像和 Kubernetes 完全没有关系。在 Master 上一些组件的监控并不是用我们之前提到的方式来实现的,是通过内部的监控组件来收集。这与我们之前提到的用户 Node 组件信息收集方式不太一样,相当于两种方式并行在跑,人力消耗也比较大。之前我们的 ETCD 是共享的,也就是每个集群都在 ETCD 中有一个自己的目录,相当于一个软隔离,没有一个硬隔离的措施。

大家知道 ETCD 其实并不是为了海量数据存储而服务的,而我们在线上运行了数万个集群,导致遇到了很多和 ETCD 有关的问题。在这样的大背景下,我们推出了第二种方案,也就是将 Kubernetes 部署在 Kubernetes 里面,通过 Kubernetes API 去管理 Master 组件,包括我们刚才提到的 apiserver、kube-controller-manager 和一些自研的组件。这样的好处在于,我们不需要再通过 SSH 的方式,比如当我们需要做一个 apiserver 的升级或 kube-controller-manager 的 bug 修复的情况,我们不需要再通过 SSH 的方式去每台机器上进行操作,可以直接通过 API Kubernetes 提供的 deployment 的滚动升级的能力来完成这一点。

超详细的腾讯云 Kubernetes 一键部署实践

我们也可以充分利用 Kubernetes 运维的能力,包括健康检查和就绪检查的机制实现故障自愈。基于之前提到的 hpa-metrics-server,可以实现 apiserver 的动态扩容,应对用户的集群结点有一个大规模的上升或者突然下降的情况,更好地满足 Kubernetes 集群里面的结点对于 apiserver 性能的需求。在这个基础上,我们还需要解决一个问题:之前基于 CVM 的部署方案是将所有的组件部署在用户的 VPC 里面,如果我们将所有组件部署在 kubernetes Master 中又要怎么做呢?

我们并不能给每个用户部署一个 Kubernetes 集群,然后再跑它的 Master,这样听起来好像和第一种方案没有什么区别。所以我们提供了一个专门的 Kubernetes 集群,在这个集群里面运行着现在线网所有集群的 Master,这个集群运行在我们自己的 VPC 里面,它又要怎么和用户的 VPC 结点进行通信呢?

我们利用了 VPC 提供的弹性网卡能力,这个弹性网卡会被直接绑定到运行 apiserver 的 pod 中。大家可以理解为这个 pod 既加入了我们用来运行 Master 组件集群的 VPC,又加入了用户的 VPC,也就是一个 pod 同时在两个网络中,这样就可以很好的去实现和用户 Node 相关的互通。另外在这个基础上,我们也可以复用之前提到的一些监控和日志设施,更好地去做信息的收集和运维。

值得提到的一点是,这个方案中,我们可以利用一个叫做 Etcd operator 的组件,也就是 coreos 其中的一个组件来为每个集群提供独立的 Etcd 的部署,这样就可以解决在集群数量不断上升的情况下 Etcd 性能吃紧的问题。通过在 Kubernetes 集群里面部署 Kubernetes Master 组件,降低了运维的成本。

同时也统一了我们做运维工作的方式,我们都是通过 Kubernetes 的方式进行运维的。有效降低了 Master 组件的资源消耗,因为之前 Master 组件是免费提供给客户的,但有些客户的集群规模非常大,Master 配置也非常高,这时集群的 Master 存在资源浪费的情况。

Q/A

Q:我想问一下您最后讲的运营 K8s,Master 假如让你们来管理,作为开发来说,是否就没有时间控制这个东西?

A:目前产品形态是这样的,这取决于我们对于产品的定义。我们希望提供一个运维透明的 Kubernetes 服务,不希望用户操心 apiserver 的扩容和 Etcd 的维护工作,但这就相当于将运维工作落到了用户那边。

Q:这样做是为了将权利归到你们,我们作为开发来说只是使用这个?例如我是一个 Node,我要换点资源,就要通过你们的页面去加一个 Node?

A:目前来说是这样的,你也可以在同样的 VPC 里去购买 CVM,然后把所有组件都初始化,然后再通过 Kubernetes 的 API 去注册,这样也是没有问题的。

Q:刚才您提到日志监控方面,现在这种方式我只需要统一部署一个基于主机去收集这个日志就好,还是需要针对每个 pod 单独收集它的日志?

A:因为时间关系,没有很详细来讲。现在我们的界面上可以一键给一个集群开通一个日志收集服务,背后所做的工作,我们会去集群中创建一个 daemonset,这个 daemonset 里面的 pod 运行的是我们日志收集控制器。你可以通过我们的界面,或直接通过 Kubernetes crd 的方式配置日志的收集规则,例如我想收集 default namespace 下面所有容器的日志,或者我想收集 default namespace 下面一个服务名字叫做 nginx 的日志,这些都是可以通过我们界面去配置,并不需要单独为每个 pod 去配置。

Q:所以从开发的角度来说,需要稍微配置一下想要收集哪些信息?

A:是的,并不需要额外再做 pod 发现的逻辑开发。

作者介绍:
王蕴达,腾讯高级工程师。现任腾讯云容器服务后台开发。主要负责容器服务运行时的研发工作,专注于在公有云上通过基于开源 kubernetes 提供产品化的 kubernetes 服务和周边服务。

本文转载自公众号云加社区(ID:QcloudCommunity)。

原文链接:

https://mp.weixin.qq.com/s/IxgJyvejg2Q2kB6EIgJVow

评论

发布