pod 是 Kubernetes 中创建和管理的最小可部署单元。pod 就像一台逻辑主机,是一组共享上下文的容器集合,共享存储、网络,一同调度。
pod 遵循预定义的生命周期,起始于 Pending
阶段, 如果至少其中有一个主要容器正常启动,则进入 Running
,之后取决于 Pod 中是否有容器以失败状态结束而进入 Succeeded
或者 Failed
阶段。pod 在其生命周期中只会被调度一次。 一旦 pod 被调度到某个节点,pod 会一直在该节点运行,直到 pod 停止或者被终止。
1、Pod 定义
pod 使用 yaml 文件定义,使用下面命令查看 pod 资源文档的说明。
kubectl explain pods
pod 有两种用法:
- 运行单个容器的 pod
- 运行多个容器的 pod
例如定义 myhello-pod.yaml 配置文件,运行多个容器的 pod
# api 版本 apiVersion: v1 # 类型 kind: Pod # 元数据 metadata: # 名字 name: myhello-pod # 命名空间 namespace: default # 标签,svc,deployment 通过 labels 管理 pod labels: name: myhello-pod env: dev # 规则 spec: # 容器重启策略:Always、Never、OnFailure restartPolicy: Always # 定义容器 containers: # 容器1: myhello - name: myhello image: xlhmzch/hello:1.0.0 # 镜像拉取策略:Always、Never、IfNotPresent # Always:总是拉取最新镜像,测试环境使用 # Never:不拉取镜像,使用本地镜像 # IfNotPresent:本地没有镜像,则拉取镜像 imagePullPolicy: IfNotPresent # 容器端口 ports: - containerPort: 80 # 容器启动命令,二进制文件名 app command: ["./app"] # 容器启动命令参数 args: ["--param1=k8s-p1", "--param2=k8s-p2"] # 容器使用资源设置 resources: # 容器可分配的最低资源配额 requests: cpu: 100m # cpu 资源单位:1CPU=1000m,通常容器使用的 cpu 配额 100m~300m memory: 200Mi # 内存资源:单位 Ei、Pi、Ti、Gi、Mi、Ki # 容器可分配的最高资源配额 limits: cpu: 200m memory: 500Mi # 注入到容器的环境变量 env: - name: env1 value: "k8s-env1" - name: env2 value: "k8s-env2" # 容器2: redis - name: myredis image: redis imagePullPolicy: IfNotPresent port: - containerPort: 6379 resources: requests: cpu: 100m memory: 200Mi limits: cpu: 200m memory: 500Mi
测试
# 将新的配置应用到资源上 kubectl apply -f myhello-pod.yaml # 获取资源信息 kubectl get pod/myhello-pod kubectl get -f myhello-pod.yaml # 显示资源详细信息 kubectl describe pod/myhello-pod kubectl describe -f myhello-pod.yaml
2、Pod 使用
完整测试代码见 2.4 部分。
2.1、init 容器
作用:业务容器启动前,通常用于数据卷初始化
init 容器与普通容器的区别
- init 容器总是运行到完成。init 容器不是通过守护进程提供服务,而是通过启动容器来执行处理任务。任务完成,则容器完成。
- 每个 init 容器都必须在下一个启动之前成功完成。多个 init 容器按定义顺序逐个执行一次, 每个 init 容器必须运行成功,这样下一个 init 容器才能够运行。只有当所有 init 容器完成时,k8s 才会启动 pod 应用容器。
例:在上述例子基础上,定义 init 容器:init-myservice,init-mydb
spec: --- # 定义 init 容器 initContainers: - name: init-myservice image: busybox # 查找命名空间下 myservice 服务,如果存在则执行成功,如果不存在则一直查找 command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"] - name: init-mydb image: busybox # 查找命名空间下 mydb 服务,如果存在则执行成功,如果不存在则一直查找 command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"] --- containers:
测试过程
创建 pod
kubectl delete -f myhello-pod.yaml kubectl apply -f myhello-pod.yaml
查看 pod 状态和容器日志
# 获取 pod # 显示 pod 一直处于 init 状态 kubectl get pod # 查看 pod 详细信息 # 显示 pod 状态为 Pending,容器状态:init-myservice: running,其他容器:Waiting kubectl describe -f myhello-pod.yaml # 查看容器日志 # 显示:waiting for myservice kubectl logs -f myhello-pod -c init-myservice
这是因为 myservice 服务和 mydb 服务没有定义,init 容器空转。
创建 myservice 服务
apiVersion: v1 kind: Service metadata: name: myservice spec: ports: - protocol: TCP port: 80
查看 pod 状态和容器日志
# 启动服务 kubectl apply -f myservice.yaml # 查看 pod 状态,容器日志 # STATUS:Init:1/2 kubectl get -f myhello-pod.yaml # init-myservice 容器:State: Terminated,Reason: Completed,Exit Code: 0 kubectl describe -f myhello-pod.yaml # 查看 init-mydb 容器 kubectl logs -f myhello-pod -c init-mydb
创建 mydb 服务
apiVersion: v1 kind: Service metadata: name: mydb spec: ports: - protocol: TCP port: 80
查看 pod 状态和容器日志
kubectl apply -f mydb.yaml kubectl get -f myhello-pod.yaml kubectl describe -f myhello-pod.yaml
2.2、容器生命周期处理函数
k8s 支持 postStart 和 preStop 事件。
- postStart 事件:容器启动后,k8s 发送该事件
- preStop 事件:容器停止前,k8s 发送该事件
例:
containers: --- # 容器生命周期处理函数 lifecycle: postStart: exec: command: ["/bin/sh", "-c","echo post start command exec >> /tmp/data"] preStop: exec: command: ["/bin/sh", "-c","echo pre stop command exec >> /tmp/data"] ---
创建 pod
kubectl delete pod myhello-pod kubectl create -f myhello-pod.yaml
测试
# 使用 shell 连接到 pod 中的容器 kubectl exec -it pod/myhello-pod -c myhello --sh # 查看 postStart 处理函数写入的文本 cat /tmp/data
2.3、容器的探测
probe 是由 kubelet 对容器执行的定期诊断,既可以在容器内执行代码,也可以发出一个网络请求。
2.3.1、探测机制
- exec:在容器内执行指定命令。若退出码为0, 则诊断成功。
- httpGet:对容器的 IP 地址上指定端口和路径执行 HTTP GET 请求。如果响应的状态码大于等于 200 且小于 400,则诊断成功。
- tcpSocket:对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的
- grpc:使用 grpc 执行一个远程过程调用。目标应该实现 grpc 健康检查。 如果响应的状态是 "SERVING",则诊断成功。 gRPC 探针是一个 alpha 特性。
2.3.2、探测结果
Success
:容器通过了诊断。Failure
:容器未通过诊断。Unknown
:诊断失败,不会采取任何行动。
2.3.3、探测类型
若容器不提供探针,则默认状态为 Success。
startupProbe
启动探测:检测容器是否已经启动。探测成功后,则停止探测;探测失败,kubelet 将杀死容器,容器根据重启策略决定未来。
readinessProbe
就绪探测:检查容器是否准备好为请求提供服务。探测成功,继续探测;探测失败,端点控制器 将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址,即流量不再分配对应的节点。对 pod 本身不做任何处理。
使用场景:就绪探测检测后端服务是否可用。例如:数据库服务。就绪探测标识容器具备接收请求的能力,可以处理流量。
livenessProbe
存活探测:检测容器是否正在运行。探测成功,继续探测;探测失败,kubelet 将杀死容器,容器根据重启策略决定未来。
2.3.4、案例
containers: --- # 1、启动探测 startupProbe: exec: command: ["/bin/sh", "-c","statuscode=`curl -o /dev/null -s -w %{http_code} http://localhost/healthz`; [ $$statuscode -le 400 ] || exit 1"] # 指定初始化时间,即探针在容器启动之后的延迟时长 initialDelaySeconds: 5 # 探测周期,即每隔多长时间探测一次 periodSeconds: 5 # 最小连续失败次数,即连续失败多少次表示探测失败 failureThreshold: 3 # 最小连续成功次数,即连续探测成功多少次表示探测成功,liveness 和 startup 必须为1 successThreshold: 1 # 探测超时时间 timeoutSeconds: 1 # 2、就绪探测 readinessProbe: httpGet: path: /health port: 80 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 successThreshold: 1 timeoutSeconds: 1 # 3、存活检测 livenessProbe: httpGet: path: /health port: 80 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 successThreshold: 1 timeoutSeconds: 1 --- - name: myredis # 存活检测 livenessProbe: tcpSocket: port: 6379 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 successThreshold: 1
创建 pod
kubectl delete pod myhello-pod kubectl create -f myhello-pod.yaml
查看启动探测
# 查看启动检测 kubectl logs -f pod/myhello-pod -c myhello
查看就绪探测和存活探测
kubectl describe pod/myhello-pod # 容器 myhello Liveness: http-get http://:80/health delay=5s timeout=1s period=5s #success=1 #failure=3 Readiness:http-get http://:80/health delay=5s timeout=1s period=5s #success=1 #failure=3 # 容器 redis Liveness: tcp-socket :6379 delay=5s timeout=1s period=5s #success=1 #failure=3
2.4、测试代码
# api 版本 apiVersion: v1 # 类型 kind: Pod # 元数据 metadata: # 名字 name: myhello-pod # 命名空间 namespace: default # 标签,svc,deployment 通过 labels 管理 pod labels: name: myhello-pod env: dev # 规则 spec: # 容器重启策略 restartPolicy: Always # 定义 init 容器 initContainers: - name: init-myservice image: busybox # 查找命名空间下myservice服务,如果存在则执行成功,如果不存在则一直查找 command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"] - name: init-mydb image: busybox # 查找命名空间下mydb服务,如果存在则执行成功,如果不存在则一直查找 command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"] # 定义容器 containers: # 容器1:myhello - name: myhello image: xlhmzch/hello:1.0.0 # 镜像拉取策略 imagePullPolicy: IfNotPresent # 容器端口 ports: - containerPort: 80 # 容器启动命令,二进制文件名 app command: ["./app"] # 容器启动命令参数 args: ["--param1=k8s-p1", "--param2=k8s-p2"] # 容器使用资源设置 resources: # 容器可分配的最低资源配额 requests: cpu: 100m memory: 200Mi # 容器可分配的最高资源配额 limits: cpu: 200m memory: 500Mi # 注入到容器的环境变量 env: - name: env1 value: "k8s-env1" - name: env2 value: "k8s-env2" # 容器生命周期处理函数 lifecycle: postStart: exec: command: ["/bin/sh", "-c","echo post start command exec >> /tmp/data"] preStop: exec: command: ["/bin/sh", "-c","echo pre stop command exec >> /tmp/data"] # 启动探测 startupProbe: exec: command: ["/bin/sh", "-c","statuscode=`curl -o /dev/null -s -w %{http_code} http://localhost/healthz`; [ $$statuscode -le 400 ] || exit 1"] # 指定初始化时间,即探针在容器启动之后的延迟时长 initialDelaySeconds: 5 # 探测周期,即每隔多长时间探测一次 periodSeconds: 5 # 最小连续失败次数,即连续失败多少次表示探测失败 failureThreshold: 3 # 最小连续成功次数,即连续探测成功多少次表示探测成功,liveness 和 startup 必须为1 successThreshold: 1 # 探测超时时间 timeoutSeconds: 1 # 就绪探测 readinessProbe: httpGet: path: /health port: 80 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 successThreshold: 1 timeoutSeconds: 1 # 存活检测 livenessProbe: httpGet: path: /health port: 80 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 successThreshold: 1 timeoutSeconds: 1 # 容器2: redis - name: myredis image: redis imagePullPolicy: IfNotPresent ports: - containerPort: 6379 resources: requests: cpu: 100m memory: 200Mi limits: cpu: 200m memory: 500Mi # 存活检测 livenessProbe: tcpSocket: port: 6379 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 successThreshold: 1
3、Pod 的部署
在 Kubernetes 中,Pod 工作负载,是集群上处于运行状态的一组容器的集合。为了减轻用户的使用负担,通常不需要用户直接管理每个 Pod。 而是使用负载资源来替用户管理一组 Pod。 这些负载资源通过配置控制器来管理。
负载资源
Deployment
:管理无状态应用StatefulSet
:管理有状态应用DaemonSet
:每个节点只运行 1 个 pod 副本Job
和CronJob
:定义任务。
3.1、Deployment
管理无状态应用部署。内部自动创建 RS (ReplicaSet
) 用于 pod 副本控制。
相较于 RC | RS 具有以下优势:
- deployment 资源对象会自动创建 RS 资源对象来完成部署,对 deployment 的修改并发布会产生新的 RS 资源对象,为新的发布版本服务。
- 支持查看部署进度,以确定部署操作是否完成
- 更新 deployment,会触发部署从而更新 pod
- 支持 pause | resume 操作,暂停后修改不会触发发布,恢复后发布新的 deployment
- 支持回滚操作
- 支持重新启动操作,触发 pod 更新。
- 自动清理不需要的 RS
例:定义 deployment 配置文件
apiVersion: apps/v1 kind: Deployment metadata: name: myapp-deploy namespace: default labels: name: myapp-deploy spec: # pod 副本数 replicas: 3 # 标签选择器:查找管理的 pod selector: # 匹配 template.metadata.labels matchLabels: app: myapp # pod 模板:定义 pod template: metadata: labels: app: myapp spec: restartPolicy: Always containers: - name: myhello image: xlhmzch/hello:1.0.0 imagePullPolicy: IfNotPresent ports: - containerPort: 80 command: ["./app"] args: ["--param1=k8s-p1", "--param2=k8s-p2"] resources: requests: cpu: 100m memory: 200Mi limits: cpu: 200m memory: 500Mi env: - name: env1 value: "k8s-env1" - name: env2 value: "k8s-env2" startupProbe: exec: command: ["/bin/sh", "-c","statuscode=`curl -o /dev/null -s -w %{http_code} http://localhost/healthz`; [ $$statuscode -le 400 ] || exit 1"] initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 successThreshold: 1 timeoutSeconds: 1 readinessProbe: httpGet: path: /health port: 80 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 successThreshold: 1 timeoutSeconds: 1 livenessProbe: httpGet: path: /health port: 80 initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 successThreshold: 1 timeoutSeconds: 1
测试方法:
# 应用部署 kubectl apply -f myapp-deployment.yaml # 获取部署信息 kubectl get -f myapp-deployment.yaml # 获取副本控制器 kubectl get rs # 修改 deployment # 查看线上与线下的区别 kubectl diff -f myapp-deployment.yaml # 应用新的配置文件,实现 deployment 升级 kubectl apply -f myapp-deployment.yaml # 获取部署信息 kubectl get -f myapp-deployment.yaml # 获取副本控制器 kubectl get rs # 管理发布版本 kubectl rollout # 查看版本发布的历史 kubectl rollout history deployment myapp-deploy # 查看指定版本 kubectl rollout history deployment myapp-deploy --revision 1 # 回滚指定版本,回滚后也算新版本(revision + 1) kubectl rollout undo deployment myapp-deploy --to-revision 1 # 查看发布状态 kubectl rollout status deployment myapp-deploy # 暂停部署 kubectl rollout pause deployment myapp-deploy # 期间任何修改,不会上线 kubectl apply -f myapp-deployment.yaml # 恢复部署 kubectl rollout resume deployment myapp-deploy # 重启部署 kubectl rollout restart deployment myapp-deploy # 自动伸缩容 kubectl autoscale deployment myapp-deploy --min 2 --max 3 --cpu-percent 20 kubectl get horizontalpodautoscalers.autoscaling
3.2、DaemonSets
每个节点只运行一个 pod 副本。
应用:每个节点上运行集群守护进程、日志收集守护进程和监控守护进程。
例:编写 DaemonSet 配置文件
apiVersion: apps/v1 kind: DaemonSet metadata: name: myapp-ds namespace: default labels: app: myapp-ds spec: selector: matchLabels: app: myapp-ds template: metadata: labels: app: myapp-ds spec: tolerations: # 设置容忍度,即能否部署到有污点的节点上 master - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule containers: - name: myhello image: xlhmzch/hello:1.0.0 imagePullPolicy: IfNotPresent ports: - containerPort: 80 env: - name: env1 value: "k8s-env1" - name: env2 value: "k8s-env2"
测试方法同上。
3.3、静态 pod
静态 pod 直接由指定节点上的 kubelet
守护进程管理。 大多数 Pod 都是通过控制面(例 Deployment)来管理的,对于静态 Pod 而言,kubelet
直接监控每个 Pod。
例:编写静态 pod 配置文件
apiVersion: v1 kind: Pod metadata: name: myhello-static-pod namespace: default labels: name: myhello-static-pod env: dev spec: containers: - name: myhello image: xlhmzch/hello:1.0.0 imagePullPolicy: IfNotPresent ports: - containerPort: 80 command: ["./app"] args: ["--param1=k8s-p1", "--param2=k8s-p2"] resources: requests: cpu: 100m memory: 200Mi limits: cpu: 200m memory: 500Mi env: - name: env1 value: "k8s-env1" - name: env2 value: "k8s-env2"