# K8s 资源调度 deployment、statefulset、daemonset
# 1. 无状态应用管理 Deployment
[root@k8s-master01 ~]# cat nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
labels:
app: nginx-deploy
annotations:
app: nginx-deploy
namespace: default
spec:
selector:
matchLabels:
app: nginx-deploy
replicas: 3
template:
metadata:
labels:
app: nginx-deploy
spec:
containers:
- name: nginx-deploy
image: nginx:1.21.0
imagePullPolicy: IfNotPresent
restartPolicy: Always
示例解析:
nginx-deploy:Deployment 的名称;
replicas: 创建 Pod 的副本数;
selector:定义 Deployment 如何找到要管理的 Pod,与 template 的 label(标签)对应,apiVersion 为 apps/v1 必须指定该字段;
template 字段包含以下字段:
app: nginx-deploy 使用 label(标签)标记 Pod;
spec:表示 Pod 运行一个名字为 nginx 的容器;
image:运行此 Pod 使用的镜像;
Port:容器用于发送和接收流量的端口。
# 1.1 更新 Deployment
假如更新 Nginx Pod 的 image 使用 nginx:latest,并使用 --record 记录当前更改的参数,后期回滚时可以查看到对应的信息:
[root@k8s-master01 ~]# kubectl set image deployment nginx-deploy nginx-deploy=nginx:latest --record
更新过程为新旧交替更新,首先新建一个 Pod,当 Pod 状态为 Running 时,删除一个旧的 Pod,同时再创建一个新的 Pod。当触发一个更新后,会有新的 ReplicaSet 产生,旧的 ReplicaSet 会被保存,查看此时 ReplicaSet,可以从 AGE 或 READY 看出来新旧 ReplicaSet:
[root@k8s-master01 ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deploy-65bfb77869 0 0 0 50s
nginx-deploy-85b94dddb4 3 3 3 8s
通过 describe 查看 Deployment 的详细信息:
[root@k8s-master01 ~]# kubectl describe deploy nginx-deploy
Name: nginx-deploy
Namespace: default
CreationTimestamp: Mon, 14 Apr 2025 11:28:03 +0800
Labels: app=nginx-deploy
Annotations: app: nginx-deploy
deployment.kubernetes.io/revision: 2
kubernetes.io/change-cause: kubectl set image deployment nginx-deploy nginx-deploy=nginx:latest --record=true
Selector: app=nginx-deploy
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx-deploy
Containers:
nginx-deploy:
Image: nginx:latest
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Node-Selectors: <none>
Tolerations: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: nginx-deploy-65bfb77869 (0/0 replicas created)
NewReplicaSet: nginx-deploy-85b94dddb4 (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 71s deployment-controller Scaled up replica set nginx-deploy-65bfb77869 from 0 to 3
Normal ScalingReplicaSet 29s deployment-controller Scaled up replica set nginx-deploy-85b94dddb4 from 0 to 1
Normal ScalingReplicaSet 28s deployment-controller Scaled down replica set nginx-deploy-65bfb77869 from 3 to 2
Normal ScalingReplicaSet 28s deployment-controller Scaled up replica set nginx-deploy-85b94dddb4 from 1 to 2
Normal ScalingReplicaSet 27s deployment-controller Scaled down replica set nginx-deploy-65bfb77869 from 2 to 1
Normal ScalingReplicaSet 27s deployment-controller Scaled up replica set nginx-deploy-85b94dddb4 from 2 to 3
Normal ScalingReplicaSet 26s deployment-controller Scaled down replica set nginx-deploy-65bfb77869 from 1 to 0
在 describe 中可以看出,第一次创建时,它创建了一个名为 nginx-deploy-65bfb77869 的 ReplicaSet,并直接将其扩展为 3 个副本。更新部署时,它创建了一个新的 ReplicaSet,命名为 nginx-deploy-85b94dddb4,并将其副本数扩展为 1,然后将旧的 ReplicaSet 缩小为 2,这样至少可以有 2 个 Pod 可用,最多创建了 4 个 Pod。以此类推,使用相同的滚动更新策略向上和向下扩展新旧 ReplicaSet,最终新的 ReplicaSet 可以拥有 3 个副本,并将旧的 ReplicaSet 缩小为 0。
# 1.2 回滚 Deployment
当更新了版本不稳定或配置不合理时,可以对其进行回滚操作,假设我们又进行了几次更新(此处以更新镜像版本触发更新,更改配置效果类似):
# kubectl set image deployment nginx-deploy nginx-deploy=nginx:1.21.1 --record
# kubectl set image deployment nginx-deploy nginx-deploy=nginx:1.21.2 --record
使用 kubectl rollout history 查看更新历史:
[root@k8s-master01 ~]# kubectl rollout history deployment nginx-deploy
deployment.apps/nginx-deploy
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment nginx-deploy nginx-deploy=nginx:latest --record=true
3 kubectl set image deployment nginx-deploy nginx-deploy=nginx:1.21.1 --record=true
4 kubectl set image deployment nginx-deploy nginx-deploy=nginx:1.21.2 --record=true
查看 Deployment 某次更新的详细信息,使用 --revision 指定某次更新版本号:
# kubectl rollout history deployment nginx-deploy --revision=4
deployment.apps/nginx-deploy with revision #4
Pod Template:
Labels: app=nginx-deploy
pod-template-hash=65b576b795
Annotations: kubernetes.io/change-cause: kubectl set image deployment nginx-deploy nginx-deploy=nginx:1.21.2 --record=true
Containers:
nginx-deploy:
Image: nginx:1.21.2
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Node-Selectors: <none>
Tolerations: <none>
如果只需要回滚到上一个稳定版本,使用 kubectl rollout undo 即可:
# kubectl rollout undo deployment nginx-deploy
再次查看更新历史,发现 REVISION3 回到了 nginx:1.21.1:
# kubectl rollout history deployment nginx-deploy
deployment.apps/nginx-deploy
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment nginx-deploy nginx-deploy=nginx:latest --record=true
4 kubectl set image deployment nginx-deploy nginx-deploy=nginx:1.21.2 --record=true
5 kubectl set image deployment nginx-deploy nginx-deploy=nginx:1.21.1 --record=true
如果要回滚到指定版本,使用 --to-revision 参数:
# kubectl rollout undo deployment nginx-deploy --to-revision=2
# 1.3 扩容 Deployment
当公司访问量变大,或者有预期内的活动时,三个 Pod 可能已无法支撑业务时,可以提前对其进行扩展。
使用 kubectl scale 动态调整 Pod 的副本数,比如增加 Pod 为 5 个:
# kubectl scale deployment nginx-deploy --replicas=5
查看 Pod,此时 Pod 已经变成了 5 个:
# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deploy-85b94dddb4-2qrh6 1/1 Running 0 2m9s
nginx-deploy-85b94dddb4-gvkqj 1/1 Running 0 2m10s
nginx-deploy-85b94dddb4-mdfjs 1/1 Running 0 22s
nginx-deploy-85b94dddb4-rhgpr 1/1 Running 0 2m8s
nginx-deploy-85b94dddb4-vwjhl 1/1 Running 0 22s
# 1.4 暂停和恢复 Deployment 更新
上述演示的均为更改某一处的配置,更改后立即触发更新,大多数情况下可能需要针对一个资源文件更改多处地方,而并不需要多次触发更新,此时可以使用 Deployment 暂停功能,临时禁用更新操作,对 Deployment 进行多次修改后在进行更新。
使用 kubectl rollout pause 命令即可暂停 Deployment 更新:
# kubectl rollout pause deployment nginx-deploy
然后对 Deployment 进行相关更新操作,比如先更新镜像,然后对其资源进行限制(如果使用的是 kubectl edit 命令,可以直接进行多次修改,无需暂停更新,kubectlset 命令一般会集成在 CICD 流水线中):
# kubectl set image deployment nginx-deploy nginx-deploy=nginx:1.21.3
# kubectl set resources deployment nginx-deploy -c=nginx-deploy --limits=cpu=200m,memory=512Mi
通过 rollout history 可以看到没有新的更新:
# kubectl rollout history deployment nginx-deploy
进行完最后一处配置更改后,使用 kubectl rollout resume 恢复 Deployment 更新:
# kubectl rollout resume deployment nginx-deploy
可以查看到恢复更新的 Deployment 创建了一个新的 RS(ReplicaSet 缩写):
# kubectl get rs
可以查看 Deployment 的 image(镜像)已经变为 nginx:1.21.3
[root@k8s-master01 ~]# kubectl get pods -oyaml|grep image
- image: nginx:1.21.3
imagePullPolicy: IfNotPresent
image: docker.io/library/nginx:1.21.3
imageID: docker.io/library/nginx@sha256:644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36
- image: nginx:1.21.3
imagePullPolicy: IfNotPresent
image: docker.io/library/nginx:1.21.3
imageID: docker.io/library/nginx@sha256:644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36
- image: nginx:1.21.3
imagePullPolicy: IfNotPresent
image: docker.io/library/nginx:1.21.3
imageID: docker.io/library/nginx@sha256:644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36
- image: nginx:1.21.3
imagePullPolicy: IfNotPresent
image: docker.io/library/nginx:1.21.3
imageID: docker.io/library/nginx@sha256:644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36
- image: nginx:1.21.3
imagePullPolicy: IfNotPresent
image: docker.io/library/nginx:1.21.3
imageID: docker.io/library/nginx@sha256:644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36
# 1.5 更新 Deployment 的注意事项
在默认情况下,revision 保留 10 个旧的 ReplicaSet,其余的将在后台进行垃圾回收,可以在.spec.revisionHistoryLimit 设置保留 ReplicaSet 的个数。当设置为 0 时,不保留历史记录。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
namespace: default
labels:
app: nginx-deploy
spec:
replicas: 5
selector:
matchLabels:
app: nginx-deploy
template:
metadata:
labels:
app: nginx-deploy
spec:
containers:
- name: nginx-deploy
image: nginx:1.21.3
resources:
limits:
cpu: 200m
memory: 512Mi
imagePullPolicy: IfNotPresent
restartPolicy: Always
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
revisionHistoryLimit: 10
更新策略:
- spec.strategy.type==Recreate,表示重建,先删掉旧的 Pod 再创建新的 Pod;
strategy:
type: Recreate
spec.strategy.type==RollingUpdate,表示滚动更新,可以指定 maxUnavailable 和 maxSurge 来控制滚动更新过程;
spec.strategy.rollingUpdate.maxUnavailable,指定在回滚更新时最大不可用的 Pod 数量,可选字段,默认为 25%,可以设置为数字或百分比,如果 maxSurge 为 0,则该值不能为 0;
spec.strategy.rollingUpdate.maxSurge 可以超过期望值的最大 Pod 数,可选字段,默认为 25%,可以设置成数字或百分比,如果 maxUnavailable 为 0,则该值不能为 0。
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
# 2. 有状态应用管理 StatefulSet
apiVersion: v1
kind: Service
metadata:
name: web
namespace: default
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: nginx
type: ClusterIP
clusterIP: None
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx
namespace: default
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
resources:
limits:
cpu: '1'
memory: 1Gi
requests:
cpu: 100m
memory: 128Mi
restartPolicy: Always
serviceName: web
- kind: Service 定义了一个名字为 web 的 Headless Service,创建的 Service 格式为 nginx-0.web.default.svc.cluster.local,其他的类似,因为没有指定 Namespace(命名空间),所以默认部署在 default;
- kind: StatefulSet 定义了一个名字为 nginx 的 StatefulSet,replicas 表示部署 Pod 的副本数,本实例为 3。
# 2.1 创建 StatefulSet
[root@k8s-master01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-0 1/1 Running 0 8m51s
nginx-1 1/1 Running 0 8m50s
nginx-2 1/1 Running 0 8m48s
[root@k8s-master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d1h
web ClusterIP None <none> 80/TCP 9m28s
[root@k8s-master01 ~]# kubectl get sts
NAME READY AGE
nginx 3/3 8m58s
# 2.2 StatefulSet 创建 Pod 流程
StatefulSet 管理的 Pod 部署和扩展规则如下:
- 对于具有 N 个副本的 StatefulSet,将按顺序从 0 到 N-1 开始创建 Pod;
- 当删除 Pod 时,将按照 N-1 到 0 的反顺序终止;
- 在缩放 Pod 之前,必须保证当前的 Pod 是 Running(运行中)或者 Ready(就绪);
- 在终止 Pod 之前,它所有的继任者必须是完全关闭状态。
StatefulSet 的 pod.Spec.TerminationGracePeriodSeconds(终止 Pod 的等待时间)不应该指定为 0,设置为 0 对 StatefulSet 的 Pod 是极其不安全的做法,优雅地删除 StatefulSet 的 Pod 是非常有必要的,而且是安全的,因为它可以确保在 Kubelet 从 APIServer 删除之前,让 Pod 正常关闭。
当创建上面的 Nginx 实例时,Pod 将按 nginx-0、nginx-1、nginx-2 的顺序部署 3 个 Pod。在 nginx-0 处于 Running 或者 Ready 之前,nginx-1 不会被部署,相同的,nginx-2 在 web-1 未处于 Running 和 Ready 之前也不会被部署。如果在 nginx-1 处于 Running 和 Ready 状态时,nginx-0 变成 Failed 失败)状态,那么 nginx-2 将不会被启动,直到 nginx-0 恢复为 Running 和 Ready 状态。
如果用户将 StatefulSet 的 replicas 设置为 1,那么 nginx-2 将首先被终止,在完全关闭并删除 nginx-2 之前,不会删除 nginx-1。如果 nginx-2 终止并且完全关闭后,nginx-0 突然失败,那么在 nginx-0 未恢复成 Running 或者 Ready 时,nginx-1 不会被删除。
# 2.3 tatefulSet 扩容和缩容
和 Deployment 类似,可以通过更新 replicas 字段扩容 / 缩容 StatefulSet,也可以使用 kubectlscale、kubectl edit 和 kubectl patch 来扩容 / 缩容一个 StatefulSet。
# kubectl scale sts nginx --replicas=5
# 2.4 StatefulSet 更新策略
On Delete 策略
OnDelete 更新策略实现了传统(1.7 版本之前)的行为,它也是默认的更新策略。当我们选择这个更新策略并修改 StatefulSet 的.spec.template 字段时,StatefulSet 控制器不会自动更新 Pod,必须手动删除 Pod 才能使控制器创建新的 Pod。
updateStrategy:
type: OnDelete
RollingUpdate 策略
RollingUpdate(滚动更新)更新策略会自动更新一个 StatefulSet 中所有的 Pod,采用与序号索引相反的顺序进行滚动更新。
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0
# 2.5 分段更新
将分区改为 2,此时会自动更新 nginx-2、nginx-3、nginx-4(因为之前更改了更新策略),但是不会更新 nginx-0 和 nginx-1:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2
将 sts 镜像为 nginx:1.21.1
# kubectl set image sts nginx nginx=nginx:1.21.1
按照上述方式,可以实现分阶段更新,类似于灰度 / 金丝雀发布。查看最终的结果如下:
[root@k8s-master01 ~]# kubectl get pods -oyaml|grep image
- image: nginx:latest
imagePullPolicy: IfNotPresent
image: docker.io/library/nginx:latest
imageID: docker.io/library/nginx@sha256:fad8e1cd52e24bce7b72cd7cb674a2efad671647b917055f5bd8a1f7ac9b1af8
- image: nginx:latest
imagePullPolicy: IfNotPresent
image: docker.io/library/nginx:latest
imageID: docker.io/library/nginx@sha256:fad8e1cd52e24bce7b72cd7cb674a2efad671647b917055f5bd8a1f7ac9b1af8
- image: nginx:1.21.1
imagePullPolicy: IfNotPresent
image: docker.io/library/nginx:1.21.1
imageID: docker.io/library/nginx@sha256:a05b0cdd4fc1be3b224ba9662ebdf98fe44c09c0c9215b45f84344c12867002e
- image: nginx:1.21.1
imagePullPolicy: IfNotPresent
image: docker.io/library/nginx:1.21.1
imageID: docker.io/library/nginx@sha256:a05b0cdd4fc1be3b224ba9662ebdf98fe44c09c0c9215b45f84344c12867002e
- image: nginx:1.21.1
imagePullPolicy: IfNotPresent
image: docker.io/library/nginx:1.21.1
imageID: docker.io/library/nginx@sha256:a05b0cdd4fc1be3b224ba9662ebdf98fe44c09c0c9215b45f84344c12867002e
# 2.6 StatefulSet 挂载动态存储
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.20
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "rook-ceph-block"
resources:
requests:
storage: 10Gi
# 3. 守护进程集 DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-ds
labels:
app: nginx-ds
namespace: default
spec:
selector:
matchLabels:
app: nginx-ds
template:
metadata:
labels:
app: nginx-ds
spec:
containers:
- name: nginx-ds
image: nginx:latest
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 1024Mi
cpu: 1
requests:
memory: 128Mi
cpu: 100m
此时会在每个节点创建一个 Pod:
[root@k8s-master01 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-ds-47dxc 1/1 Running 0 56s 172.16.85.213 k8s-node01 <none> <none>
nginx-ds-4m89f 1/1 Running 0 56s 172.16.32.143 k8s-master01 <none> <none>
nginx-ds-mtpc2 1/1 Running 0 56s 172.16.195.12 k8s-master03 <none> <none>
nginx-ds-t5rxc 1/1 Running 0 56s 172.16.122.142 k8s-master02 <none> <none>
nginx-ds-x86kc 1/1 Running 0 56s 172.16.58.222 k8s-node02 <none> <none>
指定节点部署 Pod
nodeSelector:
ingress: 'true'
更新和回滚 DaemonSet
# kubectl set image ds nginx-ds nginx-ds=1.21.0 --record=true
# kubectl rollout undo daemonset <daemonset-name> --to-revision=<revision>
DaemonSet 的更新和回滚与 Deployment 类似,此处不再演示。
# 4. HPA
创建 deployment、service
apiVersion: v1
kind: Service
metadata:
name: nginx-hpa-svc
namespace: default
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: nginx-hpa
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-hpa
labels:
app: nginx-hpa
namespace: default
spec:
selector:
matchLabels:
app: nginx-hpa
replicas: 1
template:
metadata:
labels:
app: nginx-hpa
spec:
restartPolicy: Always
containers:
- name: nginx-hpa
image: nginx:latest
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 1024Mi
cpu: 1
requests:
memory: 128Mi
cpu: 100m
创建 HPA
# kubectl autoscale deployment nginx-hpa --cpu-percent=10 --min=1 --max=10
# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-hpa Deployment/nginx-hpa cpu: 0%/10% 1 10 1 16s
测试自动扩缩容
while true; do wget -q -O- http://10.96.18.221 > /dev/null; done
[root@k8s-master01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-hpa-d8bcbdf7d-4mkxp 1/1 Running 0 66s
nginx-hpa-d8bcbdf7d-974q5 1/1 Running 0 6m36s
nginx-hpa-d8bcbdf7d-g6p2h 1/1 Running 0 66s
nginx-hpa-d8bcbdf7d-lvvsq 1/1 Running 0 111s
nginx-hpa-d8bcbdf7d-tgqmr 1/1 Running 0 111s
nginx-hpa-d8bcbdf7d-tzfbs 1/1 Running 0 21s