Kubernetes系列-入门篇

1.前言

  虽然Kubernetes已经不是什么新鲜的东西,在互联网,我们可以轻易的获取相关的文档及书籍。于我而言,Kubernetes无非就是一种技术,一种为满足某些目标而设计开发的管理平台。它之所以那么流行,除了有大厂的背景加持之外,更重要的是它真正的解决了某些问题,以及它背后一些优秀的设计。Kubernetes对于准备使用Kubernetes的人来说,那是必须要学习的。但是对于那些平时工作中不会用到的技术人员来说,学习和了解它有什么用呢?
  这一次,我选择一些认为无论是即将使用Kubernetes,或者开发人员都值得学习的地方。包括一些设计方面的内容,学习它们,摸清规律。希望我们跳出Kubernetes本身,在今后的技术工作中进行实际的转化。本文主要包含五个部分,发展背景、架构、设计理念、关键组件、Kubernetes对象。以及最后一个简单的总结。

2.发展背景

  在讲Kubernetes之前,我们先来简单的了解一下docker容器(容器不仅只有docker一种,如AppC、Mesos Container),Docker作为容器技术的一种,其Docker镜像解决的根本问题就是应用打包的问题。说到底,Docker通过提供一种非常方便的打包机制,直接打包了应用运行所需要的整个操作系统(其实只打包了文件系统),从而保证了本地环境和云端环境的高度一致。将镜像还原成运行时的过程(就是读取镜像文件,还原那个时刻的过程)就是容器运行的过程。Docker毕竟只是解决了打包的问题。在微服务化情况下,容器数量会非常多,如何管理容器,持续集成和编排的问题不可避免。
  这时候,Kubernetes、Mesos,Swarm三大平台也为解决容器的问题而出现,互相竞争,抢占市场。Kubernetes基于google公司已经使用了十多年的Borg项目进行了沉淀和升华才提出的一套框架,使其拥有一套完整的全新的设计理念,在设计上有很强的扩展性。同时,有Google的背书,以及相对优秀的社区运营能力。所以最终Kubernetes赢得了胜利,成为了容器生态的行业标准。
  Kubernetes是容器集群管理系统,是一个开源的平台,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。kubernetes主要解决容器编排问题。

3.架构

学习Kubernetes的架构,有助于我们了解Kubernetes的全貌,帮助我们进一步学习Kubernetes其他内容。

avatar
  Kubernetes的功能组件是松耦合的,它们各司其职,功能明确。有负责外部资源请求的安全访问和控制,有的负责内部资源任务的调度(Scheduler)。然后由统一的api将对应的操作请求分发到对应的物理节点。再由各个节点的代理人(kubelet)根据资源的请求去管理容器(pod)。
  Kubernetes本身细节很多,但从它的架构图来看,整体简单明了。我认为优秀的设计,往往在整体上是简单的,而且是易于理解的。

4.关键组件

  Kubernetes集群有各种二进制组件,这些组件承担不同的角色,共同支持kubernetes集群的运行。下面是一些kubernetes核心主键。Kubernetes集群角色有管理节点和工作节点,它们都是可以分布式部署的。管理节点主要负责整个集群的控制。对资源调度和配置,以及持久化集群的数据(节点,容器状态等,而非具体某个业务应用的日志等数据)。工作节点提供Kubernetes运行时环境,以及维护Pod,也就是实际运行我们应用的节点。

