Kubernetes 简介

1. Kubernetes 简介

  • 什么是 Kubernetes?
  • K8S 与 Docker
  • K8S 的定位
  • 容器云技术栈:示例
  • 为什么要用 K8S?

2. Kubernetes 核心组件

  • 集群
  • Master
  • Node
  • Pod
  • Label
  • Replication Controller
  • Deployment
  • Service
  • Statefulset
  • PV、PVC
  • Namespace

 

 

1. Kubernetes 简介

什么是 Kubernetes?

Kubernetes(K8S)是 Google 开源的容器集群管理系统,它构建于容器技术之上,为容器化的应用提供资源调度、部署运行、服务发现、扩容缩容等整一套功能,本质上可看做是基于容器技术的 paas 平台。

相对来说,Kubernetes 属于容器引擎的上层:编排调度层,它可以对不同机器的每份容器进行编排、调度,从而组成分布式系统。

Kubernetes 具有完备的集群管理能力:

  • 自动化的容器部署和复制。
  • 随时扩展或收缩容器规模。
  • 将容器组织成组,并且提供内建的容器间负载均衡。
  • 很容易地升级应用程序容器的新版本。
  • 提供容器弹性,如果容器失效就替代它。
  • 只需一个部署文件,使用一条命令就可以部署多层容器的完整集群。
  • 多层次的安全防护和准入机制。
  • 多租户应用支撑能力。
  • 透明的服务注册和服务发现机制。
  • 强大的故障发现和自我修复能力。
  • 以及多粒度的资源配额管理能力。
  • 提供了完善的管理工具,这些工具涵盖了包括开发、部署测试、运维监控在内的各个环节。

因此,Kubernetes 是一个全新的基于容器技术的分布式架构解决方案,并且是一个一站式的、完备的、分布式的、系统开发和支撑平台。

Kubernetes 是谷歌十几年以来大规模应用容器技术的经验积累和升华的重要成果。确切地说,Kubernetes 是谷歌严格保密十几年的秘密武器 —— Borg 的一个开源版本。

十几年以来,谷歌一直通过 Borg 系统管理着数量庞大的应用程序集群。由于谷歌员工都签署了保密协议,即便离职也不能泄露 Borg 的内部设计,所以外界一直无法了解关于它的更多信息。直到 2015 年 4 月,传闻许久的 Borg 论文伴随 Kubernetes 的高调宣传被谷歌首次公开。

如果我们的系统设计遵循了 Kubernetes 的设计思想,那么传统系统架构中那些和业务没有多大关系的底层代码或功能模块,都可以立刻从我们的视线中消失,我们不必再费心于负载均衡器的选型和部署实施问题,不必再考虑引入或自己开发一个复杂的服务治理框架,不必再头疼于服务监控和故障处理模块的开发。

Kubernetes 是一个开放的开发平台。与 J2EE 不同,它不局限于任何一种语言,没有限定任何编程接口,所以不论是用 Java、Go、C++ 还是用 Python 编写的服务,都可以被映射为 Kubernetes 的 Service(服务),并通过标准的 TCP 通信协议进行交互。

K8S 与 Docker

近几年,Kubernetes 已经成为自有机房、云上广泛使用的容器编排方案,最广泛的使用方式是 Kubernetes+Docker。运维工程师一方面用 Kubernetes 中的 kubctl 命令、K8S API 来操作集群,一方面在单机用 Docker 命令来管理镜像、运行镜像。技术专家杨明越给 InfoQ 总结道:“单独用 Kubernetes,下层不是 Docker 的情况,并不算很多”。

在生产环境中,企业大量的使用的是经典 Kubernetes+ Docker 方案,同时在一些公司的场景中也有单独使用 Docker 的情况。

