一、kubernetes
1 概述
Kubernetes(也称 k8s 或 “kube”)是一 个开源的容器编排平台,可以自动完成在部署、管理和扩展容器化应用过程中涉及的许多手动操作。
我们常说的编排的英文单词为 “Orchestration”,它常被解释为:
- 本意:为管弦乐中的配器法,主要是研究各种管弦乐器的运用和配合方法,通过各种乐器的不同音色,以便充分表现乐曲的内容和风格。
- 计算机领域:引申为描述复杂计算机系统、中间件 (middleware) 和业务的自动化的安排、协调和管理。
当我们在说容器编排的时候,我们在说什么?
在传统的单体式架构的应用中,我们开发、测试、交付、部署等都是针对单个组件,我们很少听到编排这个概念。而在云的时代,微服务和容器大行其道,除了为我们显示出了它们在敏捷性,可移植性等方面的巨大优势以外,也为我们的交付和运维带来了新的挑战:我们将单体式的架构拆分成越来越多细小的服务,运行在各自的容器中,那么该如何解决它们之间的依赖管理,服务发现,资源管理,高可用等问题呢?
容器编排的一些好处包括:
- 高效的资源管理。
- 服务的无缝扩展。
- 高可用性。
- 在规模上操作开销较低。
- 一个声明式模型(对于大多数编制工具)减少了对更加自治化管理的阻碍。
- 操作式基础设施即服务(IaaS),但又具备类似平台即服务(PaaS) 可管理的功能。
容器解决了开发人员的生产力问题,使DevOps工作流变得可以无缝连接。开发人员可以创建Docker镜像、运行Docker容器并在该容器中开发代码。然而,对开发人员生产力的提升并不能自动转化为生产环境中的效率。
典型的容器编排平台的基本特征包括:
- 调度
- 资源管理
- 服务发现
- 健康检查
- 自动伸缩
- 更新和升级
Kubernetes并不固定要求容器的格式,并且使用它自己的API和命令行接口(CLI)来进行容器编排。
图1.3:Kubernetes的控制器与节点之间的关系,以前也称为“minions”
它支持多种容器格式,不只是包括Docker,也支持rkt——最初由CoreOS创建,现在是CNCF托管的项目。该系统也是高度模块化且易于定制的,允许用户选择任何调度器、网络系统、存储系统和监控工具集。它以单个集群开始,并可以无缝地扩展到大规模的 Web应用。
之前提到的一个编排器的六个主要特征体现在Kubernetes中的以下方面中:
- 调度:Kubernetes调度程序确保在任何时候都可以满足对基础设施上的资源的需求。
- 资源管理:在Kubernetes的环境中,资源是编排器可以实例化和管理的逻辑结构,例如服务或应用程序部署。
- 服务发现:Kubernetes支持共享系统的服务,可以通过名称来发现。这样,包含服务的pods可以在整个物理基础结构中分布运行,而不必保留网络服务来定位它们。
- 健康检查:Kubernetes利用称为“活性探针”和“就绪探针”的功能,给编排器提供应用程序状态的周期性的指示。
- 自动伸缩:使用Kubernetes,当一个pod的指定CPU资源未被充分利用时,pod自动伸缩器能自动生成更多的副本。
- 更新/升级:一个自动化的滚动升级系统使每个Kubernetes的部署保持更新和稳定。
容器编排是如何运作的?
使用容器编排工具(例如 Kubernetes)时,需要用到 YAML 或 JSON 文件来描述应用的配置。该配置文件会告诉配置管理工具到哪里查找容器镜像,如何建立网络,以及将日志存储在哪里。
部署新容器时,容器管理工具会考虑所有已定义的要求或限制,自动将部署调度到集群并查找适合的主机。之后,编排工具将根据 compose 文件中所确定的规范来管理容器的生命周期。
您可以使用 Kubernetes 模式来管理基于容器的应用和服务的配置、生命周期及规模。这些可重复的模式是 Kubernetes 开发人员构建完整系统所需的工具。
容器编排可以在运行容器的任何环境中使用,包括内部服务器和公共云或私有云环境。
2 什么是 Kubernetes 集群?
您可以将运行 Linux® 容器的多组主机聚集在一起,由 Kubernetes 帮助您轻松高效地管理这些集群。
Kubernetes 集群可跨本地、公共云、私有云或混合云部署主机。因此,对于要求快速扩展的云原生应用而言(例如借助 Apache Kafka 进行的实时数据流处理),Kubernetes 是理想的托管平台。
3 Kubernetes 的功能和优势是什么?
在生产环境中(尤其是当您要面向云优化应用开发时)使用 Kubernetes 的主要优势在于,它提供了一个便捷有效的平台,让您可以在物理机和虚拟机(VM)集群上调度和运行容器。
更广泛一点说,它可以帮助您在生产环境中,完全实施并依赖基于容器的基础架构。由于 Kubernetes 的实质在于实现运维任务自动化,所以您可以将其它应用平台或管理系统分配给您的许多相同任务交给容器来执行。
借助 Kubernetes 模式,开发人员可以使用 Kubernetes 作为运行时平台来创建云原生应用。模式是 Kubernetes 开发人员在构建基于容器的应用和服务时所需的工具。
有了 Kubernetes,您可以:
- 跨多个主机编排容器。
- 更充分地利用硬件,最大化企业应用运行所需资源的效用。
- 控制和自动化应用的部署与更新。
- 挂载和添加运行有状态应用所需的存储。
- 动态扩展容器化应用及其资源。
- 对服务进行声明式管理,保证所部署的应用始终按照您期望的方式运行。
- 应用健康检查和自我修复,以及自动放置、自动启动、启动复制和自动扩展。
但是,Kubernetes 需要依赖其它项目来全面提供这些经过编排的服务。增添了其他开源项目后,您就能全面发挥 Kubernetes 的潜力。这些必要的部分包括(还有更多):
- 注册表,通过 Docker 注册表等项目实现。
- 网络,通过 OpenvSwitch 等项目和智能边缘路由来实现。
- 遥测,通过 Kibana、Hawkularz 和 Elastic 等项目实现。
- 安全性,通过 LDAP、SELinux、RBAC 和 OAUTH 等项目以及多租户层来实现。
- 自动化,通过添加 Ansible playbook 来进行安装和集群生命周期管理。
- 服务,通过常用应用模式的丰富目录来提供。
4 Kubernetes 常用术语盘点
与大多数技术一样,特定于 Kubernetes 的语言或可成为有碍入门的绊脚石。为了帮助您更好地了解 Kubernetes,我们在下面来解释一些常用术语。
控制平面(Control Plane):控制 Kubernetes 节点的进程的集合。所有任务分配都来自于此。
节点(Node):这些机器负责执行由控制平面分配的请求任务。
容器集(Pod):部署在单个节点上的一个或多个容器组成的容器组。同一容器集中的所有容器共享同一个 IP 地址、IPC、主机名称及其它资源。容器集会将网络和存储从底层容器中抽象出来。这样,您就能更加轻松地在集群中移动容器。
复制控制器(Replication controller):用于控制容器集在集群上运行的实例数量。
服务(Service):将工作定义与容器集分离。Kubernetes 服务代理会自动将服务请求分发到正确的容器集——无论这个容器集会移到集群中的哪个位置,甚至可以被替换掉。
Kubelet:运行在节点上的服务,可读取容器清单(container manifest),确保指定的容器启动并运行。
kubectl:Kubernetes 的命令行配置工具。
5 Kubernetes 如何发挥作用?
我们把一个有效的 Kubernetes 部署称为集群。您可以将 Kubernetes 集群可视化为两个部分:控制平面与计算设备(或称为节点)。
每个节点都是其自己的 Linux® 环境,可以是物理机也可以是虚拟机。每个节点都运行由若干容器组成的容器集。
控制平面负责维护集群的预期状态,例如运行哪个应用以及使用哪个容器镜像。计算机则负责应用和工作负载的实际运行。
Kubernetes 在操作系统(例如,红帽® 企业 Linux®)之上运行,并与节点上运行的容器集交互。
Kubernetes 控制平面接受来自管理员(或 DevOps 团队)的命令,并将这些指令转发给计算机。
此交接将与许多服务协同,以自动决定哪一个节点最适合执行该任务。然后,它将在该节点分配资源,并指派容器集来完成任务请求。
Kubernetes 集群的预期状态定义了应运行哪些应用或其他工作负载、应使用哪些镜像、应提供哪些资源,以及其他配置详情。
就基础架构而言,管理容器的方式基本不变。但您对容器的掌控得到提升,无需对独立的容器或节点实施微观管理,就能更好地控制容器。
您的工作包括配置 Kubernetes,以及定义节点、容器集以及其中的容器。Kubernetes 负责编排这些容器。
您可以自己决定具体在哪里运行 Kubernetes。可以是裸机服务器、虚拟机、公共云提供商、私有云和混合云环境。Kubernetes 的一大优势就是它可以在许多不同类型的基础架构上运行。
6 Kubernetes 与 Docker 的关系是什么?
Docker 可被用作由 Kubernetes 编排的容器运行时。当 Kubernetes 将容器集调度到节点上时,该节点上的 kubelet 会指示 Docker 启动指定的容器。
然后,kubelet 持续从 Docker 收集这些容器的状态,并将该信息聚合到控制平面中。Docker 将容器拉到该节点上,并且启动和停止这些容器。
将 Kubernetes 与 Docker 搭配使用带来的区别在于,自动化系统要求 Docker 在所有节点上对所有容器执行这些操作,而非要求管理员手动操作。
7 Kubernetes 体系结构
现在一个打包为一组容器的应用程序需要一个足够健壮的基础设施来处理集群的需求和动态编排的压力。这样的基础设施应该为跨主机的调度、监视、升级和重新定位容器提供支撑。它必须将底层的计算、存储和网络原语作为资源池来处理。每个容器的工作负载应该能够充分利用提供给它的资源,包括CPU内核、存储单元和网络。
图1.4:从容器编制引擎的角度来看的系统资源层
Kubernetes是一个开放源码的集群管理器,它封装了底层的物理基础设施,使其更容易在很大规模上运行容器应用程序。一个应用程序,通过Kubernetes的整个生命周期管理,是由一组pod成集合作并协调成一个单元工作的。一个高效的集群管理器层让Kubernetes能够有效地将这个应用程序与它的支持基础结构分离开来,如图1.4所示。一旦Kubernetes基础结构被完全配置好,DevOps团队就可以专注于管理已部署的工作负载,而不是管理底层资源池。
7.1 Kubernetes API
可以用来创建提供微服务的关键构建块的组件。这些组件是自主的,这意味着它们独立于其他组件存在。它们被设计为松散耦合、可扩展和适用于各种工作负载。API为内部组件提供了这种扩展性,以及在Kubernetes上运行扩展组件和容器。
7.2 Pod
Pod是Kubernetes的工作负载管理的核心单元,它充当了共享相同运行环境和资源的容器的逻辑边界。将相关的容器分组到pod中,就可以弥补由于容器化取代了第一代虚拟化所带来的配置挑战,从而使其能够一起运行多个相互依赖的进程。
每个pod是一个或多个使用远程过程调用(RPC)进行通信的容器的集合,共享存储和网络堆栈。适用的场景是一些容器需要耦合和共存的应用——例如,一个web服务器容器和一个缓存容器。它们很容易被包装在一个pod里。一个pod可以手动伸缩,也可以通过称为水平pod自动伸缩(HPA)的特性来定义。通过这种方法,pod内包装的容器数量按比例增加。
Pod支持开发和部署之间的功能分离。当开发人员专注于他们的代码时,操作人员可以将注意力集中在更广阔的视角上,考虑相关容器可能被组合成一个功能单元。这样有助于达到可移植性的最佳数量,因为一个pod只是多个容器镜像像管理的一个清单。
7.3 服务
Kubernetes的服务模型依赖于微服务的最基本、最重要的方面:发现机制。
一个单独的pod或一个副本集(稍后解释)可以通过服务暴露给内部或外部客户,该服务将一组带有特定标准的pod联起来。任何其标签匹配选择器的pod将自动被服务发现。该体系结构提供了一种灵活的、松散耦合的服务发现机制。
当一个pod被创建时,它被分配一个只有在集群内访问的IP地址。但不能保证pod的IP地址在整个生命周期中保持不变。Kubernetes可以在运行时迁移或重新实例化pod,从而为pod提供一个新的IP地址。
为了解决这种不确定性,服务能确保路由到集群中适当的pod,而不考虑它的调度节点,每个服务公开一个IP地址,也可能公开一个DNS地址,这两个入口都不会改变。需要与一组pod进行通信的内部或外部用户将使用该服务的IP地址,或者更常见的DNS地址。这样,服务就充当了连接pod与其他pod的粘合剂的角色。
7.4 服务发现
Kubernetes中的任何API对象,包括一个节点或一个pod,都可能具有与其相关的键值对--用于标识和组合共享一个公共特征或属性的对象的附加的元数据。Kubernetes引用这些键值对作为标签。
选择器是用来查询与标签值匹配的Kubernetes对象的一种标准。这种强大的技术支持对象的松散耦合。可以生成新的对象,其标签与选择器的值相匹配。标签和选择器形成了Kubernetes的主要分组机制,用于标识操作应用的组件。
一个副本集依赖于标签和选择器来决定哪个pod将参与缩放操作。在运行时,可以通过副本集对pod进行缩放,确保每次部署都运行所需数量的pod。每个副本集始终维护一个预定义的pod集。
图1.5:Kubernetes集群关注的是pod,而它们以服务的方式提供给外面的世界
任何标签与服务定义的选择器相匹配的pod将可以被终端公开访问。当一个缩放操作由一个副本集发起时,由该操作创建的新pod将立即开始接收流量。然后,服务通过在匹配的pod中路由流量提供基本的负载平衡。
图1.5描述了服务发现如何在Kubernetes集群中工作。这里有三种类型的pod,由红、绿、黄相间的盒子代表。一个副本控制器已经扩展了这些pod,以在所有可用节点上运行实例。每个类的pod都通过一个由彩色圆圈表示的服务向客户发布。假设每个pod都有color=value的标签,它的相关服务将有一个匹配它的选择器。
当客户到达红色服务时,请求被路由到任何与标签color=red匹配的pod中。如果一个新的红色pod被缩放调度器产生,那么它将立即被服务发现,因为它的具有匹配的标签和选择器。
可以将服务配置为将pod公开给内部和外部的使用者。一个暴露给内部的服务可以通过一个ClusterIP地址获得,这个地址只能在集群中路由。不需要暴露给外部的数据库pod和其他敏感资源,可以配置为使用ClusterIP。当一个服务需要被外部访问时,它可能通过每个节点的特定端口来发布,这被称为NodePort。在公共云环境中,Kubernetes可以提供自动配置的负载平衡器,以便将流量路由到相应的节点。
7.5 Master控制节点
与大多数现代分布式计算平台一样,Kubernetes利用了一个主/从架构。 如图1.6所示,master封装了从API中运行应用程序的节点,这些节点是与编排调度器进行通信的。
图1.6:Master 在Kubernetes体系结构中的位置
Master负责提供Kubernetes API,调度工作负载的部署,管理集群,并控制整个系统的通信。如图1.6所示,Master监视每个节点上运行的容器以及所有注册节点的健康状态。容器镜像作为可部署的构件,必须通过私有或公共的镜像(image)仓库让Kubernetes集群可以访问使用。负责调度和运行应用程序的节点从镜像仓库中获得应用服务的镜像。
如图1.7所示,Kubernetes Master运行以下组件,它们组成了控制面板:
图1.7:Kubernetes Master的组件
7.6 etcd
etcd是由CoreOS开发的,它是一个持久的、轻量级的、分布式的、键值的数据存储服务,它维护集群的配置数据。它表示在任何时间点上集群的总体状态,作为唯一的事实数据来源。其他各种组件和服务监视对etcd存储进行更改,以保持应用程序达到期望的状态。该状态由声明式策略(实际上是一个声明该应用程序的最佳环境的文件)定义,因此编排调度器可以通过一系列工作来获得该环境。该策略定义了编排调度器如何处理应用程序的各种属性,例如实例数量、存储需求和资源分配。
7.7 API Server
API服务器通过HTTP协议以JSON方式发布Kubernetes API,为编排调度器的内部和外部端点提供REST接口。CLI、web UI或其他工具可能向API服务器发出请求。服务器处理并验证请求,然后更新etcd中API对象的状态。这使得客户终端能够跨工作节点来配置工作负载和容器。
7.8 Scheduler 调度器
调度器基于对资源可用性评估来为每个pod选择运行的节点,然后跟踪资源利用率,以确保pod不会超过它分配的限额。它维护和跟踪资源需求、资源可用性以及各种其他用户提供的约束和策略指标;例如,服务质量(QoS)、亲和/反亲和性需求和局部性数据。操作团队可以声明式的定义资源模型。调度器将这些声明解释为为每个工作负载提供和分配正确资源集的指令。
7.9 Controller 控制器
控制器是Kubernetes体系结构的一部分,是Master的一部分。控制器的职责是确保集群始终保持节点和pod的期望状态。通过我们所说的期望状态,达到由pods的YAML配置文件所声明和请求使用的资源和系统的当前需求和约束的平衡。
控制器通过不断地监视集群的健康状况和部署在集群上的工作负载来维护节点和pod的稳定状态。例如,当一个节点变得不健康时,在该节点上运行的那些pod可能无法访问。在这种情况下,控制器的任务是在不同的节点中调度相同数量的新pod提供服务。这个活动确保集群在任何时间点都保持预期状态。
Kubernetes控制器在生产中运行容器化的工作负载时起着至关重要的作用,这使得一个团队可以部署和运行容器化的应用程序,这些应用程序复杂程度远远超出了典型的无状态和扩展的场景。控制器管理者负责管理核心Kubernetes控制器:
- ReplicationController (ReplicaSet) 维护集群中特定部署的pod数目。它保证任何时候都能有指定数量的pod在系统的节点上运行。
- StatefulSet 有状态集类似于副本集,是对于需要持久性和定义良好的标识符的pod的定义。
- DaemonSet 确保包在一个pod里的一个或多个容器被运行在集群的每个节点中。这是一种特殊类型的控制器,它强制一个pod在每个节点上运行。作为DaemonSet的一部分运行中的pod的数量与节点的数量成正比。
- Job和cron job 控制器处理后台进程和批处理进程。
这些控制器与API服务器通信,以创建、更新和删除它们管理的资源,如pod和服务节点。
关键型应用程序需要更高级别的资源可用性。通过使用一个副本集,Kubernetes确保了预定义的pod数量一直在运行。但这些pod是无状态的,短暂的。由于以下原因,很难运行有状态的工作负载,例如数据库集群或大数据栈:
- 每个pod在运行时分配一个任意名称。
- 在任何可用节点上都可以安排一个pod,除非使用关联规则,在这种情况下,pod只能在有特定标签或在规则中指定的标签的节点上调度。
- 在任何时间点都可以重新启动和重新定位。
- 一个pod可能永远不会被它的名称或IP地址直接引用。
从1.5版本开始,Kubernetes引入了有状态集(由StatefulSets表示)的概念,用于运行高可用的工作负载。参与有状态集的有状态pod具有以下属性:
- 一个稳定的主机名,它将永远由DNS解析。
- 一个序数索引号,用来表示副本集中的pod的顺序位置。
- 与主机名和序号相关联的稳定存储。
稳定的主机名及其序号索引号使一个pod能够以可预测和一致的方式与另一个pod进行通信。这是无状态pod和有状态pod之间的根本区别。
7.10 Node
节点是Kubernetes集群的工作站,负责运行容器化的工作负载;日志、监视和服务发现;和可选的附加组件。它的目的是向应用程序公开计算、网络和存储资源。每个节点包括一个容器运行时,例如Docker或rkt,以及与控制器Master通信的代理。节点可以是在云中运行的虚拟机(VM),也可以是数据中心内的实体机服务器。
如图1.8所示,每个节点包含以下内容:
图1.8:显示了Kubernetes节点中的大量组件的一个分解视图
7.11 容器运行器
容器运行器负责在节点中运行的每个容器的生命周期管理。在节点上安排了一个pod之后,容器运行器就会从镜像仓库中拉取出pod所指定的镜像。当一个pod被终止时,运行器将杀死属于该pod的容器。Kubernetes可以与任何兼容OCI的容器运行器进行通信,包括Docker和rkt。
7.12 Kubelet
kubelet是确保节点上所有容器都健康运行的组件。它与容器运行器进行通信,以执行诸如启动、停止和维护容器等操作。
每个kubelet还监测pod的状态。当一个pod不满足副本控制器定义的理想状态时,它可能在相同的节点上被重新启动。节点的状态每隔几秒通过心跳消息传递给控制器。如果控制器检测到节点故障,副本控制器会观察到这个状态的变化,并将这些pod安排到其他健康的节点上。
7.13 Kube-proxy
kube-proxy组件是作为一个网络代理和负载均衡器来实现的。它根据IP地址和传入请求的端口号将流量路由到合适的容器。它还利用了基于os的特定网络功能,通过操作iptables定义的策略和规则。每个kube-proxy都可以与特定容器的网络层集成,如flannel和Calico。
图1.9:headless服务提供状态数据库和服务之间的连接点
可以使用一个名为headless服务的东西直接将pod用于外部服务,如缓存、对象存储和数据库。如图1.9所示,这本质上与服务是一样的,但不需要使用kube-proxy或负载平衡。对headless服务的调用将解析为服务专门选择的集群的IP地址。通过这种方式,您可以使用自定义的逻辑来选择IP地址,绕过正常的路由。
7.14 Logging Layer
编排调度器经常使用日志记录作为在每个节点上收集资源使用和性能指标的方法,例如CPU、内存、文件和网络使用。CNCF定义了一个统一的日志层,用于Kubernetes或其他的编排调度器,称为Fluentd。该组件产生的原因是Kubernetes 主节点控制器需要跟踪可用的集群资源,以及整个基础设施的健康状态。
7.15 Add-Ins
Kubernetes支持附加插件形式的服务。这些可选的服务(如DNS和仪表板)与其他应用程序一样部署,但与诸如Fluentd和kube-proxy等节点上的其他核心组件集成。例如,仪表板外接程序从Fluentd提取指标,以充分的显示资源利用率。DNS插件通过名称解析扩展了kube-proxy。
7.16 区分各种Kubernetes平台
自从云计算成为现代企业的规范以来,系统架构师一直困惑如何平衡云平台应该采用的封装方式。适当的封装可以提高操作效率,同时提高开发人员的生产力。
在云计算的早期,虚拟机是计算的基本单元。最终用户被迫在所谓的IaaS+平台之间进行选择,比如AWS,以及像Heroku、Engine Yard、Cloud Foundry和OpenShift这样的PaaS平台。封装级别决定了开发人员对平台底层基础结构的直接控制程度。更深层次的封装意味着开发人员需要使用平台提供的API来驱动编码。
现在,容器作为计算的基本单元出现了,用户也面临着类似的问题,关于选择正确封装级别的需求。正如下面的表格所示,在容器编排领域有许多不同类型的Kubernetes发行版。用户的需求——包括工作环境、专业知识的可用性以及用户正在处理的特定场境——将确定是把容器作为服务(CaaS)还是封装的平台的重要考虑因素。没有一个简单明了的框架能够保证完美的满足一切需求。但表1.1可能是一个开始。
表1.1:Kubernetes发行类型的比较,从完全社区生产到完全商业化
一个CaaS平台包括Kubernetes项目,和为其部署和管理提供所需的额外工具。一个封装好的平台,在规模化的另一端,远远超出了CaaS提供的运营效率,专注于提高开发人员的生产力。在CaaS中,开发人员需要将他们自己的代码打包到一个容器中,以便它可以部署在集群中。尽管基于docker的容器在开发人员的名义上解决了这个打包问题,封装好的应用程序平台还可以在内部完全封装构建容器映像的过程——自动化过程,而不是由开发人员那样手动控制。一旦他把他的代码Push到像GitHub这样的源代码控制工具上,或者像Jenkins这样的持续集成/持续交付(CI/ CD)系统里,开发人员的任务就停止了,而交给平台做其余的事情。
通过设计,CaaS与Kubernetes开源项目紧密地结合在一起,帮助它大规模的运行和管理容器。但CaaS模型希望开发人员能够处理所有应用程序的依赖项。从文化的角度来看,DevOps模型遵循了Dev和Ops一起与跨功能知识协同工作的文化。在这个场景中,两个团队都知道的是完成一件事是需要依赖很长的列表。
封装好的应用程序平台使用Kubernetes作为核心组件,帮助它以比CaaS更少的开销运行容器。开发人员不必担心管理运行时或任何应用程序依赖项。相反,他们可以专注于编写应用程序代码并将其推送到源代码控制存储库或CI/CD系统。封装好的平台提高了开发人员的生产力,同时削减了底层组件的控制。可以说DevOps仍然是这个模型的中心。但由于封装,并不需要这种跨功能的知识——它变成了冗余的繁杂工作。开发人员不需要了解Kubernetes的基础,也不需要了解如何管理它。
正如我们前面提到的,在CaaS和封装好平台之间选择不存在简单的框架。您的选择取决于您的团队希望达到的开发生产力水平和您所能预见的团队的特定需求集合。
8 Kubernetes生态系统
8.1 分层架构
核心层:Kubernetes 最核心的功能,对外提供 API 构建高层的应用,对内提供插件式应用执行环境。
应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS 解析
等)。
管理层:系统度量(如基础设施、容器和网络的度量)、自动化(如自动扩展、动态 Provision 等)、
策略管理(RBAC、Quota、PSP、NetworkPolicy 等)。
接口层:Kubectl 命令行工具、客户端 SDK 以及集群联邦。
生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴:
Kubernetes 外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS 应用、
ChatOps 等;
Kubernetes 内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等。
Kubernetes 如何通过对象的组合完成业务描述
8.2 架构设计原则
只有 APIServer 可以直接访问 etcd 存储,其他服务必须通过 Kubernetes
API 来访问集群状态;
单节点故障不应该影响集群的状态;
在没有新请求的情况下,所有组件应该在故障恢复后继续执行上次最后收到的请求
(比如网络分区或服务重启等);
所有组件都应该在内存中保持所需要的状态,APIServer 将状态写入 etcd 存储,而其
他组件则通过 APIServer 更新并监听所有的变化;
优先使用事件监听而不是轮询。
常用 Kubernetes 对象及其分组
核心技术概念和 API 对象
API 对象是 Kubernetes 集群中的管理操作单元。
Kubernetes 集群系统每支持一项新功能,引入一项新技术,一定会新引入对应的 API 对象,支持对该功能的管理操作。
每个 API 对象都有四大类属性:
- TypeMeta
- MetaData
- Spec
- Status
TypeMeta
Kubernetes对象的最基本定义,它通过引入GKV(Group,Kind,Version)模型定义了一个对象的类型。
1 Group
Kubernetes 定义了非常多的对象,如何将这些对象进行归类是一门学问,将对象依据其功能范围归入不同的分组,
比如把支撑最基本功能的对象归入 core 组,把与应用部署有关的对象归入 apps 组,会使这些对象的可维护性和可
理解性更高。
2 Kind
定义一个对象的基本类型,比如 Node、Pod、Deployment 等。
3 Version
与软件版本类似,通常社区提出一个模型定义以后,随着该对象不断成熟,其版本可能会从 v1alpha1 到 v1alpha2,或者到 v1beta1,最终变成生产就绪版本 v1。
Metadata
Metadata 中有两个最重要的属性:Namespace和Name,分别定义了对象的Namespace 归属及名字,这两个属性唯一定义了某个对象实例。
1 Label
顾名思义就是给对象打标签,一个对象可以有任意对标签,其存在形式是键值对。Label 定义了对象的可识别属性,Kubernetes API 支持以 Label 作为过滤条件查询对象。
Label 定义好后其他对象可以使用 Label Selector 来选择一组相同 label 的对象。
Label Selector 支持以下几种方式:等式,如 app=nginx 和 env!=production;集合,如 env in (production, qa);多个 label(它们之间是 AND 关系),如 app=nginx,env=test。
2 Annotation
Annotation 与 Label 一样用键值对来定义,但 Annotation 是作为属性扩展,
(1)Annotations 是 key/value 形式附加于对象的注解。
(2)不同于 Labels 用于标志和选择对象,Annotations 则是用来记录一些附加信息,用来辅助应用部署、安全策略以及调度策略等。
(3)比如 deployment 使用 annotations 来记录 rolling update 的状态。
3 Finalizer
(1)Finalizer 本质上是一个资源锁,Kubernetes 在接收某对象的删除请求时,会检查 Finalizer 是否为空,如果不为空则只对其做逻辑删除(pod会处于terminating状态),即只会更新对象中的metadata.deletionTimestamp 字段。
4 ResourceVersion
ResourceVersion 可以被看作一种乐观锁,每个对象在任意时刻都有其ResourceVersion,当 Kubernetes 对象被客户端读取以后,ResourceVersion信息也被一并读取。此机制确保了分布式系统中任意多线程能够无锁并发访问对
象,极大提升了系统的整体效率。
乐观锁是一种并发控制机制,常用于多个并发操作同时更新同一数据资源的场景,例如数据库中的记录更新或缓存中的数据更新。在乐观锁机制中,每个并发操作都认为自己在操作之前,其他并发操作没有对数据做出修改,因此每次操作都会先读取数据并记录版本号或时间戳,然后执行操作,最后提交时再次比较版本号或时间戳是否发生变化。如果没有变化则认为操作成功,否则认为操作失败需要重新尝试。
乐观锁的优点是可以提高并发性能,减少锁的使用,避免死锁等问题。缺点是可能会出现并发冲突,需要重新尝试,而且在某些场景下需要额外的存储空间记录版本号或时间戳。
悲观锁是另一种并发控制机制,它的思想是在并发操作时,对数据资源持悲观态度,认为其他并发操作可能会对数据进行修改,因此在进行任何操作之前,先对数据进行加锁,保证同一时间只有一个操作能够访问数据,其他操作需要等待锁被释放才能进行操作。
悲观锁的优点是可以保证数据的一致性和安全性,不会出现并发冲突的问题,缺点是由于需要频繁的加锁和释放锁操作,因此对性能有一定影响,容易出现死锁和活锁等问题。
乐观锁和悲观锁的区别在于对并发操作的态度不同,乐观锁相信并发操作之间不会产生冲突,因此在操作前不会加锁,而是在提交时检查版本号或时间戳是否有变化;悲观锁则认为并发操作之间可能会产生冲突,因此在进行任何操作之前都会先对数据进行加锁,保证同一时间只有一个操作能够访问数据。乐观锁适用于读多写少的场景,能够提高并发性能;悲观锁适用于写多读少的场景,能够保证数据的一致性和安全性。
Spec 和 Status 才是对象的核心。
(1)Spec 是用户的期望状态,由创建对象的用户端来定义。
(2)Status 是对象的实际状态,由对应的控制器收集实际状态并更新。
(3)与 TypeMeta 和 Metadata 等通用属性不同,Spec 和 Status 是每个对象独有的。
8.2.1 核心对象概览
1 Node
Node 是 Pod 真正运行的主机,可以物理机,也可以是虚拟机。
为了管理 Pod,每个 Node 节点上至少要运行 container runtime(比如 Docker 或者 Rkt)、Kubelet 和 Kube-proxy 服务。
2 Namespace
Namespace 是对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或用户组。
常见的 pods, services, replication controllers 和 deployments 等都是属于某一个 Namespace 的(默认是 default),而 Node, persistentVolumes等则不属于任何 Namespace。
3 Pod
(1)Pod 是一组紧密关联的容器集合,它们共享 PID、IPC、Network 和 UTS namespace,是 Kubernetes调度的基本单位。
(2)Pod 的设计理念是支持多个容器在一个 Pod 中共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。
(3)同一个 Pod 中的不同容器可共享资源:
1)共享网络 Namespace;
2)可通过挂载存储卷共享存储;
3)共享 Security Context。
副本集(ReplicaSet)
- Pod 描述的是具体的应用实例,当 Pod 被删除后,就彻底消失了;
- 为保证应用的高可用,引入副本集来确保应用的总副本数永远与期望一致;
- 若某个 Pod隶属于某个副本集,若该 Pod 被删除,则 ReplicaSet Controller 会发现当前运行的副本数量与用户的期望不一致,则会创建新的 Pod。
部署( Deployment)
- 部署表示用户对Kubernetes集群的一次更新操作。
- 部署是一个比RS应用模式更广的API对象,可以是创建一个新的应用,更新一个已存在的应用,也可以是滚动升级一个应用。
- 滚动升级一个服务,实际是创建一个新的 RS,然后逐渐将新RS中副本数增加到理想状态将旧RS中的副本数减小到0的复合操作;这样一个复合操作用一个RS是不太好描述的,所以用一个更通用的 Deployment 来描述。以Kubernetes 的发展方向,未来对所有长期伺服型的的业务的管理,都会通过 Deployment 来管理。
服务(Service)
RC、RS 和 Deployment 只是保证了支撑服务的微服务 Pod 的数量,但是没有解决如何访问这些服务的问题。
一个Pod 只是一个运行服务的实例,随时可能在一个节点上停止,在另一个节点以一个新的IP 启动一个新的 Pod,因此不能以确定的 P 和端口号提供服务。要稳定地提供服务需要服务发现和负载均衡能力。服务发现完成的工作,是针对客户端访问的服务,找到对应的的后端服务实例。
在 Kubernetes 集群中,客户端需要访问的服务就是 Service 对象。每个Service 会对应一个集群内部有效的虚拟IP,集群内部通过虚拟 IP 访问一个服务
在 Kubernetes 集群中微服务的负载均衡是由 kube-proxy 实现的。kube-proxy 是 Kubernetes 集群内部的负载均衡器。它是一个分布式代理服务器,在 Kubernetes 的每个节点上都有一个;这一设计体现了它的伸缩性优势,需要访问服务的节点越多,提供负载均衡能力的 kube-proxy 就越多,高可用节点也随之增多。
与之相比,我们平时在服务器端使用反向代理作负载均衡,还要进一步解决反向代理的高可用问题。
4 存储卷
(1)通过存储卷可以将外挂存储挂载到 Pod 内部使用。
(2)存储卷定义包括两个部分: Volume 和 VolumeMounts。
1)Volume:定义 Pod 可以使用的存储卷来源;
2)VolumeMounts:定义存储卷如何 Mount 到容器内部。
apiVersion: v1
kind: Pod
metadata:
name: hello-volume
spec:
containers:
- image: nginx:1.15
name: nginx
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
emptyDir: {}
5 资源限制
Kubernetes 通过 Cgroups 提供容器资源管理的功能,可以限制每个容器的CPU 和内存使用,比如对于刚才创建的 deployment,可以通过下面的命令限制nginx 容器最多只用 50% 的 CPU 和 128MB 的内存:
$ kubectl set resources deployment nginx-app -c=nginx --limits=cpu=500m,memory=128Mi
deployment "nginx" resource requirements updated
等同于在每个 Pod 中设置 resources limits
6 健康检查
Kubernetes 作为一个面向应用的集群管理工具,需要确保容器在部署后确实处在正常的运行状态。
1 探针类型:
(1)LivenessProbe
探测应用是否处于健康状态,如果不健康则删除并重新创建容器。
(2)ReadinessProbe
探测应用是否就绪并且处于正常服务状态,如果不正常则不会接收来自 Kubernetes Service 的流量。
(3)StartupProbe
探测应用是否启动完成,如果在failureThreshold*periodSeconds 周期内未就绪,则会应用进程会被重启。
2 探活方式:
- Exec
- TCP socket
- HTTP
7 ConfigMap
- ConfigMap 用来将非机密性的数据保存到键值对中。
- 使用时, Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。
- ConfigMap 将环境配置信息和 容器镜像解耦,便于应用配置的修改。
8 Secret
- Secret 是用来保存和传递密码、密钥、认证凭证这些敏感信息的对象。
- 使用 Secret 的好处是可以避免把敏感信息明文写在配置文件里。
用户帐户和服务帐户的一个区别是作用范围:
- 用户帐户对应的是人的身份,人的身份与服务的 Namespace 无关,所以用户账户是跨Namespace 的;
- 而服务帐户对应的是一个运行中程序的身份,与特定 Namespace 是相关的。
9 Service
Service 是应用服务的抽象,通过 labels 为应用提供负载均衡和服务发现。匹配 labels 的 Pod IP 和端口列表组成 endpoints,由 Kube-proxy 负责将服务IP 负载均衡到这些 endpoints 上。
每个 Service 都会自动分配一个 cluster IP(仅在集群内部可访问的虚拟地址)和 DNS 名,其他容器可以通过该地址或 DNS 来访问服务,而不需要了解后端容器的运行。
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
ports:
- port: 8078 # the port that this service should serve on
name: http
# the container on each pod to connect to, can be a name
# (e.g. 'www') or a number (e.g. 80)
targetPort: 80
protocol: TCP
selector:
app: nginx
10 有状态服务集(StatefulSet)
- 对于 StatefulSet 中的 Pod,每个 Pod 挂载自己独立的存储,如果一个 Pod 出现故障,从其他节点启动一个同样名字的Pod,要挂载上原来 Pod 的存储继续以它的状态提供服务。
- 适合于 StatefulSet 的业务包括数据库服务 MySQL 和 PostgreSQL,集群化管理服务 ZooKeeper、etcd 等有状态服务。
- 使用 StatefulSet,Pod 仍然可以通过漂移到不同节点提供高可用,而存储也可以通过外挂的存储来提供高可靠性,StatefulSet 做的只是将确定的 Pod 与确定的存储关联起来保证状态的连续性
Statefulset 与 Deployment 的差异
(1)身份标识
StatefulSet Controller 为每个 Pod 编号,序号从0开始。
(2)数据存储
StatefulSet 允许用户定义 volumeClaimTemplates,Pod 被创建的同时,Kubernetes 会以volumeClaimTemplates 中定义的模板创建存储卷,并挂载给 Pod。
StatefulSet 的升级策略不同
- onDelete
- 滚动升级
- 分片升级
11 任务(Job)
- Job 是 Kubernetes 用来控制批处理型任务的 API 对象。
- Job 管理的 Pod 根据用户的设置把任务成功完成后就自动退出。
- 成功完成的标志根据不同的 spec.completions 策略而不同:
- 单 Pod 型任务有一个 Pod 成功就标志完成;
- 定数成功型任务保证有 N 个任务全部成功;
- 工作队列型任务根据应用确认的全局成功而标志成功。
12 后台支撑服务集(DaemonSet)
- 长期伺服型和批处理型服务的核心在业务应用,可能有些节点运行多个同类业务的 Pod,有些节点上又没有这类 Pod 运行;
- 而后台支撑型服务的核心关注点在 Kubernetes 集群中的节点(物理机或虚拟机),要保证每个节点上都有一个此类 Pod 运行。
- 节点可能是所有集群节点也可能是通过 nodeSelector 选定的一些特定节点。
- 典型的后台支撑型服务包括存储、日志和监控等在每个节点上支撑 Kubernetes 集群运行的服务。
13 存储PV和PVC
- PersistentVolume(PV)是集群中的一块存储卷,可以由管理员手动设置,或当用户创建 PersistentVolumeClaim(PVC)时根据 StorageClass 动态设置。
- PV 和 PVC 与 Pod 生命周期无关。也就是说,当 Pod 中的容器重新启动、Pod 重新调度或者删除时,PV 和 PVC 不会受到影响,Pod 存储于 PV 里的数据得以保留。
- 对于不同的使用场景,用户通常需要不同属性(例如性能、访问模式等)的 PV。
资料来源
- 现在很火的云原生到底是什么? - 知乎
- 知乎 - 有问题,就会有答案
- 什么是云原生
- 安全验证 - 知乎tes
- 从零开始入门 K8s:详解 K8s 核心概念_云计算_李响_InfoQ精选文章
- Kubernetes 入门指南:一文带你快速理解 K8s 架构
- 知乎 - 有问题,就会有答案
- 安全验证 - 知乎
- 云原生时代(五):Kubernetes与容器编排之战
- Kubernetes(k8s)中文文档 kubernetes设计理念_Kubernetes中文社区
- https://cloud.tencent.com/developer/article/1814676
- 入门
- Kubernetes 入门教程 - 掘金
- 10分钟看懂Docker和K8S
- 深入理解container--容器运行时
- 一文看懂 Container Runtime
- 念对了这些技术名词,你已经赢了研发大佬
- https://blog.csdn.net/qq_39578545/article/details/124649312
- 【云原生训练营】模块五 Kubernetes 控制平面组件:etcd_怎么查看etcd集群的主节点_果子哥丶的博客-CSDN博客
- 安全验证 - 知乎
- 知乎 - 有问题,就会有答案