Pod是K8s中的最小调度单元。可以在一个Pod里部署多个容器。同个Pod中共享网络资源、存储资源以及命名空间等。
在Pod中,所有容器都被同一安排和调度,并运行在共享的上下文中。对于具体应用而言,Pod是它们的逻辑主机,Pod包含业务相关的多个应用容器。
从《从零开始学容器》中我们知道,一个容器类似于一个进程,那么容器就不能像使用Vm一样使用。所以,Pod对于容器的编排起着重要作用。
Pod的特点
- 网络:每一个Pod都会被指派一个唯一的Ip地址,在Pod中的每一个容器共享网络命名空间,包括Ip地址和网络端口。在同一个Pod中的容器可以同locahost进行互相通信。当Pod中的容器需要与Pod外的实体进行通信时,则需要通过端口等共享的网络资源。
- 存储:Pod能够被指定共享存储卷的集合,在Pod中所有的容器能够访问共享存储卷,允许这些容器共享数据。存储卷也允许在一个Pod持久化数据,以防止其中的容器需要被重启。
Pod的声明
在K8s中,不直接创建Pod,每个Pod都是通过控制器和一个API模版配置来管理。而一个API模板有元数据(metadata)、规范(spec) 和 状态(status)。
1. 元数据(metadata)
metadata中包含3个对该对象至关重要的元信息: 命名空间(namespace)、对象名(name)和对象ID(uid)
- namespace: Kubernetes 中比较重要的一个概念,是对一组资源和对象的抽象集合,namespace 主要用于逻辑上的隔离
内置的namespace如下:
名称 | 描述 |
---|---|
default | 这是默认的缺省命名空间 |
kube-system | 主要是部署集群最关键的核心组件,比如一般会将 CoreDNS 声明在这个 namespace 中 |
kube-public | 是由 kubeadm 创建出来的,主要是保存一些集群 bootstrap 的信息,比如 token 等 |
kube-node-lease | 从 v1.12 版本开始开发的,到 v1.14 版本变为 beta 可用版本,在 v1.17 的时候已经正式 GA 了,它要用于 node 汇报心跳(我们在第一节课已经解释过了心跳的概念),每一个节点都会有一个对应的 Lease 对象 |
- name: 标识对象的名称
- uid: 系统自动生成,由K8s内部标识使用
2. 规范(Spec)
在 Spec 中描述了该对象的详细配置信息,即用户希望的状态(Desired State)。Kubernetes 中的各大组件会根据这个配置进行一系列的操作,将这种定义从“抽象”变为“现实”,我们称之为调和(Reconcile)。用户不需要过度关心怎么达到终态,也不用参与。
3. 状态(Status)
在这个字段里面,包含了该对象的一些状态信息,会由各个控制器定期进行更新。也是不同控制器之间进行相互通信的一个渠道。在 Kubernetes 中,各个组件都是分布式部署的,围绕着 kube-apiserver 进行通信,那么不同组件之间进行信息同步,就可以通过 status 进行。像 Node 的 status 就记录了该节点的一些状态信息,其他的控制器,就可以通过 status 知道该 Node 的情况,做一些操作,比如节点宕机修复、可分配资源等。
Pod的配置文件.yaml
创建文件study_two_container.yaml
apiVersion: v1 #指定当前描述文件遵循v1版本的Kubernetes API
kind: Pod #我们在描述一个pod
metadata:
name: c-containers #指定pod的名称
namespace: default #指定当前描述的pod所在的命名空间
labels: #指定pod标签
app: c-containers
annotations: #指定pod注释
version: v1
releasedBy: chensy
purpose: demo
spec:
containers:
- name: c-containers #容器的名称
image: registry.cn-hangzhou.aliyuncs.com/chensyf/container:1.0 #创建容器所使用的镜像
ports:
- containerPort: 9000 #应用监听的端口
- name: c-nginx
image: nginx:1.7.9
ports:
- containerPort: 80
Pod各字段说明
1. apiVersion
与k8s集群版本有关,使用 kubectl api-versions
即可查看当前集群支持的版本
其中:
名称 | 描述 |
---|---|
extensions/v1beta1 | k8s 1.6版本以前使用 |
apps/v1beta1 | 1.6~1.9版本使用 |
apps/v1 | 1.9版本以后使用 |
2. Kind
资源类别,有如下类别
类别 | 名称 |
---|---|
资源对象 | Pod、ReplicaSet、ReplicationController、Deployment、StatefulSet、DaemonSet、Job、CronJob、HorizontalPodAutoscaling |
配置对象 | Node、Namespace、Service、Secret、ConfigMap、Ingress、Label、ThirdPartyResource、 ServiceAccount |
存储对象 | Volume、Persistent Volume |
策略对象 | SecurityContext、ResourceQuota、LimitRange |
3. Spec
资源清单
Spec(Object):详细定义对象,固定值就写Spec
spec.containers:Spec对象的容器列表定义,是个列表
spec.containers[].name(String):定义容器的名字
spec.containers[].image(String):容器用到的镜像名称
主要的属性:
spec.containers[].name(String):容器的名称
spec.containers[].imagePullPolicy(String):定义镜像拉取策略,有Always(每次都尝试重新拉取镜像)、Never(仅使用本地镜像)、IfNotPresent(如果本地有镜像就使用本地镜像,没有则拉取镜像)三个值可选,默认是Always
spec.containers[].command:指定容器启动命令,因为是数组可以指定多个,不指定则使用镜像打包时使用的启动命令
spec.containers[].args:指定容器启动命令参数,因为是数组可以指定多个
spec.containers[].workingDir(String):指定容器的工作目录
spec.containers[].volumeMounts:指定容器内部的存储卷配置
spec.containers[].volumeMounts[].name(String):指定被容器挂载的存储卷的名称
spec.containers[].volumeMounts[].mountPath(String):指定被容器挂载的存储卷的路径
spec.containers[].volumeMounts[].readOnly(String):设置存储卷路径的读写模式,true或者false,默认为读写模式
spec.containers[].ports:指定容器需要用到的端口列表
spec.containers[].ports[].name(String):指定端口名称
spec.containers[].ports[].containerPort(String):指定容器需要监听的端口号
spec.containers[].ports[].hostPort(String):指定容器所在主机需要监听的端口号,默认跟containerPort相同,注意设置了hostPort,同一台主机无法启动该容器的相同副本(因为主机的端口号不能相同,会冲突)
spec.containers[].ports[].protocol(String):指定端口协议,支持TCP和UDP,默认值是TCP
spec.containers[].env:指定容器运行前需要设置的环境变量列表
spec.containers[].env[].name(String):指定环境变量名称
spec.containers[].env[].value(String):指定环境变量值
spec.containers[].resources(Object):指定资源限制和资源请求的值
spec.containers[].resources.limits(Object):指定设置容器运行时资源的运行上限
spec.containers[].resources.limits.cpu(String):指定cpu的限制,单位为core数,将用于docker run --cpu-shares参数
spec.containers[].resources.limits.memory(String):指定mem内存的限制
spec.containers[].resources.requests(Object):指定容器启动和调度时的限制设置
spec.containers[].resources.requests.cpu(String):cpu请求,单位为core数,容器启动时初始化可用数量
spec.containers[].resources.requests.memory(String):内存请求,容器启动的初始化可用数量
额外属性
spec.restartPolicy(String):定义Pod的重启策略,
可选值为
- Always(Pod一旦终止运行,则无论容器是如何终止的,kubelet服务都将重启它)、
- OnFailure(只有Pod以非零码终止时,kubelet才会重启该容器,如果是正常结束,退出码为0,则kubelet将不会重启它)、
- Never(Pod终止后,kubelet将退出码报告给master,不会重启该Pod)
spec.nodeSelector(Object):定义Node的Label过滤标签,以key:value格式指定
spec.imagePullSecrets(Object):定义pull镜像时使用secret名称,以name:secretkey格式指定
spec.hostNetwork(Boolean):定义是否使用主机网络模式,默认值为false,设置true表示使用宿主机网络,不使用docker网桥,同时设置了true将无法在同一台宿主机上启动第二个副本
创建Pod
使用命令kubectl apply -f study_two_container.yaml
创建Pod
查看Pod状态
[root@kubernetes yaml]# kubectl get pods
NAME READY STATUS RESTARTS AGE
c-containers 0/2 ContainerCreating 0 2m46s
使用命令kubectl describe pod twocontainers
查看详情描述
[root@kubernetes yaml]# kubectl describe pod c-containers
Name: c-containers
Namespace: default
Priority: 0
Node: kubernetes.node01.local/10.10.10.122
Start Time: Wed, 16 Dec 2020 09:04:18 +0800
Labels: app=c-containers
Annotations: cni.projectcalico.org/podIP: 10.110.166.3/32
cni.projectcalico.org/podIPs: 10.110.166.3/32
purpose: demo
releasedBy: chensy
version: v1
Status: Pending
....................省略
可以看到此时的Pod处于Pending状态, 处于Pending状态的两个原因:
- Pod还未被调度
- Pod内容器镜像在待运行的节点上不存在,需要从镜像中心拉取
K8s对Pod的健康检查
Kubernetes 中提供了一系列的健康检查,可以定制调用,来帮助解决类似的问题,称之为 Probe(探针)。
目前有如下三种 Probe:
-
livenessProbe可以用来探测容器是否真的在“运行”,即“探活”。如果检测失败的话,这个时候 kubelet 就会停掉该容器,容器的后续操作会受到其重启策略的影响。
-
readinessProbe常常用于指示容器是否可以对外提供正常的服务请求,即“就绪”,比如 nginx 容器在 reload 配置的时候无法对外提供 HTTP 服务。
-
startupProbe则可以用于判断容器是否已经启动好,就比如上面提到的容器启动慢的例子。我们可以通过参数,保证有足够长的时间来应对“超长”的启动时间。 如果检测失败的话,同livenessProbe的操作。这个 Probe 是在 1.16 版本才加入进来的,到 1.18 版本变为 beta。也就是说如果你的 Kubernetes 版本小于 1.18 的话,你需要在 kube-apiserver 的启动参数中,显式地在 feature gate 中开启这个功能。可以参考这个文档查看如何配置该参数。
Kubernetes容器的生命周期
在Kubernetes的生命周期有两种,容器启动之后和容器被终止之前
- PostStart 可以在容器启动之后就执行。但需要注意的是,此 hook 和容器里的 ENTRYPOINT 命令的执行顺序是不确定的。
- PreStop 则在容器被终止之前被执行,是一种阻塞式的方式。执行完成后,Kubelet 才真正开始销毁容器。
例子:
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
namespace: demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx:1.19
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
Kubernetes的Init容器
在 Kubernetes 中还有一种特殊的容器,即 init 容器。看名字就知道,这个容器工作在正常容器(为了方便区分,我们这里称为应用容器)启动之前,通常用来做一些初始化工作,比如环境检测、OSS 文件下载、工具安装,等等。
应用容器专注于业务处理,其他一些无关的初始化任务就可以放到 init 容器中。这种解耦有利于各自升级,也降低相互依赖。
一个 Pod 中允许有一个或多个 init 容器。init 容器和其他一般的容器非常像,其与众不同的特点主要有:
-
总是运行到完成,可以理解为一次性的任务,不可以运行常驻型任务,因为会 block 应用容器的启动运行;
-
顺序启动执行,下一个的 init 容器都必须在上一个运行成功后才可以启动;
-
禁止使用 readiness/liveness 探针,可以使用 Pod 定义的activeDeadlineSeconds,这其中包含了 Init Container 的启动时间;
-
禁止使用 lifecycle hook。
例子:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
namespace: demo
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.31
command: [‘sh’, ‘-c’, ‘echo The app is running! && sleep 3600‘]
initContainers:
- name: init-myservice
image: busybox:1.31
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox:1.31
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']