目录
- Kubernetes之Pod控制器,ReplicaSet,Deployment,DaemonSet
- ReplicaSet
- Deployment控制器
- 创建Deployment
- Deployment更新
- Deployment扩容
- 金丝雀发布
- Deployment回滚
- DaemonSet
- 定义
- DaemonSet演示 redis-filebeat
- DaemonSet的滚动更新
Kubernetes之Pod控制器,ReplicaSet,Deployment,DaemonSet
Kubernetes中内建了很多controller(控制器),这些相当于?个状态机,?来控制Pod的具体状态和?为。
Pod控制器有多种类型:
ReplicaSet:它的核心作用是代用户创建指定数量的Pod副本,并确定Pod副本一直处于满足用户期望数量的状态,多退少补,同时支持扩缩容机制。主要有三个组件:
- 用户期望的Pod副本数量;
- 标签选择器,选择属于自己管理和控制的Pod;
- 当前Pod数量不满足用户期望数量时,根据资源模板进行新建。
Deployment:工作在ReplicaSet之上,用于管理无状态应用,除了ReplicaSet的机制,还增加了滚动更新和回滚功能,提供声明式配置。
DaemonSet:用于确保集群中的每一个节点只运行特定的pod副本,通常用于实现系统级后台任务。比如ELK服务。要求:服务是无状态的;服务必须是守护进程
Job::只要完成就立即退出,不需要重启或重建。
Cronjob:周期性任务控制,不需要持续后台运行。
StatefulSet:管理有状态应用,如mysql,redis等。
ReplicaSet
ReplicationController用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收。
虽然ReplicaSet可以独立使用,但一般还是建议使用 Deployment 来自动管理ReplicaSet,这样就无需担心跟其他机制的不兼容问题(比如ReplicaSet不支持rolling-update但Deployment支持)。
举例
[[email protected] ~]# kubectl explain rs
[[email protected] manifests]# vim rs-demo.yaml
apiVersion: apps/v1 #群组/版本
kind: ReplicaSet #类型 注意大小写
metadata: #元数据
name: myapp
namespace: default
spec:
replicas: 2 #定义ReplicaSet副本数量
selector: #定义选择器
matchLabels: #匹配 ,创建的模板的标签需要包含此处的标签,不然无法被创建
app: myapp
release: canary
template: #定义模板
metadata: #模板元数据
name: myapp-pod #此处名称定义没有用,被控制器创建的pod都会以控制器的名称后加随机字符串来命名
labels: #此处要包含replicaset处定义的标签选择器
app: myapp
release: canary
spec:
containers: #定义模板的容器
- name: myapp-container
image: ikubernetes/myapp:v1
ports: #端口
- name: http
containerPort: 80
#运行后验证
[[email protected] manifests]# kubectl create -f rs-demo.yaml
replicaset.apps/myapp created
[[email protected] manifests]# kubectl get rs #replicaset 简写rs
NAME DESIRED CURRENT READY AGE
client-f5cdb799f 1 1 1 3d19h
myapp 2 2 2 10s
nginx-deploy-84cbfc56b6 1 1 1 3d23h
#查看pod
[[email protected] manifests]# kubectl get pods
NAME READY STATUS RESTARTS AGE
client-f5cdb799f-pklmc 1/1 Running 0 3d19h
myapp-fxmjz 1/1 Running 0 59s
myapp-hxvnz 1/1 Running 0 59s
nginx-deploy-84cbfc56b6-tcssz 1/1 Running 0 3d22h
pod-demo 2/2 Running 7 2d22h
poststart-pod 1/1 Running 1 2d16h
readiness-httpget-pod 1/1 Running 0 2d16h
#可以使用kubectl describe pods <POD_NAME>来查看详细信息
修改Pod的副本数量:
[[email protected] manifests]# kubectl edit rs myapp
replicas: 5
[[email protected] manifests]# kubectl get pods
NAME READY STATUS RESTARTS AGE
client-f5cdb799f-pklmc 1/1 Running 0 3d19h
myapp-64qnb 1/1 Running 0 9s
myapp-fxmjz 1/1 Running 0 3m53s
myapp-hdv9c 1/1 Running 0 9s
myapp-hxvnz 1/1 Running 0 3m53s
myapp-mwb27 1/1 Running 0 9s
nginx-deploy-84cbfc56b6-tcssz 1/1 Running 0 3d23h
pod-demo 2/2 Running 7 2d22h
poststart-pod 1/1 Running 1 2d16h
readiness-httpget-pod
修改Pod的镜像版本:
[[email protected] manifests]# kubectl edit rs myapp
- image: ikubernetes/myapp:v2
此时查看原来存在的pod副本还是v1版本,只有重建后的pod才会是V2版本。
kubectl delete -f *** && kube create -f &&
Deployment控制器
Deployment为Pod和Replica Set(下一代Replication Controller)提供声明式更新。
只需要在 Deployment 中描述想要的目标状态,Deployment controller 就会帮您将 Pod 和ReplicaSet 的实际状态改变到您的目标状态。也可以定义一个全新的 Deployment 来创建 ReplicaSet 或者删除已有的 Deployment 并创建一个新的来替换。
典型的用例如下:
- 使用Deployment来创建ReplicaSet。ReplicaSet在后台创建pod。检查启动状态。
- 通过更新Deployment的PodTemplateSpec字段来声明Pod的新状态。这会创建一个新的ReplicaSet,Deployment会按照控制的速率将pod从旧的ReplicaSet移动到新的ReplicaSet中.
- 如果当前状态不稳定,回滚到之前的Deployment revision。每次回滚都会更新Deployment的revision.
- 扩容Deployment以满足更高的负载。
- 暂停Deployment来应用PodTemplateSpec的多个修复,然后恢复上线。
- 根据Deployment 的状态判断上线是否hang住了。
- 清除旧的不必要的 ReplicaSet。
官方定义
查看deployment.spec定义所需字段:
[[email protected] ~]# kubectl explain deploy.spec.
KIND: Deployment
VERSION: extensions/v1beta1 #现在最新式apps/v1
minReadySeconds <integer>
paused <boolean> #暂停
progressDeadlineSeconds <integer>
replicas <integer> #replicas
revisionHistoryLimit <integer> #保存历史版本数量,用于回滚,默认10
rollbackTo <Object> #回滚
selector <Object> #选择器,和replicaset的一样
strategy <Object> #更新策略 rollingUpdate滚动更新和Recreate重建更新,如果使用rollingUpdate,内部还可以定义更新粒度
maxSurge #更新时最多可超出副本数 可以是数量和百分比
maxUnavailable #最大不可用 可以是数量和百分比,和上面的maxSurge不能同为0
template <Object> -required- #模板,和replicaset的一样
创建Deployment
举例
[[email protected] manifests]# vim deploy-damo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: myapp
release: canary
template:
metadata:
labels:
app: myapp
release: canary
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
[[email protected] manifests]# kubectl apply -f deploy-damo.yaml
deployment.apps/myapp-deploy created
Deployment更新
apply表示声明式创建,也可以用来更新
查看创建结果
[[email protected] manifests]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
myapp-deploy 2/2 2 2 2m31s
[[email protected] manifests]# kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp-deploy-65df64765c 2 2 2 3m22s
[[email protected] manifests]# kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-deploy-65df64765c-95dwm 1/1 Running 0 3m24s
myapp-deploy-65df64765c-btbhg 1/1 Running 0 3m24s
pod-demo 2/2 Running 8 2d22h
poststart-pod 1/1 Running 2 2d17h
readiness-httpget-pod 1/1 Running 0 2d17h
Deployment扩容
直接编辑deploy-damo.yaml,副本数修改为4,然后使用kubectl apply -f 更新 也可以使用-w 监控实时更新
[[email protected] manifests]# vim deploy-damo.yaml
replicas: 4
[[email protected] manifests]# kubectl apply -f deploy-damo.yaml
deployment.apps/myapp-deploy configured
再次查看
[[email protected] manifests]# kubectl get rs,deploy,pods
NAME DESIRED CURRENT READY AGE
replicaset.extensions/myapp-deploy-65df64765c 4 4 4 6m53s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/myapp-deploy 4/4 4 4 6m53s
NAME READY STATUS RESTARTS AGE
pod/myapp-deploy-65df64765c-8q8bq 1/1 Running 0 31s
pod/myapp-deploy-65df64765c-95dwm 1/1 Running 0 6m53s
pod/myapp-deploy-65df64765c-btbhg 1/1 Running 0 6m53s
pod/myapp-deploy-65df64765c-x742g 1/1 Running 0 31s
pod/pod-demo 2/2 Running 8 2d22h
pod/poststart-pod 1/1 Running 2 2d17h
pod/readiness-httpget-pod 1/1 Running 0 2d17h
每次使用apply改变的信息都会被保存在annotations里面。
[[email protected] manifests]# kubectl describe deploy myapp-deploy
Annotations: deployment.kubernetes.io/revision: 1
kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"myapp-deploy","namespace":"default"},"spec":{"replicas":4...
StrategyType: RollingUpdate #默认滚动策略
RollingUpdateStrategy: 25% max unavailable, 25% max surge #更新策略25% 低于1补为1,
如果只是更新模板的image 可以使用kubectl set image命令
[[email protected] manifests]# kubectl set image deployment/myapp-deploy myapp=ikubernetes/myapp:v2
如果修改其他配置,例如修改副本数量,滚动更新策略等,可以使用kubectl patch 打补丁后面使用对象的jason格式内容
[[email protected] ~]# kubectl patch deployment myapp-deploy -p '{"spec":{"replicas":5}}'
[[email protected] ~]# kubectl patch deployment myapp-deploy -p '{"spec":{"strategy":{"rollingupdate":{"maxsurge“:1,"maxUnavailable":0}}}}'
金丝雀发布
Deployment控制器支持自定义控制更新过程中的滚动节奏,如“暂停(pause)”或“继续(resume)”更新操作。比如等待第一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的Pod应用,继续观察能否稳定地按期望的方式运行。确定没问题之后再继续完成余下的Pod资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布(Canary Release),如下:
#更新deloyment的v3版本,并配置暂停deployment
[[email protected] manifests]# kubectl set image deployment myapp-deploy myapp=ikubernetes/myapp:v3 && kubectl rollout pause deployment myapp-deploy
deployment.extensions/myapp-deploy image updated
deployment.extensions/myapp-deploy paused
#查看
[[email protected] ~]# kubectl get pod -w
NAME READY STATUS RESTARTS AGE
myapp-deploy-65df64765c-48bjw 1/1 Running 0 8m2s
myapp-deploy-65df64765c-5qd4x 1/1 Running 0 18s
myapp-deploy-65df64765c-w4htd 1/1 Running 0 8m2s
pod-demo 2/2 Running 8 2d23h
poststart-pod 1/1 Running 3 2d17h
readiness-httpget-pod 1/1 Running 0 2d18h
myapp-deploy-548f47d899-cbhsf 0/1 Pending 0 0s
myapp-deploy-548f47d899-cbhsf 0/1 Pending 0 0s
myapp-deploy-548f47d899-cbhsf 0/1 ContainerCreating 0 0s
myapp-deploy-548f47d899-cbhsf 1/1 Running 0 1s
此时只有一个pod版本更新为V3版本
[[email protected] ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-deploy-548f47d899-cbhsf 1/1 Running 0 49s
myapp-deploy-65df64765c-48bjw 1/1 Running 0 9m2s
myapp-deploy-65df64765c-5qd4x 1/1 Running 0 78s
myapp-deploy-65df64765c-w4htd 1/1 Running 0 9m2s
pod-demo 2/2 Running 8 2d23h
poststart-pod 1/1 Running 3 2d17h
readiness-httpget-pod 1/1 Running 0 2d18h
确保更新的pod没问题了,继续更新
[[email protected] manifests]# kubectl rollout resume deployment myapp-deploy
deployment.extensions/myapp-deploy resumed
[[email protected] ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-deploy-548f47d899-5rdgh 1/1 Running 0 59s
myapp-deploy-548f47d899-cbhsf 1/1 Running 0 2m54s
myapp-deploy-548f47d899-prvpl 1/1 Running 0 57s
pod-demo 2/2 Running 8 2d23h
poststart-pod 1/1 Running 3 2d17h
readiness-httpget-pod 1/1 Running 0 2d18h
也可以使用kubectl rollout status <TYPE> <TYPE_NAME>监控
[[email protected] ~]# kubectl rollout status deployment myapp-deploy
deployment "myapp-deploy" successfully rolled out
Deployment回滚
默认情况下,kubernetes 会在系统中保存前两次的 Deployment 的 rollout 历史记录,以便可以随时回退(您可以修改revision history limit来更改保存的revision数)。
注意: 只要 Deployment 的 rollout 被触发就会创建一个 revision。也就是说当且仅当 Deployment 的 Pod template(如.spec.template)被更改,例如更新template 中的 label 和容器镜像时,就会创建出一个新的 revision。
其他的更新,比如扩容 Deployment 不会创建 revision——因此我们可以很方便的手动或者自动扩容。这意味着当您回退到历史 revision 时,只有 Deployment 中的 Pod template 部分才会回退。
[[email protected] ~]# kubectl rollout history deploy myapp-deploy #查看升级记录
deployment.extensions/myapp-deploy
REVISION CHANGE-CAUSE
9 <none>
10 <none>
这里在创建deployment时没有增加--record参数,所以并不能看到revision的变化。在创建 Deployment 的时候使用了--record参数可以记录命令,就可以方便的查看每次 revision 的变化。
查看单个revision的详细信息:
[[email protected] ~]# kubectl rollout history deployment/myapp-deploy --revision=10
回滚历史版本,默认是上一个版本
[[email protected] ~]# kubectl rollout undo deployment/myapp-deploy
deployment.extensions/myapp-deploy rolled back
使用 --to-revision参数指定某个历史版本:
[[email protected] ~]# kubectl rollout undo deployment/myapp-deploy --to-revision=11
deployment.extensions/myapp-deploy skipped rollback (current template already matches revision 11)
DaemonSet
定义
DaemonSet确保全部(或者一些)Node上运行一个Pod的副本。当有Node加入集群时也会为他们新增一个Pod。当有Node从集群移除时,这些Pod也会被回收。删除DaemonSet将会删除它创建的所有Pod。
DaemonSet 的一些典型用法:
- 运行集群存储 daemon,例如在每个 Node 上运行 glusterd、ceph。
- 在每个 Node 上运行日志收集 daemon,例如fluentd、logstash。
- 在每个 Node 上运行监控 daemon,例如 Prometheus Node Exporter、collectd、Datadog 代理、New Relic 代理,或 Ganglia gmond。
一个简单的用法是,在所有的 Node 上都存在一个 DaemonSet,将被作为每种类型的 daemon 使用。 一个稍微复杂的用法可能是,对单独的每种类型的 daemon 使用多个 DaemonSet,但具有不同的标志,和/或对不同硬件类型具有不同的内存、CPU要求。
[[email protected] ~]# kubectl explain ds.spec.
KIND: DaemonSet
minReadySeconds <integer>
revisionHistoryLimit <integer>
selector <Object>
template <Object> -required-
templateGeneration <integer>
updateStrategy <Object>
DaemonSet演示 redis-filebeat
[[email protected] manifests]# vim ds-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace": default
spec:
replicas: 1
selector:
matchLabels:
app: redis
role: logstore
template:
metadata:
labels:
app: redis
role: logstore
spec:
containers:
- name: redis
image: redis:4.0-alpine
ports:
- name: redis
containerPort: 6379
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: filebeat-ds
namespace: default
spec:
selector:
matchLabels:
app: filebeat
release: stable
template:
metadata:
labels:
app: filebeat
release: stable
spec:
containers:
- name: filebeat
image: ikubernetes/filebeat:5.6.5-alpine
env:
- name: REDIS_HOST
value: redis.default.svc.cluster.local
- name: REDIS_LOG_LEVEL
value: info
创建并暴漏端口
[[email protected] manifests]# kubectl apply -f ds-demo.yaml
deployment.apps/redis created
daemonset.apps/filebeat-ds created
[[email protected] manifests]# kubectl expose deploy redis --port=6379
service/redis exposed
[[email protected] manifests]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d19h
myapp NodePort 10.104.138.182 <none> 80:30298/TCP 3d21h
nginx ClusterIP 10.98.2.12 <none> 80/TCP 4d
redis ClusterIP 10.97.149.76 <none> 6379/TCP 15s
测试redis是否收到日志
[[email protected] manifests]# kubectl get pods
NAME READY STATUS RESTARTS AGE
filebeat-ds-28kmv 1/1 Running 0 48s
filebeat-ds-wmlzj 1/1 Running 0 48s
myapp-deploy-65df64765c-9g9bv 1/1 Running 0 29m
myapp-deploy-65df64765c-ll8bk 1/1 Running 0 29m
myapp-deploy-65df64765c-xd2kn 1/1 Running 0 29m
pod-demo 2/2 Running 9 3d
poststart-pod 1/1 Running 3 2d18h
readiness-httpget-pod 1/1 Running 0 2d18h
redis-76c85b5744-7zt7l 1/1 Running 0 48s
#进入redis
[[email protected] manifests]# kubectl exec -it redis-76c85b5744-7zt7l -- /bin/sh
/data # netstat -lnt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN
tcp 0 0 :::6379 :::* LISTEN
/data # nslookup redis.default.svc.cluster.local
nslookup: can't resolve '(null)': Name does not resolve
Name: redis.default.svc.cluster.local
Address 1: 10.97.149.76 redis.default.svc.cluster.local
#由于redis在filebeat后面才启动,日志可能已经发走了,所以查看key为空
/data # redis-cli -h redis.default.svc.cluster.local
redis.default.svc.cluster.local:6379> keys *
(empty list or set)
#进入filebeat容器
[[email protected] manifests]# kubectl get pods
NAME READY STATUS RESTARTS AGE
filebeat-ds-28kmv 1/1 Running 0 4m48s
filebeat-ds-wmlzj 1/1 Running 0 4m48s
myapp-deploy-65df64765c-9g9bv 1/1 Running 0 33m
myapp-deploy-65df64765c-ll8bk 1/1 Running 0 33m
myapp-deploy-65df64765c-xd2kn 1/1 Running 0 33m
pod-demo 2/2 Running 9 3d
poststart-pod 1/1 Running 3 2d18h
readiness-httpget-pod 1/1 Running 0 2d18h
redis-76c85b5744-7zt7l 1/1 Running 0 4m48s
[[email protected] manifests]# kubectl exec -it filebeat-ds-28kmv -- /bin/sh
#查看filebeat配置文件
filebeat.registry_file: /var/log/containers/filebeat_registry
filebeat.idle_timeout: 5s
filebeat.spool_size: 2048
logging.level: info
filebeat.prospectors:
- input_type: log
paths:
- "/var/log/containers/*.log"
- "/var/log/docker/containers/*.log"
- "/var/log/startupscript.log"
- "/var/log/kubelet.log"
- "/var/log/kube-proxy.log"
- "/var/log/kube-apiserver.log"
- "/var/log/kube-controller-manager.log"
- "/var/log/kube-scheduler.log"
- "/var/log/rescheduler.log"
- "/var/log/glbc.log"
- "/var/log/cluster-autoscaler.log"
symlinks: true
json.message_key: log
json.keys_under_root: true
json.add_error_key: true
multiline.pattern: '^\s'
multiline.match: after
document_type: kube-logs
tail_files: true
fields_under_root: true
output.redis:
hosts: ${REDIS_HOST:?No Redis host configured. Use env var REDIS_HOST to set host.}
key: "filebeat"
DaemonSet的滚动更新
DaemonSet有两种更新策略类型:
[[email protected] ~]# kubectl explain ds.spec.updateStrategy.type.
KIND: DaemonSet
VERSION: extensions/v1beta1
FIELD: type <string>
DESCRIPTION:
Type of daemon set update. Can be "RollingUpdate" or "OnDelete". Default is
OnDelete.
- OnDelete:向后兼容性的默认更新策略。使用 OnDelete更新策略,在更新DaemonSet模板后,只有在手动删除旧的DaemonSet pod时才会创建新的DaemonSet pod。这与Kubernetes 1.5或更早版本中DaemonSet的行为相同
- RollingUpdate:使用RollingUpdate更新策略,在更新DaemonSet模板后,旧的DaemonSet pod将被终止,并且将以受控方式自动创建新的DaemonSet pod。
要启用DaemonSet的滚动更新功能,必须将其设置 .spec.updateStrategy.type为RollingUpdate。
查看当前的更新策略:
[[email protected] manifests]# kubectl get ds/filebeat-ds -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}'
RollingUpdate
更新DaemonSet模板
对RollingUpdateDaemonSet的任何更新都.spec.template将触发滚动更新。这可以通过几个不同的kubectl命令来完成。
1) 声明式命令方式: 修改配置文件后使用kubectl apply更新
2)补丁式命令方式:kubectl edit ds/filebeat-ds
kubectl patch ds/filebeat-ds -p=<strategic-merge-patch>
仅仅更新容器镜像还可以使用以下命令:
kubectl set image ds/<daemonset-name> <container-name>=<container-new-image>
对filebeat-ds的镜像进行版本更新
[[email protected] manifests]# kubectl set image ds filebeat-ds filebeat=ikubernetes/filebeat:5.6.6-alpine
daemonset.extensions/filebeat-ds image updated
[[email protected] manifests]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
filebeat-ds-28kmv 1/1 Running 0 21m
filebeat-ds-wmlzj 0/1 Terminating 0 21m
myapp-deploy-65df64765c-9g9bv 1/1 Running 0 50m
myapp-deploy-65df64765c-ll8bk 1/1 Running 0 50m
myapp-deploy-65df64765c-xd2kn 1/1 Running 0 50m
pod-demo 2/2 Running 10 3d
poststart-pod 1/1 Running 4 2d18h
readiness-httpget-pod 1/1 Running 0 2d19h
redis-76c85b5744-7zt7l 1/1 Running 0 21m
filebeat-ds-wmlzj 0/1 Terminating 0 21m
filebeat-ds-wmlzj 0/1 Terminating 0 21m
filebeat-ds-j69gd 0/1 Pending 0 0s
filebeat-ds-j69gd 0/1 Pending 0 0s
filebeat-ds-j69gd 0/1 ContainerCreating 0 0s
filebeat-ds-j69gd 1/1 Running 0 14s
filebeat-ds-28kmv 1/1 Terminating 0 21m
filebeat-ds-28kmv 0/1 Terminating 0 21m
filebeat-ds-28kmv 0/1 Terminating 0 21m
filebeat-ds-28kmv 0/1 Terminating 0 21m
filebeat-ds-p5r4n 0/1 Pending 0 0s
filebeat-ds-p5r4n 0/1 Pending 0 0s
filebeat-ds-p5r4n 0/1 ContainerCreating 0 0s
filebeat-ds-p5r4n 1/1 Running 0 13s
从上面的滚动更新,可以看到在更新过程中,是先终止旧的pod,再创建一个新的pod,逐步进行替换的,这就是DaemonSet的滚动更新策略。
参考资料
https://www.cnblogs.com/linuxk
马永亮. Kubernetes进阶实战 (云计算与虚拟化技术丛书)
Kubernetes-handbook-jimmysong-20181218
原文地址:https://www.cnblogs.com/wlbl/p/10652892.html