如果用户的业务部署比较简单,规模也较小,仅仅是为了使用容器做应用交付的便利,也没有其他的功能需求的话,仅仅通过 docker run/docker-compose 这种方式也能管得好的话,实际上是没必要非得引入 Kubernetes 这样非常重的编排组件的。反而直接仅仅使用 Docker 会更轻量,也更好运维。比如 toB 的软件交付的情况,如果要部署的软件的部署架构比较简单的话,仅仅涉及少量几台机器,服务进程也不多的情况下,也有不少是直接使用 Docker,而不引入 Kubernetes 的,这样比较轻量、简单。

还有一个就是,使用 kubernetes 做编排引擎的情况下,实际开发者在日常的开发中,也会比较多地使用 Docker。

但在 2020 年,Kubernetes 官方发布公告,宣布自 v1.20 起放弃对 Docker 的支持,届时用户将收到 Docker 弃用警告,并需要改用其他容器运行时(但 Docker 作为容器镜像构建工具的作用将不受影响,用其构建的容器镜像将一如既往地在集群中与所有容器运行时正常运转)。

K8S 的定位

  • K8S 不代表容器云,只是容器云中很小一部分。
  • K8S 曾经三分天下占其一,并有机会一统天下。
  • K8S 仍努力一统天下。
  • 目前趋势是回归简单的玩法。

容器云技术栈:示例

为什么要用 K8S?

2015 年,谷歌联合 20 多家公司一起建立了 CNCF(Cloud Native Computing Foundation,云原生计算基金会)开源组织来推广 Kubernetes,并由此开创了云原生应用(Cloud Native Application)的新时代。作为 CNCF“钦定”的官方云原生平台,Kubernetes 正在颠覆应用程序的开发方式。

  • 可以“轻装上阵”地开发复杂系统。
  • 可以全面拥抱微服务架构。
  • 可以随时随地将系统整体“搬迁”到公有云上。
  • Kubernetes 内在的服务弹性扩容机制可以让我们轻松应对突发流量。
  • Kubernetes 系统架构超强的横向扩容能力可以让我们的竞争力大大提升。

 

2. Kubernetes 核心组件

集群

集群是一组节点(Node),可以是物理机或虚拟机,之上安装 kubernetes 平台。

Master

Kubernetes 里的 Master 指的是集群控制节点,在每个 Kubernetes 集群里都需要有一个 Master 来负责整个集群的管理和控制,基本上 Kubernetes 的所有控制命令都发给它,它负责具体的执行过程,我们执行的所有命令基本都是在 Master 上运行的。

  • Kubernetes API Server (kube-apiserver):提供了 HTTP Rest 接口的关键服务进程,是 Kubernetes 里所有资源的增、删、改、查等操作的唯一入口,也是集群控制的入口进程。
  • Kubernetes Controller Manager (kube-controller-manager): Kubernetes 里所有资源对象的自动化控制中心,可以将其理解为资源对象的“大总管”。
  • Kubernetes Scheduler (kube-scheduler):负责资源调度(Pod 调度)的进程,相当于公交公司的“调度室”。

另外,在 Master 上通常还需要部署 etcd 服务,因为 Kubernetes 里的所有资源对象的数据都被保存在 etcd 中。

Node

除了 Master , Kubernetes 集群中的其他机器被称为 Node。

Node 是 Kubernetes 集群中的工作负载节点,每个 Node 都会被 Master 分配一些工作负载(Docker 容器),当某个 Node 宕机时,其工作负载会被 Master 自动转移到其他节点上。

  • Kubelet :负责 Pod 对应的容器的创建、启停等任务,同时与 Master 密切协作,实现集群管理的基本功能。
  • Kube Proxy :实现 Kubernetes Service 的通信与负载均衡机制的重要组件。
  • Docker Engine:Docker 引擎,负责本机的容器创建和管理工作。

Pod

Pod 是 Kubernetes 最重要的基本概念,在 Kubernetes 中,创建、调度和管理的最小部署单位是 Pod,而不是容器。

Pod 安排在节点上,包含一组容器和卷。Pod 是短暂的,不是持续性实体。

下图是 Pod 的组成示意图,我们看到每个 Pod 都有一个特殊的被称为“根容器”的 Pause 容器。 Pause 容器对应的镜像属于 Kubernetes 平台的一部分,除了 Pause 容器,每个 Pod 还包含一个或多个紧密相关的用户业务容器。

