一、什么是 Kubernetes ?
Kubernetes 是一个可以移植、可扩展的开源平台,使用声明式的配置并依据配置信息自动地执行容器化应用程序的管理。在所有的容器编排工具中(类似的还有 docker swarm / mesos等),Kubernetes 的生态系统更大、增长更快,有更多的支持、服务和工具可供用户选择。
Kubernetes 的名字起源于希腊语,含义是 舵手、领航员、向导。Google 于 2014 年将 Brog 系统开源为 Kubernetes。Kubernetes 构建在 Google Brog 十五年运行大规模分布式系统的经验基础之上,并结合了开源社区最好的想法和实践。
以下是使用近三年中国地区 google trends 对比 kubernetes 、 docker swarm、 mesos 三个关键词的截图:
二、Kubernetes 基本概念
1、集群 Cluster
超大计算机抽象,由节点组成,这些节点可以是物理服务器或者虚拟机,在上面安装了 Kubernetes 平台。
2、节点 Node
Node(节点)是 kubernetes 集群中的计算机,可以是虚拟机或物理机。每个 Node(节点)都由 master 管理。一个 Node(节点)可以有多个Pod(容器组),kubernetes master 会根据每个 Node(节点)上可用资源的情况,自动调度 Pod(容器组)到最佳的 Node(节点)上。
每个 Kubernetes Node(节点)至少运行:
- Kubelet,负责 master 节点和 worker 节点之间通信的进程;管理 Pod(容器组)和 Pod(容器组)内运行的 Container(容器)。
- 容器运行环境(如Docker)负责下载镜像、创建和运行容器等。
3、命名空间 Namespace
Namespace(命名空间)是对一组资源和对象的抽象集合,Kubernetes 资源逻辑的隔离机制,比如可以用来将系统内部的对象划分为不同的项目组或用户组。常见的 Pods、Services、Deployments 等都是属于某一个 Namespace 的(默认是default),比如 Nginx Pod 没有指定 namespace,则默认就在 default 命名空间下面,而 Node, PersistentVolumes 等资源则不属于任何 Namespace,是全局的。
注意它并不是 Linux Namespace,二者没有任何关系,它只是 Kubernetes 划分不同工作空间的一个逻辑单位。
4、容器 Container
应用居住和运行在容器中,容器实际上是一个 Linux Namespace
、Linux Cgroups
和 rootfs 三种技术构造出来的进程的隔离环境。
- 容器的静态视图:一组联合挂载在
/var/lib/docker/…
上的 rootfs,即 “容器镜像” Container Image。 - 容器的动态视图:一个由 Namespace 和 Cgroup 构成的隔离环境,即 “容器运行时” Container Runtime。
作为一个测试者,不关心容器运行时,容器镜像才是真正承载容器信息进程传递的。容器编排由此出现,容器从此走向容器云。
5、调度单位 Pod
Kubernetes 的基本调度单位,Pod 是一组紧密关联的容器集合,它们共享 PID、IPC、Network 和 UTS namespace。Pod 的设计理念是支持多个容器在一个 Pod 中共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。我们知道容器本质上就是进程,那么 Pod 实际上就是进程组了,只是这一组进程是作为一个整体来进行调度的。
6、标签 Label/Selector
Kubernetes 资源打标签和定位机制,Label 标签在 Kubernetes 资源对象中使用很多,也是非常重要的一个属性,Label 是识别 Kubernetes 对象的标签,以 key/value
的方式附加到对象上(key最长不能超过63字节,value 可以为空,也可以是不超过253字节的字符串)。Label 不提供唯一性,并且实际上经常是很多对象(如Pods)都使用相同的 Label 来标志具体的应用。Label 定义好后其他对象可以使用 Label Selector 来选择一组相同 Label 的对象(比如 Service 用 Label 来选择一组 Pod)。
Label Selector 支持以下几种方式:
- 等式,如:
app=webapp
和env!=prod
- 集合,如:env in (prod, test)
- 多个 Label(它们之间是 AND 关系),如:
app=webapp
,env=prod
,,version=v2
7、副本集 ReplicaSet
手动创建 Pod,如果想要创建同一个容器的多份拷贝,需要一个个分别创建出来么,能否将 Pods 划到逻辑组里?
主要用于创建和管理 Pod,支持无状态应用,ReplicaSet 确保任意时间都有指定数量的 Pod “副本”在运行。如为某个 Pod 创建了 ReplicaSet 并且指定 3 个副本,它会创建 3 个Pod,并且持续监控它们。如果某个 Pod 不响应,那么 ReplicaSet 会替换它,保持总数为 3:
8、服务 Service
应用 Pods 的访问点,屏蔽 IP 寻址和负载均衡,具体上说 Service 是应用服务的抽象,通过 Labels 为应用提供负载均衡和服务发现。匹配 Labels 的 Pod IP 和端口列表组成 Endpoints,由 kube-proxy 负责将服务 IP 负载均衡到这些 Endpoints 上。
每个 Service 都会自动分配一个 cluster IP(仅在集群内部可访问的虚拟地址)和 DNS 名,其他容器可以通过该地址或 DNS 来访问服务,而不需要了解后端容器的运行。
9、发布 Deployment
Deployment 管理 ReplicaSet,支持滚动等高级发布机制,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 的标签。
10、配置管理 ConfigMap/Secret
日常一个重要的需求就是应用的配置管理、敏感信息的存储和使用(如:密码、Token 等)、容器运行资源的配置、安全管控、身份认证等等。
对于应用的可变配置在 Kubernetes 中是通过一个 ConfigMap 资源对象来实现的,我们知道许多应用经常会有从配置文件、命令行参数或者环境变量中读取一些配置信息的需求,这些配置信息我们肯定不会直接写死到应用程序中去的,比如你一个应用连接一个 redis 服务,下一次想更换一个了的,还得重新去修改代码,重新制作一个镜像,这肯定是不可取的,而 ConfigMap 就给我们提供了向容器中注入配置信息的能力,不仅可以用来保存单个属性,还可以用来保存整个配置文件,比如我们可以用来配置一个 redis 服务的访问地址,也可以用来保存整个 redis 的配置文件。
一般情况下 ConfigMap 是用来存储一些非安全的配置信息,如果涉及到一些安全相关的数据的话用 ConfigMap 就非常不妥了,因为 ConfigMap 是明文存储的,这个时候我们就需要用到另外一个资源对象了:Secret,Secret 用来保存敏感信息,例如密码、OAuth 令牌和 ssh key 等等,将这些信息放在 Secret 中比放在 Pod 的定义中或者 Docker 镜像中要更加安全和灵活。
11、守护进程 DaemonSet
DaemonSet 用于在每个 Kubernetes 节点中将守护进程的副本作为后台进程运行,说简单点就是在每个节点部署一个 Pod 副本,当节点加入到 Kubernetes 集群中,Pod 会被调度到该节点上运行,当节点从集群只能够被移除后,该节点上的这个 Pod 也会被移除,当然,如果我们删除 DaemonSet,所有和这个对象相关的 Pods 都会被删除。
那么在哪种情况下我们会需要用到这种业务场景呢?其实这种场景还是比较普通的,比如:
- 集群存储守护程序,如 glusterd、ceph 要部署在每个节点上以提供持久性存储;
- 节点监控守护进程,如 Prometheus 监控集群,可以在每个节点上运行一个 node-exporter 进程来收集监控节点的信息;
- 日志收集守护程序,如 fluentd 或 logstash,在每个节点上运行以收集容器的日志;
- 节点网络插件,比如 flannel、calico,在每个节点上运行为 Pod 提供网络服务。
12、数据卷 Volume
Kubernetes Volume(数据卷)主要解决了如下两方面问题:
- 数据持久性:通常情况下,容器运行起来之后,写入到其文件系统的文件暂时性的。当容器崩溃后,kubelet 将会重启该容器,此时原容器运行后写入的文件将丢失,因为容器将重新从镜像创建;
- 数据共享:同一个 Pod(容器组)中运行的容器之间,经常会存在共享文件/文件夹的需求。
Docker 里同样也存在一个 volume(数据卷)的概念,但是 docker 对数据卷的管理相对 kubernetes 而言要更少一些。在 Docker 里,一个 Volume(数据卷)仅仅是宿主机(或另一个容器)文件系统上的一个文件夹。Docker 并不管理 Volume(数据卷)的生命周期。
在 Kubernetes 里,Volume(数据卷)存在明确的生命周期(与包含该数据卷的容器组相同)。因此,Volume(数据卷)的生命周期比同一容器组中任意容器的生命周期要更长,不管容器重启了多少次,数据都能被保留下来。当然,如果容器组退出了,数据卷也就自然退出了。此时,根据容器组所使用的 Volume(数据卷)类型不同,数据可能随数据卷的退出而删除,也可能被真正持久化,并在下次容器组重启时仍然可以使用。
从根本上来说,一个 Volume(数据卷)仅仅是一个可被容器组中的容器访问的文件目录(也许其中包含一些数据文件)。这个目录是怎么来的,取决于该数据卷的类型(不同类型的数据卷使用不同的存储介质)。
使用 Volume(数据卷)时,我们需要先在容器组中定义一个数据卷,并将其挂载到容器的挂载点上。容器中的一个进程所看到(可访问)的文件系统是由容器的 docker 镜像和容器所挂载的数据卷共同组成的。Docker 镜像将被首先加载到该容器的文件系统,任何数据卷都被在此之后挂载到指定的路径上。Volume(数据卷)不能被挂载到其他数据卷上,或者通过引用其他数据卷。同一个容器组中的不同容器各自独立地挂载数据卷,即同一个容器组中的两个容器可以将同一个数据卷挂载到各自不同的路径上。
我们现在通过下图来理解 容器组、容器、挂载点、数据卷、存储介质(nfs、PVC、ConfigMap)等几个概念之间的关系:
- 一个容器组可以包含多个数据卷、多个容器;
- 一个容器通过挂载点决定某一个数据卷被挂载到容器中的什么路径;
- 不同类型的数据卷对应不同的存储介质(图中列出了 nfs、PVC、ConfigMap 三种存储介质)。
13、PersistentVolume/PersistentVolumeClaims
Kubernetes 超大磁盘存储抽象和分配机制。
- PersistentVolume(持久化卷)简称为 PV ,是对底层共享存储的一种抽象,PV 由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如 Ceph、GlusterFS、NFS、hostPath 等,都是通过插件机制完成与共享存储的对接。
- PersistentVolumeClaim(持久化卷声明)简称为 PVC ,PVC 是用户存储的一种声明,PVC 和 Pod 比较类似,Pod 消耗的是节点,PVC 消耗的是 PV 资源,Pod 可以请求 CPU 和内存,而 PVC 可以请求特定的存储空间和访问模式。对于真正使用存储的用户不需要关心底层的存储实现细节,只需要直接使用 PVC 即可。
- 通过 PVC 请求到一定的存储空间也很有可能不足以满足应用对于存储设备的各种需求,而且不同的应用程序对于存储性能的要求可能也不尽相同,比如读写速度、并发性能等,为了解决这一问题,Kubernetes 又为我们引入了一个新的资源对象:StorageClass,通过 StorageClass 的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了,此外 StorageClass 还可以为我们自动生成 PV,免去了每次手动创建的麻烦。
14、StatefulSet
类似 ReplicaSet,支持有状态应用。无状态服务利用我们前面的 Deployment 可以很好的进行编排,对应有状态服务,需要考虑的细节就要多很多了,容器化应用程序最困难的任务之一,就是设计有状态分布式组件的部署体系结构。由于无状态组件没有预定义的启动顺序、集群要求、点对点 TCP 连接、唯一的网络标识符、正常的启动和终止要求等,因此可以很容易地进行容器化。诸如数据库,大数据分析系统,分布式 key/value 存储、消息中间件需要有复杂的分布式体系结构,都可能会用到上述功能。
为此,Kubernetes 引入了 StatefulSet 这种资源对象来支持这种复杂的需求。StatefulSet 类似于 ReplicaSet,但是它可以处理 Pod 的启动顺序,为保留每个 Pod 的状态设置唯一标识,具有以下几个功能特性:
- 稳定的、唯一的网络标识符
- 稳定的、持久化的存储
- 有序的、优雅的部署和缩放
- 有序的、优雅的删除和终止
- 有序的、自动滚动更新
15、Job/CronJob
运行一次就结束的任务/周期性运行的任务。
我们在日常的工作中经常都会遇到一些需要进行批量数据处理和分析的需求,当然也会有按时间来进行调度的工作,在我们的 Kubernetes 集群中为我们提供了 Job 和 CronJob 两种资源对象来应对我们的这种需求。
Job 负责处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束。而 CronJob 则就是在 Job 上加上了时间调度。
16、Readiness Probe(就绪探针)
有了活性探针后能保证程序在运行中如果挂掉能够自动重启,但是还有个经常遇到的问题,比如说,在 Kubernetes 中启动 Pod,显示明明 Pod 已经启动成功,且能访问里面的端口,但是却返回错误信息。还有就是在执行滚动更新时候,总会出现一段时间,Pod 对外提供网络访问,但是访问却发生 404,这两个原因,都是因为 Pod 已经成功启动,但是 Pod 的的容器中应用程序还在启动中导致,考虑到这点 Kubernetes 推出了就绪探针机制。
就绪探针,流量接入 Pod 判断依据, 用于判断容器中应用是否启动完成,当探测成功后才使 Pod 对外提供网络访问,设置容器 Ready 状态为 true,如果探测失败,则设置容器的 Ready 状态为 false。对于被 Service 管理的 Pod,Service 与 Pod、EndPoint 的关联关系也将基于 Pod 是否为 Ready 状态进行设置,如果 Pod 运行过程中 Ready 状态变为 false,则系统自动从 Service 关联的 EndPoint 列表中移除,如果 Pod 恢复为 Ready 状态。将再会被加回 Endpoint 列表。通过这种机制就能防止将流量转发到不可用的 Pod 上。
17、 Liveness Probe(存活探针)
在 Kubernetes 中 Pod 是最小的计算单元,而一个 Pod 又由多个容器组成,相当于每个容器就是一个应用,应用在运行期间,可能因为某也意外情况致使程序挂掉。那么如何监控这些容器状态稳定性,保证服务在运行期间不会发生问题,发生问题后进行重启等机制,就成为了重中之重的事情,考虑到这点 kubernetes 推出了存活探针机制。
存活探针,是否 kill Pod 的判断依据 ,用指定的方式进入容器检测容器中的应用是否正常运行,如果检测失败,则认为容器不健康,那么 Kubelet 将根据 Pod 中设置的 restartPolicy (重启策略)来判断,Pod 是否要进行重启操作,如果容器配置中没有配置 livenessProbe 存活探针,Kubelet 将认为存活探针探测一直为成功状态。
三、小结
以上,下表做个基本概念总结:
概念 | 作用 |
---|---|
Cluster | 超大计算机抽象,由节点(node)组成 |
node | 集群中的计算机 |
Container | 应用居住和运行在容器中 |
Pod | Kubernetes 基本调度单位 |
ReplicaSet | 创建和管理 Pod,支持无状态应用 |
Service | 应用 Pods 的访问点,屏蔽 IP 寻址和负载均衡 |
Deployment | 管理 ReplicaSet,支持滚动等高级发布机制 |
ConfigMap/Secrets | 应用配置,secret 敏感数据配置 |
DaemonSet | 保证每个节点有且仅有一个 Pod,常见于监控 |
StatefulSet | 类似 ReplicaSet,但支持有状态应用 |
Job | 运行一次就结束的任务 |
CronJob | 周期性运行的任务 |
Volume | 可装载磁盘文件存储 |
PersisentVolume/PersistentVolumeClaims | 超大磁盘存储抽象和分配机制 |
Label/Selector | 资源打标签和定位机制 |
Namespace | 资源逻辑隔离机制 |
Readiness Probe | 就绪探针,流量接入 Pod 判断依据 |
Liveness Probe | 存活探针,是否 kill Pod 的判断依据 |