欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

容器化技术(No.3) -- Kubernetes 进阶(一) Pod 对象与常用控制器

程序员文章站 2022-05-09 14:06:25
...

前言

今天的分享希望大家能够:

  • 理解和掌握 Pod 对象的使用
  • 深入了解和使用常用控制器的使用

回顾

容器化技术(No.2) – Kubernetes 基础

Pod 对象

Pod 概述

Pod 是一个逻辑概念, 也是我们最长打交道的部分之一. 示例如下

  ...
  template:
    metadata:
      labels:
        app: ms-lbs
    spec:
      containers:
      - image: ms-lbs-service:latest
        imagePullPolicy: Always
        name: lbs 
  • 最小部署单元
  • 一组容器的集合
  • 一个Pod中的容器共享网络命名空间
  • Pod 是短暂的
  • 为亲密性应用而存在

什么是亲密性

> - 两个应用之间发生文件交互
> - 两个应用需要通过127.0.0.1或者socket通信
> - 两个应用需要发生频繁的调用

Pod 详解

刚才说到, Pod 是一组容器的集合, 他是一个逻辑概念, 我们知道容器之间是通过 Namespace 隔离的, Pod要想解决上述应用场景, 那么就要让Pod里的容器之间高效共享.

具体分为两个部分:网络和存储. Kubernetes 是如何实现的呢?

实现策略

  • 共享网络
    kubernetes 的方案是这样的:会在每个 Pod 里先启动一个 infrastructure container 容器 pause (700+KB), 然后让其他的容器连接进来这个网络命名空间, 然后其他容器看到的网络试图就完全一样了, 即网络设备、IP地址、Mac地址等, 这就是解决网络共享问题. 在 Pod 的 IP 地址就是 infrastructure container 的 IP 地址.

每次启动 pod 时都会启动一个 pause 容器.
容器化技术(No.3) -- Kubernetes 进阶(一) Pod 对象与常用控制器

如下示例:

[aaa@qq.com ~]# docker ps|grep flannel
409bec1f9110        ff281650a721                                               "/opt/bin/flanneld -…"   3 weeks ago         Up 3 weeks                              k8s_kube-flannel_kube-flannel-ds-amd64-wrhfc_kube-system_dc83ec9d-df8d-4757-9dd5-b0a087c2c7b2_9
4d58e801b677        registry.aliyuncs.com/google_containers/pause:3.1          "/pause"                 3 weeks ago         Up 3 weeks                              k8s_POD_kube-flannel-ds-amd64-wrhfc_kube-system_dc83ec9d-df8d-4757-9dd5-b0a087c2c7b2_9
  • 共享存储
    通过挂载共享 volume, 使得大家到共享目录的内容一样. 如下方示例:
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: write
    image: centos
    command: ["bash","-c","for i in {1..100};do echo $i >> /data/count;sleep 1;done"]
    volumeMounts:
      - name: data
        mountPath: /data

  - name: read
    image: centos
    command: ["bash","-c","tail -f /data/count"]
    volumeMounts:
      - name: data
        mountPath: /data
  
  volumes:
  - name: data
    emptyDir: {}

上述示例中有两个容器, write 容器负责提供数据, read 消费数据, 通过数据卷将写入数据的目录和读取数据的目录都放到了该卷中, 这样每个容器都能看到该目录.

验证:

$ kubectl apply -f pod.yaml
$ kubectl logs my-pod -c read -f
  • 在 Pod 中容器分为以下几个类型:

- Infrastructure Container:基础容器, 维护整个Pod网络空间, 对用户不可见, 即 pause 容器.
- InitContainers:初始化容器, 先于业务容器开始执行, 一般用于业务容器的初始化工作.
- Containers:业务容器, 具体跑应用程序的镜像, 并行启动.

镜像拉取策略

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
    - name: nginx
      image: nginx
      imagePullPolicy: IfNotPresent

imagePullPolicy 字段有三个可选值:

  • IfNotPresent:默认值, 镜像在宿主机上不存在时才拉取
  • Always:每次创建 Pod 都会重新拉取一次镜像
  • Never: Pod 永远不会主动拉取这个镜像

如果拉取公开的镜像, 直接按照上述示例即可, 但要拉取私有的镜像, 是必须认证镜像仓库才可以, 即 docker login, 而在 Kubernetes 集群中会有多个 Node, 显然这种方式是很不放方便的!为了解决这个问题, Kubernetes 实现了自动拉取镜像的功能. 以 secret 方式保存到 Kubernetes 中, 然后传给 kubelet.

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  imagePullSecrets:
    - name: nexus-registrykey
  containers:
    - name: nginx
      image: nginx:latest
      imagePullPolicy: IfNotPresent

上述中名为 nexus-registrykey 的 secret 是由 kubectl create secret docker-registry 命令创建:

