[TOC]

基本概述

Docker

描述:Docker是一个开放源代码软件项目(开源的应用容器引擎),让应用程序部署在软件货柜下的工作可以自动化进行,借此在Linux操作系统上,提供一个额外的软件抽象层,以及操作系统层虚拟化的自动管理机制。

补充说明:

  • 1.主要负责下载镜像、创建和运行容器,以及打包和分发
  • 2.容器是完全使用沙箱机制,相互之间不会有任何接口;

此处Kubernetes使用的容器技术来创建容器。


Etcd

描述:etcd 是兼具一致性和高可用性的键值数据库,可以作为保存 Kubernetes 所有集群数据的后台数据库。
是集群所有组件当中唯一一个有状态服务,对于高可用的集群建议采用 3 或 5 节点的 etcd 集群。
关于etcd进一步学习请参考: Etcd基础学习之架构及工作原理

Kubernetes

Q:什么是Kubernetes?
A:Kubernetes(k8s)是自动化容器操作的开源平台,这些操作包括部署,调度和节点集群间扩展, 可以将K8s看做Docker容器技术的上层插件,它不仅仅支持Docker还支持Rocket容器技术;

功能作用:

1
2
3
4
5
自动化容器的部署和复制
随时扩展或收缩容器规模
将容器组织成组,并且提供容器间的负载均衡
很容易地升级应用程序容器的新版本
提供容器弹性,Node节点故障业务系统自动恢复,如果容器失效就替换它,等等


K8s架构一览图:

WeiyiGeek.架构

WeiyiGeek.架构


k8s与vSphere之间的对比:

  • 1.对于虚拟机来讲述: vSphere 平台为虚拟机准备好运行环境,而 kubernetes 则是为容器准备好运行的环境。
  • 2.对于存储数据库来讲述: 在 vSphere 中有一个 vCenter DB(Postgresql / oralce)里面存放了集群中所有虚拟机以及集群有关的信息,而K8s中则是采用etcd数据库它用来持久化存储k8s集群中所有资源的信息
  • 2.在 vSphere 虚拟化平台中,vCenter 相当于主节点、管理节点、控制节点,是一个控制平面,而 ESXi 主机则是工作节点、虚拟机运行的节点,是应用负载的一个平面。
  • 3.在 k8s 的 master 节点和 vCenter 的角色一样,也是主节点、管理节点、控制节点,是一个控制平面,而 k8s node 节点节点和 ESXi 主机一样是工作节点、Pod和容器运行的节点、是一个应用负载的平面。
    WeiyiGeek.

    WeiyiGeek.


Cluster 集群

描述:集群是一组节点,这些节点可以是物理服务器或者虚拟机,之上安装了Kubernetes平台

典型的Kubernetes架构图:

WeiyiGeek.

WeiyiGeek.


Master 节点

描述: Kubernetes Master提供集群的独特视角,且集群需要一个以上的Master节点;

Master 节点作用:

  • 用来创建和复制Pod的Replication Controller

Kbue-apiserver

描述:它是 Kubernetes 控制面的前端,主节点上负责提供 Kubernetes API 服务的组件
API-Server作为K8s最核心的组件,其他组件(kubelet、kubectl、调度器、控制器管理器等)都会去调用它。

应用场景:

  • 1.API-Server以 RESTful API 的形式提供了可以查询、修改集群状态的 CRUD(Create、Read、Update、Delete)接口, 使其将这些对集群中各种资源的增删改查的状态存储到 etcd 中。
  • 2.API-Server为k8s中系统组件间提供交流的平台,并且是与etcd 通信的唯一组件,其他组件不会直接和 etcd 通信,而是通过 API 服务器来修改集群状态。
  • 3.API-Server提供可以用来和集群交互的REST端点

API-Server 接收到请求后会先经过一系列安全防护的处理,包括授权插件和准入控制插件

  • API 服务器接收到的请求会经过一个认证插件的列表,列表中的每个插件都可以检查这个请求和尝试确定谁在发送这个请求。
  • 列表中的第一个插件可以提取请求中客户端的用户名、用户 ID 和组信息,并返回给 API 服务器。
  • API 服务器会停止调用剩余的认证插件并继续进入授权阶段。授权通过的话才允许创建相应的资源

