k8s核心详解系列-pod,资源调度相关讲解

miloyang
0 评论
/ /
636 阅读
/
18577 字
28 2023-12

pod

之前我们创建过pod,就是nginx,但是是基于原由的创建,我们可以基于配置文件来创建pod。

配置文件

apiVersion: v1 # api 文档版本
kind: Pod  # 资源对象类型,也可以配置为像Deployment、StatefulSet这一类的对象
metadata: # Pod 相关的元数据,用于描述 Pod 的数据
  name: nginx-demo # Pod 的名称
  labels: # 定义 Pod 的标签
    type: app # 自定义 label 标签,名字为 type,值为 app
    test: 1.0.0 # 自定义 label 标签,描述 Pod 版本号
  namespace: 'default' # 命名空间的配置
spec: # 期望 Pod 按照这里面的描述进行创建
  containers: # 对于 Pod 中的容器描述
  - name: nginx # 容器的名称
    image: nginx:1.7.9 # 指定容器的镜像
    imagePullPolicy: IfNotPresent # 镜像拉取策略,指定如果本地有就用本地的,如果没有就拉取远程的
    command: # 指定容器启动时执行的命令
    - nginx
    - -g
    - 'daemon off;' # nginx -g 'daemon off;'
    workingDir: /usr/share/nginx/html # 定义容器启动后的工作目录
    ports:
    - name: http # 端口名称
      containerPort: 80 # 描述容器内要暴露什么端口
      protocol: TCP # 描述该端口是基于哪种协议通信的
    - env: # 环境变量
      name: JVM_OPTS # 环境变量名称
      value: '-Xms128m -Xmx128m' # 环境变量的值
    reousrces:
      requests: # 最少需要多少资源
        cpu: 100m # 限制 cpu 最少使用 0.1 个核心
        memory: 128Mi # 限制内存最少使用 128兆
      limits: # 最多可以用多少资源
        cpu: 200m # 限制 cpu 最多使用 0.2 个核心
        memory: 256Mi # 限制 最多使用 256兆
  restartPolicy: OnFailure # 重启策略,只有失败的情况才会重启

执行yaml

-rw-r--r-- 1 root root 694 Jan 27 15:17 nginx-demo.yaml
[root@k8s-master pods]# kubectl create -f nginx-demo.yaml
pod/nginx-demo created
[root@k8s-master pods]# kubectl get pod
NAME         READY   STATUS    RESTARTS   AGE
nginx-demo   1/1     Running   0 

探针

容器内应用的监测机制,根据不同的探针来判断容器应用当前的状态,比如pod中那个容器宕机了,是根据什么机制、策略来重启容器呢?

类型

目前探针有三种类型:StartupProbe、LivenessProbe、ReadlinessProbe。

  • StartupProbe

    k8s 1.16 版本新增的探针,用于判断应用程序是否已经启动了。

    当配置了 startupProbe 后,会先禁用其他探针,直到 startupProbe 成功后,其他探针才会继续。

    作用:由于有时候不能准确预估应用一定是多长时间启动成功,因此配置另外两种方式不方便配置初始化时长来检测,而配置了 statupProbe 后,只有在应用启动成功了,才会执行另外两种探针,可以更加方便的结合使用另外两种探针使用。

    startupProbe:
      httpGet:
        path: /api/startup
        port: 80
    
  • LivenessProbe

    用于探测容器中的应用是否运行,如果探测失败,kubelet 会根据配置的重启策略进行重启,若没有配置,默认就认为容器启动成功,不会执行重启策略。

    livenessProbe:
      failureThreshold: 5
      httpGet:
        path: /health
        port: 8080
        scheme: HTTP
      initialDelaySeconds: 60
      periodSeconds: 10
      successThreshold: 1
      timeoutSeconds: 5
    
  • ReadlinessProbe

    用于探测容器内的程序是否健康,它的返回值如果返回 success,那么就认为该容器已经完全启动,并且该容器是可以接收外部流量的。

    readinessProbe:
      failureThreshold: 3 # 错误次数
      httpGet:
        path: /ready
        port: 8181
        scheme: HTTP
      periodSeconds: 10 # 间隔时间
      successThreshold: 1
      timeoutSeconds: 1
    

生命周期

我们看如下示意图:

podshengminglive

  • 容器环境初始化好之后,才会正式的进入到生命周期之内,初始化包括下载镜像、exec等等。
  • 第一个到达初始化阶段,是由专门的初始化容器来做的操作,可能有0个或者多个初始化容器。
  • 初始化容器的时候,会有两个钩子函数,postStart和preStop两个函数,这两个函数的中间就是pod的生命周期,从创建到停止。
  • 启动完成后,Pod内的主容器启动。
  • 开始启动探针,最后就绪探针和存活探针启动。

以下是配置的命令:

lifecycle:
  postStart: # 容创建完成后执行的动作,不能保证该操作一定在容器的 command 之前执行,一般不使用
    exec: # 可以是 exec / httpGet / tcpSocket
      command:
        - sh
        - -c
        - 'mkdir /data'
  preStop: # 在容器停止前执行的动作
    httpGet: # 发送一个 http 请求
      path: /
      port: 80
    exec: # 执行一个命令
      command:
        - sh
        - -c
        - sleep 9

pod退出流程:

endpoint删除pod的ip地址 --> pod变成Terminating状态 --> 执行preStop命令

资源调度

目前修改pod,或者针对扩缩容是没办法在pod运行时修改,需要删除pod,然后修改配置文件再启动pod。接下来一系列的介绍,会逐步的使用热修改资源文件的方式。

Label 和 Selector

在 Kubernetes 中,标签(Labels)和选择器(Selectors)是关键概念,用于标识和组织资源。它们在实现应用程序部署、服务发现、资源管理等方面发挥着重要的作用。

label

标签是键值对,可以附加到 Kubernetes 对象(如 Pod、Service、Deployment 等)上,在各类资源的 metadata.labels 中进行配置。它们是用于对对象进行分类和标识的元数据。标签通常用于描述对象的特征、用途、环境等信息。

例如,你可以为一个 Pod 添加标签,表示它属于生产环境:

metadata:
  labels:
    environment: production

通过kubectl的操作如下:

  • 临时创建 label

    kubectl label po <资源名称> app=hello

  • 修改已经存在的标签

    kubectl label po <资源名称> app=hello2 --overwrite

  • 查看 label

        # selector 按照 label 单值查找节点
        kubectl get po -A -l app=hello
    
        # 查看所有节点的 labels
        kubectl get po --show-labels
    

选择器(Selectors)

选择器是一种通过标签来筛选和选择 Kubernetes 对象的机制。通过标签选择器,你可以从集群中选择带有特定标签的对象。

例如,你可以使用标签选择器选择所有环境是生产环境的 Pod:

selector:
  matchLabels:
    environment: production

通过kubectl的操作如下:

# 匹配单个值,查找 app=hello 的 pod
kubectl get po -A -l app=hello

# 匹配多个值
kubectl get po -A -l 'k8s-app in (metrics-server, kubernetes-dashboard)'

# 查找 version!=1 and app=nginx 的 pod 信息
kubectl get po -l version!=1,app=nginx

# 不等值 + 语句
kubectl get po -A -l version!=1,'app in (busybox, nginx)'

Deployment

在 Kubernetes 中,Deployment 是一种控制器(Controller)类型,用于定义和管理 Pod 的部署。Deployment 提供了一种声明式的方式来描述应用程序的期望状态,以及在实际状态和期望状态之间的调解和处理。以下是 Deployment 的一些主要作用:

  • 声明式部署: Deployment 允许你通过声明式的 YAML 文件来描述应用程序的期望状态。
  • 自动化滚动升级: Deployment 具有滚动升级的能力,可以在不中断服务的情况下更新应用程序。
  • 故障恢复: 如果某个节点上的 Pod 失效或被删除,Deployment 会自动创建新的 Pod 以替代它,确保应用程序的副本数符合定义。
  • 伸缩调整: 你可以通过修改 Deployment 的副本数来实现应用程序的伸缩调整,根据流量负载和资源需求动态地调整 Pod 的数量。
  • 版本回滚: 如果滚动升级后发现问题,你可以使用 Deployment 实现版本回滚,返回到之前的稳定版本。
  • 标签选择器管理: Deployment 使用标签选择器来管理和关联 Pod,确保它们属于同一个应用程序。这为服务发现和关联性提供了方便。

创建

