7

k8s 中 Pod 的控制器 - ZhanLi

 1 year ago
source link: https://www.cnblogs.com/ricklz/p/16743702.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

k8s 中 Pod 的控制器

Pod 是 Kubernetes 集群中能够被创建和管理的最小部署单元。所以需要有工具去操作和管理它们的生命周期,这里就需要用到控制器了。

Pod 控制器由 master 的 kube-controller-manager 组件提供,常见的此类控制器有 Replication Controller、ReplicaSet、Deployment、DaemonSet、StatefulSet、Job 和 CronJob 等,它们分别以不同的方式管理 Pod 资源对象。

Replication Controller

RC 是 k8s 集群中最早的保证 Pod 高可用的 API 对象。它的作用就是保证集群中有指定数目的 pod 运行。

当前运行的 pod 数目少于指定的数目,RC 就会启动新的 pod 副本,保证运行 pod 数量等于指定数目。

当前运行的 pod 数目大于指定的数目,RC 就会杀死多余的 pod 副本。

直接上栗子

cat <<EOF >./pod-rc.yaml
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
EOF

在新版的 Kubernetes 中建议使用 ReplicaSet (RS)来取代 ReplicationController。

ReplicaSet 跟 ReplicationController 没有本质的不同,只是名字不一样,但 ReplicaSet 支持集合式 selector。

关于 ReplicationController 这里也不展开讨论了,主要看下 ReplicaSet。

ReplicaSet

RS 是新一代 RC,提供同样的高可用能力,区别主要在于 RS 后来居上,能支持支持集合式 selector。

副本集对象一般不单独使用,而是作为 Deployment 的理想状态参数使用。

下面看下 Deployment 中是如何使用 ReplicaSet 的。

Deployment

一个 Deployment 为 Pod 和 ReplicaSet 提供声明式的更新能力,每一个 Deployment 都对应集群中的一次部署。

一般使用 Deployment 来管理 RS,可以用来创建一个新的服务,更新一个新的服务,也可以用来滚动升级一个服务。

滚动升级一个服务,滚动升级一个服务,实际是创建一个新的 RS,然后逐渐将新 RS 中副本数增加到理想状态,将旧 RS 中的副本数减小到 0 的复合操作;这样一个复合操作用一个 RS 是不太好描述的,所以用一个更通用的 Deployment 来描述。

cat <<EOF >./nginx-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels: # 这里定义需要管理的 pod,通过 Pod 的标签进行匹配
      app: nginx
  template:
    metadata:
      labels: # 运行的 pod 的标签
        app: nginx
    spec:
      containers: # pod 中运行的容器
      - name: nginx
        image: nginx:1.14.2 
        ports:
        - containerPort: 80
EOF
$ kubectl apply -f nginx-deployment.yaml -n study-k8s
deployment.apps/nginx-deployment created

$ study-k8s kubectl describe deployment nginx-deployment -n study-k8s
Name:                   nginx-deployment
Namespace:              study-k8s
CreationTimestamp:      Mon, 26 Sep 2022 09:06:16 +0800
Labels:                 app=nginx
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=nginx
Replicas:               3 desired | 3 updated | 3 total | 1 available | 2 unavailable
StrategyType:           RollingUpdate # 默认是滚动更新  
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge  # 滚动更新的策略,最大 25% 不可用,最大 25% 增加
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        nginx:1.14.2
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      False   MinimumReplicasUnavailable
  Progressing    True    ReplicaSetUpdated