4.1 管理节点(Master)主要组件

  • kube-apiserver
    kube-apiserver用于暴露Kubernetes API。任何的资源请求/调用操作都是通过kube-apiserver提供的接口进行。

  • ETCD
    etcd是Kubernetes提供默认的存储系统,保存所有集群数据,使用时需要为etcd数据提供备份计划。

  • kube-controller-manager
    kube-controller-manager运行管理控制器,它们是集群中处理常规任务的后台线程。逻辑上,每个控制器是一个单独的进程,但为了降低复杂性,它们都被编译成单个二进制文件,并在单个进程中运行。
    这些控制器主要包括:
    节点(Node)控制器。
    副本(Replication)控制器:负责维护系统中每个副本中的pod。
    端点(Endpoints)控制器:填充Endpoints对象(即连接Services&Pods)。
    Service Account和Token控制器:为新的Namespace 创建默认帐户访问API Token。

  • cloud-controller-manager
    云控制器管理器负责与底层云提供商的平台交互。云控制器管理器是Kubernetes版本1.6中引入的,它是Kubernetes中开放给云厂商的通用接口,便于Kubernetes自动管理和利用云服务商提供的资源,这些资源包括虚拟机资源、负载均衡服务、弹性公网IP、存储服务等。
    云控制器管理器仅运行云提供商特定的(controller loops)控制器循环。可以通过将–cloud-provider flag设置为external启动kube-controller-manager ,来禁用控制器循环。
    cloud-controller-manager 具体功能:
    节点(Node)控制器
    路由(Route)控制器
    Service控制器
    卷(Volume)控制器

4.2 工作节点(Node)主要组件

  • kubelet
    kubelet是主要的节点代理,它会监视已分配给节点的pod,具体功能:
    安装Pod所需的volume。
    下载Pod的Secrets。
    Pod中运行的 docker(或experimentally,rkt)容器。
    定期执行容器健康检查。
    报告容器个节点的状态
  • kube-proxy
    kube-proxy通过在主机上维护网络规则并执行连接转发来实现Kubernetes服务抽象。简单来说,就是负责Kubernetes集群内部网络通信的。

5.Kubernetes对象

  上文出现了不少名词概念,如Namespaces、pod、service、卷(Volume)、副本(Replication)等,它们都是Kubernetes的对象类型,它们有不同的特性,负责不同的功能。而Kubernetes对象是Kubernetes系统中的持久实体。Kubernetes使用这些实体来表示集群的状态。具体来说它们可以描述:
容器化应用正在运行(以及在哪些节点上)
这些应用可用的资源关于这些应用如何运行的策略,如重新策略,升级和容错
  Kubernetes对象是“record of intent”,一旦创建了对象,Kubernetes系统会确保对象存在。通过创建对象,可以有效地告诉Kubernetes系统你希望集群的工作负载是什么样的。
要使用Kubernetes对象(无论是创建,修改还是删除),都需要使用Kubernetes API。例如,当使用kubectl命令管理工具时,CLI会为提供Kubernetes API调用。你也可以直接在自己的程序中使用Kubernetes API,Kubernetes提供一个golang客户端库 (其他语言库正在开发中-如Python)。
常见的对象类型:

  • Pod
    Pod是Kubernetes创建或部署的最小/最简单的基本单位,一个Pod代表集群上正在运行的一个进程。
    一个Pod封装一个应用容器(也可以有多个容器),存储资源、一个独立的网络IP以及管理控制容器运行方式的策略选项。Pod代表部署的一个单位:Kubernetes中单个应用的实例,它可能由单个容器或多个容器共享组成的资源。

  • Replication Controller
    ReplicationController(简称RC)是确保用户定义的Pod副本数保持不变。在用户定义范围内,如果pod增多,则ReplicationController会终止额外的pod,如果减少,RC会创建新的pod,始终保持在定义范围。例如,RC会在Pod维护(例如内核升级)后在节点上重新创建新Pod。

  • ReplicaSet

