在实践之前,必须先学习k8s的几个重要概念,它们是组成k8s集群的基石。
1. Cluster
Cluster是计算、存储和网络资源的组合,k8s利用这些资源运行各种基于容器的应用。
2. Master
Master是Cluster的大脑,它的主要职责是调度,即决定将应用放在哪里运行。Master运行Linux操作系统,可以是物理机或者虚拟机。为了实现高可用,可以运行多个Master。
3. Node
Node的职责是运行容器应用。Node由Master管理,Node负责监控并汇报容器的状态,同时根据Master的要求管理容器的生命周期。Node运行在Linux操作系统上,可以是物理机或者是虚拟机。
4. Pod
Pod时k8s的最小工作单元。每个Pod包含一个或多个容器。Pod中的容器会作为一个整体被Master调度到一个Node上运行。
kubernets引入Pod主要基于下面的两个目的:
可管理性
有些容器天生就是需要紧密联系到一起工作。Pod提供了比容器更高层次的抽象,将它们封装到一个部署单元中。k8s以Pod为最小单位进行调度、扩展、共享资源、管理声明周期。
通信和资源共享
Pod中的所有容器使用同一个网络NameSpace,即相同的IP地址和Port空间。它们之间直接可以使用localhost通信。同样的这些容器可以共享存储,当k8s挂载volume到Pod,本质上是将volume挂载到Pod中的每一个容器。
Pods有两种使用方式:
1. 运行单一容器
one-container-per-Pod 是 k8s最常见的模型,这种情况下,只是将单个容器简单封装成Pod。即便是只有一个容器,k8s管理的也是Pod而不是直接管理容器。
2. 运行多个容器
问题在于: 哪些容器应该被放到一个Pod中?
答案是:这些容器联系必须非常紧密,而且需要直接共享资源
5. Controller
k8s通常不会直接创建Pod,而是通过Controller来管理Pod的。Controller中定义了Pod的部署特性,比如有几个副本、在什么样的Node上运行等。为了满足不同业务场景,k8s提供了多种Controller,包括Deployment、ReplicaSet、DaemonSet、StatefuleSet、Job等。
- (1)Deployment是最常用的Controller,Deployment可以管理Pod的多个副本,并确保Pod是按照期望的状态运行。
- (2)ReplicaSet实现了Pod的多副本管理。使用Deployment时会自动创建ReplicaSet,也就是说Deployment是通过ReplicaSet来管理Pod的多个副本的,我们通常不需要直接使用ReplicaSet。
- (3) DaemonSet用于每个Node最多运行一个Pod副本场景。正如其名称揭示的,DaemonSet通常用于运行Daemon。
- (4) StatefuleSet 能够保证Pod的每个副本在整个生命周期中名称是不变的,而其他Controller不提供这个功能。当某个Pod发生故障需要删除并重新启动时,Pod的名称会发生变化,同时StatefuleSet会保证副本按照固定的顺序启动、更新或者删除。
- (5) Job用于运行结束就删除的应用,而其他Controller中的Pod通常是长期持续运行。
6. Service
Deployment可以部署多个副本,每个Pod都有自己的IP,外界如何访问这些副本呢?
通过Pod的IP吗?
要知道Pod很可能被频繁的销毁和重启,它们的IP会发生变化,用IP来访问不太现实。
答案是Service。
kubernetes Service 定义了外界访问一组特定Pod的方式。Service有自己的IP和端口,Service为Pod提供了负载均衡。
k8s运行容器(Pod)与访问容器(Pod)这两项任务分别由Controller和Service执行。
7. Namespace
如果有多个用户或项目组使用同一个Kubernetes Cluster,如何将他们创建的Controller、Pod等资源分开呢?
使用Namespace可以将一个物理的Cluster逻辑上划分成多个虚拟Cluster,每个Cluster就是一个Namespace。不同Namespace里的资源是完全隔离的。
k8s架构图
从上面的图我们可以看出k8s由Master和Node两种节点组成,这两种角色分别对应着控制节点和工作节点。
1. Master
节点由三个独立的组件组成。
-
kube-apiserver
是负责整个集群通信的API服务 -
kube-scheduler
是负责容器调度的 -
kube-controller-manager
是负责维护集群状态的
整个集群的数据
都是通过kube-apiserver
保存到etcd
数据库中的,而其他所有组件的通信也都是通过kube-apiserver
作为媒介来和etcd
数据库进行通信的,都不会直接和etcd
进行通信。
2. Node
节点上最核心的组件就是kubelet
以及底层的容器运行时,比如Docker
。
kubelet
主要是用来实现和底层的容器运行时进行通信的。这个通信过程也被k8s抽象成了一个CRI(Container Runtime Interface)
的远程调用接口
,这个接口里面定义
了容器运行时的所有标准操作,比如创建容器、删除容器等
,只是目前kubelet里面内置了Docker关于这个CRI实现的一个shim,所以如果我们底层时Docker容器的话就不需要单独安装这个CRI的实现的组件了。所以对于 Kubernetes 来说他根本不关心你部署的到底是什么容器运行时,只要你这个容器运行时可以实现 CRI 接口就可以被 Kubernetes 来管理。-
kubelet
的另外一个重要功能就是调用网络插件(CNI)和存储插件(CSI)为容器配置网络和存储功能
,同样的 kubelet 也是把这两个重要功能通过接口暴露给外部了,所以如果我们想要实现自己的网络插件,只需要使用 CNI 就可以很方便的对接到 Kubernetes 集群当中去。
组件
上面介绍了k8s集群的整体架构,下面我们再来更加详细的了解下这些组件的功能。
1. kube-apiserver
API Server提供了资源对象的唯一操作入口,其他所有组件都必须通过它提供的API来操作资源数据。只有API Server会与etcd进行通信,其他模块都必须通过API Server访问集群状态
。
API Server作为k8s系统的入口,封装了核心对象的增删改查操作。API Server以RESTFul接口方式提供给外部客户端和内部组件调用,API Server再对相关的资源数据(全量查询+变化监听)进行操作,以达到实时完成相关的业务功能。以API Server为k8s入口的设计主要有以下好处:
- 保证了集群状态的访问安全
- API Server隔离了集群状态访问和后端存储实现,这样API Server状态访问的方式不会因为后端存储技术Etcd的改变而改变,让后端存储方式选择更加灵活,方便了整个架构的扩展。
2.kube-controller-manager
Controller Manager用于实现k8s集群故障检测和恢复的自动化工作
。主要负责执行各种控制器。
- Replication Controller: 主要是定期关联Replication Controller(RC)和Pod,以保证集群中一个RC(一种资源对象)所关联的Pod副本数始终保持为与预设值一致。
- Node Controller:
Kubelet在启动时会通过API Server注册自身的节点信息,并定时向API Server汇报状态信息。
API Server在接收到信息后将信息更新到Etcd中。Node Controller通过API Server实时获取Node的相关信息,实现管理和监控集群中的各个Node节点的相关控制功能。
- ResourceQuota Contraller: 资源配额管理控制器用于确保指定的资源对象在任何时候都不会超量占用系统上物理资源。
- Namespace Controller: 用户通过API Server可以创建新的Namespace并保存在Etcd中。Namespace Controller定时通过API Server读取这些Namespace信息来操作Namespace。比如:Namespace被API标记为优雅删除,则将该Namespace状态设置为Terminating并保存到Etcd中。同时Namespace Controller删除该Namespace下的ServiceAccount、Deployment、Pod等资源。
- Service Account Controller:服务账号控制器,主要在命名空间内管理ServiceAccount,以保证名为default的ServiceAccount在每个命名空间存在。
- Token Controller:令牌控制器作为Controller Manager的一部分,主要用作:监听ServiceAccount 的创建和删除动作以及监听secret的添加、删除动作。
- Service Controller:服务控制器主要用作监听Service的变化。比如:创建的是一个LoadBalancer类型的Service,Service Controller则要确保外部的云平台上对该Service对应的LoadBalancer实例被创建、删除以及相应的路由转发表被更新。
- Endpoint Controller: Endpoints表示一个Service对应的所有Pod副本的访问地址,而Endpoints Controller是负责生成和维护所有Endpoints对象的控制器。Endpoint Controller负责监听Service和对应的Pod副本的变化。定期关联Service和Pod(关联信息由Endpoint对象维护),以保证Service到Pod的映射总是最新的。
3. kube-scheduler
Scheduler是负责整个集群的资源调度的,主要的职责如下所示:
- 主要用于收集和分析当前k8s集群中所有Node节点的资源(包括内存、CPU等)负载等情况,然后根据资源占用情况分发新建的Pod到k8s集群中可用的节点
- 实时监测k8s集群中未分发和已分发的所有运行的Pod
- 实时监测Node节点信息,由于会频繁查找Node节点,所以Scheduler同时会缓存一份最新的信息在本地
- 在分发Pod到指定的Node节点后,会把Pod相关的Binding信息写回API Server,以方便其他组件使用
4. kubelet
kubelet是负责容器真正运行的核心组件,主要的职责如下所示:
- 负责Node节点上Pod的创建、修改、监控、删除等全生命周期的管理
- 定时上报本地Node的状态信息给API Server
- kubelet是Master和Node之间的桥梁,接收API Server分配给它任务并执行
- kubelet通过API Server间接与Etcd集群交互来读取集群配置信息
- kubelet在Node上做的主要工作具体如下:
- a. 设置容器的环境变量、给容器绑定Volume、给容器绑定Port、根据指定的Pod运行一个单一容器、给指定的Pod创建Network容器
- b. 同步Pod的状态
- c. 在容器中运行命令、杀死容器、删除Pod的所有容器
5. kube-proxy
kube-proxy是为了解决外部网络能够访问集群中容器提供的应用服务而设计的,Proxy运行在每一个Node上。
每创建一个Service,kube-proxy就会从API Server获取Services和Endpoints的配置信息,然后根据其配置信息在Node上启动一个Proxy的进程并监听相应的服务端口。
当接收到外部请求时,kube-proxy会根据Load Balancer将请求分发到后端正确的容器处理。
kube-proxy不但解决了同一宿主机相同服务端口冲突的问题,还提供了Service转发服务端口对外提供服务的能力。
kube-proxy后端使用随机、轮训
等负载均衡算法进行调度。
6. kubectl
Kubectl是k8s的集群管理命令行客户端工具集。通过Kubectl命令对API Server进行操作,API Server响应并返回对应的命令结果,从而达到对k8s集群的管理。
核心资源对象
上面我们都是在架构层面了解k8s,但是似乎没有关于容器的说明,k8s作为容器编排引擎,那么它是怎么去对容器进行编排的呢?在k8s集群中抽象了很多集群内部的资源对象,我们可以通过这些资源对象去操作容器的编排工作。
Pod
Pod是一组紧密关联的容器集合
,它们共享PID、IPC、Network和UTS namespace,是k8s调度的基本单位
。Pod的设计理念是支持多个容器在一个Pod中共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。我们知道容器本质上就是进程,那么Pod实际上就是进程组了,只是这一组进程是作为一个整体来进行调度的。
在k8s中,所有资源对象都是用资源清单(yaml或json)来定义,比如我们可以定义一个简单的nginx服务,它包含一个镜像为nginx的容器:( nignx-pod.yaml)
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
定义了这样一个资源清单文件后,我们就可以利用上面我们提到的kubectl工具将这个Pod创建到k8s集群中:
kubectl apply -y nginx-pod.yaml
Pod在k8s集群中被创建的基本流程如下所示:
注意实线和虚线的流程。理解整个流程以及各个组件的功能和组件之间的关系非常重要
- 用户通过REST API创建一个Pod
- apiservcer将其写入etcd
- scheduler检测到未绑定Node的Pod,开始调度并更新Pod的Node绑定
- kubelet检测到有新的Pod调度过来,通过container runtime运行该Pod
- kubelet通过container runtime 取到Pod状态,并更新到apiserver中
Label
Label标签在k8s资源对象中使用很多,也是非常重要的一个属性,Lable是识别k8s对象的标签
,以key/value
的方式附加到对象上(key最长不能超过63字节,value可以为空,但是不能超过253字节,且为字符串)。上面我们定义的Nginx的Pod就添加了一个app=nginx
的Label标签。Label不提供唯一性
,并且实际上经常是很多对象(如Pods)都使用相同的Label来标志具体的应用。Label定义好后其他对象可以使用Label Selector
来选择一组相同Label的对象(比如Service用Label来选择一组Pod)。Label Selector支持以下几种方式:
- 等式,如
app=nginx
和env != production
- 集合,如
env in (production, qa)
- 多个Label(它们之间是
and
关系),如app=nginx,env=test
Namespace
Namespace(命名空间)是一组资源和对象的抽象集合,比如可以用来讲系统内部的对象划分为不同的项目组或用户组。常见的Pods、Service、Deployments等都属于某一个Namespace的(默认是default),比如上面我们的Nginx Pod没有指定namespace,则默认就在default的命名空间下面,而Node,PersistentVolumes等资源则不属于任何Namespace,它们是全局的
。
注意:这里说的Namespace并不是Linux Namespace,二者之间没有任何关系。这里说的Namespace只是k8s划分不同工作区间的一个逻辑单位。
Deployment
我们说了Pod是k8s集群中的最基本的调度单元,但是如果想要创建同一个容器的多份拷贝,需要一个一个分别创建出来吗?那么能否将Pods划分到一个逻辑组里面呢?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的标签
现在已经创建了Pod的一些副本,那么这些副本上如何进行负载呢?如何把这些Pod暴露出去呢?这个时候我们就需要用到Service这种资源对象了。
Service
Service是应用服务的抽象
,通过Labels
为应用提供负载均衡和服务发现
。匹配Labels的Pod IP和端口列表组成Endpoints,由kube-proxy负责将服务IP负载均衡到这些Endpoints上
。
每个Service都会自动分配一个Cluster IP(仅在集群内部可访问的虚拟地址)和DNS 名,其他容器可以通过该地址或DNS来访问服务,而不需要了解后端容器的运行。
-
Kubernetes的三个重要组件的说明