Kubernetes 也称为 K8s,是用于自动部署、扩缩和管理容器化应用程序的开源系统。Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。Kubernetes 拥有一个庞大且快速增长的生态,其服务、支持和工具的使用范围相当广泛。Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。k8s 这个缩写是因为 k 和 s 之间有八个字符的关系。Google 在 2014 年开源了 Kubernetes 项目。Kubernetes 建立在 Google 大规模运行生产工作负载十几年经验的基础上, 结合了社区中最优秀的想法和实践。
时光回溯
我们来了解一下为何 Kubernetes 能够裨益四方
传统部署时代:早期,各个组织是在物理服务器上运行应用程序。由于无法限制在物理服务器中运行的应用程序资源使用,因此会导致资源分配问题。例如,如果在同一台物理服务器上运行多个应用程序, 则可能会出现一个应用程序占用大部分资源的情况,而导致其他应用程序的性能下降。一种解决方案是将每个应用程序都运行在不同的物理服务器上, 但是当某个应用程序资源利用率不高时,剩余资源无法被分配给其他应用程序, 而且维护许多物理服务器的成本很高。
虚拟化部署时代:因此,虚拟化技术被引入了。虚拟化技术允许你在单个物理服务器的 CPU 上运行多台虚拟机(VM)。虚拟化能使应用程序在不同 VM 之间被彼此隔离,且能提供一定程度的安全性, 因为一个应用程序的信息不能被另一应用程序随意访问。虚拟化技术能够更好地利用物理服务器的资源,并且因为可轻松地添加或更新应用程序, 而因此可以具有更高的可扩缩性,以及降低硬件成本等等的好处。通过虚拟化,你可以将一组物理资源呈现为可丢弃的虚拟机集群。每个 VM 是一台完整的计算机,在虚拟化硬件之上运行所有组件,包括其自己的操作系统。
容器部署时代:容器类似于 VM,但是更宽松的隔离特性,使容器之间可以共享操作系统(OS)。因此,容器比起 VM 被认为是更轻量级的。且与 VM 类似,每个容器都具有自己的文件系统、CPU、内存、进程空间等。由于它们与基础架构分离,因此可以跨云和 OS 发行版本进行移植。容器因具有许多优势而变得流行起来,例如:
- 敏捷应用程序的创建和部署:与使用 VM 镜像相比,提高了容器镜像创建的简便性和效率。
- 持续开发、集成和部署:通过快速简单的回滚(由于镜像不可变性), 提供可靠且频繁的容器镜像构建和部署。
- 关注开发与运维的分离:在构建、发布时创建应用程序容器镜像,而不是在部署时, 从而将应用程序与基础架构分离。
- 可观察性:不仅可以显示 OS 级别的信息和指标,还可以显示应用程序的运行状况和其他指标信号。
- 跨开发、测试和生产的环境一致性:在笔记本计算机上也可以和在云中运行一样的应用程序。
- 跨云和操作系统发行版本的可移植性:可在 Ubuntu、RHEL、CoreOS、本地、 Google Kubernetes Engine 和其他任何地方运行。
- 以应用程序为中心的管理:提高抽象级别,从在虚拟硬件上运行 OS 到使用逻辑资源在 OS 上运行应用程序。
- 松散耦合、分布式、弹性、解放的微服务:应用程序被分解成较小的独立部分, 并且可以动态部署和管理 - 而不是在一台大型单机上整体运行。
- 资源隔离:可预测的应用程序性能。
- 资源利用:高效率和高密度。
为什么需要 Kubernetes,它能做什么
容器是打包和运行应用程序的好方式。在生产环境中, 你需要管理运行着应用程序的容器,并确保服务不会下线。例如,如果一个容器发生故障,则你需要启动另一个容器。如果此行为交由给系统处理,是不是会更容易一些?这就是 Kubernetes 要来做的事情!Kubernetes 为你提供了一个可弹性运行分布式系统的框架。Kubernetes 会满足你的扩展要求、故障转移你的应用、提供部署模式等。例如,Kubernetes 可以轻松管理系统的 Canary (金丝雀) 部署。Kubernetes 为你提供:
- 服务发现和负载均衡:Kubernetes 可以使用 DNS 名称或自己的 IP 地址来暴露容器,为多个容器提供一个统一访问入口(内部IP地址和一个DNS名称),并且负载均衡关联的所有容器,使得用户无需考虑容器IP问题。
- 存储编排:支持外挂存储并对外挂存储资源进行编排,挂载外部存储系统,无论是来自本地存储,公有云(如:AWS),还是网络存储(如:NFS、Glusterfs、Ceph)都作为集群资源的一部分使用,极大提高存储使用灵活性。
- 自动部署和回滚:K8S采用滚动策略更新应用,一个更新一个Pod,而不是同时删除所有的Pod,如果更新过程中出现问题,将回滚更改,确保升级不收影响业务。
- 自动完成资源计算:Kubernetes 提供许多节点组成的集群,在这个集群上运行容器化的任务。你告诉 Kubernetes 每个容器需要多少 CPU 和内存 (RAM)。Kubernetes 可以将这些容器按实际情况调度到你的节点上,以最佳方式利用你的资源。
- 自我修复:在节点故障时重新启动失败的容器,替换和重新部署,保证预期的副本数量;杀死健康检查失败的容器,并且在未准备好之前不会处理客户端请求,确保线上服务不中断。
- 集中化配置管理和密钥管理:管理机密数据和应用程序配置,而不需要把敏感数据暴露在镜像里,提高敏感数据安全性,并可以将一些常用的配置存储在K8S中,方便应用程序使用。
- 任务批量处理运行:提供一次性任务,定时任务,满足批量数据处理和分析的场景。
Kubernetes 组件
K8S 是属于主从架构(Master-Slave 架构),即有 Master 节点负责集群的调度、管理和运维,Slave 节点是集群中的运算工作负载节点。主节点一般被称为 Master 节点,master节点上有 apiserver、controller-manager、scheduler 以及使用 etcd 做k8s集群存储;而从节点则被称为 Worker Node 节点,node节点上有 kubelet、kube-proxy、容器引擎(比如docker)。
Master组件
Kube-apiserver
kube-apiserver 是 Kubernetes 最重要的核心组件之一,主要提供以下的功能。
- 提供集群管理的 REST API 接口,包括认证授权、数据校验以及集群状态变更等。
- 提供其他模块之间的数据交互和通信的枢纽(其他模块通过 API Server 查询或修改数据,只有 API Server 才直接操作 etcd)。
API Server 负责接收 K8S 所有请求(来自 UI 界面或者 CLI 命令行工具), 然后根据用户的具体请求,去通知其他组件干活。可以说 API Server 是 K8S 集群架构的大脑,是所有资源对象的操作入口。
Kube-controller-manager
执行并管理各种控制器,是 K8S 集群中处理常规任务的后台线程,是 K8S 集群里所有资源对象的自动化控制中心。在 K8S 集群中,一个资源对应一个控制器,而 Controller manager 就是负责管理这些控制器的。由一系列控制器组成,通过 API Server 监控整个集群的状态,并确保集群处于预期的工作状态,比如当某个 Node 意外宕机时,Controller Manager 会及时发现并执行自动化修复流程,确保集群始终处于预期的工作状态。这些控制器主要包括:
- Node Controller(节点控制器):负责在节点出现故障时发现和响应。
- Replication Controller(副本控制器):负责保证集群中一个 RC(资源对象 Replication Controller)所关联的 Pod 副本数始终保持预设值。可以理解成确保集群中有且仅有 N 个 Pod 实例,N 是 RC 中定义的 Pod 副本数量。
- Endpoints Controller(端点控制器):填充端点对象(即连接 Services 和 Pods),负责监听 Service 和对应的 Pod 副本的变化。可以理解端点是一个服务暴露出来的访问点,如果需要访问一个服务,则必须知道它的 endpoint。
- Service Account & Token Controllers(服务帐户和令牌控制器):为新的命名空间创建默认帐户和 API 访问令牌。
- ResourceQuota Controller(资源配额控制器):确保指定的资源对象在任何时候都不会超量占用系统物理资源。
- Namespace Controller(命名空间控制器):管理 namespace 的生命周期。
- Service Controller(服务控制器):属于 K8S 集群与外部的云平台之间的一个接口控制器
Kube-scheduler
负责整个集群资源调度,根据调度算法为新创建的 Pod 选择一个合适的 Node 节点。当用户要部署服务时,Scheduler 会根据调度算法选择最合适的 Node 节点来部署 Pod。调度算法:
- 预选策略(predicate)
- 优选策略(priorities)
API Server 接收到请求创建一批 Pod ,API Server 会让 Controller-manager 按照所预设的模板去创建 Pod,Controller-manager 会通过 API Server 去找 Scheduler 为新创建的 Pod 选择最适合的 Node 节点。比如运行这个 Pod 需要 2C4G 的资源,Scheduler 会通过预选策略过滤掉不满足策略的 Node 节点。Node 节点中还剩多少资源是通过汇报给 API Server 存储在 etcd 里,API Server 会调用一个方法找到 etcd 里所有 Node 节点的剩余资源,再对比 Pod 所需要的资源,如果某个 Node 节点的资源不足或者不满足 预选策略的条件则无法通过预选。预选阶段筛选出的节点,在优选阶段会根据优选策略为通过预选的 Node 节点进行打分排名, 选择得分最高的 Node。例如,资源越富裕、负载越小的 Node 可能具有越高的排名。
Etcd存储
集群数据库,保存整个集群的状态 etcd 作为服务发现系统,有以下的特点:
- 简单:安装配置简单,而且提供了HTTP API进行交互,使用也很简单
- 安全:支持SSI证书验证
- 快速:单实例支持每秒2k+读操作
- 可靠:采用rat算法,实现分布式系统数据的可用性和一致性
etcd 目前默认使用2379端口提供HTTP API服务, 2380端口和peer通信(这两个端口已经被IANA官方预留给etcd)。即etcd默认使用2379端口对外为客户端提供通讯,使用端口2380来进行服务器间内部通讯。etcd 在生产环境中一般推荐集群方式部署。由于etcd 的leader选举机制,要求至少为3台或以上的奇数台。
Node 组件
Kubelet
真正运行容器的组件,管理pod的声明周期,每个 Node 上都会启动一个 kubelet 服务进程。该进程用于处理 Master 下发到本节点的任务,管理 Pod 及 Pod 中的容器。每个 kubelet 进程都会在 API Server 上注册节点自身的信息,定期向 Master 汇报节点资源的使用情况,并通过 cAdvisor 监控容器和节点资源。
Kube-Proxy
在 K8S 集群中微服务的负载均衡是由 Kube-proxy 实现的。Kube-proxy 是 K8S 集群内部的负载均衡器。它是一个分布式代理服务器,在 K8S 的每个节点上都会运行一个 Kube-proxy 组件。在每个 Node 节点上实现 Pod 网络代理,负责维护网络规则和四层负载均衡工作。负责写入规则至iptables、ipvs实现服务映射访问的,转发请求并管理负载均衡的进程。Kube-apiserver 通过监控 Kube-Proxy 进行对 Kubernetes Service 的更新和端点的维护。
容器运行时(Container Runtime)
真正运行应用的载体 ,当 kubernetes 把 pod 调度到节点上,节点上的 kubelet会指示 docker 启动特定的容器。接着,kubelet 会通过 docker 持续地收集容器的信息, 然后提交到主节点上。docker 会如往常一样拉取容器镜像、启动或停止容器。不同点仅仅在于这是由自动化系统控制而非管理员在每个节点上手动操作的。Kubernetes 支持许多容器运行环境,例如 containerd、 docker、CRI-O 以及 Kubernetes CRI (容器运行环境接口) 等。
Kubernetes核心对象
Kubernetes 中的所有内容都被抽象为“资源”,如 Pod、Service、Node 等都是资源。“对象”就是“资源”的实例,是持久化的实体。Kubernetes 支持多种不同的方式来创建和管理 Kubernetes 对象,比如:
- 采用kubectl的命令方式
- yaml文件方式
Pod
Pod是 Kubernetes 创建或部署的最小/最简单的基本单位,一个 Pod 由一个或多个容器组成,Pod 中容器共享网络、存储和计算资源。使用 yaml 定义一个简单的 nginx 服务,它包含一个镜像为 nginx 的容器:(nginx-pod.yaml):
apiVersion: v1 kind: Pod metadata: name: nginx labels: app: nginx spec:
containers: - name: nginx image: nginx ports: - containerPort: 80
使用 Kubectl 工具将这个 Pod 创建到 Kubernetes 集群中:
kubectl apply -f nginx-pod.yaml
Pod 在 Kubernetes 集群中被创建的基本流程如下所示:
用户提交创建POD请求
API Server 处理用户请求,存储Pod数据到Etcd
Schedule通过和 API Server的监听机制,查看到新的pod,尝试为Pod绑定Node
4、过滤主机:调度器用一组规则过滤掉不符合要求的主机,比如Pod指定了所需要的资源,那么就要过滤掉资源不够的主机
主机打分:对第一步筛选出的符合要求的主机进行打分,在此阶段,调度器会考虑一些整体优化策略,比如把一个Replication Controller的副本分布到不同的主机上,使用最低负载的主机等
选择主机:选择得分最高的主机,进行binding操作,结果存储到Etcd中
kubelet根据调度结果执行Pod创建操作:绑定成功后,会启动container, Docker run, scheduler会调用API Server的API在etcd中创建一个bound pod对象,描述在一个工作节点上绑定运行的所有pod信息。运行在每个工作节点上的kubelet也会定期与etcd同步bound pod信息,一旦发现应该在该工作节点上运行的bound pod对象没有更新,则调用Docker API创建并启动pod内的容器
POD创建完成
Namespace
Namespace(命名空间)是对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或用户组。常见的 Pods、Services、Deployments 等都是属于某一个 Namespace 的(默认是 default),比如上面我们的 Nginx Pod 没有指定 namespace,则默认就在 default 命名空间下面,而 Node, PersistentVolumes 等资源则不属于任何 Namespace,是全局的。
Label
给某个资源对象定义一个 Label,就相当于给它打了一个标签;随后可以通过标签选择器(Label selector)查询和筛选拥有某些 Label 的资源对象。
Deployment
Deployment 是来管理 Pod 的资源对象。Deployment 确保任意时间都有指定数量的 Pod“副本”在运行。如果为某个 Pod 创建了 Deployment 并且指定 3 个副本,它会创建 3 个 Pod,并且持续监控它们。如果某个 Pod 不响应,那么 Deployment 会替换它,始终保持总数为 3。如果之前不响应的 Pod 恢复了,现在就有 4 个 Pod 了,那么 Deployment 会将其中一个终止保持总数为 3。如果在运行中将副本总数改为 5,Deployment 会立刻启动 2 个新 Pod,保证总数为 5。持回滚和滚动升级。当创建 Deployment 时,需要指定两个东西:
- Pod 模板:用来创建 Pod 副本的模板
- Label 标签:Deployment 需要监控的 Pod 的标签。
Service
在K8S的集群里,虽然每个Pod会被分配一个单独的IP地址,但由于Pod是有生命周期的(它们可以被创建,而且销毁之后不会再启动),随时可能会因为业务的变更,导致这个 IP 地址也会随着 Pod 的销毁而消失。而Service 就是用来解决这个问题的核心概念。Service 是应用服务的抽象,通过 Labels 为应用提供负载均衡和服务发现。匹配 Labels 的 Pod IP 和端口列表组成 Endpoints,由 kube-proxy 负责将服务 IP 负载均衡到这些 Endpoints 上。每个 Service 都会自动分配一个 cluster IP(仅在集群内部可访问的虚拟地址)和 DNS 名,其他容器可以通过该地址或 DNS 来访问服务,而不需要了解后端容器的运行。
K8S各组件工作流程
运维人员向kube-apiserver发出指令(我想干什么,我期望事情是什么状态)
api响应命令,通过一系列认证授权,把pod数据存储到etcd,创建deployment资源并初始化。(期望状态)
controller通过list-watch机制,监测发现新的deployment,将该资源加入到内部工作队列,发现该资源没有关联的pod和replicaset,启用deployment controller创建replicaset资源,再启用replicaset controller创建pod。
所有controller被创建完成后.将deployment,replicaset,pod资源更新存储到etcd。
scheduler通过list-watch机制,监测发现新的pod,经过主机过滤、主机打分规则,将pod绑定(binding)到合适的主机。6、将绑定结果存储到etcd。
kubelet每隔 20s(可以自定义)向apiserver通过NodeName 获取自身Node上所要运行的pod清单.通过与自己的内部缓存进行比较,新增加pod。
kubelet创建pod。
kube-proxy为新创建的pod注册动态DNS到CoreOS。给pod的service添加iptables/ipvs规则,用于服务发现和负载均衡。
controller通过control loop(控制循环)将当前pod状态与用户所期望的状态做对比,如果当前状态与用户期望状态不同,则controller会将pod修改为用户期望状态,实在不行会将此pod删掉,然后重新创建pod。