阅读本文大概需要 10 分钟。
Kubernetes现在已经成为在私有云,公共云和混合云环境中大规模部署集装箱化应用程序的标准。最大的公共云平台AWS,Google Cloud,Azure,IBM Cloud和OracleCloud现在为Kubernetes提供监管服务。几年前,RedHat完全用Kubernetes取代了他们的OpenShift实施,并与Kubernetes社区合作实施了下一代容器平台。在Kubernetes流行后不久,Mesosphere将Kubernetes的主要功能,例如容器分组,覆盖网络,第4层路由,机密等整合到其容器平台DC / OS中。DC / OS还将Kubernetes与Marathon一起整合为集装箱协调员。Pivotal最近推出了基于Kubernetes的PKS,用于在Pivotal Cloud Foundry上部署第三方服务,截至目前,还有许多其他组织和技术提供商正在快速对其调整。
Kubernetes在2014年成立,在谷歌的内部集装箱集群管理者Borg和Omega的帮助下,拥有超过十多年的生产工作量管理经验。在我看来,它使新出现的软件体系结构模式(如微服务、无服务器功能、服务网格和事件驱动应用程序)的适应变得更加容易,并为整个云原生生态系统铺平了道路。最重要的是,这个云系统不可知的设计使得集装箱化应用程序可以在任何平台上运行,而不需要对应用程序代码进行任何更改。如今,大型企业部署可以使用Kubernetes生态系统,任何中小型企业也可以从长远来看节省大量的基础设施和维护成本。
在本文中,我将解释kubernetes的高级体系结构、其应用程序部署模型、服务发现和负载平衡、内部/外部路由分离、持久卷的使用、在节点上部署守护程序、部署有状态的分布式系统、运行后台作业、部署数据库、配置管理、凭证管理、推出更新、自动缩放和包管理。
Kubernetes架构
这个集群管理器所做的一个基本设计决策是,它能够部署在虚拟机上运行的现有应用程序,而不更改应用程序代码。在高层,任何运行在虚拟机上的应用程序都可以通过简单地将其组件装箱来部署在Kubernetes上。这是通过其核心功能实现的:容器分组、容器编排、覆盖网络、使用基于第4层虚拟IP的路由系统的容器到容器路由、服务发现、支持运行守护程序、部署有状态的应用程序组件,最重要的是,能够扩展容器协调器支持复杂的编排需求。
在一个非常高的层次上,Kubernetes提供了一组动态可扩展的主机,用于使用容器运行工作负载,并使用一组称为主服务器的管理主机来提供用于管理整个容器基础结构的API。工作负载可能包括长时间运行的服务,批处理作业和容器主机特定的守护程序。所有容器主机使用覆盖网络连接在一起,以提供容器到容器的路由。部署在Kubernetes上的应用程序可在群集网络中动态发现,并可使用传统负载平衡器暴露在外部网络。集群管理器的状态存储在高度分布式的密钥/值存储中,该存储在主实例中运行。
当副本数设置为多个时,Kubernetes调度程序将始终确保每个应用程序组件都经过运行状况检查,提供高可用性,在多个主机中计划每个实例,如果其中一个主机不可用,则所有容器都是在该主机中运行的计划安排在任何其余主机中。Kubernetes提供的一个令人着迷的功能是两层自动定量。首先,它提供了使用称为水平POD自动缩放器的资源自动缩放容器的能力,该资源监视资源消耗并相应地扩展所需的容器数量。其次,它可以根据资源需求添加和删除主机,从而扩展容器集群本身。此外,通过引入集群联合功能,它甚至可以管理Kubernetes集群的集合,这些集群可以使用单个API端点跨越多个数据中心。
接下来,我们将介绍它的核心特性,并解释如何在短时间内设计要部署在其上的软件应用程序。
应用程序部署模型
上图说明了Kubernetes上的高级应用程序部署模型。它使用名为容器副本的资源来编排容器。可以将容器副本视为基于YAML或基于JSON的元数据文件,该文件定义容器映像,端口,副本数,激活运行状况检查,活动运行状况检查,环境变量,卷装入以及创建和创建所需的安全规则。管理容器。容器总是在Kubernetes上创建为名为Pods的组,它是Kubernetes元数据定义或资源。每个pod允许使用Linux命名空间,cgroup和其他内核功能在容器之间共享文件系统,网络接口和操作系统用户。该容器副本可由另一个名为部署的高级资源管理,以提供用于推出更新和处理其回滚的功能。
通过执行简单的CLI命令,可以使用部署定义在Kubernetes上部署容器化应用程序,如下所示:
kubectl run <application-name> --image = <container-image> --port =<port-no>
执行上述CLI命令后,它将使用给定的容器映像创建部署定义,副本集和pod; 使用应用程序名称添加选择器标签。根据当前的设计,由此创建的每个pod将具有两个容器,一个用于给定的应用程序组件,另一个用于连接网络接口的暂停。
服务发现和负载均衡
Kubernetes的一个关键特性是它的服务发现和内部路由模型使用SkyDNS和基于第4层虚拟IP的路由系统提供。这些功能为使用服务的应用程序请求提供内部路由。通过复制集创建的一组pods可以使用集群网络中的服务进行负载平衡。这些服务使用选择器标签连接到pods。每个服务将被分配一个唯一的IP地址,一个从其名称派生的主机名,并以循环方式在吊舱之间路由请求。这些服务甚至将为可能需要会话关联的应用程序提供基于IP哈希的路由机制。服务可以定义端口集合,为给定服务定义的属性将以相同的方式应用于所有端口。因此,在一个场景中,如果一个给定的端口只需要会话关联,而使用基于循环路由所需的所有其他端口,则可能需要使用多个服务。
服务内部如何运作
Kubernetes服务已使用名为kube-proxy的组件实现。kube-proxy实例在每个节点中运行,并提供三种代理模式:Userspace,iptables和IPVS。目前的默认值是iptables。
在第一个代理模式,用户空间,kube-proxy本身将充当代理服务器,并将iptable规则接受的请求委托给后端pod。在此模式下,kube-proxy将在用户空间中运行,并将向消息流添加额外的跃点。在iptables中,kube-proxy将创建一组iptable规则,用于将来自客户端的传入请求直接转发到网络层上的后端pod端口,而不会在中间添加额外的跃点。这种代理模式比在内核模式下运行更快,而不在中间添加额外的代理服务器。
Kubernetes v1.8中增加了第三种代理模式,与第二种代理模式非常相似,它使用基于IPVS的虚拟服务器来路由请求而不使用iptable规则。IPVS是一种传输层负载平衡功能,可在基于Netfilter的Linux内核中使用,并提供一组负载平衡算法。在iptables上使用IPVS的主要原因是在使用iptables时同步代理规则的性能开销。当创建数千个服务时,更新iptable规则需要相当长的时间,相比之下,使用IPVS只需几毫秒。此外,IPVS使用哈希表来查找使用iptables进行顺序扫描的代理规则。更多关于引入IPVS代理模式的信息,请参见华为在2017年Kubecon举办的“扩展Kubernetes以支持50000项服务”演示。
内部/外部路由分离
Kubernetes服务可以通过两种主要方式向外部网络公开。第一种方法是通过流量转发到服务端口的节点上公开动态端口来使用节点端口,这些端口将流量转发到服务端口。第二种是使用通过入口控制器配置的负载平衡器,入口控制器可以通过连接到相同的覆盖网络将请求委托给服务。入口控制器是后台进程,可以在容器中运行,该容器监听Kubernetes API,根据给定的一组入口动态配置和重新加载给定的负载均衡器。入口根据主机名和使用服务的上下文路径定义路由规则。
使用kubectlrun命令在Kubernetes上部署应用程序后,可以通过负载均衡器将其暴露给外部网络,如下所示:
kubectl expose deployment <application-name> --type=LoadBalancer --name=<service-name>
上面的命令将创建一个负载均衡器类型的服务,并使用创建pod时创建的相同选择器标签将其映射到pod。因此,根据Kubernetes集群的配置方式,将通过服务或直接创建基础架构上的负载均衡器服务,以便为给定pod的路由请求创建。
永久卷的用法
需要在文件系统上持久化数据的应用程序可以使用卷将存储设备装载到临时容器,类似于卷与虚拟机的使用方式。Kubernetes通过引入一种称为持久量声明(PVC)的中间资源,将物理存储设备与容器松散耦合,从而正确设计了这一概念。PVC定义磁盘大小,磁盘类型(ReadWriteOnce,ReadOnlyMany,ReadWriteMany)并动态地将存储设备链接到针对pod定义的卷。绑定过程可以使用PVS以静态方式完成,也可以动态使用持久存储提供程序。在这两种方法中,卷将一对一地链接到PV,并且取决于配置,即使pod终止,也将保留给定数据。根据使用的磁盘类型,多个pod将能够连接到同一磁盘并进行读/写。
支持ReadWriteOnce的磁盘只能连接到一个pod,并且无法同时在多个pod之间共享。但是,支持ReadOnlyMany的磁盘将能够在只读模式下同时在多个pod之间共享。相反,具有ReadWriteMany支持的磁盘可以连接到多个pod,以便在读写模式下共享数据。Kubernetes提供了一系列卷插件,用于支持公共云平台上可用的存储服务,(例如AWS EBS,GCE永久磁盘,Azure文件,Azure磁盘)上提供的存储服务和许多其他有名的存储系统,如NFS,Glusterfs,Cinder等。
在节点部署守护进程
kubernetes提供一个名为守护进程的资源,用于在每个kubernetes节点中运行pod的副本作为守护进程。守护程序的一些用例如下:
- 集群存储守护程序,例如glusterd,ceph要部署在每个节点上以提供持久性存储。
- 要在每个节点上运行节点监视守护程序(如Prometheus NodeExporter)以监视容器主机。
- 日志收集守护程序,例如fluentd或logstash将在每个节点上运行以收集容器和Kubernetes组件日志。
- 要在节点集合上运行的入口控制器pod,以提供外部路由。
部署有状态分布式系统
容器化应用程序最困难的任务之一是设计有状态分布式组件的部署体系结构的过程。无状态组件可以很容易地进行容器化,因为它们可能没有预定义的启动顺序,集群要求,点对点TCP连接,唯一的网络标识符,正常的启动和终止要求等。数据库,大数据分析系统,分布式密钥/值存储和消息代理可能具有复杂的分布式体系结构,可能需要上述功能。Kubernetes引入了状态集资源来支持这些复杂的需求。
在高级别上,StatefulSets类似于ReplicaSet,除了它提供处理pod的启动顺序的能力,唯一地标识每个pod以保留其状态,同时提供以下特征:
- 稳定性,唯一的网络标识符。
- 稳定性,持久化存储。
- 有序的部署和扩展。
- 有序的删除和终止。
- 有序的自动滚动更新。
Pod调度运行时,如果应用不需要任何稳定的标示、有序的部署、删除和扩展,则应该使用一组无状态副本的控制器来部署应用如上图所示。Kubernetes提供了StatefulSets的示例,用于以分布式方式部署Cassandra和Zookeeper。
运行后台工作
除了ReplicaSet和StatefulSets之外,Kubernetes还提供了两个额外的控制器,用于在后台运行称为Jobs和CronJobs的工作负载。Jobs和CronJobs之间的区别在于,Jobs执行一次并终止,而CronJobs会按照与标准Linux cron作业类似的给定时间间隔定期执行。
部署数据库
在容器平台上部署数据库以供生产使用比部署应用程序稍微困难一些,因为它们对集群、点对点连接、复制、着色、管理备份等都有要求。正如前面提到的,StatefulSets专门用于支持这样的复杂要求和今天在Kubernetes上运行PostgreSQL和MongoDB集群有几个选项。YouTube的数据库集群系统Vitess现在是一个CNCF项目将是一个很好的选择,可以在Kubernetes上使用阴影大规模运行MySQL。通过说最好注意这些选项仍然处于非常早期阶段,并且如果现有的生产级数据库系统可用于给定的基础架构,例如AWS上的RDS,GCP上的Cloud SQL或内部部署数据库集群考虑到安装复杂性和维护开销,可能更好地选择其中一个选项。
配置管理
容器通常使用环境变量来参数化其运行时配置。但是,典型的企业应用程序使用大量配置文件来提供给定部署所需的静态配置。Kubernetes提供了一种使用名为ConfigMap的简单资源管理此类配置文件的绝妙方法,而无需将它们捆绑到容器映像中。可以使用以下CLI命令使用目录,文件或文字值创建ConfigMap:
kubectl create configmap <map-name> <data-source>
# map-name: name of the config map
# data-source: directory, file or literal value
创建ConfigMap后,可以使用卷装入将其装入pod。通过这种松散耦合的体系结构,只需更新相关的ConfigMap并执行滚动更新过程,就可以无缝更新已经运行的系统的配置,我将在下一节中介绍。值得注意的是,ConfigMaps目前不支持嵌套文件夹。因此,如果应用程序的嵌套目录结构中有可用的配置文件,则需要为每个目录级别创建ConfigMap。
凭证管理
与configmap类似,kubernetes提供了另一种有价值的资源,叫做secrets,用于管理诸如密码、OAuth令牌和ssh密钥等敏感信息。否则,在已经运行的系统上更新该信息可能需要重建容器映像。
可以使用以下方法创建用于管理基本身份验证凭据的机密:
# write credentials to two files
$ echo -n 'admin' > ./username.txt
$ echo -n '1f2d1e2e67df' > ./password.txt
# create a secret
$ kubectl create secret generic app-credentials--from-file=./username.txt --from-file=./password.txt
一旦创建了一个密钥,pod就可以使用环境变量或卷装载来读取它。同样,任何其他类型的敏感信息都可以使用相同的方法注入pods。
可更新
上面的动画图像说明了如何使用蓝/绿部署方法为已经运行的应用程序推出应用程序更新,而不需要系统停机。这是kubernetes的另一个非常重要的特性,它允许应用程序无需花费太多精力即可无缝地推出安全更新和向后兼容的更改。如果更改不向后兼容,则可能需要使用单独的部署定义执行手动蓝/绿部署。
此方法允许使用简单的CLI命令执行转出以更新容器映像:
$ kubectl setimage deployment / <application-name> <container-name> = <container-image-name>:<new-version>
执行转出后,可以按如下方式检查转出过程的状态:
$ kubectl rolloutstatus deployment/<application-name>Waiting forrollout to finish: 2 out of 3new replicas have been updated...deployment "<application-name>" successfully rolled out
使用相同的CLI命令kubectl set image deployment,可以将更新回滚到先前的状态。
包管理器
Kubernetes社区发起了一个单独的项目,用于为Kubernetes实施名为Helm的包管理器。这允许使用名为chart的资源模板化和打包Kubernetes资源(如部署,服务,配置映射,入口等),并允许使用输入参数在安装输入参数配置它们。更重要的是,它允许在使用依赖项实现安装包时重用现有图表。Helm存储库可以托管在公共和私有云环境中,用于管理应用程序图表。Helm提供了一个CLI,用于将应用程序从给定的Helm存储库安装到选定的Kubernetes环境中。
在它的Github存储库和中央helm服务器kubeapps hub中,可以找到各种适用于著名软件应用程序的稳定helm图表。
Kubernetes的设计拥有十多年在谷歌大规模运行集装箱化应用程序的经验。它已经被最大的公共云供应商和技术供应商所采用,并且在这边文章写的过程中正被更多的软件供应商和企业所接受。它甚至导致了2015年云计算本地计算基金会(CNCF)的成立,是第一个在CNCF下毕业的项目,并开始简化集装箱生态系统以及其他与集装箱相关的项目,如CNI,Containerd,Envoy ,Fluentd,gRPC,Jagger,Linkerd,Prometheus,rkt和Vitess。其受欢迎并在这一级别得到认可的关键原因可能是它完美的设计、与行业领导者的合作、其开源特性,以及始终对好的想法和贡献保持开放。