ReplicaSet(RS)是Replication Controller(RC)的升级版本。ReplicaSet 和 Replication Controller之间的唯一区别是对选择器的支持。

  • Deployment
    Deployment又叫部署,这是我们部署应用最常用的对象类型。
    Deployment为Pod和Replica Set提供声明式更新,它是更上一层的抽象和封装。通过Deployment控制Replica Set,用作pod 机制的创建、删除和更新。
    你只需要在 Deployment 中描述您想要的目标状态是什么,Deployment controller 就会帮您将 Pod 和ReplicaSet 的实际状态改变到您的目标状态。您可以定义一个全新的 Deployment 来创建 ReplicaSet 或者删除已有的 Deployment 并创建一个新的来替换。

  • Service
    我们知道Pod是有生命周期的,它们可以被创建,也可以被销毁,然而一旦被销毁生命就永远结束。通过 ReplicationController能够动态地创建和销毁Pod(例如,需要进行扩缩容,或者执行 滚动升级)。每个Pod都会获取它自己的IP地址,这些IP并不是固定的。 这会导致一个问题:如果前端要访问后端容器Pod,那么如果IP变了怎么办,所以这时候就需要Service登场了。
    Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。 这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector将Pod与Service关联起来。
    Service对象里面允许你定义一个固定的IP,这样,外部调用的时候,就可以直接访问这个固定IP,Service接到请求之后,自动负载均衡的转发到关联的Pod上。

  • Volume
    翻译过来叫做“卷”,显然和存储有关。
    默认情况下容器中的磁盘文件是非持久化的,对于运行在容器中的应用来说面临两个问题,第一:当容器挂掉kubelet将重启启动它时,文件将会丢失;第二:当Pod中同时运行多个容器,容器之间需要共享文件时。Kubernetes的Volume解决了这两个问题。
    卷是抽象的对象。它具体是云存储,还是本地磁盘,有专门的其他Kubernetes对象去定义。所以pod在使用时是不需要关心的。

  • StatefulSets
    StatefulSets(有状态系统服务设计),从名称我们可以看出来,它是针对那些有状态、需要持久化的应用设计的,比如mysql,zookeeper。当我们将一个pod或者一组pod用StatefulSets声明时,它将会按照StatefulSets部署的要求部署和更新。如果条件
    不满足,就不会执行。
    Pod调度运行时,如果应用不需要任何稳定的标示、有序的部署、删除和扩展,则应该使用一组无状态副本的控制器来部署应用,例如 Deployment 或 ReplicaSet更适合无状态服务需求。

  • Labels 和 Selectors
    标签,和标签选择器,是key-value的形式,一个对象可以定义多个标签。上文提到很多对象,就是利用标签的定义和选择器将它们关联起来的。

  • Namespaces(命名空间)
    命名空间是用来隔离对象资源的。上文提到的如pod、service、Deployment等于应用相关的对象都是需要指定Namespaces的
    Kubernetes可以使用Namespaces(命名空间)创建多个虚拟集群。当团队或项目中具有许多用户时,可以考虑使用Namespace来区分。比如:我们可以用Namespace来按照环境来换分命名如test1、test2、test3,也可以按照项目来换分,如project1、projec2…
    定义了命名空间,我们可以做计算机资源、网络的统一限制,比如默认开放一些端口,默认pod创建时使用多少cpu、内存等等。

6.设计理念

  Kubernetes系统最核心的两个设计理念:一个是容错性,一个是易扩展性。容错性实际是保证Kubernetes系统稳定性和安全性的基础,易扩展性是保证Kubernetes对变更友好,可以快速迭代增加新功能的基础。
  分析和理解Kubernetes的设计理念可以使我们更深入地了解Kubernetes系统,更好地利用它管理分布式部署的云原生应用,另一方面也可以让我们借鉴其在分布式系统设计方面的经验。下文列举了一些Kubernetes的部分设计原则。

6.1 API设计部分原则