OldReplicaSets:  <none>
NewReplicaSet:   nginx-deployment-66b6c48dd5 (3/3 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  3s    deployment-controller  Scaled up replica set nginx-deployment-66b6c48dd5 to 3

可以看到 Deployment 中默认的更新方式滚动更新,并且默认的滚动更新的策略是 最大 25% 不可用,最大 25% 增加。

更新 Deployment

1、直接更新

$ kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.20.1 -n study-k8s

# 或者  

$ kubectl set image deployment/nginx-deployment nginx=nginx:1.20.1 -n study-k8s

2、对 Deployment 执行 edit 操作

$ kubectl get deployment -n study-k8s
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           18m

$ kubectl edit deployment -n study-k8s
# 修改对应的镜像版本,保存即可  

    spec:
      containers:
      - image: nginx:1.20.3
        imagePullPolicy: IfNotPresent
        name: nginx

3、直接编辑部署的 YAML 文件,然后重新 Apply

$ kubectl apply -f nginx-deployment.yaml -n study-k8s
deployment.apps/nginx-deployment configured

$ kubectl get pods -n study-k8s
NAME                                READY   STATUS              RESTARTS   AGE
nginx-deployment-6bcf8f4884-dxxxg   1/1     Running             0          38s
nginx-deployment-6bcf8f4884-plbkh   0/1     ContainerCreating   0          13s
nginx-deployment-cc4b758d6-lzlxl    1/1     Running             0          7m25s
nginx-deployment-cc4b758d6-r5rkb    1/1     Running             0          7m11s

回滚 deployment

了解了如何更新 deployment,那么当部署出现问题,如果回滚呢,下面来详细介绍下

查看之前部署的版本

$ kubectl rollout history deployment/nginx-deployment -n study-k8s
deployment.apps/nginx-deployment
REVISION  CHANGE-CAUSE
1         <none>
2         <none>
3         <none>
4         <none>
5         <none>
6         <none>
7         image updated to 1.21.1

REVISION:就是之前部署的版本信息;

CHANGE-CAUSE:变动的备注信息。

$ kubectl apply -f nginx-deployment.yaml -n study-k8s --record

加上 --record,或者每次部署完之后,使用

$ kubectl annotate deployment/nginx-deployment kubernetes.io/change-cause="image updated to 1.21.1" -n study-k8s

CHANGE-CAUSE 信息就会被记录

查看历史版本的详细详细

$ kubectl rollout history deployment/nginx-deployment --revision=6 -n study-k8s
deployment.apps/nginx-deployment with revision #6
Pod Template:
  Labels:	app=nginx
	pod-template-hash=d985dd8bf
  Containers:
   nginx:
    Image:	nginx:1.20.3
    Port:	80/TCP
    Host Port:	0/TCP
    Environment:	<none>
    Mounts:	<none>
  Volumes:	<none>
# 回滚到上一个版本
$ kubectl rollout undo deployment/nginx-deployment -n study-k8s

# 回滚到指定的版本
$ kubectl rollout undo deployment/nginx-deployment --to-revision=5 -n study-k8s

缩放 Deployment

$ kubectl scale deployment/nginx-deployment --replicas=10 -n study-k8s

$  kubectl get pods -n study-k8s
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-66b6c48dd5-5scbj   1/1     Running   0          44s
nginx-deployment-66b6c48dd5-7nmtp   1/1     Running   0          44s
nginx-deployment-66b6c48dd5-f5xsg   1/1     Running   0          12m
nginx-deployment-66b6c48dd5-fnpnb   1/1     Running   0          12m
nginx-deployment-66b6c48dd5-gg4ng   1/1     Running   0          44s
nginx-deployment-66b6c48dd5-qd5z7   1/1     Running   0          44s
nginx-deployment-66b6c48dd5-qqh4m   1/1     Running   0          44s
nginx-deployment-66b6c48dd5-xww49   1/1     Running   0          44s
nginx-deployment-66b6c48dd5-zndlh   1/1     Running   0          44s
nginx-deployment-66b6c48dd5-zp45g   1/1     Running   0          12m

关于使用 deployment 实现 k8s 中的几种部署策略,可参见Kubernetes 部署策略详解

StatefulSet

StatefulSet 用来管理有状态的应用。

在 Pods 管理的基础上,保证 Pods 的顺序和一致性。与 Deployment一样,StatefulSet 也是使用容器的 Spec 来创建Pod,与之不同 StatefulSet 创建的 Pods 在生命周期中会保持持久的标记(例如Pod Name)。

StatefulSet 适用于具有以下特点的应用:

1、稳定的、唯一的网络标识符;

2、稳定的、持久的存储;

3、有序的、优雅的部署和扩缩;

4、有序的、自动的滚动更新。

那么什么是有状态服务什么是无状态服务呢?

无状态服务

无状态服务不会在本地存储持久化数据。多个服务实例对于同一个用户请求的响应结果是完全一致的。这种多服务实例之间是没有依赖关系,比如web应用,在k8s控制器 中动态启停无状态服务的pod并不会对其它的pod产生影响。

有状态服务

有状态服务需要在本地存储持久化数据,典型的是分布式数据库的应用,分布式节点实例之间有依赖的拓扑关系。

比如,主从关系。 如果 K8S 停止分布式集群中任 一实例 pod,就可能会导致数据丢失或者集群的 crash。

cat <<EOF >./pod-statefulSet.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # 必须匹配 .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # 默认值是 1
  template:
    metadata:
      labels:
        app: nginx # 必须匹配 .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: nginx:1.14.2 
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
EOF

DaemonSet

DaemonSet:主要是用来保证集群中的每个节点只运行一个 Pod,且保证只有一个 Pod,这非常适合一些系统层面的应用,例如日志收集、资源监控等,这类应用需要每个节点都运行,且不需要太多实例,一个比较好的例子就是 Kubernetes 的 kube-proxy。

当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。

DaemonSet 的应用场景:

1、在每个节点上运行集群守护进程;

2、在每个节点上运行日志收集守护进程;

3、在每个节点上运行监控守护进程。

cat <<EOF >./nginx-daemonset.yaml

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nginx-daemonset
  labels:
    app: nginx-daemonset
spec:
  selector:
    matchLabels:
      app: nginx-daemonset
  template:
    metadata:
      labels:
        app: nginx-daemonset
    spec:
      containers:
      - name: nginx-daemonset
        image: nginx:alpine
        resources:
          limits:
            cpu: 250m
            memory: 512Mi
          requests:
            cpu: 250m
            memory: 512Mi
      imagePullSecrets:
      - name: default-secret
EOF

这样就能在每个节点中部署一个 Pod 了,不过 DaemonSet 也支持通过 label 来选择部署的目标节点

cat <<EOF >./nginx-daemonset.yaml

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nginx-daemonset
  labels:
    app: nginx-daemonset
spec:
  selector:
    matchLabels:
      app: nginx-daemonset
  template:
    metadata:
      labels:
        app: nginx-daemonset
    spec:
      nodeSelector: # 节点选择,当节点拥有nodeName=node7时才在节点上创建Pod
        nodeName: node7
      containers:
      - name: nginx-daemonset
        image: nginx:alpine
        resources:
          limits:
            cpu: 250m
            memory: 512Mi
          requests:
            cpu: 250m
            memory: 512Mi
      imagePullSecrets:
      - name: default-secret
EOF

Job 和 CronJob

Job 和 CronJob 是负责处理定时任务的。

两者的区别主要在于:

Job 负责处理一次性的定时任务,即仅需执行一次的任务;

CronJob 是基于时间的 Job,就类似于 Linux 系统的 crontab 文件中的一行,在指定的时间周期运行指定的 Job。

首先来创建一个 Job

cat <<EOF >./job-demo.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: job-demo
spec:
  template:
    metadata:
      name: job-demo
    spec:
      restartPolicy: Never
      containers:
      - name: counter
        image: busybox
        command:
        - "bin/sh"
        - "-c"
        - "echo hello"  
EOF
$ kubectl apply -f job-demo.yaml

$ kubectl get pods
NAME             READY   STATUS      RESTARTS   AGE
job-demo-8qrd9   0/1     Completed   0          40s
$ study-k8s kubectl get job
NAME       COMPLETIONS   DURATION   AGE
job-demo   1/1           36s        45s
$ kubectl logs -f job-demo-8qrd9
hello

再来看下 CronJob

cat <<EOF >./cronJob-demo.yaml

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello
          restartPolicy: OnFailure
EOF

通过 kubectl get jobs --watch 就能查看 CronJob 的调度

$ kubectl get jobs --watch
NAME               COMPLETIONS   DURATION   AGE
hello-1664449860   1/1           2s         4s
hello-1664449920   0/1                      0s
hello-1664449920   0/1           0s         0s
hello-1664449920   1/1           2s         2s

关于 pod 中的几个控制器最常用的就是 Deployment 和 ReplicaSet;

使用 Deployment 来管理 RS,来实现服务的部署,更新和滚动升级;

StatefulSet 主要用来管理无状态应用;

DaemonSet:主要是用来保证集群中的每个节点只运行一个 Pod,且保证只有一个 Pod,这非常适合一些系统层面的应用,例如日志收集、资源监控等,这类应用需要每个节点都运行,且不需要太多实例,一个比较好的例子就是 Kubernetes 的 kube-proxy;

Job 和 CronJob 是负责处理定时任务的;

Job 负责处理一次性的定时任务,即仅需执行一次的任务;

CronJob 是基于时间的 Job,就类似于 Linux 系统的 crontab 文件中的一行,在指定的时间周期运行指定的 Job。

【Deployments】https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/deployment/
【Kubernetes 部署策略详解】https://www.qikqiak.com/post/k8s-deployment-strategies/
【StatefulSet 和 Deployment 区别及选择方式】https://blog.csdn.net/nickDaDa/article/details/90401635
【K8S: 有状态 vs 无状态服务】https://zhuanlan.zhihu.com/p/390440336
【StatefulSet】https://support.huaweicloud.com/basics-cce/kubernetes_0015.html
【k8s 中 Pod 的控制器】https://boilingfrog.github.io/2022/09/30/k8s中Pod控制器/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK