Pod 是 K8S 中的调度单位,它是一个逻辑概念,用于将一些关系密切的容器部署在一起提供对外服务,这些容器互相之间会发生直接的文件交换、使用 localhost 或者 Socket 文件进行本地通信、会发生非常频繁的远程调用、需要共享某些 Linux Namespace 等等,Pod 中的所有容器都共享同一个 Network Namespace。K8S 中为了实现不同的目的,在 Pod 基础之上衍生出了不同的部署模型,例如,常见的 Deployment、Replicaset、以及 StatefulSet 等等,本文就来举例并且说明它们之间的区别。
这些对象之间的关系如下图所示:
Pod
Pod 这个看似复杂的 API 对象,实际上就是对容器的进一步抽象和封装而已,Pod 对象,其实就是容器的升级版,它对容器进行了组合,添加了更多的属性和字段。依据 PodAPI 编写一个 Pod 模板,然后使用 kubectl 提交到集群中,这个 Pod 里面包含两个容器,whoami 监听 80 端口,提供一个简单的服务返回主机名,nettool 是一个工具容器,提供了很多可用的网络工具供测试使用:
通常情况下,我们在生产环境不会直接使用 ReplicaSet,因为它不支持滚动更新,所谓的滚动更新就是当我们升级 Pod 的时候,可以在不中断服务的情况下,通过交替升级的方式,让所有的 Pod 都达到最新的状态,除此之外还可以实现版本控制,根据需要回滚到具体的版本,这些操作对于 ReplicaSet 就得手动操作。下面是一个 Deployment 的示例:
.spec.strategy.type,表示 Pod 更新时的策略,默认是 RollingUpdate,表示滚动更新,如果取值 Recreate,它会直接将老的 Pod 停止,再创建新的 Pod;
.spec.strategy.rollingUpdate,表示滚动更新时的策略:
maxUnavailable,它是一个可选字段,用来指定更新过程中不可用的 Pod 的个数上限。该值可以是绝对数字(例如,5),也可以是所需 Pod 的百分比(例如,10%)。百分比值会转换成绝对数并去除小数部分。 如果 maxSurge 为 0,则此值不能为 0。 默认值为 25%。例如,当此值设置为 30% 时,滚动更新开始时会立即将旧 ReplicaSet 缩容到期望 Pod 个数的 70%。新 Pod 准备就绪后,可以继续缩容旧有的 ReplicaSet,然后对新的 ReplicaSet 扩容,确保在更新期间可用的 Pod 总数在任何时候都至少为所需的 Pod 个数的 70%。
maxSurge,是一个可选字段,用来指定可以创建的超出期望 Pod 个数的 Pod 数量。此值可以是绝对数(例如,5)或所需 Pod 的百分比(例如,10%)。如果 MaxUnavailable 为 0,则此值不能为 0。百分比值会通过向上取整转换为绝对数。此字段的默认值为 25%。例如,当此值为 30% 时,启动滚动更新后,会立即对新的 ReplicaSet 扩容,同时保证新旧 Pod 的总数不超过所需 Pod 总数的 130%。一旦旧 Pod 被杀死,新的 ReplicaSet 可以进一步扩容,同时确保更新期间的任何时候运行中的 Pod 总数最多为所需 Pod 总数的 130%。
执行上面的部署命令之后,查询 pod、rs 以及 deployment 的状态如下所示:
kubectl get pod,rs,deploy -n deploy-test -owide
从这可以看到,Deployment 通过控制 ReplicaSet 来满足 Pod 数量的要求,相比 ReplicaSet 增加了以下字段:
UP-TO-DATE:表示当前处于最新版本的 Pod 数量,所谓最新版本指的是 Pod 的 Spec 部分与 Deployment 里 Pod 模板里定义的完全一致;
AVAILABLE:当前已经可用的 Pod 的个数,即:既是 Running 状态,又是最新版本,并且已经处于 Ready(健康检查正确)状态的 Pod 的个数;
更新
使用下面的命令更新 Pod:
kubectl set image -n deploy-test deployment/nginx-deploy nginx=nginx:1.16.1 --record
root@ctrlnode:/home/ubuntu# kubectl describe deploy -n deploy-test nginx-deploy Name: nginx-deploy Namespace: deploy-test CreationTimestamp: Fri, 12 Jan 2024 23:18:16 +0800 Labels: app=nginx app.kubernetes.io/name=nginx-deploy app.kubernetes.io/part-of=nginx-deploy Annotations: deployment.kubernetes.io/revision: 2 kubernetes.io/change-cause: kubectl set image deployment/nginx-deploy nginx=nginx:1.16.1 --namespace=deploy-test --record=true Selector: app=nginx Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 1 max unavailable, 1 max surge Pod Template: ... OldReplicaSets: nginx-deploy-6b5d947665 (0/0 replicas created) NewReplicaSet: nginx-deploy-5dd86f689f (3/3 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 9m6s deployment-controller Scaled up replica set nginx-deploy-6b5d947665 to 3 Normal ScalingReplicaSet 8m9s deployment-controller Scaled up replica set nginx-deploy-5dd86f689f to 1 Normal ScalingReplicaSet 8m9s deployment-controller Scaled down replica set nginx-deploy-6b5d947665 to 2 from 3 Normal ScalingReplicaSet 8m9s deployment-controller Scaled up replica set nginx-deploy-5dd86f689f to 2 from 1 Normal ScalingReplicaSet 8m8s deployment-controller Scaled down replica set nginx-deploy-6b5d947665 to 1 from 2 Normal ScalingReplicaSet 8m8s deployment-controller Scaled up replica set nginx-deploy-5dd86f689f to 3 from 2 Normal ScalingReplicaSet 8m8s deployment-controller Scaled down replica set nginx-deploy-6b5d947665 to 0 from 1
滚动更新过程中可以使用下面的命令查看滚动更新的过程:
kubectl rollout status -n deploy-test deployment/nginx-deploy
1 2
$ kubectl rollout status -n deploy-test deployment/nginx-deploy deployment "nginx-deploy" successfully rolled out
滚动更新的历史可以使用下面的命令查看:
kubectl rollout history -n deploy-test deployment/nginx-deploy
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR service/nginx ClusterIP None <none> 80/TCP 16s app=nginx
NAME READY AGE CONTAINERS IMAGES statefulset.apps/nginx-sts 3/3 16s nginx,nettool nginx:1.14.1,praqma/network-multitool
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod/nginx-sts-0 2/2 Running 0 16s 10.42.0.6 ctrlnode <none> <none> pod/nginx-sts-1 2/2 Running 0 13s 10.42.0.7 ctrlnode <none> <none> pod/nginx-sts-2 2/2 Running 0 10s 10.42.0.8 ctrlnode <none> <none>
可以看到的是 3 个 Pod 都绑定到了不同的 PV,可以使用下面的命令进行验证:
for i in 0 1 2; do kubectl exec nginx-sts-$i -n sts-test -c nettool -- curl localhost; done
1 2 3 4
# for i in 0 1 2; do kubectl exec nginx-sts-$i -n sts-test -c nettool -- curl localhost; done hello from hostpath v1 hello from hostpath v2 hello from hostpath v3
有状态的 Pod 会有数据数据写入到 Pod 挂载的 Volume 中,模拟这个写入,可以使用下面的命令将主机明写入到 index.html 中:
for i in 0 1 2; do kubectl exec nginx-sts-$i -n sts-test -c nginx -- sh -c 'echo hello $(hostname) > /usr/share/nginx/html/index.html'; done
1 2 3 4 5
$ for i in 0 1 2; do kubectl exec nginx-sts-$i -n sts-test -c nginx -- sh -c 'echo hello $(hostname) > /usr/share/nginx/html/index.html'; done $ for i in 0 1 2; do kubectl exec nginx-sts-$i -n sts-test -c nettool -- curl localhost; done hello nginx-sts-0 hello nginx-sts-1 hello nginx-sts-2
此时查看 ctrlnode 上的 /mnt/k8s/hostpath/ 中的内容,也会被相应的修改。有状态的 Pod 重点在于 Pod 重建数据不丢失,所以即使现在删除 3 个 Pod 之后,重建出来的 Pod 还是会挂载原来的数据卷,也就是从 nginx-sts-0 中访问到的依然是 hello nginx-sts-0:
1 2 3 4 5 6 7 8
$ kubectl delete pod -n sts-test nginx-sts-0 nginx-sts-1 nginx-sts-2 pod "nginx-sts-0" deleted pod "nginx-sts-1" deleted pod "nginx-sts-2" deleted $ for i in 0 1 2; do kubectl exec nginx-sts-$i -n sts-test -c nettool -- curl localhost; done hello nginx-sts-0 hello nginx-sts-1 hello nginx-sts-2
$ kubectl get ds,pod -n ds-test -owide NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE CONTAINERS IMAGES SELECTOR daemonset.apps/fluentd-elasticsearch 1 1 1 1 1 <none> 15s fluentd-elasticsearch quay.io/fluentd_elasticsearch/fluentd:v2.5.2 name=fluentd-elasticsearch
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod/fluentd-elasticsearch-hrhn4 1/1 Running 0 15s 10.42.0.19 f00596107-px <none> <none>
NAME COMPLETIONS DURATION AGE CONTAINERS IMAGES SELECTOR job.batch/pi 4/4 116s 9m55s pi perl:5.34.0 batch.kubernetes.io/controller-uid=8d6c866d-ef96-4152-bc69-09010e705d84 root@F00596107-PX:/mnt/e/rust/hello#
kubectl get cronjob,job,pod -n cronjob-test -owide
1 2 3 4 5 6 7 8 9 10 11 12
$ kubectl get cronjob,job,pod -n cronjob-test -owide NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE CONTAINERS IMAGES SELECTOR cronjob.batch/pi */5 * * * * False 1 13s 5m43s pi perl:5.34.0 <none>
NAME COMPLETIONS DURATION AGE CONTAINERS IMAGES SELECTOR job.batch/pi-28418940 2/4 13s 13s pi perl:5.34.0 batch.kubernetes.io/controller-uid=ee359b6e-a7d1-44fe-b722-18f1992b42a9