控制平面可以请求订阅资源被创建、修改或删除的通知, 这使得组件可以在集群元数据变化时候执行任何需要做的任务。

客户端通过创建到 API 服务器的 HTTP 连接来监听变更。通过此连接,客户端会接收到监听对象的一系列变更通知,当更新对象时 API 服务器给所有监听者发送更新过的对象:


kube-scheduler (调度器)

描述: 该组件运行在Master节点之上监视那些新创建的未指定运行节点的Pod,并选择节点让 Pod 在上面运行。

我们通常不会去指定 pod 应该运行在哪个集群节点上而是将这项工作交给调度器。

  • 宏观来看调度器的操作比较简单,就是利用 API server 的监听机制等待新创建的 pod 然后给每个新的、没有节点集的 pod 分配节点。
  • 调度器不会命令选中的节点(或者节点上运行的 Kubelet)去运行 pod,通过 API-server 更新 pod 的定义然后 API server 再去通知 Kubelet,当目标节点上的 Kubelet 发现该 pod 被调度到本节点,它就会创建并且运行 pod 的容器;

调度器为 pod 查找可用节点需要检查关键项:

  • 节点是否满足硬件需求
  • 节点是否已耗尽资源
  • pod 是否配置了 nodeSelector
  • pod 是否配置了 nodePord,节点是否满足
  • 如果 pod 有 PVC 需求,该节点是否可以挂载该 PVC
  • 该节点是否有污点(taint),pod 是否能接收
  • 该节点是否满足 pod 的 affnity 配置
  • 可以通过定义 pod 的亲缘性、非亲缘规则强制 pod 分散在集群内或者集中在一起。
  • pod 会使用默认调度器(default scheduler)进行调度,也可以配置 pod 中的 schedulerName 来设置指定的调度器。

kube-controller-manager (控制器管理器)

描述:API server 只做了存储资源到 etcd 和通知客户端有变更的工作。调度器则只是给 pod 分配节点,所以需要有活跃的组件确保系统真实状态的 API 服务器定义的期望的状态收敛,该工作由控制器管理器里的控制器来实现;

k8s 内部的控制工作都通过 APIServer 这一声明式的数据存储进行了解耦,而控制器就是复杂保持各个不同资源间一致性的一种异步的管理工具。

  • 即通过 APIServer 监听各自负责的资源,一旦资源发生变化,控制器就负责修改相应的其他资源,确保各个类型的资源间保持一致。
  • 需要注意的是控制器实际上不做任何具体的操作,只是通过 APIServer 监听指定的资源,并修改相关的其他资源。

控制器做了许多不同的事情,但是它们都通过 API 服务器监听资源(部署、服务等)变更,并且不论是创建新对象还是更新、删除已有对象,都对变更执行相应操作。
大多数情况下,这些操作涵盖了新建其他资源或者更新监听的资源本身(例如更新对象的 status)。

  • 总的来说,控制器执行一个调和循环,将实际状态调整为期望状态(在资源 spec 部分定义),然后将新的实际状态写入资源的 status 部分。控制器利用监听机制来订阅变更, 但是由于使用监听机制并不保证控制器不会漏掉时间,所以仍然需要定期执行重列举操作来确保不会丢掉什么。

  • 需要强调的是所有这些控制器都是通过 APIServer 来操作 API 对象的,它们不会直接和 kubelet 通信或者发送任何类型的指令。控制器更新 APIServer 的一个资源后,kubelet 会负责做它该做的工作(它通过监听 APIServer 获取变更),所以Kubelet与控制器都不知道对方的存在;

Kubernetes 在 kube-controller-manager 中运行了大量的内建控制器(例如,Deployment Controller、Job Controller、StatefulSet Controller、DaemonSet Controller 等)。这些内建控制器提供了 Kubernetes 非常重要的核心功能。Kubernetes 可以运行一个 master 集群,以实现内建控制器的高可用。

您也可以安装一些运行在 kube-controller-manager 之外的控制器,这些控制器通常是对 Kubernetes 已有功能的一些扩展。或者,在必要的情况下,您也可以自己编写自己需要的控制器,将其部署为一组 Pod,或者在 Kubernetes 集群之外部署。如何选择,取决于您想要用这个控制器做什么;

