这篇学习笔记是基于杜宽老师在51CTO上发布的视频课程制作的。在此,感谢杜宽老师的分享和教学。如有侵权,请及时联系我。版权归原作者所有,未经允许不得转载或使用。
一、Replication Controller和ReplicaSet
Replication Controller(复制控制器,RC)和ReplicaSet(复制集,RS)是两种简单部署Pod的方式。因为在生产环境中,主要使用更高级的Deployment等方式进行Pod的管理和部署。
1、Replication Controller
Replication Controller(简称RC)可确保Pod副本数达到期望值,也就是RC定义的数量。换句话说,Replication Controller可确保一个Pod或一组同类Pod总是可用。
如果存在的Pod大于设定的值,则Replication Controller将终止额外的Pod。如果太小,Replication Controller将启动更多的Pod用于保证达到期望值。与手动创建Pod不同的是,用Replication Controller维护的Pod在失败、删除或终止时会自动替换。因此即使应用程序只需要一个Pod,也应该使用Replication Controller或其他方式管理。Replication Controller类似于进程管理程序,但是Replication Controller不是监视单个节点上的各个进程,而是监视多个节点上的多个Pod。
定义一个Replication Controller的示例如下。
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx
spec:
replicas: 3
selector:
app: nginx
template:
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
2、ReplicaSet
ReplicaSet是支持基于集合的标签选择器的下一代Replication Controller,它主要用作Deployment协调创建、删除和更新Pod,和Replication Controller唯一的区别是,ReplicaSet支持标签选择器。在实际应用中,虽然ReplicaSet可以单独使用,但是一般建议使用Deployment来自动管理ReplicaSet,除非自定义的Pod不需要更新或有其他编排等。
定义一个ReplicaSet的示例如下:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
# modify replicas according to your case
replicas: 3
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels:
app: guestbook
tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
resources:
requests:
cpu: 100m
memory: 100Mi
env:
- name: GET_HOSTS_FROM
value: dns
# If your cluster config does not include a dns service, then to
# instead access environment variables to find service host
# info, comment out the 'value: dns' line above, and uncomment the
# line below.
# value: env
ports:
- containerPort: 80
Replication Controller和ReplicaSet的创建删除和Pod并无太大区别,Replication Controller目前几乎已经不在生产环境中使用,ReplicaSet也很少单独被使用,都是使用更高级的资源Deployment、DaemonSet、StatefulSet进行管理Pod。
二、无状态应用管理Deployment
无状态:任意一个Web请求端提出请求时,请求本身包含了响应端为响应这一请求所需的全部信息(认证信息等)
有状态:Web请求端的请求必须被提交到保存有其相关状态信息(比如session)的服务器上,否则这些请求可能无法被理解,这也就意味着在此模式下服务器端无法对用户请求进行自由调度。
1、Deployment概念
Deployment一般用于部署公司的无状态服务,这个也是最常用的控制器,因为企业内部现在都是以微服务为主,而微服务实现无状态化也是最佳实践,可以利用Deployment的高级功能做到无缝迁移、自动扩容缩容、自动灾难恢复、一键回滚等功能。
2、创建一个Deployment
1、手动创建
# kubectl create deployment nginx --image=nginx:1.15.2
deployment.apps/nginx created
2、使用文件创建
创建一个 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment # Deployment的名称
labels:
app: nginx
spec:
replicas: 3 # 创建Pod的副本数
selector: # 定义Deployment如何找到要管理的Pod,与template的label(标签)对应,apiVersion为apps/v1必须指定该字段
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx # nginx使用label(标签)标记Pod
spec: # 表示Pod运行一个名字为nginx的容器
containers:
- name: nginx
image: nginx:1.15.12 # 运行此Pod使用的镜像
ports:
- containerPort: 80 # 容器用于发送和接收流量的端口
或者直接从系统中导出
# 输出一个yaml格式的deployment文件,但是不执行
# kubectl create deployment nginx --image=nginx:1.15.2 --replicas=3 -oyaml --dry-run=client > nginx-deploy.yaml
# 查看手动创建的nginx的yaml文件,然后把f开头的删了,且删了最后的status标签的内容,得到下面的yaml文件
# kubectl get deployment nginx -oyaml > nginx-deploy.yaml
cat > nginx-deploy.yaml << EFO
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
creationTimestamp: "2020-09-19T02:41:11Z"
generation: 1
labels:
app: nginx
name: nginx
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 2 #副本数
revisionHistoryLimit: 10 # 历史记录保留的个数
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx:1.15.2
imagePullPolicy: IfNotPresent
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
EFO
使用 kubectl create 创建此 Deployment
# kubectl create -f dp-nginx.yaml
deployment.apps/nginx-deployment created
3、状态解析
# kubectl get deploy -owide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
nginx 2/2 2 1 35m nginx nginx:1.15.2 app=nginx
参数 | 说明 |
---|---|
NAME | 集群中Deployment的名称 |
READY | Pod就绪个数和总副本数 |
UP-TO-DATE | 显示已达到期望状态的被更新的副本数 |
AVAILABLE | 显示用户可以使用的应用程序副本数,为0说明目前还没有达到期望的Pod |
AGE | 显示应用程序运行的时间 |
CONTAINERS | 容器名称 |
IMAGES | 容器的镜像 |
SELECTOR | 管理的Pod的标签 |
可以使用 rollout 命令查看整个 Deployment 创建的状态
# kubectl rollout status deployment/nginx-deployment
deployment "nginx-deployment" successfully rolled out
当 rollout 结束时,再次查看此 Deployment,可以看到 AVAILABLE 的数量和 yaml 文件中定义的 replicas 相同
# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 10m
查看此 Deployment 当前对应的 ReplicaSet
# kubectl get rs -l app=nginx
NAME DESIRED CURRENT READY AGE
nginx-deployment-5c689d88bb 3 3 3 12m
参数 | 说明 |
---|---|
DESIRED | 应用程序副本数 |
CURRENT | 当前正在运行的副本数 |
当 Deployment 有过更新,对应的 RS 可能不止一个,可以通过-o yaml 获取当前对应的 RS是哪个,其余的 RS 为保留的历史版本,用于回滚等操作。
查看此 Deployment 创建的 Pod,可以看到 Pod 的 hash 值 5c689d88bb 和上述 Deployment 对应的 ReplicaSet 的 hash 值一致:
# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-deployment-5c689d88bb-6b95k 1/1 Running 0 13m app=nginx,pod-template-hash=5c689d88bb
nginx-deployment-5c689d88bb-9z5z2 1/1 Running 0 13m app=nginx,pod-template-hash=5c689d88bb
nginx-deployment-5c689d88bb-jc8hr 1/1 Running 0 13m app=nginx,pod-template-hash=5c689d88bb
3、Deployment的更新
注意:当且仅当 Deployment 的 Pod 模板(即.spec.template)更改时,才会触发 Deployment更新,例如更改内存、CPU 配置或者容器的 image
更改deployment的镜像并记录
# kubectl set image deploy nginx nginx=nginx:1.15.3 --record
deployment.apps/nginx image updated
也可以使用 edit 命令直接编辑 Deployment,效果相同
# kubectl edit deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment edited
查看更新过程
# kubectl rollout status deploy nginx
deployment "nginx" successfully rolled out
# kubectl rollout status deploy nginx
Waiting for deployment "nginx" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "nginx" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "nginx" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "nginx" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "nginx" rollout to finish: 1 old replicas are pending termination...
deployment "nginx" successfully rolled out
可以看出更新过程为新旧交替更新,首先新建一个 Pod,当 Pod 状态为 Running 时,删除一个旧的 Pod,同时再创建一个新的 Pod。当触发一个更新后,会有新的 ReplicaSet 产生,旧的ReplicaSet 会被保存,查看此时 ReplicaSet,可以从 AGE 或 READY 看出来新旧 ReplicaSet
# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5c689d88bb 0 0 0 34m
nginx-deployment-6987cdb55b 3 3 3 5m14s
通过 describe 查看 Deployment 的详细信息
# kubectl describe deploy nginx
Normal ScalingReplicaSet 25m deployment-controller Scaled up replica set nginx-66bbc9fdc5 to 1
Normal ScalingReplicaSet 18m (x2 over 23m) deployment-controller Scaled up replica set nginx-66bbc9fdc5 to 2
Normal ScalingReplicaSet 7m7s deployment-controller Scaled up replica set nginx-5dfc8689c6 to 1
Normal ScalingReplicaSet 6m28s (x2 over 23m) deployment-controller Scaled down replica set nginx-66bbc9fdc5 to 1
Normal ScalingReplicaSet 6m27s deployment-controller Scaled up replica set nginx-5dfc8689c6 to 2
Normal ScalingReplicaSet 5m58s deployment-controller Scaled down replica set nginx-66bbc9fdc5 to 0
Normal ScalingReplicaSet 4m19s deployment-controller Scaled up replica set nginx-6cdd5dd489 to 1
Normal ScalingReplicaSet 3m44s deployment-controller Scaled down replica set nginx-5dfc8689c6 to 1
Normal ScalingReplicaSet 3m44s deployment-controller Scaled up replica set nginx-6cdd5dd489 to 2
Normal ScalingReplicaSet 3m6s deployment-controller Scaled down replica set nginx-5dfc8689c6 to 0
在 describe 中可以看出,第一次创建时,它创建了一个名为 nginx-deployment-5c689d88bb 的ReplicaSet,并直接将其扩展为 3 个副本。更新部署时,它创建了一个新的 ReplicaSet,命名为nginx-deployment-6987cdb55b,并将其副本数扩展为 1,然后将旧的 ReplicaSet 缩小为 2,这样至少可以有 2 个 Pod 可用,最多创建了 4 个 Pod。以此类推,使用相同的滚动更新策略向上和向下扩展新旧 ReplicaSet,最终新的 ReplicaSet 可以拥有 3 个副本,并将旧的 ReplicaSet 缩小为 0。
4、Deployment的回滚
当更新了版本不稳定或配置不合理时,可以对其进行回滚操作,假设我们又进行了几次更新(此处以更新镜像版本触发更新,更改配置效果类似):
执行更新操作
# kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record
# kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record
查看历史版本
# kubectl rollout history deployment/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment nginx-deployment nginx=nginx:1.9.1 --record=true
3 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record=true
4 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true
查看 Deployment 某次更新的详细信息,使用--revision 指定某次更新版本号
# kubectl rollout history deployment/nginx-deployment --revision=3
deployment.apps/nginx-deployment with revision #3
Pod Template:
Labels: app=nginx
pod-template-hash=645959bf6b
Annotations: kubernetes.io/change-cause: kubectl set image deployment
nginx-deployment nginx=dotbalo/canary:v1 --record=true
Containers:
nginx:
Image: dotbalo/canary:v1
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
如果只需要回滚到上一个稳定版本,使用 kubectl rollout undo 即可
# kubectl rollout undo deployment/nginx-deployment
deployment.apps/nginx-deployment
再次查看更新历史,发现 REVISION5 回到了 canary:v1
# kubectl rollout history deployment/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment nginx-deployment nginx=nginx:1.9.1 --record=true
4 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true
5 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record=true
如果要回滚到指定版本,使用--to-revision 参数
# kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment.extensions/nginx-deployment
进行多次更新
# kubectl set image deploy nginx nginx=nginx:787977da --record
deployment.apps/nginx image updated
# kubectl set image deploy nginx nginx=nginx:787977dadaa --record
deployment.apps/nginx image updated
# kubectl set image deploy nginx nginx=nginx:787977xxxxxdadaa --record
deployment.apps/nginx image updated
# kubectl set image deploy nginx nginx=nginx:787977dadxxxxxdadaa --record
deployment.apps/nginx image updated
查看历史记录
# kubectl rollout history deploy nginx
deployment.apps/nginx
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deploy nginx nginx=nginx:1.15.3 --record=true
5 kubectl set image deploy nginx nginx=nginx:1.15.4 --record=true
6 kubectl set image deploy nginx nginx=nginx:787977da --record=true
7 kubectl set image deploy nginx nginx=nginx:787977dadaa --record=true
8 kubectl set image deploy nginx nginx=nginx:787977xxxxxdadaa --record=true
9 kubectl set image deploy nginx nginx=nginx:787977dadxxxxxdadaa --record=true
查看指定版本的详细信息
# kubectl rollout history deploy nginx --revision=5
deployment.apps/nginx with revision #5
Pod Template:
Labels: app=nginx
pod-template-hash=6cdd5dd489
Annotations: kubernetes.io/change-cause: kubectl set image deploy nginx nginx=nginx:1.15.4 --record=true
Containers:
nginx:
Image: nginx:1.15.4
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
5、Deployment的扩容
当公司访问量变大,或者有预期内的活动时,三个 Pod 可能已无法支撑业务时,可以提前对其进行扩展。
Deployment的扩容
# 使用 kubectl scale 动态调整 Pod 的副本数,比如增加 Pod 为 5 个
# kubectl scale deploy nginx-deployment --replicas=5
deployment.apps/nginx-deployment scaled
查看 Pod,此时 Pod 已经变成了 5 个
# kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-deployment-5f89547d9c-5r56b 1/1 Running 0 90s
nginx-deployment-5f89547d9c-htmn7 1/1 Running 0 25s
nginx-deployment-5f89547d9c-nwxs2 1/1 Running 0 99s
nginx-deployment-5f89547d9c-rpwlg 1/1 Running 0 25s
nginx-deployment-5f89547d9c-vlr5p 1/1 Running 0 95s
6、Deployment的暂停和恢复
需要针对一个资源文件更改多处地方,而并不需要多次触发更新,此时可以使用 Deployment 暂停功能,临时禁用更新操作,对 Deployment 进行多次修改后在进行更新。
- deployment可以在线edit更改(可以一次性更改多个)
- 也可以用kubectl set image更改(也可以一次性更改多个,但是需要使用到Deployment的暂停和恢复功能)
使用 kubectl rollout pause 命令即可暂停 Deployment 更新
# kubectl rollout pause deployment nginx
deployment.apps/nginx paused
然后对 Deployment 进行相关更新操作,比如先更新镜像,然后对其资源进行限制(如果使用的是 kubectl edit 命令,可以直接进行多次修改,无需暂停更新,kubectl set 命令一般会集成在CICD 流水线中)
进行多次配置变更
# kubectl set image deploy nginx nginx=nginx:1.15.3 --record
deployment.apps/nginx image updated
# 添加内存CPU配置
# kubectl set resources deploy nginx -c nginx --limits=cpu=200m,memory=128Mi --requests=cpu=10m,memory=16Mi
deployment.apps/nginx resource requirements updated
通过 rollout history 可以看到没有新的更新
# kubectl rollout history deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
5 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record=true
7 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true
8 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true
使用 kubectl rollout resume 恢复 Deployment 更新
# kubectl rollout resume deplo nginx-deployment
deployment.apps/nginx-deployment resumed
可以查看到恢复更新的 Deployment 创建了一个新的 RS(ReplicaSet 缩写)
# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-57895845b8 5 5 4 11s
可以查看 Deployment 的变化
# kubectl describe deploy nginx-deployment
7、Deployment注意事项
# kubectl get deploy nginx -oyaml
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
labels:
app: nginx
name: nginx
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx:1.15.3
imagePullPolicy: IfNotPresent
name: nginx
resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 10m
memory: 16Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
历史版本清理策略:在默认情况下,revision 保留 10 个旧的 ReplicaSet,其余的将在后台进行垃圾回收,可以在.spec.revisionHistoryLimit 设置保留 ReplicaSet 的个数。当设置为 0 时,不保留历史记录。
参数 | 说明 |
---|---|
.spec.revisionHistoryLimit | 设置保留RS旧的revision的个数,设置为0的话,不保留历史数据 |
.spec.minReadySeconds | 可选参数,指定新创建的Pod在没有任何容器崩溃的情况下视为Ready最小的秒数,默认为0,即一旦被创建就视为可用,通常和容器探针连用 |
滚动更新的策略
策略 | 说明 |
---|---|
.spec.strategy.type | 更新deployment的方式,默认是RollingUpdate |
RollingUpdate | 滚动更新,可以指定maxSurge和maxUnavailable |
maxUnavailable | 指定在回滚或更新时最大不可用的Pod的数量,可选字段,默认25%,可以设置成数字或百分比,如果该值为0,那么maxSurge就不能0 |
maxSurge | 可以超过期望值的最大Pod数,可选字段,默认为25%,可以设置成数字或百分比,如果该值为0,那么maxUnavailable不能为0 |
Recreate | 重建,先删除旧的Pod,在创建新的Pod |
三、有状态应用管理StatefulSet
StatefulSet(有状态集,缩写为sts)常用于部署有状态的且需要有序启动的应用程序,比如在进行SpringCloud项目容器化时,Eureka的部署是比较适合用StatefulSet部署方式的,可以给每个Eureka实例创建一个唯一且固定的标识符,并且每个Eureka实例无需配置多余的Service,其余Spring Boot应用可以直接通过Eureka的Headless Service即可进行注册。
Eureka的statefulset的资源名称是eureka,eureka-0 eureka-1 eureka-2
Service:headless service,没有ClusterIP eureka-svc
Eureka-0.eureka-svc.NAMESPACE_NAME eureka-1.eureka-svc …
1、StatefulSet的基本概念
StatefulSet主要用于管理有状态应用程序的工作负载API对象。比如在生产环境中,可以部署ElasticSearch集群、MongoDB集群或者需要持久化的RabbitMQ集群、Redis集群、Kafka集群和ZooKeeper集群等。
一般StatefulSet用于有以下一个或者多个需求的应用程序:
- 需要稳定的独一无二的网络标识符。
- 需要持久化数据。
- 需要有序的、优雅的部署和扩展。
- 需要有序的自动滚动更新。
2、Headless Service
和Deployment类似,一个StatefulSet也同样管理着基于相同容器规范的Pod。不同的是,StatefulSet为每个Pod维护了一个粘性标识。这些Pod是根据相同的规范创建的,但是不可互换,每个Pod都有一个持久的标识符,在重新调度时也会保留,一般格式为StatefulSetName-Number。比如定义一个名字是Redis-Sentinel的StatefulSet,指定创建三个Pod,那么创建出来的Pod名字就为Redis-Sentinel-0、Redis-Sentinel-1、Redis-Sentinel-2。而StatefulSet创建的Pod一般使用Headless Service(无头服务)进行通信,和普通的Service的区别在于Headless Service没有ClusterIP,它使用的是Endpoint进行互相通信,Headless一般的格式为:
statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local。
说明:
serviceName为Headless Service的名字,创建StatefulSet时,必须指定Headless Service名称;
0..N-1为Pod所在的序号,从0开始到N-1;
statefulSetName为StatefulSet的名字;
namespace为服务所在的命名空间;
.cluster.local为Cluster Domain(集群域)。
如果应用程序不需要任何稳定的标识符或者有序的部署、删除或者扩展,应该使用无状态的控制器部署应用程序,比如Deployment或者ReplicaSet。
StatefulSet是Kubernetes 1.9版本之前的beta资源,在1.5版本之前的任何Kubernetes版本都没有。
Pod所用的存储必须由PersistentVolume Provisioner(持久化卷配置器)根据请求配置StorageClass,或者由管理员预先配置,当然也可以不配置存储。
为了确保数据安全,删除和缩放StatefulSet不会删除与StatefulSet关联的卷,可以手动选择性地删除PVC和PV。
StatefulSet目前使用Headless Service(无头服务)负责Pod的网络身份和通信,需要提前创建此服务。
删除一个StatefulSet时,不保证对Pod的终止,要在StatefulSet中实现Pod的有序和正常终止,可以在删除之前将StatefulSet的副本缩减为0。
3、定义一个StatefulSet资源文件
定义一个简单的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:
serviceName: "nginx"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:laster
ports:
- containerPort: 80
name: web
其中:
- kind: Service定义了一个名字为Nginx的Headless Service,创建的Service格式为nginx-0.nginx.default.svc.cluster.local,其他的类似,因为没有指定Namespace(命名空间),所以默认部署在default。
- kind: StatefulSet定义了一个名字为web的StatefulSet,replicas表示部署Pod的副本数,本实例为2。
在StatefulSet中必须设置Pod选择器(.spec.selector)用来匹配其标签(.spec.template.metadata.labels)。在1.8版本之前,如果未配置该字段(.spec.selector),将被设置为默认值,在1.8版本之后,如果未指定匹配Pod Selector,则会导致StatefulSet创建错误。
当StatefulSet控制器创建Pod时,它会添加一个标签statefulset.kubernetes.io/pod-name,该标签的值为Pod的名称,用于匹配Service。
4、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 将按 web-0、web-1、web-2 的顺序部署 3 个 Pod。在 web-0 处于 Running 或者 Ready 之前,web-1 不会被部署,相同的,web-2 在 web-1 未处于 Running和 Ready 之前也不会被部署。如果在 web-1 处于 Running 和 Ready 状态时,web-0 变成 Failed(失败)状态,那么 web-2 将不会被启动,直到 web-0 恢复为 Running 和 Ready 状态。
如果用户将 StatefulSet 的 replicas 设置为 1,那么 web-2 将首先被终止,在完全关闭并删除web-2 之前,不会删除 web-1。如果 web-2 终止并且完全关闭后,web-0 突然失败,那么在 web-0 未恢复成 Running 或者 Ready 时,web-1 不会被删除。
5、StatefulSet 中pod 访问机制
假如公司某个项目需要在Kubernetes中部署一个主从模式的Redis,此时使用StatefulSet部署就极为合适,因为StatefulSet启动时,只有当前一个容器完全启动时,后一个容器才会被调度,并且每个容器的标识符是固定的,那么就可以通过标识符来断定当前Pod的角色。
比如用一个名为redis-ms的StatefulSet部署主从架构的Redis,第一个容器启动时,它的标识符为redis-ms-0,并且Pod内主机名也为redis-ms-0,此时就可以根据主机名来判断,当主机名为redis-ms-0的容器作为Redis的主节点,其余从节点,那么Slave连接Master主机配置就可以使用不会更改的Master的Headless Service,此时Redis从节点(Slave)配置文件如下:
port 6379
slaveof redis-ms-0.redis-ms.public-service.svc.cluster.local 6379
tcp-backlog 511
timeout 0
tcp-keepalive 0
……
其中redis-ms-0.redis-ms.public-service.svc.cluster.local是Redis Master的Headless Service,在同一命名空间下只需要写redis-ms-0.redis-ms即可,后面的public-service.svc.cluster.local可以省略。
解析无头service
cat<<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- name: busybox
image: busybox:1.28
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always
EOF
# kubectl exec -ti busybox -- sh
/ # ls
bin dev etc home proc root sys tmp usr var
/ # nslookup web-0.nginx
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-0.nginx
Address 1: 10.244.32.152 web-0.nginx.default.svc.cluster.local
/ # exit
6、创建一个StatefulSet
# kubectl create -f sts-web.yaml # 也可以使用-n 部署到其他 namespace
service/nginx created
statefulset.apps/web created
# kubectl get sts
NAME DESIRED CURRENT AGE
web 2 2 12s
# 查看svc信息
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d2h
nginx ClusterIP None <none> 80/TCP 16s
# 查看pod信息
# kubectl get po -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2m5s
web-1 1/1 Running 0 115s
7、StatefulSet 扩容和缩容
和 Deployment 类似,可以通过更新 replicas 字段扩容/缩容 StatefulSet,也可以使用 kubectl scale、kubectl edit 和 kubectl patch 来扩容/缩容一个 StatefulSet。
StatefulSet的扩容
# 更改副本数为3
# kubectl scale sts web --replicas=5
statefulset.apps/web scaled
# 查看pod、发现名字是固定增长的,而且是逐个启动
# kubectl get po
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2m58s
web-1 1/1 Running 0 2m48s
web-2 1/1 Running 0 116s
web-3 1/1 Running 0 79s
web-4 1/1 Running 0 53s
# 也可使用以下命令动态查看
# kubectl get pods -w -l app=nginx
StatefulSet的缩容
# kubectl scale --replicas=3 sts web
# kubectl patch sts web -p '{"spec":{"replicas":3}}'
statefulset.apps/web scaled
# 先删最后一个
# kubectl get pods -w -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 4m37s
web-1 1/1 Running 0 4m27s
web-2 1/1 Running 0 3m35s
web-3 1/1 Running 0 2m58s
web-4 1/1 Running 0 2m32s
web-0 1/1 Running 0 5m8s
web-0 1/1 Running 0 5m11s
web-4 1/1 Terminating 0 3m36s
web-4 0/1 Terminating 0 3m38s
web-4 0/1 Terminating 0 3m47s
web-4 0/1 Terminating 0 3m47s
web-3 1/1 Terminating 0 4m13s
web-3 0/1 Terminating 0 4m14s
web-3 0/1 Terminating 0 4m22s
web-3 0/1 Terminating 0 4m22s
7、StatefulSet更新策略
# kubectl get sts web -o yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
namespace: default
spec:
podManagementPolicy: OrderedReady
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
app: nginx
serviceName: nginx
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx:1.15.2
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
name: web
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
updateStrategy:
rollingUpdate:
partition: 0
type: RollingUpdate
策略 | 说明 |
---|---|
On Delete | OnDelete更新策略实现了传统(1.7版本之前)的行为,它也是默认的更新策略。当我们选择这个更新策略并修改StatefulSet的.spec.template字段时,StatefulSet控制器不会自动更新Pod,必须手动删除Pod才能使控制器创建新的Pod。 |
RollingUpdate | RollingUpdate(滚动更新)更新策略会自动更新一个StatefulSet中所有的Pod,采用与序号索引相反的顺序进行滚动更新。 |
partition | 分段更新,可用于灰度发布,大于指定副本数量的进行更新,0为全部更新 |
比如更改一个名称为 web 的 StatefulSet 使用 RollingUpdate 方式更新
# kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type": "RollingUpdate"}}}'
statefulset.apps/web patched
查看更改后的 StatefulSet
# kubectl get sts web -o yaml | grep -A 1 "updateStrategy"
updateStrategy:
type: RollingUpdate
然后改变容器的镜像触发滚动更新(此处使用的 jsonPath 的方式更改的资源配置,可以使用set 或 edit 减少复杂度)
# kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"dotbalo/canary:v1"}]'
statefulset.apps/web patched
在更新过程中可以使用 kubectl rollout status sts/<name>
来查看滚动更新的状态
# kubectl rollout status sts/web
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 1 pods at revision web-56b5798f76...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 2 pods at revision web-56b5798f76...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
statefulset rolling update complete 3 pods at revision web-56b5798f76...
查看更新后的镜像
# for p in 0 1 2; do kubectl get po web-$p --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
dotbalo/canary:v1
dotbalo/canary:v1
dotbalo/canary:v1
比如我们定义一个分区"partition":3,可以使用patch直接对StatefulSet进行设置
# kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate", "rollingUpdate":{"partition":3}}}}'
statefulset "web" patched
然后再次使用 patch 改变容器的镜像
# kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"k8s.gcr.io/nginx-slim:0.7"}]'
statefulset.apps/web patched
删除 Pod 触发更新
# kubectl delete po web-1
pod "web-1" deleted
此时,因为 Pod web-2 的序号小于分区 3,所以 Pod 不会被更新,还是会使用以前的容器恢复 Pod
将分区改为 2,此时会自动更新 web-2(因为之前更改了更新策略),但是不会更新 web-0和 web-1
# kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate", "rollingUpdate":{"partition":2}}}}'
statefulset "web" patched
按照上述方式,可以实现分阶段更新,类似于灰度/金丝雀发布。查看最终的结果如下
# for p in 0 1 2; do kubectl get po web-$p --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
dotbalo/canary:v1
dotbalo/canary:v1
dotbalo/canary:v2
8、删除StatefulSet
删除StatefulSet有两种方式,即级联删除和非级联删除。使用非级联方式删除StatefulSet时,StatefulSet的Pod不会被删除;使用级联删除时,StatefulSet和它的Pod都会被删除
1、非级联删除
使用 kubectl delete sts xxx 删除 StatefulSet 时,只需提供--cascade=false 参数,就会采用非级联删除,此时删除 StatefulSet 不会删除它的 Pod
# kubectl get po
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 16m
web-1 1/1 Running 0 16m
web-2 1/1 Running 0 11m
# kubectl delete statefulset web --cascade=false # 采用非级联删除
statefulset.apps "web" deleted
# kubectl get sts # 查看此时 sts 已经被删除
No resources found.
# kubectl get po # 该 StatefulSet 管理的 Pod 并未被删除
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 16m
web-1 1/1 Running 0 16m
web-2 1/1 Running 0 11m
由于此时删除了 StatefulSet,它管理的 Pod 变成了“孤儿”Pod,因此单独删除 Pod 时,该Pod 不会被重建
# kubectl get po
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 16m
web-1 1/1 Running 0 16m
web-2 1/1 Running 0 11m
# kubectl delete po web-0
pod "web-0" deleted
# kubectl get po
NAME READY STATUS RESTARTS AGE
web-1 1/1 Running 0 18m
web-2 1/1 Running 0 12m
再次创建 sts
# kubectl apply -f sts-web.yaml
statefulset.apps/web created
# kubectl get po
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 32s
web-1 1/1 Running 0 19m
2、级联删除
省略--cascade=false 参数即为级联删除
# kubectl delete statefulset web
statefulset.apps "web" deleted
# kubectl get po
No resources found.
也可以使用-f 指定创建 StatefulSet 和 Service 的 yaml 文件,直接删除 StatefulSet 和 Service(此文件将 StatefulSet 和 Service 写在了一起)
# kubectl delete -f sts-web.yaml
service "nginx" deleted
Error from server (NotFound): error when deleting "sts-web.yaml": statefulsets.apps "web" not found
# 因为 StatefulSet 已经被删除,所以会提示该StatefulSet 不存在
四、守护进程DaemonSet
1、DaemonSet是什么?
DaemonSet(守护进程集,缩写为ds)和守护进程类似,它在符合匹配条件的节点上均部署一个Pod。当有新节点加入集群时,也会为它们新增一个Pod,当节点从集群中移除时,这些Pod也会被回收,删除DaemonSet将会删除它创建的所有Pod。
使用DaemonSet的场景
- 运行集群存储daemon(守护进程),例如在每个节点上运行Glusterd、Ceph等;
- 在每个节点运行日志收集daemon,例如Fluentd、Logstash;
- 在每个节点运行监控daemon,比如Prometheus Node Exporter、Collectd、Datadog代理、New Relic代理或Ganglia gmond。
2、创建一个DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: nginx
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:1.15.12
imagePullPolicy: IfNotPresent
name: nginx
创建一个ds,查看ds信息,每个节点都有一个
# kubectl create -f nginx-ds.yaml
# kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-6ssvl 1/1 Running 0 56s
nginx-8ddgd 1/1 Running 0 36s
nginx-cfx5d 1/1 Running 0 9s
nginx-k48bt 1/1 Running 0 46s
nginx-p84ng 1/1 Running 0 11s
# 使用-o wide 可以查看 Pod 所在的节点时
# kubectl get po -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox 1/1 Running 1 108m 10.244.32.153 k8s-master01 <none> <none>
nginx-8vrcz 1/1 Running 0 24s 10.244.195.20 k8s-master03 <none> <none>
nginx-dt2bx 1/1 Running 0 24s 10.244.32.165 k8s-master01 <none> <none>
nginx-gv8bd 1/1 Running 0 24s 10.244.122.145 k8s-master02 <none> <none>
nginx-k6dj6 1/1 Running 0 24s 10.244.85.219 k8s-node01 <none> <none>
nginx-mgrks 1/1 Running 0 24s 10.244.58.204 k8s-node02 <none> <none>
指定节点部署 Pod
如果指定了.spec.template.spec.nodeSelector,DaemonSet Controller 将在与 Node Selector(节点选择器)匹配的节点上创建 Pod,比如部署在磁盘类型为 ssd 的节点上(需要提前给节点定义标签 Label)
nodeSelector:
disktype: ssd
给节点打标签
# kubectl label node k8s-node01 k8s-node02 disktype=ssd
node/k8s-node01 labeled
node/k8s-node02 labeled
# kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master01 Ready master 13d v1.19.0
beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master01,kubernetes.io/os=linux,node-role.kubernetes.io/master=,node.kubernetes.io/master=,node.kubernetes.io/node=
k8s-master02 Ready <none> 13d v1.19.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master02,kubernetes.io/os=linux,node.kubernetes.io/node=
k8s-master03 Ready <none> 13d v1.19.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master03,kubernetes.io/os=linux,node.kubernetes.io/node=
k8s-node01 Ready <none> 13d v1.19.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node01,kubernetes.io/os=linux,node.kubernetes.io/node=
k8s-node02 Ready <none> 13d v1.19.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node02,kubernetes.io/os=linux,node.kubernetes.io/node=
更新pod
# vim nginx-ds.yaml
......
spec:
nodeSelector: # 添加标签限制
disktype: "ssd"
containers:
- image: nginx:1.15.2
imagePullPolicy: IfNotPresent
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
......
# kubectl replace -f nginx-ds.yaml
daemonset.apps/nginx replaced
# kubectl get po -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox 1/1 Running 1 111m 10.244.32.153 k8s-master01 <none> <none>
nginx-2khfj 1/1 Running 0 67s 10.244.58.205 k8s-node02 <none> <none>
nginx-n9q4d 1/1 Running 0 75s 10.244.85.220 k8s-node01 <none> <none>
3、DaemonSet的更新和回滚
如果添加了新节点或修改了节点标签(Label),DaemonSet 将立刻向新匹配上的节点添加Pod,同时删除不能匹配的节点上的 Pod。
在 Kubernetes 1.6 以后的版本中,可以在 DaemonSet 上执行滚动更新,未来的 Kubernetes 版本将支持节点的可控更新。
DaemonSet 滚动更新可参考:https://kubernetes.io/zh-cn/docs/tasks/manage-daemon/update-daemon-set/
DaemonSet 更新策略和 StatefulSet 类似,也有 OnDelete 和 RollingUpdate 两种方式。
策略 | 说明 |
---|---|
On Delete | 默认的更新策略。当我们选择这个更新策略并修改DaemonSet的.spec.template字段时,DaemonSet控制器不会自动更新Pod,我们必须手动删除Pod才能使控制器创建新的Pod |
RollingUpdate | 滚动更新策略,会更新一个DaemonSet中所有的Pod,采用与序号索引相反的顺序进行滚动更新 |
RollingUpdate建议为1,推荐使用On Delete策略
命令式更新
# kubectl edit ds/<daemonset-name>
# kubectl patch ds/<daemonset-name> -p=<strategic-merge-patch>
更新镜像
# kubectl set image ds/<daemonset-name><container-name>=<container-new-image>--record=true
查看更新状态
# kubectl rollout status ds/<daemonset-name>
列出所有修订版本
# kubectl rollout history daemonset <daemonset-name>
回滚到指定revision
# kubectl rollout undo daemonset <daemonset-name> --to-revision=<revision>
五、Label&Selector
Label:对k8s中各种资源进行分类、分组,添加一个具有特别属性的一个标签。
Selector:通过一个过滤的语法进行查找到对应标签的资源
当Kubernetes对系统的任何API对象如Pod和节点进行“分组”时,会对其添加Label(key=value形式的“键-值对”)用以精准地选择对应的API对象。而Selector(标签选择器)则是针对匹配对象的查询方法。注:键-值对就是key-value pair。
例如,常用的标签tier可用于区分容器的属性,如frontend、backend;或者一个release_track用于区分容器的环境,如canary、production等。
1、定义Label
应用案例:
公司与xx银行有一条专属的高速光纤通道,此通道只能与192.168.7.0网段进行通信,因此只能将与xx银行通信的应用部署到192.168.7.0网段所在的节点上,此时可以对节点进行Label(即加标签):
# kubectl label node k8s-node02 region=subnet7
node/k8s-node02 labeled
然后,可以通过Selector对其筛选:
# kubectl get no -l region=subnet7
NAME STATUS ROLES AGE VERSION
k8s-node02 Ready <none> 44h v1.20.0
最后,在Deployment或其他控制器中指定将Pod部署到该节点:
containers:
......
dnsPolicy: ClusterFirst
nodeSelector:
region: subnet7 # 指定刚刚我们打的标签
restartPolicy: Always
......
也可以用同样的方式对Service进行Label:
# kubectl label svc canary-v1 -n canary-production env=canary version=v1
service/canary-v1 labeled
查看Labels:
# kubectl get svc -n canary-production --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
canary-v1 ClusterIP 10.110.253.62 <none> 8080/TCP 24h env=canary,version=v1
还可以查看所有Version为v1的svc:
[root@k8s-master01 canary]# kubectl get svc --all-namespaces -l version=v1
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
canary-production canary-v1 ClusterIP 10.110.253.62 <none> 8080/TCP 25h
其他资源的Label方式相同。
2、Selector条件匹配
Selector主要用于资源的匹配,只有符合条件的资源才会被调用或使用,可以使用该方式对集群中的各类资源进行分配。
假如对Selector进行条件匹配,目前已有的Label如下:
# kubectl get svc --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
details ClusterIP 10.99.9.178 <none> 9080/TCP 45h app=details
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d19h component=apiserver,provider=kubernetes
nginx ClusterIP 10.106.194.137 <none> 80/TCP 2d21h app=productpage,version=v1
nginx-v2 ClusterIP 10.108.176.132 <none> 80/TCP 2d20h <none>
productpage ClusterIP 10.105.229.52 <none> 9080/TCP 45h app=productpage,tier=frontend
ratings ClusterIP 10.96.104.95 <none> 9080/TCP 45h app=ratings
reviews ClusterIP 10.102.188.143 <none> 9080/TCP 45h app=reviews
选择app为reviews或者productpage的svc:
# kubectl get svc -l 'app in (details, productpage)' --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
details ClusterIP 10.99.9.178 <none> 9080/TCP 45h app=details
nginx ClusterIP 10.106.194.137 <none> 80/TCP 2d21h app=productpage,version=v1
productpage ClusterIP 10.105.229.52 <none> 9080/TCP 45h app=productpage,tier=frontend
选择app为productpage或reviews但不包括version=v1的svc:
# kubectl get svc -l version!=v1,'app in (details, productpage)' --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
details ClusterIP 10.99.9.178 <none> 9080/TCP 45h app=details
productpage ClusterIP 10.105.229.52 <none> 9080/TCP 45h app=productpage,tier=frontend
选择labelkey名为app的svc:
# kubectl get svc -l app --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
details ClusterIP 10.99.9.178 <none> 9080/TCP 45h app=details
nginx ClusterIP 10.106.194.137 <none> 80/TCP 2d21h app=productpage,version=v1
productpage ClusterIP 10.105.229.52 <none> 9080/TCP 45h app=productpage,tier=frontend
ratings ClusterIP 10.96.104.95 <none> 9080/TCP 45h app=ratings
reviews ClusterIP 10.102.188.143 <none> 9080/TCP 45h app=reviews
3、修改标签(Label)
在实际使用中,Label的更改是经常发生的事情,可以使用overwrite参数修改标签。
修改标签,比如将version=v1改为version=v2:
[root@k8s-master01 canary]# kubectl get svc -n canary-production --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
canary-v1 ClusterIP 10.110.253.62 <none> 8080/TCP 26h env=canary,version=v1
[root@k8s-master01 canary]# kubectl label svc canary-v1 -n canary-production version=v2 --overwrite
service/canary-v1 labeled
[root@k8s-master01 canary]# kubectl get svc -n canary-production --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
canary-v1 ClusterIP 10.110.253.62 <none> 8080/TCP 26h env=canary,version=v2
4、删除标签(Label)
删除标签,比如删除version:
[root@k8s-master01 canary]# kubectl label svc canary-v1 -n canary-production version-
service/canary-v1 labeled
[root@k8s-master01 canary]# kubectl get svc -n canary-production --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
canary-v1 ClusterIP 10.110.253.62 <none> 8080/TCP 26h env=canary
六、Horizontal Pod Autoscaler
HPA(Horizontal Pod Autoscaler,水平Pod自动伸缩器)可根据观察到的CPU、内存使用率或自定义度量标准来自动扩展或缩容Pod的数量。HPA不适用于无法缩放的对象,比如DaemonSet
HPA控制器会定期调整RC或Deployment的副本数,以使观察到的平均CPU利用率与用户指定的目标相匹配
HPA需要metrics-server(项目地址:https://github.com/kubernetes-incubator/metrics-server)获取度量指标,由于在高可用集群安装中已经安装了metrics-server,所以本节的实践部分无须再次安装
HPA(自动扩缩容)
可以通过调整Pod数量以使设定的CPU和内存进行匹配
1、HPA原理
1、为什么要使用HPA
在生产环境中,总会有一些意想不到的事情发生,比如公司网站流量突然升高,此时之前创建的Pod已不足以撑住所有的访问,而运维人员也不可能24小时守着业务服务,这时就可以通过配置HPA,实现负载过高的情况下自动扩容Pod副本数以分摊高并发的流量,当流量恢复正常后,HPA会自动缩减Pod的数量
2、HPA中一些细节的处理
1、噪声处理:
通过上面的公式可以发现,Target的数目很大程度上会影响最终的结果,而在Kubernetes中,无论是变更或者升级,都更倾向于使用Recreate而不是Restart的方式进行处理。这就导致了在Deployment的生命周期中,可能会出现某一个时间,Target会由于计算了Starting或者Stopping的的Pod而变得很大。这就会给HPA的计算带来非常大的噪声,在HPA Controller的计算中,如果发现当前的对象存在Starting或者Stopping的Pod会直接跳过当前的计算周期,等待状态都变为Running再进行计算。
2、冷却周期:
在弹性伸缩中,冷却周期是不能逃避的一个话题,很多时候我们期望快速弹出与快速回收,而另一方面,我们又不希望集群震荡,所以一个弹性伸缩活动冷却周期的具体数值是多少,一直被开发者所挑战。在HPA中,默认的扩容冷却周期是3分钟,缩容冷却周期是5分钟。
3、边界值计算:
我们回到刚才的计算公式,第一次我们算出需要弹出的容器数目是5,此时扩容后整体的负载是42%,但是我们似乎忽略了一个问题,一个全新的Pod启动会不会自己就占用了部分资源?此外,8%的缓冲区是否就能够缓解整体的负载情况,要知道当一次弹性扩容完成后,下一次扩容要最少等待3分钟才可以继续扩容。为了解决这些问题,HPA引入了边界值△,目前在计算边界条件时,会自动加入10%的缓冲,这也是为什么在刚才的例子中最终的计算结果为6的原因
3、原理
通过集群内的资源监控系统(metrics-server),来获取集群中资源的使用状态。
根据CPU、内存、以及用户自定义的资源指标数据的使用量或连接数为参考依据,来制定一个临界点,一旦超出这个点,HPA就会自动创建出pod副本
HPA通过定期(定期轮询的时间通过–horizontal-pod-autoscaler-sync-period选项来设置,默认的时间为30秒)通过Status.PodSelector来查询pods的状态,获得pod的CPU使用率。然后,通过现有pods的CPU使用率的平均值(计算方式是最近的pod使用量(最近一分钟的平均值,从metrics-serve中获得)
除以设定的每个Pod的CPU使用率限额)跟目标使用率进行比较,并且在扩容时,还要遵循预先设定的副本数限制:MinReplicas <= Replicas <= MaxReplicas。
计算扩容后Pod的个数:sum(最近一分钟内某个Pod的CPU使用率/量的平均值)/CPU使用上限的整数+1
4、流程
1、创建HPA资源,设定目标CPU使用率限额,以及最大、最小实例数
2、收集一组中(PodSelector)每个Pod最近一分钟内的CPU使用率,并计算平均值
3、读取HPA中设定的CPU使用限额
4、计算:平均值之和/限额,求出目标调整的实例个数
5、目标调整的实例数不能超过1中设定的最大、最小实例数,如果没有超过,则扩容;超过,则扩容至最大的实例个数
6、回到2,不断循环
2、HPA 接口类型
uHPA v1为稳定版自动水平伸缩,只支持CPU指标
uV2为beta版本,分为v2beta1(支持CPU、内存和自定义指标)
uv2beta2(支持CPU、内存、自定义指标Custom和额外指标ExternalMetrics)
3、实现一个Web服务器的自动伸缩特性
不能扩容无法缩放的对象,比如DaemonSet
必须安装metrics-server或其他自定义metrics-server,必须配置requests参数
导出配置文件
kubectl create deployment hpa-nginx --image=nginx --dry-run=client -oyaml > hpa-nginx.yaml
vim hpa-nginx.yaml
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
resources: #重点添加以下参数
requests:
cpu: 100m
memory: 100Mi
运行hpa资源,名称为hpa-nginx,并暴露一个80端口
kubectl expose deployment hpa-nginx --port=80
对deployment服务的CPU进行设置,当CPU大于10%时进行扩容,最大扩容量为10个,最小扩容量为1
kubectl autoscale deployment hpa-nginx --cpu-percent=10 --min=1 --max=10
评论区