1. 概述
Kubernetes,简称k8s
(因为k和s之间有8个字符),是一种诞生于谷歌内部的开源的容器编排引擎,用来对容器化应用进行自动化部署、 扩缩和管理。目前托管在云原生计算基金会 CNCF(Cloud Native Computing Foundation)。
Kubernetes 的 logo 表示船舵,寓意成为运输容器集装箱(剑指 Docker)货轮的舵手。
2. Kubernetes对象
Kubernetes 对象是通过 Kubernetes API
创建和维护并使其运行在对象规约(Spec)期望状态的的持久化实体,又叫 API对象
。例如 Pod、Deployment、Service等。
创建 Kubernetes 对象时,必须提供对象的规约,用来描述该对象的期望状态, 以及关于对象的一些基本信息(例如名称),这些信息通常定义在YAML
或JSON
格式的文件中(YAML文件会转为JSON格式),通过kubectl
工具进行创建或更新,当然也可直接通过调用 API 传递JSON
格式的对象信息进行操作。
每个对象的定义都必须提供以下字段:
apiVersion
- 创建该对象所使用的 Kubernetes API 的版本kind
- 想要创建的对象的类别metadata
- 帮助唯一性标识对象的一些数据,包括一个name
字符串、UID 和可选的namespace
spec
- 期望的对象的状态,每个对象不同
此外,不同的对象还有各自对应的字段。
2.1 常见对象
Pod
Pod 是Kubernetes中最小的可部署的计算单元,是一组(一个或多个)容器的运行环境, 这些容器共享存储、网络、以及怎样运行这些容器的声明,是相对紧密的耦合在一起的,所以又叫容器组,Kubernetes通过Pod管理这些容器。
通常一个 Pod 只运行一个容器,因为一个Pod中的容器有相同的生命周期,而通常容器之间不希望相互影响。当然一个Pod也可以管理多个紧密耦合且需要共享资源的共处容器,通常这些容器会有相同的存储资源(可了解“边车”(sidecar)的概念)。
通常不需要直接创建 Pod,而是通过Deployment
、StatefulSet
或Job
这种工作负载来创建和管理,这些工作负载可保证 Pod 以特定的需求场景运行。
Deployment
Deployment 是用来管理无状态应用 Pod 的工作负载对象。Pod需要横向扩展为多个时每个Pod是一个副本(Replication),这些副本的集合称为ReplicaSet
,而ReplicaSet 的创建和管理由Deployment 来完成,因此通过Deployment实现Pod副本的创建和管理。Deployment 控制器会以受控的速率监控和更改实际状态, 使其变为期望状态。
StatefulSet
StatefulSet 是用来管理有状态应用 Pod 的工作负载对象。由 StatefulSet 管理的 Pod 具有唯一标识,包括顺序标识、稳定的网络标识和稳定的存储。虽然StatefulSet的 Pod是从同一个Pod模板创建的,但每个Pod都是唯一的,不可互换,无论怎么更新和调度,每个 Pod 都有一个永久不变的 ID。
DaemonSet
DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。DaemonSet 通常可用来部署监控或日志收集等程序。
Job
通过 Job 管理的 Pod 适用于只需要执行一次但执行时间较长的任务,例如数据库迁移。Job 会创建一个或者多个 Pods直到任务执行完毕。当第一个 Pod 失败或者被删除(比如因为节点硬件失效或者重启)时,Job 对象会启动一个新的 Pod。
CronJob
顾名思义,CronJob用来管理执行定时任务的 Pod,例如数据库备份。
2.2 创建和使用 Deployment
无状态应用部署是最常见的 Kubernetes应用场景,下面以 Deployment 的创建来说明工作负载如何创建和管理 Pod。
Kubernetes Deployment对象 定义如下:
则创建一个 Deployment对象也需要这些信息。使用下面的nginx-deployment.yaml
文件创建一个名为nginx-deployment
的 Deployment,其又会创建一个 ReplicaSet,负责启动三个 nginx Pod:
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
重点关注 .spec
中的信息。
replicas
副本数selector
Pod 选择器,用来定义 Deployment 如何查找要管理的 Pods,与标签匹配的Pod才会被 Deployment管理,这里Depolyment只会管理具有app:nginx
的 Pod。template
Pod 模板(PodTemplate),用来声明 Deployment 如何创建 Pod。它和 Pod 的语法规则完全相同。只是这里它是嵌套的,因此不需要 apiVersion 或 kind。template.metadata.labels
表示定义在该Pod 上的标签。标签(Labels) 是附加到对象上的键值对,用来标识对象。app:nginx
表示该Pod有键为app
、值为nginx
的标签,这样该Pod会被在Pod选择器
中声明了app:nginx
的工作负载所管理。由于当前 Deployment 会管理其所创建的 Pod,故在selector
中声明了同样的标签。template.spec
Pod规约,这里只定义了containers
属性,指定Pod运行的容器信息。containers.name
是自定义的容器名,containers.image
是容器镜像名,这里使用官方1.14.2版本的nginx镜像,containers.ports
指定容器在Pod上暴露的端口。
执行kubectl apply -f nginx-deployment.yaml
命令创建Deployment后,会首先创建一个Deployment对象,然后自动创建一个ReplicaSet对象和三个运行nginx容器的Pod对象。
Deployment 控制器针对每个 Deployment 对象确保运行中的 Pod 与当前的 Pod 模版匹配。如果模版被更新,则 Deployment 必须删除现有的 Pod,基于更新后的模版创建新的 Pod。每个工作负载资源都实现了自己的规则,用来处理对 Pod 模版的更新。
其他工作负载和 Deployment 相同,都是通过这种方式创建和管理Pod的。
3. Kubernetes组件
控制平面组件
控制平面组件通常运行在同一个节点上,该节点视为主节点。
- kube-apiserver
Kubernetes的 API 服务器,可为操作kubernetes集群提供 HTTP API和不同语言的客户端库。kubectl
和kubeadm
命令行工具也是通过API实现的,实际操作中也可直接通过直接调用 API 进行操作。 - etcd
分布式键值对数据库,保存 Kubernetes 所有集群数据。 - kube-scheduler
Kubernetes 调度器,基于约束和节点的可用资源将 Pods 指派到合适的 Node 上。 - kube-controller-manager
运行控制器
进程的控制器管理器。 - cloud-controller-manager
云控制器管理器。
Node 组件
每个kubernetes集群的节点上都需要运行的组件
- 容器运行时(Container Runtime)
负责运行容器的软件,所有实现容器运行时接口CRI (Container Runtime Interface)的容器都可以作为容器运行时。Docker是最常用的容器运行时。 - kubelet
运行在每个节点上的节点代理,确保容器按 PodSpecs 中描述的状态运行。 - kube-proxy
节点的网络代理,维护节点上的网络规则,可通过Service
允许从集群内部或外部的网络会话与 Pod 进行网络通信。
插件(Addons)
- DNS
- Web 界面(仪表盘)
- 容器资源监控
- 集群层面日志
4. Kubernetes网络模型
Kubernetes网络系统主要解决四个主要问题:
- Pod内的容器之间可通过 localhost 通信
- 集群内Pod之间可以不通过 NAT 和所有节点上的 Pod 通信
- Service 和 Pod 通信
- 集群外部 和 Service 的通信
4.1 单Pod单IP模型
每一个 Pod 都有它自己的IP地址,Pod内的容器共享网络命名空间(network namespace)。从端口分配、命名、服务发现、 负载均衡、应用配置和迁移的角度来看, Pod 可以被视作虚拟机或者物理主机,Pod内容器的通信就像是虚拟机或物理主机上的进程间通信。
4.2 网络插件
容器网络接口 CNI(Container Network Interface)
是容器网络配置的规范接口,Kubernetes网络插件遵循CNI规范,实现为可执行的二进制文件,这些文件默认放在/opt/cni/bin
目录下,负责在创建Pod时进入 Pod 的网络空间配置 Pod 的网络,为Pod分配IP地址。常见的网络插件有 flannel、calico、weave等。
创建Pod时网络配置过程如下:
- kubernetes创建 Pod
- Kubelet观察到新 Pod创建后调用 CRI 创建第一个特殊的容器
pause
,该容器运行的paause
程序会一直阻塞,其作用就是占用一个network namespace
,网络插件会初始化 pause 容器的网络设备,给容器分配IP。 - 随后在Pod 内新创建的容器会通过加入 pause 的 network namespace 的方式共享同一个network namespace,也会与 pause 容器使用相同的 IP,从而实现
单Pod单IP模型
,这个IP是集群私有 IP 地址。
集群内 Pod 的 IP 地址是相互可见的,因此 Pod 之间可不需要通过 NAT 转换实现相互访问,这样就实现了一个扁平的、集群范围的网络。
4.3 Service
服务不可能只在集群范围内访问,如何将Pod内运行的服务暴露到公网访问?Kubernetes提供了 Service
和Ingress
两个对象实现。
Service 是集群中提供相同功能的一组 Pod 的抽象表达。 当一个 Service 创建时,会被分配一个唯一的 IP 地址(称为 ClusterIP
)。 这个 IP 地址与 Service 的生命周期绑定在一起,只要 Service 存在,它就不会改变。 可以配置 Pod 使它与 Service 进行通信,Pod 与 Service 的通信将被自动地负载均衡
到该 Service 中的某些 Pod 上。
一个简单的创建 Service 的YAML
配置如下:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
run: nginx
spec:
ports:
- port: 80
protocol: TCP
targetPort: 8080
selector:
run: nginx
同Deployment
一样,.spec
属性声明了 Service 如何 和指定的 Pod 通信:
spec.ports
Service 暴露的端口信息spec.ports.port
是抽象的 Service 端口,其他 Pod 通过该端口访问 Service。ports.protocol
端口协议,默认为TCP,还可以是 UDP 和 SCTP;ports.targetPort
Pod内容器的端口,如果不配置则与 port 相同。spec.selector
Pod选择器,和 Deployment 的spec.selector
作用相同,用来识别哪些 Pod 可作为该 Service 的 Endpoint,一个 Service 指向的一组 Pod 是由标签选择算符定义的。。
也可以直接通过kubectl expose
命令将一个 Deployment 暴露为 Service。例如将上面创建的 nginx-deployment
暴露为 Service:
kubectl expose nginx-deployment
Service有三种类型,用spec.type
属性指定,标识Service以何种方式暴露。
ClusterIP
默认类型,如果不指定 type,则使用该类型。ClusterIP类型的Service 主要用于在集群内通过 Service 访问 Pod服务,比如 Web 应用的前端访问后端。NodePort
NodePort 服务是 ClusterIP 服务的扩展,其有一个spec.ports.nodePort
属性,用来指定 Service 暴露到节点的 端口,范围是30000-32767,如果不指定,则 Kubernetes 会自动分配一个。NodePort 服务暴露后可以直接通过<NodeIP>:<NodePort>访问。LoadBalancer
LoadBalancer 服务是 NodePort 服务的扩展,需要和基于云的负载均衡器集成使用。ExternalName
主要作用是把集群外部的服务以DNS CNAME记录的方式映射到集群内,从而让集群内的Pod资源能够访问外部的Service。
使用NodePort类型进行服务暴露的时候,每个Service都需要为其打开一个端口。当Service数量比较多的时候,端口管理变得困难。另外,NodePort类型的服务暴露是基于四层代理转发的,无法根据HTTP的header或者path进行路由转发。Ingress
解决了这些问题。
4.4 Ingress
Ingress 公开了从集群外部到集群内服务的 HTTP 和 HTTPS 路由,流量路由由 Ingress 资源上定义的规则(rules)控制,具体规则的实现由 Ingress控制器完成。Ingress控制器需要单独安装,常用的有 ingress-nginx
。
Ingress可以基于客户端请求的URL做流量分发,转发给不同的Service后端。
如下
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
backend:
service:
name: s1
port:
number: 80
- path: /bar
backend:
service:
name: s2
port:
number: 80
上面的配置可实现如下根据URL流量分发的功能,将来自不同路径的请求理由到不同的 Service。