Replication Controller(复制控制器)

描述:Replication Controller(简称RC)确保任意时间都有指定数量的Pod“副本”在运行;当创建RC时,需要指定两个东西:

  • Pod模板:用来创建Pod副本的模板,从而进行手动创建Pod。
  • Label:Replication Controller需要监控的Pod的标签。现在已经创建了Pod的一些副本,那么在这些副本上如何均衡负载呢?我们需要的是Service。

RC示例图:

RC

RC

补充说明:

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

注意事项:

  • 最新 Kubernetes 版本里,推荐使用 Deployment

Deployment (部署)


Worker 节点

描述:Worker节点(Node)是k8s中的工作计算机,可能是VM或物理计算机,具体取决于群集。

补充说明:

  • 多个Pod可以在一个节点上运行。

描述:Node(节点)是 kubernetes 集群中的计算机,可以是虚拟机或物理机,它由master进行管理;

PS: Worker 节点 等同于 Node 节点

比如下图:显示一个Node(节点)上含有4个 Pod(容器组)

WeiyiGeek.

WeiyiGeek.

补充说明:

  • 1.一个 Node(节点)可以有多个Pod(容器组) ,master 会根据每个 Node(节点)上可用资源的情况自动调度 Pod(容器组)到最佳的 Node(节点)上

注意事项:

  • 1) 每个 Node(节点)至少运行 Kubelet 以及容器运行环境(如Docker)负责下载镜像、创建和运行容器等

Kubelet

描述:负责 master 节点和 worker 节点之间通信的进程(node processes), 管理 Pod(容器组)和 Pod(容器组)内运行的 Container(容器); 简单地说 Kubelet 就是负责所有运行在工作节点上内容的组件。
它第一个任务就是在 API 服务器中创建一个 Node 资源来注册该节点。然后需要持续监控 API 服务器是否把该节点分配给 pod 然后启动 pod 容器。

具体实现方式是告知配置好的容器运行时(Docker、CoreOS 的 Rkt,或者其他一些东西)来从特定容器镜像运行容器。

  • Kubelet 随后持续监控运行的容器,向 API 服务器报告它们的状态、事件和资源消耗。
  • Kubelet 也是运行容器存活探针的组件,当探针报错时它会重启容器。
  • Kubelet 基于 API 服务器/本地文件目录中的 pod 定义运行 pod,当 pod 从 API 服务器删除时,Kubelet 终止容器,并通知服务器 pod 已经被终止了。

Kube-proxy

描述:Kube-prxoy是运行在每个工作节点上,用于确保客户端可以通过 kubernetes API 连接到你定义的服务。
kube-proxy 确保对服务 IP 和端口的连接最终能够到达支持服务的某个pod,如果有多个 pod 它还负责做负载均衡。

kube-proxy 服务去 watch kubernetes 集群的ServiceEndpoint对象,当这两个资源对象有状态变化时,会把它们保存在ServiceMapEndPonintMap中,然后会通过async.BoundedFrequencyRunner去异步的执行syncProxyRules去下发规则。

  • userspace 代理:通过修改 iptables 让连接到达 kube-proxy,再转发到 pod(轮询)。
  • iptables 代理:通过修改 iptables 让连接直接到达 pod(随机选择)。
  • ipvs 代理:IPVS 是一个用于负载均衡的 Linux 内核功能。IPVS 模式下 kube-proxy 使用 IPVS 负载均衡代替了 iptable。

kube-proxy在使用iptables和ipvs实现对Service的负载均衡区别?

  • IPTABLES 实现方式:由于Iptables本身的特性新增规则,更新规则是非增量式的,需要先iptables-save然后在内存中更新规则,在内核中修改规则在iptables-restore 并且Iptables在进行规则查找匹配时是线性查找耗费很长时间,时间复杂度为O(n)
  • IPVS 实现方式:其连接过程的时间复杂度是O(1)。基本就是说连接的效率与集群Service的数量是无关的, 因此随着集群内部Service的不断增加,IPVS的性能优势就体现出来了。

具体的性能对比可以参照 kube-proxy 模式对比iptables 还是 IPVS 文章


Pod (容器组)