Kubernetes 将应用的具体实例抽象为 Pod。每个 Pod 首先会启动一个 google_containers/pause docker 容器,然后再启动应用真正的 Docker 容器。这样做的目的是为了可以将多个 Docker 容器封装到一个 Pod 中,Pod 里的多个业务容器共享 Pause 容器的 IP 和共享 Pause 容器挂接的 Volume,这样既简化了密切关联的业务容器之间的通信问题,也很好地解决了它们之间的文件共享问题

在容器的生命周期里,每个 Pod 被分配到节点(Node)上运行直至运行结束或被删除。当一个节点消失时,该节点上的 Pod 也随之被删除。每个 Pod 只会被调度一次,不会重复分配给别的节点。由 Replication  Controller 负责创建新的 Pod 来代替旧的。

         

你可能会有这些问题:

  • 如果 Pod 是短暂的,那么我怎么才能持久化容器数据使其能够跨重启而存在呢? 

   答:Kubernetes 支持“卷”(Volume)的概念,因此可以使用持久化的卷类型。

  • 是否手动创建 Pod?如果想要创建同一个容器的多份拷贝,需要一个个分别创建出来么?

     答:可以手动创建单个 Pod,但是也可以使用 Replication Controller 使用 Pod 模板创建出多份拷贝。

  • 如果 Pod 是短暂的,那么重启时 IP 地址可能会改变,那么怎么才能从前端容器正确可靠地指向后台容器呢?

   答:这时可以使用 Service。

开发 Pod 的原因——资源共享和通信

Pod 的存在使同个 Pod 之下的容器之间能更方便的共享数据和通信。

同个 Pod 下的容器使用相同的网络命名空间、IP 地址和端口区间,相互之间能通过 localhost 来发现和通信。在一个五层次的共享网络中,每个 Pod 都有一个 IP 地址用于跟别的物理主机和容器通信,Pod 的名字就用做容器通信时的主机名。

在同个 Pod 内运行的容器还共享一块存储卷空间(Volumn),存储卷内的数据不会在容器重启后丢失,同时能被同 Pod 下别的容器读取。

管理

相比原生的容器接口,Pod 通过提供更高层次的抽象,简化了应用的部署和管理。Pod 就像一个横向部署和管理的单元,主机托管、资源共享、协调复制和依赖管理都可以自动处理。

Pod 功能

Pod 能应用于构建垂直集成应用栈,但它主要为了集中管理一些辅助程序,如:

  • 内容管理系统,文件和数据载入器,本地缓存管理等;
  • 日志和检查点备份,压缩,轮换,快照系统等;
  • 数据变化监视,日志末端数据读取,日志和监控适配器,事件打印等;
  • 代理,桥接和适配器;
  • 控制器,管理器,配置编辑器和更新器。

Pod 的设计并不是为了运行同一个应用的多个实例。

其他考虑因素

为什么不直接在单个 Docker 容器中运行多个程序?主要是出于以下几个原因:

  • 透明性:将 Pod 内的容器向基础设施可见,底层系统就能向容器提供如进程管理和资源监控等服务,这样能给用户带来极大便利;
  • 解绑软件的依赖:这样单个的容器可以独立地重建和重新部署。未来有可能在 Kubernetes 上实现独立容器的实时更新;
  • 易用性:用户不需要运行自己的进程管理器,也不需负责信号量和退出码的传递等;
  • 高效性:因为底层设备负责更多了管理,容器因而能更轻量化。

为什么不直接将相近的容器集中管理呢?那种方法虽然提供了集中管理的功能,但却没有 Pod 所拥有的大部分便利之处,如不提供资源共享和进程间通信,无法保证容器同时开始和结束,不能简化管理等。

Volume(卷)

一个卷就是一个目录,容器对其有访问权限。