$ kubectl create secret docker-registry nexus-registrykey --docker-username=admin --docker-password=aaa@qq.com --docker-email=aaa@qq.com --docker-server=192.168.0.13

–docker-server: 指定docke仓库地址
–docker-username: 指定docker仓库账号
–docker-password: 指定docker仓库密码
–docker-email: 指定邮件地址(选填)

资源限制

Pod 资源限制配置分为两类:

  • 申请配额: 调度时使用, 参考是否有节点满足该配置
    spec.containers[].resources.requests.cpu
    spec.containers[].resources.requests.memory
    
  • 限制配额: 容器能使用的最大配置
    spec.containers[].resources.limits.cpu
    spec.containers[].resources.limits.memory
    

示例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128M"
        cpu: 0.5

其中cpu值比较抽象, 可以这么理解:

1核 = 1000m
1.5核 = 1500m

那上面限制配置就是1核的二分之一(500m), 即该容器最大使用半核CPU.

该值也可以写成浮点数, 更容易理解:

半核 = 0.5
1核 = 1
1.5核 = 1.5

启用资源限制, 避免某容器资源利用率异常突发影响其他容器, 可能会产生雪崩效应!

重启策略

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
    - name: nginx
      image: nginx
    restartPolicy: Always

restartPolicy 有三种选项:

  • Always:默认策略. 当容器终止退出后, 总是重启容器.
  • OnFailure:当容器异常退出(退出状态码非0)时, 才重启容器. 适于job
  • Never:当容器终止退出, 从不重启容器. 适于job

健康检查

默认情况下, kubelet 根据容器状态作为健康依据(而不是容器中应用程序状态), 因此当出现类似程序假死的情况, 就会导致无法提供服务, 丢失流量的问题. 因此引入健康检查机制确保容器健康存活.

健康检查有两种类型:

  • livenessProbe
    如果检查失败, 将杀死容器, 根据Pod的restartPolicy来操作.
  • readinessProbe
    如果检查失败, Kubernetes会把Pod从service endpoints中剔除.

这两种类型支持三种检查方法:

  • httpGet
    发送HTTP请求, 返回200-400范围状态码为成功.
  • exec
    执行Shell命令返回状态码是0为成功.
  • tcpSocket
    发起TCP Socket建立成功.

示例:

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 60
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

上述示例:启动容器第一件事创建文件, 停止30s, 删除该文件, 再停止60s, 确保容器还在运行中.

验证现象:容器启动正常, 30s后异常, 会restartPolicy策略自动重建, 容器继续正常, 反复现象.

调度策略

创建一个 Pod 的工作流程:
容器化技术(No.3) -- Kubernetes 进阶(一) Pod 对象与常用控制器

Pod根据调度器默认算法将Pod分配到合适的节点上, 一般是比较空闲的节点. 但有些情况我们希望将Pod分配到指定节点, 这里就需要用到如下调度策略.

  • nodeName
  • nodeSelector
  • taint & tolerations

1. nodeName

nodeName 用于将 Pod 调度到指定的 Node 名称上.

例如:下面示例会绕过调度系统, 直接分配到 k8s-node1 节点.

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: busybox
  name: busybox
  namespace: default
spec:
  nodeName: k8s-node1
  containers:
  - image: busybox
    name: bs
    command:
    - "ping"
    - "www.baidu.com"

2. nodeSelector

nodeSelector用于将Pod调度到匹配Label的Node上.

先给规划node用途, 然后打标签, 例如将两台node划分给不同团队使用:

$ kubectl label nodes k8s-node1 group=lab
$ kubectl label nodes k8s-node2 group=hive

然后在创建Pod只会被调度到含有 group=lab 标签的节点上.

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  nodeSelector: 
    group: lab
  containers:
  - image: busybox
    name: test-box
    command:
    - "ping"
    - "www.baidu.com"

3. taint(污点)与tolerations(容忍)

污点应用场景:节点独占, 例如具有特殊硬件设备的节点, 如GPU

设置污点命令:

kubectl taint node [node] key=value[effect] 

其中[effect] 可取值:

  • NoSchedule :一定不能被调度.
  • PreferNoSchedule:尽量不要调度.
  • NoExecute:不仅不会调度, 还会驱逐Node上已有的Pod.

示例:

先给节点设置污点, 说明这个节点不是谁都可以调度过来的:

$ kubectl taint node k8s-node1 nodelable=gpu:NoSchedule

查看污点:

$ kubectl describe node k8s-node1 |grep Taints

然后在创建Pod只有声明了容忍污点(tolerations), 才允许被调度到 nodelable=gpu 污点节点上.

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: busybox
  name: busybox3
  namespace: default
spec:
  tolerations:
  - key: "nodelable"
    operator: "Equal"
    value: "gpu"
    effect: "NoSchedule"
  containers:
  - image: busybox
    name: busybox
    command:
    - "ping"
    - "www.baidu.com"