描述: Pod 容器组是k8s中的一个抽象的概念,它是集群上的最基本的单元且总是在 Node 节点上运行,用于存放应用程序实例的container(可包含一个或多个 container 容器)以及这些 container (容器)的一些共享资源;


在这里补充一下Pod与容器间的关系?
容器的设计原则就是为了隔离,即在单一的容器内运行单一的进程,而 Pod 将这些单一的进程聚集在一起。从某种程度上来说这样的 Pod 就像是运行多个进程的操作系统,而比操作系统更加轻量。


共享资源包括:

  • 网络:每个 Pod(容器组)在集群中有个唯一的 IP,且pod(容器组)中的 container(容器)共享该IP地址;
  • 共享存储:称为卷(Volumes),即图紫色圆柱
  • 容器信息:例如容器的镜像版本,对外暴露的端口等
WeiyiGeek.Pods概述

WeiyiGeek.Pods概述


补充说明:

  • 1.每个Pod都与运行它的 worker 节点(Node)绑定,并保持在那里直到终止或被删除。
  • 2.每个Pod中运行着一个特殊的被称为Pause的容器,其它的则为业务容器;
  • 3.简单的说:Pod 是一组容器(可包含一个或多个应用程序容器),以及共享存储(卷 Volumes)、IP 地址和有关如何运行容器的信息, 它将应用的容器、存储资源以及独立的网络 IP 地址等资源打包到了一起;
  • 4.Pod 内的 容器都可以访问共享的存储和网络。
  • 5.每一个 Pod 中的运行的容器可能不止一个,因为设计之初就是为了同一 Pod 内的容器能够共享存储和网络,方便多个进程之间进行协调,从而构建出一个高内聚的服务单元;
  • 6.Pod 的设计不就是虚拟机下多进程业务在容器时代没办法的一个办法


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

  • 1.会为Service创建一个本地集群的DNS入口,因此前端Pod只需要DNS查找主机名为 ‘backend-service’,就能够解析出前端应用程序可用的IP地址。
  • 2.现在前端已经得到了后台服务的IP地址但是由于有两个后台pod,Service在这2个后台Pod之间提供透明的负载均衡,会将请求分发给其中的任意一个(通过每个Node上运行的代理(kube-proxy)完成);

下述动画展示了Service的功能:

功能

功能

注意事项:

  • 1.如果多个容器紧密耦合并且需要共享磁盘等资源,则他们应该被部署在同一个Pod(容器组)中。
  • 2.如果节点(Node)发生故障,则会在群集中的其他可用节点(Node)上运行相同的 Pod(从同样的镜像创建 Container,使用同样的配置但是IP 地址不同,Pod 名字不同)。
  • 3.由于Pod中的容器共享 IP 地址和端口空间,即同一个Pod内的容器可以使用 localhost + 端口号互相访问,同时同一个Pod内的容器端口不能冲突;

F&Q

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

  • 是的,Kubernetes支持 卷 的概念,因此可以使用持久化的卷类型。

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

  • 可以手动创建单个Pod,但是也可以使用Replication Controller使用Pod模板创建出多份拷贝(现在可以直接使用Deployment进行部署)

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

  • 可以使用Service

Service

描述:Services(服务)是分布式集群架构的核心, 并且Kubernetes 中的 Service(服务) 提供了这样的一个抽象层,它选择具备某些特征的 Pod(容器组)并为它们定义一个访问方式。
它通过 LabelSelector 选择了一组 Pod(容器组),把这些 Pod 的指定端口公布到到集群外部,并支持负载均衡和服务发现。

作用:

  • 使 Pod(容器组)之间的相互依赖解耦(原本从一个 Pod 中访问另外一个 Pod,需要知道对方的 IP 地址),公布 Pod 的端口以使其可访问;

示例:下图中有两个服务Service A(黄色虚线)和Service B(蓝色虚线)

  • Service A 将请求转发到 IP 为 10.10.10.1 的Pod上
  • Service B 将请求转发到 IP 为 10.10.10.2、10.10.10.3、10.10.10.4 的Pod上。
WeiyiGeek.services

WeiyiGeek.services

补充说明:

  • 1.一个 Service(服务)选定哪些 Pod(容器组) 通常由 LabelSelector(标签选择器) 来决定。
  • 2.Service 将外部请求路由到一组 Pod 中,使得k8s可以在不影响服务调用者的情况下,动态调度容器组(在容器组失效后重新创建容器组,增加或者减少同一个 Deployment 对应容器组的数量等)。