Volume 是一个能够被容器访问的目录,它可能还会包含一些数据。Kubernetes Volumes 与 Docker Volumes 类似,但并不完全相同。

一个 Pod 会在它的 Container Manifest 属性中指明其容器需要哪些 Volumes。

容器中的进程可见的文件系统视图由两个源组成:一个单独的 Docker image 和零个或多个 Volumes。Docker image 位于文件层次结构的根部。所有的 Volumes 都挂载在 Docker image 的节点上。Volumes 不能挂载在其他的 Volumes 上,也没有连接其他 Volumes 的硬链接。

Pod 中的每个容器都单独地指明了其 Image 挂载的 Volume。这会通过 Volume Mount 属性来确定。

Label

一个 Label 是一个 key=value 的键值对,其中 key 与 value 由用户自己指定。Label 可以被附加到各种资源对象上,例如 Node、Pod、Service、RC 等,一个资源对象可以定义任意数量的 Label,同一个 Label 也可以被添加到任意数量的资源对象上。Label 通常在资源对象定义时确定,也可以在对象创建后动态添加或者删除。

Label 的主要作用是为了筛选想要的资源。

比如,你可能创建了一个"tier"和“app”标签,通过 Label( tier=frontend, app=myapp )来标记前端 Pod 容器,使用 Label(tier=backend, app=myapp)标记后台 Pod。然后可以使用 Selectors 选择带有特定 Label 的 Pod,并且将 Service 或者 Replication Controller 应用到上面。

Replication Controller

是否需要手动创建 Pod?如果想要创建同一个容器的多份拷贝,需要一个个分别创建出来么,能否将 Pods 划到逻辑组里?

在大多数情况下,我们通过定义一个 Replication Controller(RC)实现 Pod 的创建及副本数量的自动控制。

Replication Controller 确保任意时间都有指定数量的 Pod “副本”在运行。如果为某个 Pod 创建了 Replication Controller 并且指定 3 个副本,它会创建 3 个 Pod,并且持续监控它们。如果某个 Pod 不响应,那么 Replication Controller 会替换它,保持总数为 3。如果之前不响应的 Pod 恢复了,现在就有 4 个 Pod 了,那么 Replication Controller 会将其中一个终止保持总数为 3。如果在运行中将副本总数改为 5,Replication Controller 会立刻启动 2 个新 Pod,保证总数为 5。还可以按照这样的方式缩小 Pod,这个特性在执行滚动升级时很有用。

 当创建 Replication Controller时,需要指定两个东西:

  • Pod 模板 :用来创建 Pod 副本的模板。
  • Label :Replication Controller 需要监控的 Pod 的标签。

RC 目前已经被 RS(Replica Set)替代。

现在已经创建了 Pod 的一些副本,那么在这些副本上如何均衡负载呢?我们需要的是 Service。

Deployment

Deployment 用于更好地解决 Pod 的编排问题。为此, Deployment 在内部使用了 Replica Set 来实现目的,无论从 Deployment 的作用与目的、 YAML 定义,还是从它的具体命令行操作来看,我们都可以把它看作 RC 的一次升级,两者的相似度超过 90%。

Deployment 相对于 RC 的一个最大升级是我们可以随时知道当前 Pod “部署”的进度。

  • 创建一个 Deployment 对象来生成对应的 Replica Set 并完成 Pod 副本的创建。
  • 检查 Deployment 的状态来看部署动作是否完成(Pod 副本数量是否达到预期的值)。
  • 更新 Deployment 以创建新的 Pod(比如镜像升级)。
  • 如果当前 Deployment 不稳定,则回滚到一个早先的 Deployment 版本。
  • 暂停 Deployment 以便于一次性修改多个 PodTemplateSpec 的配置项,之后再恢复 Deployment,进行新的发布。
  • 扩展 Deployment 以应对高负载。
  • 查看 Deployment 的状态,以此作为发布是否成功的指标。
  • 清理不再需要的旧版本 ReplicaSets 。

Service

如果 Pods 是短暂的,那么重启时 IP 地址可能会改变,怎么才能从前端容器正确可靠地指向后台容器呢?——通过 Service。

Service 是定义一系列 Pod 以及访问这些 Pod 的策略的一层抽象,Service 通过 Label 找到 Pod 组。Service 实际上为其管理的一组 pod 提供了负载均衡机制。

现在,假定有 2 个后台 Pod,并且定义后台 Service 的名称为“backend-service”,label 选择器为( tier=backend, app=myapp)。 backend-service 的 Service 会完成如下两件重要的事情:

  1. 会为 Service 创建一个本地集群的 DNS 入口,因此前端 Pod 只需要 DNS 查找主机名为“backend-service”,就能够解析出前端应用程序可用的 IP 地址。现在前端已经得到了后台服务的 IP 地址,但是它应该访问 2 个后台 Pod 的哪一个呢?
  2. Service 在这 2 个后台 Pod 之间提供透明的负载均衡,会将请求分发给其中的任意一个(如下面的动画所示)。通过每个 Node 上运行的代理(kube-proxy)完成。

下述动画展示了 Service 的功能(注意该图作了很多简化):

Statefulset

我们常用 RC 和 Deployment 管理无状态 pod,用 Statefulset 管理有状态 pod。

有状态服务的需求如下:

  • 每个节点都有固定的身份 ID,通过这个 ID ,集群中的成员可以相互发现并通信。
  • 集群的规模是比较固定的,集群规模不能随意变动。
  • 集群中的每个节点都是有状态的,通常会持久化数据到永久存储中。
  • 如果磁盘损坏,则集群里的某个节点无法正常运行,集群功能受损。

StatefulSet 里的每个 Pod 都有稳定、唯一的网络标识,可以用来发现集群内的其他成员。假设 StatefulSet 的名称为 kafka ,那么第 1 个 Pod 叫 kafka-0,第 2 个叫 kafka-1,以此类推。

StatefulSet 控制的 Pod 副本的启停顺序是受控的,操作第 n 个 Pod 时,前 n-1 个 Pod 已经是运行且准备好的状态。

StatefulSet 里的 Pod 采用稳定的持久化存储卷,通过 PV 或 PVC 来实现,删除 Pod 时默认不会删除与 StatefulSet 相关的存储卷(为了保证数据的安全)。

PV、PVC

持久存储卷(Persistent Volumn,PV)和持久存储卷声明(Persistent Volumn Claim,PVC)使得 K8s 集群具备了存储的逻辑抽象能力,使得在配置 Pod 的逻辑里可以忽略对实际后台存储技术的配置,而把这项配置的工作交给 PV 的配置者,即集群的管理者。

为了在 Pod 上带来持久性数据,不管他们在哪个节点上被调度,Kubernetes 都支持 PV 和 PVC requests。当一个 Pod 被创建的时候,它可以通过 claim 联系到特定数据卷。PV 可以基于各种各样的插件,比如 GCE 持久性硬盘,亚马逊弹性块储(EBS)、网络文件系统(NFS)、小型计算机系统接口(ISCSI)、GlusterFS 和 RBD。

PV 和 PVC 的这种关系,跟 Node 和 Pod 的关系是非常类似的;PV 和 Node 是资源的提供者,根据集群的基础设施变化而变化,由 K8S 集群管理员配置;而 PVC 和 Pod 是资源的使用者,根据业务服务的需求变化而变化,由 K8S 集群的使用者即服务的管理员来配置。

设置持久化的工作流包括配置底层文件系统或者云数据卷和创建持久性数据卷,最后创建 claim 来将 Pod 跟数据卷关联起来。这个解耦方法可以将 Pod 和数据卷完全分离,或者 Pod 不需要知道确切的文件系统或者支持它的持久性引擎。

Namespace

Namespace在很多情况下用于实现多租户的资源隔离。

Namespace通过将集群内部的资源对象“分配”到不同的 Namespace 中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。

 

posted @ 2021-04-22 12:25  Juno3550  阅读(165)  评论(0编辑  收藏  举报