如果不配置容忍污点, 则永远不会调度到k8s-node1.

去掉污点:

# kubectl taint node [node] key:[effect]-
$ kubectl taint node k8s-node1 abc:NoSchedule-

故障排查常用命令

# 查看事件, 可用于大部分资源
kubectl describe TYPE/NAME    
# 如果pod启动失败, 先查看日志
kubectl logs TYPE/NAME [-c CONTAINER]  
# 进入到容器中debug
kubectl exec POD [-c CONTAINER] -- COMMAND [args...]  

控制器

Pod 与 controller 的关系

  • controllers:在集群上管理和运行容器的对象 (有时也称为工作负载 - workload)
  • 通过 label-selector 相关联, 如下图所示.
  • Pod 通过控制器实现应用的运维, 如伸缩, 滚动升级等
    容器化技术(No.3) -- Kubernetes 进阶(一) Pod 对象与常用控制器

无状态应用部署控制器 Deployment

Deployment功能:

  • 部署无状态应用(无状态应用简单来讲, 就是Pod可以漂移任意节点, 而不用考虑数据和IP变化)
  • 管理Pod和ReplicaSet(副本数量管理控制器)
  • 具有上线部署、副本设定、滚动升级、回滚等功能
  • 提供声明式更新, 例如只更新一个新的Image

应用场景:Web服务, 微服务

如下示例为 Deployment 标准 YAML, 通过标签与 Pod 关联.

# 控制器定义
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3    # 设置副本数
  selector:
    matchLabels:
      app: web
# 被控制对象
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: nginx
        name: nginx

将这个应用暴露到集群外部访问:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: web
  name: web
spec:
  ports:
  - port: 80             # 集群内容访问应用端口
    protocol: TCP
    targetPort: 8080     # 容器镜像端口
    nodePort: 30008      # 对外暴露的端口
  selector:
    app: web
  type: NodePort

查看资源:

$ kubectl get pods,svc
NAME                       READY   STATUS    RESTARTS   AGE
pod/web-7dc5f5b565-cxh9t   1/1     Running   0          9s
pod/web-7dc5f5b565-gl4jz   1/1     Running   0          9s
pod/web-7dc5f5b565-qw6dq   1/1     Running   0          12s

NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP   10.99.103.5   <none>        443/TCP        1m
service/web          NodePort    10.1.147.15   <none>        80:30008/TCP   48s

浏览器输入:http://NodeIP:30008 即可访问到该应用.

# 升级项目, 即更新最新镜像版本, 这里换一个nginx镜像为例:
$ kubectl set image deployment/web nginx=nginx:1.15
$ kubectl rollout status deployment/web # 查看升级状态

# 如果该版本发布失败想回滚到上一个版本可以执行:
$ kubectl rollout undo deployment/web   # 回滚最新版本

# 也可以回滚到指定发布记录:
$ kubectl rollout history deployment/web  # 查看发布记录
$ kubectl rollout undo deployment/web --revision=2  # 回滚指定版本

# 扩容/缩容:
$ kubectl scale deployment nginx-deployment --replicas=5 
# --replicas设置比现在值大就是扩容, 反之就是缩容. 

kubectl set image触发滚动更新.

滚动更新原理其实很简单, 利用新旧两个replicaset, 例如副本是 3 个, 首先 Scale Up 增加新 RS 副本数量为 1, 准备就绪后, Scale Down 减少旧 RS 副本数量为 2, 以此类推, 逐渐替代, 最终旧 RS 副本数量为 0, 新 RS 副本数量为 3, 完成本次更新. 这个过程可通过 kubectl describe deployment web 看到.

守护进程控制器 DaemonSet

DaemonSet功能:

  • 在每一个Node上运行一个Pod
  • 新加入的Node也同样会自动运行一个Pod

应用场景:Agent, 例如监控采集工具, 日志采集工具

任务控制器 Job & CronJob

Job: 一次性执行

应用场景: 离线数据处理, 视频解码等业务

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never   # 作业失败后会不再尝试创建新的Pod
  backoffLimit: 4   # .spec.backoffLimit字段限制重试次数. 默认情况下, 这个字段默认值是6. 

上述示例中将 π 计算到 2000 个位置并将其打印出来. 完成大约需要10秒.

查看任务:

$ kubectl get pods,job 

CronJob: 定时任务, 像 Linux 的 Crontab 一样.

应用场景:通知, 备份

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            args:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure  # 作业失败并返回状态码非0时, 尝试创建新的Pod运行任务

上述示例中将每分钟打印一次 Hello.

查看任务:

$ kubectl get pods,cronjob

总结

Pod 对象:

Pod 网络存储实现策略
镜像拉取策略
资源限制
重启策略
健康检查
调度策略
故障排查常用命令

常用控制器:

无状态应用部署控制器 Deployment
守护进程控制器 DaemonSet
任务控制器 Job & CronJob

相关标签: 容器化技术