K8s核心概念

本文是CNCF × Alibaba 云原生技术公开课的第三章Kubernetes 核心概念学习笔记。

关键词:k8s

k8s的架构

k8s的架构是比较典型的二层架构和server-client架构。Master作为中央的管控节点,会与Node进行一个链接。

所有用户侧的组件,只会和Master进行连接,把希望的状态或者想执行的命令发给Master,Master会把这些命令发给相应的节点。

Master

Master节点主要包含四个主要的组件:API Server、Controller、Scheduler以及etcd:

  • API Server:用来处理API操作,k8s的所有组件都会和API Server进行连接,组件和组件之间一般不进行独立链接,都要依赖API Server进行消息的传送。
  • Controller:控制器。用来完成对集群状态的一些管理。例如自动对容器进行修复,或者自动进行水平扩张
  • Scheduler:调度器。顾名思义就是完成调度的操作。例如把一个用户提交的Container,依赖于它对CPU、对memory请求大小,找一台合适的节点,进行放置。
  • etcd:分布式存储系统。API Server中所需要的原信息存在etcd中,etcd本身是一个高可用系统,通过etcd保证整个k8s Master节点的高可用性。

Node

Node是真正运行业务负载的,每个业务负载以Pod的形式运行。一个Pod中运行一个或者多个容器,真正运行这些Pod组件的是kubelet,也就是Node上面最关键的组件,它通过API Server接收到需要Pod运行的状态,然后提交到Container Runtime组件中,在OS上创建容器所需要的环境,最终把容器或者Pod运行起来。Node需要对存储和网络进行管理。K8s不直接管理存储和网络,K8s通过Storage Plugin或者Network Plugin进行管理,完成存储和网络操作。

在K8s自己的环境里,也会有k8s的Network,他是为了提供Service Network来进行组网。真正完成组网的组件是Kube-proxy。Kube-proxy利用iptable的能力来组建k8s集群的网络。以上就是Node上面的四个组件(Container Runtime、Storage Plugin、Network Plugin、Kube-proxy)。

注意:K8s上的Node不会直接和用户进行交互,Node和用户之间的交互只会通过Master节点。用户通过Master节点下发这些信息。K8s上的每个Node,都会运行上面的这几个组件。

例子

通过一个例子看K8s架构中的这些组件是如何进行交互的。

用户通过UI或者CLI提交一个Pod到K8s进行部署,这个Pod请求会首先通过UI或者CLI提交给K8s API Server,下一步API Server会把这个信息写入到存储系统etcd,之后Scheduler会通过API Server的watch机制得到这个信息:有一个Pod需要被调度。

此时Scheduler会根据这个内存状态进行一次调度决策,在完成这次调度以后,Scheduler向API Server报告。此时API Server接收到消息以后,把这次结果写入etcd,然后API Server会通知相应的节点。相应节点的Kubelet会得到这个通知,Kubelet调用Container Runtime来启动配置这个容器和这个容器的运行环境,去调度Storage Plugin来配置存储,Network Plugin去配置网络。

核心概念

Pod

Pod是K8s的一个最小调度和属性单元,用户可以通过K8s的Pod API来生产一个Pod,让K8s对这个Pod进行调度,也就是把他放到另一个K8s管理的节点上运行起来。

一个Pod简单来说是一组容器的抽象,它包含一个或者多个容器。

Pod包含一些其他所需要的资源,比如卷。

Pod的共享上下文包括一组Linux命名空间,控制组和可能一些其他的隔离方面,这些都是用来隔离Docker容器的技术。在Pod的上下文中,每个独立的应用可能会进一步实施隔离。

就Docker使用的概念术语来说,Pod类似于共享名字空间和文件系统卷的一组Docker容器

Pod给这些容器提供了一个共享的运行环境,他们会共享同一个网络环境,这些容器可以用localhost来进行直接的连接。

使用Pod
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

要创建上面的Pod,请运行以下命令

1
kubectl apply -f simple-pod.yaml

注意:Pod一般不是直接创建的,而是使用工作负载资源创建的,

Volume

Volume用来管理K8s存储,Volume声明容器中的容器可以访问文件的目录。

一个卷可以被挂载Pod中一个或者多个容器指定的路径下面。

Volume本身是一个抽象的概念,一个Volume可以支持多种后端存储。K8s的Volume就支持了很多存储插件,可以支持本地的存储,也可以支持分布式存储。它也可以支持云存储,例如阿里云上的云盘、AWS上的云盘、Google上的云盘等。

Docker 也有 卷(Volume) 的概念,但对它只有少量且松散的管理。 Docker 卷是磁盘上或者另外一个容器内的一个目录。 Docker 提供卷驱动程序,但是其功能非常有限。

Kubernetes 支持很多类型的卷。 Pod 可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。 对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。

挂载本地卷
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
volumeMounts:
- mountPath: /mnt/logs
name: logs
volumes:
- name: go-logs
hostPath:
path: /logs
挂载网络卷NFS

待补充

Deployment

Deployment为Pod和ReplicaSet(副本)提供声明式的更新能力。

Deployment是在Pod上层的抽象,用Deployment这个抽象来做应用的真正管理,而Pod是组成Deployment的最小单元。

K8s通过Controller也就是控制器去维护Deployment中Pod的数目,帮助Deployment自动恢复失败的Pod。

例如可以定义一个Deployment,这个Deployment需要两个Pod,当一个Pod失败后,控制器会检测到,他会重新把Deployment中的Pod数据从一个恢复到两个,然后再去重新生成一个Pod。

通过控制器,还可以完成发布的策略,例如进行滚动升级,或者版本回滚等。

创建Deployment

下面是一个 Deployment 示例。其中创建了一个 ReplicaSet,负责启动三个 nginx Pods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

在该例中:

  • 创建名为nginx-deployment的Deployment
  • 该Deployment创建3个(由replicas字段标明)Pod副本
  • selector字段定义了Deployment如何查找要管理的Pods。在这里选择Pod模板中定义的标签(app:nginx)。

spec.selector.matchLabels 字段是key-Value键值对映射,在matchLabels映射的每个Key-Value映射等效于matchExpressions中的一个元素,即key字段是key,操作符是Invalues数组仅包含value。在matchLabelsmatchExpressions中给出的所有条件都满足才能匹配。

  • template包含以下子字段:
    • Pod被使用.metadata.labels字段打上app:nginx标签
    • Pod模板规约即.template.spec字段指示Pods运行一个nginx容器,该容器运行nginx:1.14.2的Docker Hub 镜像
    • 创建一个容器并使用.spec.template.spec.containers[0].name字段并将其命名为nginx

Service

Service提供了一个或者多个Pod实例的稳定访问地址。

从上文中可以看到,一个Deployment可以有两个甚至更多个完全相同的Pod,对于一个外部的用户来说,访问哪一个Pod其实都属于一样的,所以他希望做一次负载均衡,在做负载均衡的同时,只想访问某一个固定的VIP,VirtualIP地址,而不希望得到一个具体的Pod的IP地址。

由于Pod本身可能会终止,如果一个Pod失败了,可能会换成另外一个新的。

对于外部用户来说,提供了多个具体的Pod地址,这个用户需要不停的去更新Pod地址。K8s的Service把所有Pod的访问能力抽象成一个第三方的IP地址。

实现Service有多种方式,K8s支持Cluster IP,上面讲过的kuber-proxy组网,也支持nodePort,LoadBalancer等其他的一些访问能力。

使用Service

例如,假定有一组Pod,他们对外暴露了9376端口,同时还被打上了app=Myapp标签

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376

上述配置创建了一个名为my-service的对象,它会将请求代理到TCP端口9376,并且具有标签app=MyApp的Pod上。