[root@k8s-master ~]# kubectl create deploy nginx-deploy --image=nginx:1.7.9
deployment.apps/nginx-deploy created
[root@k8s-master ~]# kubectl get deploy
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deploy   1/1     1            1           2s
[root@k8s-master ~]# kubectl get replicaset
NAME                      DESIRED   CURRENT   READY   AGE
nginx-deploy-5c6485bbd4   1         1         1       75s
[root@k8s-master ~]# kubectl get po
NAME                            READY   STATUS    RESTARTS   AGE
nginx-deploy-5c6485bbd4-wvghb   1/1     Running   0          81s
[root@k8s-master ~]# kubectl get po,rs,deploy --show-labels
NAME                                READY   STATUS    RESTARTS   AGE   LABELS
pod/nginx-deploy-5c6485bbd4-wvghb   1/1     Running   0          28m   app=nginx-deploy,pod-template-hash=5c6485bbd4
NAME                                      DESIRED   CURRENT   READY   AGE   LABELS
replicaset.apps/nginx-deploy-5c6485bbd4   1         1         1       28m   app=nginx-deploy,pod-template-hash=5c6485bbd4
NAME                           READY   UP-TO-DATE   AVAILABLE   AGE   LABELS
deployment.apps/nginx-deploy   1/1     1            1           28m   app=nginx-deploy

这里可以看出一点端倪,我们的deploy和replicaset以及pod的关系。 而且这三者都是通过label来进行关联的

这个只是通过命令行创建一个deploy,一般情况下我们都是通过配置文件来创建,但是可以先通过kubectl来创建出一个yaml的模版,然后通过 kubectl get deploy nginx-deploy -o yaml 来查看yaml,再复制下来修修改改即可。 比如下面就是修修改改之后得到的:

apiVersion: apps/v1 # deployment api 版本
kind: Deployment # 资源类型 
metadata: # 原信息
  labels: # 标签
    app: nginx-deploy
  name: nginx-deploy
  namespace: default
spec:
  replicas: 1 # 期望副本数
  revisionHistoryLimit: 10 # 滚动更新后保留的历史版本数
  selector: # 选择器,用于找到匹配的RS
    matchLabels: # 按照标签匹配
      app: nginx-deploy
  strategy: # 更新策略
    rollingUpdate: # 滚动更新配置
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate # 更新类型,采用滚动更新
  template: # pod模版,该deploy下的pod模版
    metadata: # pod元信息
      labels: # pod标签
        app: nginx-deploy
    spec: # pod期望信息
      containers:
      - image: nginx:1.7.9
        imagePullPolicy: IfNotPresent
        name: nginx
      restartPolicy: Always # pod重启策略
      terminationGracePeriodSeconds: 30 # 删除操作最长宽限多少时间,也就是经过多少秒后删除

接下来聊聊几个作用。

作用

所有的操作,都是修改deploy的配置信息 如 kubectl edit deploy nginx-deploy

动态扩容

刚下面就一个副本,也就是只有一个nginx,下面变为三个副本。

spec:
  progressDeadlineSeconds: 600
  replicas: 3

即可

[root@k8s-master ~]# kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
nginx-deploy-5c6485bbd4-8v5zw   1/1     Running   0          33s
nginx-deploy-5c6485bbd4-gqqjk   1/1     Running   0          33s
nginx-deploy-5c6485bbd4-wvghb   1/1     Running   0          34m

滚动更新

比如把nginx的版本修改一下后,下面也是滚动更新过程:

[root@k8s-master ~]# kubectl get po
NAME                            READY   STATUS        RESTARTS   AGE
nginx-deploy-5c6485bbd4-8v5zw   0/1     Terminating   0          5m33s
nginx-deploy-5c6485bbd4-gqqjk   0/1     Terminating   0          5m33s
nginx-deploy-5c6485bbd4-wvghb   0/1     Terminating   0          39m
nginx-deploy-6568ff7c5d-4zqf7   1/1     Running       0          9s
nginx-deploy-6568ff7c5d-n92vs   1/1     Running       0          38s
nginx-deploy-6568ff7c5d-wlj4w   1/1     Running       0          11s
[root@k8s-master ~]# kubectl get po
NAME                            READY   STATUS    RESTARTS   AGE
nginx-deploy-6568ff7c5d-4zqf7   1/1     Running   0          14s
nginx-deploy-6568ff7c5d-n92vs   1/1     Running   0          43s
nginx-deploy-6568ff7c5d-wlj4w   1/1     Running   0          16s

当然,也可以通过命令行工具直接更新:

[root@k8s-master ~]# kubectl set image deployment/nginx-deploy nginx=nginx:1.7.9
deployment.apps/nginx-deploy image updated
[root@k8s-master ~]# kubectl get po
NAME                            READY   STATUS              RESTARTS   AGE
nginx-deploy-5c6485bbd4-72kmq   0/1     ContainerCreating   0          1s
nginx-deploy-5c6485bbd4-l9bbv   1/1     Running             0          3s
nginx-deploy-5c6485bbd4-s9jgj   1/1     Running             0          4s
nginx-deploy-6568ff7c5d-n92vs   1/1     Running             0          5m35s
nginx-deploy-6568ff7c5d-wlj4w   0/1     Terminating         0          5m8s
[root@k8s-master ~]# kubectl get po
NAME                            READY   STATUS        RESTARTS   AGE
nginx-deploy-5c6485bbd4-72kmq   1/1     Running       0          4s
nginx-deploy-5c6485bbd4-l9bbv   1/1     Running       0          6s
nginx-deploy-5c6485bbd4-s9jgj   1/1     Running       0          7s
nginx-deploy-6568ff7c5d-n92vs   0/1     Terminating   0          5m38s
[root@k8s-master ~]# kubectl get po
NAME                            READY   STATUS    RESTARTS   AGE
nginx-deploy-5c6485bbd4-72kmq   1/1     Running   0          6s
nginx-deploy-5c6485bbd4-l9bbv   1/1     Running   0          8s
nginx-deploy-5c6485bbd4-s9jgj   1/1     Running   0          9s

它会一个关掉一个启动的,不是全部停止。

回滚

先查看历史版本: kubectl rollout history deployment/nginx-deploy

[root@k8s-master ~]# kubectl rollout history deployment/nginx-deploy
deployment.apps/nginx-deploy 
REVISION  CHANGE-CAUSE
2         <none>
3         <none>
[root@k8s-master ~]# kubectl rollout history deployment/nginx-deploy
deployment.apps/nginx-deploy 
REVISION  CHANGE-CAUSE
2         <none>
3         <none>

[root@k8s-master ~]# kubectl rollout history deployment/nginx-deploy --revision=2
deployment.apps/nginx-deploy with revision #2
Pod Template:
  Labels:       app=nginx-deploy
        pod-template-hash=6568ff7c5d
  Containers:
   nginx:
    Image:      nginx:1.9.1
    Port:       <none>
    Host Port:  <none>
    Environment:        <none>
    Mounts:     <none>
  Volumes:      <none>

然后 比如说要回退到版本为2. kubectl rollout undo deployment/nginx-deploy --to-revision=2

[root@k8s-master ~]# kubectl rollout undo deployment/nginx-deploy --to-revision=2
deployment.apps/nginx-deploy rolled back
[root@k8s-master ~]# kubectl rollout status deployment/nginx-deploy
deployment "nginx-deploy" successfully rolled out

注意的是:可以通过设置 .spec.revisonHistoryLimit 来指定 deployment 保留多少 revison,如果设置为 0,则不允许 deployment 回退了。

扩缩容

在流量高峰的时候需要扩容,低谷的时候需要缩减,其实刚刚我们已经实现了,就是修改replicas的值。
但是,这么常用的场景,每次修改yaml文件的话,是有点麻烦,所以可以通过kubectl的命令来修改。

kubectl scale --replicas=6 deploy nginx-deploy

[root@k8s-master ~]# kubectl scale --replicas=6 deploy nginx-deploy
deployment.apps/nginx-deploy scaled
[root@k8s-master ~]# kubectl get deploy
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deploy   6/6     6            6           115m
[root@k8s-master ~]# kubectl get po
NAME                            READY   STATUS    RESTARTS   AGE
nginx-deploy-6568ff7c5d-6584j   1/1     Running   0          6m36s
nginx-deploy-6568ff7c5d-k4fzb   1/1     Running   0          13s
nginx-deploy-6568ff7c5d-pqlv5   1/1     Running   0          13s
nginx-deploy-6568ff7c5d-prh9q   1/1     Running   0          6m39s
nginx-deploy-6568ff7c5d-wg7z8   1/1     Running   0          6m40s
nginx-deploy-6568ff7c5d-zn5cs   1/1     Running   0          13s

缩容,也是修改减少值。

暂停和恢复

