目 录CONTENT

文章目录

资源调度

简中仙
2022-05-10 / 0 评论 / 0 点赞 / 88 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于2024-01-14,若内容或图片失效,请留言反馈。 本文如有错误或者侵权的地方,欢迎您批评指正!

这篇学习笔记是基于杜宽老师在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的名称
READYPod就绪个数和总副本数
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 部署和扩展规则如下:

  1. 对于具有N个副本的StatefulSet,将按顺序从0到N-1开始创建Pod;
  2. 当删除Pod时,将按照N-1到0的反顺序终止;
  3. 在缩放Pod之前,必须保证当前的Pod是Running(运行中)或者Ready(就绪);
  4. 在终止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 DeleteOnDelete更新策略实现了传统(1.7版本之前)的行为,它也是默认的更新策略。当我们选择这个更新策略并修改StatefulSet的.spec.template字段时,StatefulSet控制器不会自动更新Pod,必须手动删除Pod才能使控制器创建新的Pod。
RollingUpdateRollingUpdate(滚动更新)更新策略会自动更新一个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(自动扩缩容)

image-20220209154035143

可以通过调整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
0

评论区