Labels

描述: 通过在Deployment的yaml文件中配置Labels标签,可以灵活定位一个或多个资源;Label是attach到Pod的一对键/值对组成,用来传递用户定义的属性,可以使用 Selectors 选择带有特定Label的Pod,并且将Service或者Replication Controller应用到上面。

示例:你可能创建了一个”tier”和“app”标签,通过Label(tier=frontend, app=myapp)来标记前端Pod容器,使用Label(tier=backend, app=myapp)标记后台Pod。

补充说明:

  • 1) 利用Service将多个Node节点的Pod中的相同标签容器进行关联,以便于提供负载均衡,同时也方便用户根据标签来查询过滤应用;

LabelSelector

描述:Service使用 Labels、LabelSelector(标签和选择器)匹配一组 Pod。Labels(标签)是附加到 Kubernetes 对象的键/值对;

标签用途有多种:

  • 将 Kubernetes 对象(Node、Deployment、Pod、Service等)指派用于开发环境、测试环境或生产环境
  • 嵌入版本标签,使用标签区别不同应用软件版本
  • 使用标签对 Kubernetes 对象进行分类

示例:下图体现了 Labels(标签)和 LabelSelector(标签选择器)之间的关联关系:

  • Deployment B 含有 LabelSelector 为 app=B 通过此方式声明含有 app=B 标签的 Pod 与之关联(通过 Deployment B 创建的 Pod 包含标签为 app=B
  • Service B 通过标签选择器 app=B 选择可以路由的 Pod;
    WeiyiGeek.LabelSelector

    WeiyiGeek.LabelSelector

补充说明:

  • 1.Labels(标签)可以在创建 Kubernetes 对象时附加上去,也可以在创建之后再附加上去。
  • 2.任何时候都可以修改一个 Kubernetes 对象的 Labels(标签)

网络模型

描述:对于K8s初学者来说K8s中各工作节点与Pod中网络的配置就会让您欲仙欲死,所以这里我们需要对其有个大致了解即可;

简单图解(从下往上):

  • 1.通过Api-server监控服务以及端点,通过Kube-proxy代理设置负载规则
  • 2.客户端通过 iptables 实现的负载均衡重定向到 Kube-proxy 代理,最终代理到后端的Pod中;
WeiyiGeek.网络模型示意图

WeiyiGeek.网络模型示意图


F&Q

问:Deployment(部署)、Pod(容器组) 和 Node(节点) 之间的关系吗?

kubernetes与容器

kubernetes在初期版本里,就对多个容器引擎做了兼容,因此可以使用docker、rkt对容器进行管理。以docker为例,kubelet中会启动一个docker manager,通过直接调用docker的api进行容器的创建等操作。

在k8s 1.5版本之后,kubernetes推出了自己的运行时接口api–CRI(container runtime interface)。cri接口的推出,隔离了各个容器引擎之间的差异,而通过统一的接口与各个容器引擎之间进行互动。

与oci不同,cri与kubernetes的概念更加贴合,并紧密绑定。cri不仅定义了容器的生命周期的管理,还引入了k8s中pod的概念,并定义了管理pod的生命周期。在kubernetes中,pod是由一组进行了资源限制的,在隔离环境中的容器组成。而这个隔离环境,称之为PodSandbox。在cri开始之初,主要是支持docker和rkt两种。其中kubelet是通过cri接口,调用docker-shim,并进一步调用docker api实现的。

如上文所述,docker独立出来了containerd。kubernetes也顺应潮流,孵化了cri-containerd项目,用以将containerd接入到cri的标准中。

WeiyiGeek.

WeiyiGeek.

为了进一步与oci进行兼容,kubernetes还孵化了cri-o,成为了架设在cri和oci之间的一座桥梁。通过这种方式,可以方便更多符合oci标准的容器运行时,接入kubernetes进行集成使用。可以预见到,通过cri-o,kubernetes在使用的兼容性和广泛性上将会得到进一步加强。

Kubernetes 与 CRI 以及 Docker-manager 关系图:

WeiyiGeek.

WeiyiGeek.