由于每次对 pod template 中的信息发生修改后,都会触发更新 deployment 操作,那么此时如果频繁修改信息,就会产生多次更新,而实际上只需要执行最后一次更新即可,当出现此类情况时我们就可以暂停 deployment 的 rollout

通过kubectl rollout pause deployment <name> 就可以实现暂停,直到你下次恢复后才会继续进行滚动更新

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:1.7.9
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
[root@k8s-master statefulset]# kubectl create -f web.yaml 
service/nginx created
statefulset.apps/web created
[root@k8s-master statefulset]# kubectl get sts
NAME   READY   AGE
web    2/2     5s
[root@k8s-master statefulset]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   2d18h
nginx        ClusterIP   None         <none>        80/TCP    13s
[root@k8s-master statefulset]# kubectl get po
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          29s
web-1   1/1     Running   0          28s

扩缩容

扩缩容也是一行命令,kubectl scale sts web --replicas=5 来调整,但是它是有顺序操作的,并非和deploy一样是随机的。

# 扩容到5
[root@k8s-master statefulset]# kubectl scale sts web --replicas=5
statefulset.apps/web scaled
[root@k8s-master statefulset]# kubectl get po
NAME    READY   STATUS              RESTARTS   AGE
web-0   1/1     Running             0          6m36s
web-1   1/1     Running             0          6m35s
web-2   1/1     Running             0          2s
web-3   0/1     ContainerCreating   0          1s
[root@k8s-master statefulset]# kubectl get po
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          6m39s
web-1   1/1     Running   0          6m38s
web-2   1/1     Running   0          5s
web-3   1/1     Running   0          4s
web-4   1/1     Running   0          2s

# 缩容2

[root@k8s-master statefulset]# kubectl scale sts web --replicas=2
statefulset.apps/web scaled
[root@k8s-master statefulset]# kubectl get po
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          7m58s
web-1   1/1     Running   0          7m57s

以上例子可以说明,扩容是2/3/4,缩容的话也是一样的从后开始删减,保证顺序性。

镜像更新、灰度

目前还不支持直接更新 image,需要 patch 来间接实现

kubectl patch sts web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"nginx:1.9.1"}]'

这其中可以进行灰度发布: 利用滚动更新中的 partition 属性,可以实现简易的灰度发布的效果

例如我们有 5 个 pod,如果当前 partition 设置为 3,那么此时滚动更新时,只会更新那些 序号 >= 3 的 pod

利用该机制,我们可以通过控制 partition 的值,来决定只更新其中一部分 pod,确认没有问题后再主键增大更新的 pod 数量,最终实现全部 pod 更新

删除

# 删除 StatefulSet 和 Headless Service
# 级联删除:删除 statefulset 时会同时删除 pods
kubectl delete statefulset web
# 非级联删除:删除 statefulset 时不会删除 pods,删除 sts 后,pods 就没人管了,此时再删除 pod 不会重建的
kubectl deelte sts web --cascade=false
# 删除 service
kubectl delete service nginx
# StatefulSet删除后PVC还会保留着,数据不再使用的话也需要删除
$ kubectl delete pvc www-web-0 www-web-1

DaemonSet

在 Kubernetes 中,DaemonSet 是一种控制器(Controller)类型,用于确保集群中的每个节点都运行一个副本(Pod)的特定 Pod。DaemonSet 通常用于在集群的每个节点上运行一些基础设施性质的服务,如日志收集、监控、网络代理等。

DaemonSet 是一种非常有用的资源控制器,用于在 Kubernetes 集群中管理分布在每个节点上的特定服务或任务的 Pod。

配置

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
spec:
  selector:
    matchLabels:
      app: logging
  template:
    metadata:
      labels:
        app: logging
        id: fluentd
      name: fluentd
    spec:
      containers:
      - name: fluentd-es
        image: agilestacks/fluentd-elasticsearch:v1.3.0
        env:
         - name: FLUENTD_ARGS
           value: -qq
        volumeMounts:
         - name: containers
           mountPath: /var/lib/docker/containers
         - name: varlog
           mountPath: /varlog
      volumes:
         - hostPath:
             path: /var/lib/docker/containers
           name: containers
         - hostPath:
             path: /var/log
           name: varlog
人未眠
工作数十年
脚步未曾歇,学习未曾停
乍回首
路程虽丰富,知识未记录
   借此博客,与之共进步