对于云计算系统,系统API实际上处于系统设计的统领地位,正如本文前面所说,Kubernetes集群系统每支持一项新功能,引入一项新技术,一定会新引入对应的API对象,支持对该功能的管理操作,理解掌握的API,就好比抓住了Kubernetes系统的牛鼻子。Kubernetes系统API的设计有以下几条原则:

  • 所有API应该是声明式的。正如前文所说,声明式的操作,相对于命令式操作,对于重复操作的效果是稳定的,这对于容易出现数据丢失或重复的分布式环境来说是很重要的。另外,声明式操作更容易被用户使用,可以使系统向用户隐藏实现的细节,隐藏实现的细节的同时,也就保留了系统未来持续优化的可能性。此外,声明式的API,同时隐含了所有的API对象都是名词性质的,例如Service、Volume这些API都是名词,这些名词描述了用户所期望得到的一个目标分布式对象。
  • API对象是彼此互补而且可组合的。这里面实际是鼓励API对象尽量实现面向对象设计时的要求,即“高内聚,松耦合”,对业务相关的概念有一个合适的分解,提高分解出来的对象的可重用性。事实上,Kubernetes这种分布式系统管理平台,也是一种业务系统,只不过它的业务就是调度和管理容器服务。所以我们,会看到如Pod、ReplicaSet、Deployment之类的对象,而它们可以组合。
  • API对象状态不能依赖于网络连接状态。由于众所周知,在分布式环境下,网络连接断开是经常发生的事情,因此要保证API对象状态能应对网络的不稳定,API对象的状态就不能依赖于网络连接状态。

6.2 控制机制设计部分原则

  • 控制逻辑应该只依赖于当前状态。这是为了保证分布式系统的稳定可靠,对于经常出现局部错误的分布式系统,如果控制逻辑只依赖当前状态,那么就非常容易将一个暂时出现故障的系统恢复到正常状态,因为你只要将该系统重置到某个稳定状态,就可以自信的知道系统的所有控制逻辑会开始按照正常方式运行。
  • 假设任何错误的可能,并做容错处理。在一个分布式系统中出现局部和临时错误是大概率事件。错误可能来自于物理系统故障,外部系统故障也可能来自于系统自身的代码错误,依靠自己实现的代码不会出错来保证系统稳定其实也是难以实现的,因此要设计对任何可能错误的容错处理。
  • 假设任何操作都可能被任何操作对象拒绝,甚至被错误解析。由于分布式系统的复杂性以及各子系统的相对独立性,不同子系统经常来自不同的开发团队,所以不能奢望任何操作被另一个子系统以正确的方式处理,要保证出现错误的时候,操作级别的错误不会影响到系统稳定性。
  • 每个模块都可以在出错后自动恢复。由于分布式系统中无法保证系统各个模块是始终连接的,因此每个模块要有自我修复的能力,保证不会因为连接不到其他模块而自我崩溃。

7.最后总结

  通过对Kubernetes的时代背景,我们不难看出一些,当我们使用一项新的技术解决问题的时候,往往也暗藏着潜在的问题。
随着时间的推移,规模的变化,这些问题就会暴露出来。而为了去解决新的问题,又创造出新的技术。技术并不是一成不变的,
所以,我们在设计系统的时候,应该尽可能的做到各模块之间彼此独立,不过度依赖,才能保持更灵活的扩展性。

  分层的对象设计,做到了高内聚,松耦合。各个对象组合起来就像洋葱一样,看似一体,实则又是一个个独立的个体对象。比如从Pod,到ReplicaSet,到Deployment,再到Service。从内到外,它们一层包着一层,剥掉最外的一层,它还是能独立运行,再拨一层也还是能独立运行。

  我们看到了Kubernetes里运用的对象设计、幂等性设计、统一的Api入口等设计,以及将高容错、自我修复等机制融入到设计之中,使得Kubernetes如此的健壮和高扩展性。这些理念在我们设计分布式系统的时候,如分布式调度平台是值得去借鉴和使用的。

8.参考文档

Kubernetes中文社区 | 中文文档
Kubernetes第1篇:Kubernetes发展简史
你对容器及容器管理平台到底了解多少?
Kubernetes时代背景
Kubernetes:从Cloud Provider到Cloud Controller Mananger全解析

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