Pod基本用法
in DevOps with 0 comment

Pod基本用法

in DevOps with 0 comment

1.实验介绍

1.1 实验内容

Pod 是 Kubernetes 最重要的核心概念,Kubernetes 中其它的对象都是在管理、暴露 Pod 或是被 Pod 使用。本节实验将会向大家介绍 Pod 这一核心概念。

1.2 实验知识点

1.3 推荐阅读

1.4 课程环境

本课程全部为云主机环境,采用 Kubeadm-dind 方式在云主机环境里部署 Kubernetes 集群,DIND 代表 Docker in Docker,因为这种方式部署的集群里节点都是通过 Docker 容器模拟的,而部署到集群里的应用又以 Docker 容器运行在节点容器里。目前支持 Kubernetes 1.10.x 到 1.15.x 几个版本,我们实验环境中使用的是最新版的 1.15.x。

实验环境中已经下载好对应的脚本,文件地址为 /home/shiyanlou/dind-cluster-v1.15.sh,使用这个脚本就可以直接启动整个集群。

先来启动集群:(整个启动过程大概需要一两分钟)

$ ./dind-cluster-v1.15.sh up

* Making sure DIND image is up to date
sha256:12574f2350c69da756ae10b85af0e1ff689a2b5fd3728a2f3112c68195c08d8c: Pulling from mirantis/kubeadm-dind-cluster
Digest: sha256:12574f2350c69da756ae10b85af0e1ff689a2b5fd3728a2f3112c68195c08d8c
Status: Image is up to date for mirantis/kubeadm-dind-cluster@sha256:12574f2350c69da756ae10b85af0e1ff689a2b5fd3728a2f3112c68195c08d8c
docker.io/mirantis/kubeadm-dind-cluster:62f5a9277678777b63ae55d144bd2f99feb7c824-v1.15@sha256:12574f2350c69da756ae10b85af0e1ff689a2b5fd3728a2f3112c68195c08d8c
* Restoring containers
* Restoring master container
* Restoring node container: 1
* Restoring node container: 2
* Starting DIND container: kube-master
* Starting DIND container: kube-node-1
* Starting DIND container: kube-node-2
* Node container restored: 1
* Node container restored: 2
* Master container restored
Creating static routes for bridge/PTP plugin
* Setting cluster config
Cluster "dind" set.
Context "dind" modified.
Switched to context "dind".
* Waiting for kube-proxy and the nodes
........................[done]
* Bringing up coredns
deployment.extensions/coredns scaled
....[done]
* Bringing up kubernetes-dashboard
deployment.extensions/kubernetes-dashboard scaled
..[done]
NAME          STATUS   ROLES    AGE     VERSION
kube-master   Ready    master   3d12h   v1.15.0
kube-node-1   Ready    <none>   3d12h   v1.15.0
kube-node-2   Ready    <none>   3d12h   v1.15.0
* Access dashboard at: http://127.0.0.1:32768/api/v1/namespaces/kube-system/services/kubernetes-dashboard:/proxy
* Access dashboard at: http://127.0.0.1:32768/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy (if version>1.6 and HTTPS enabled)

如果在启动集群的过程中,一直卡在 deployment.extensions/coredns scaled 部分可以尝试使用 Ctrl+C 中断启动脚本,然后重新执行启动命令。如果试过好几次依然失败,可以关掉当前的云主机环境,重新启动环境进行实验。

在这个集群中有一个 master 节点为 kube-master,两个 node 节点分别为 kube-node-1 和 kube-node-2,它们的 IP 地址分别为:

kube-master:10.192.0.2
kube-node-1:10.192.0.3
kube-node-2:10.192.0.4

2.Pod 简介

一个 Pod 是 Kubernetes 最基本的构建单元,也是最小、最简单的构建单元。Pod 也是扩缩容的基本单位,Kubernetes 的扩容和缩容都是直接对 Pod 进行操作的,直接增加 Pod 或是减少 Pod。

一个 Pod 包含一个应用容器(有的时候有多个)、存储资源、唯一的网络 IP、以及其它容器运行所必须的资源。

Pod 在 Kubernetes 集群中主要有两种使用方式:

在本实验中,我们只讲解单容器 Pod 的情况。

每个 Pod 意味着运行了一个单独的应用实例,如果你想要运行多个应用实例对应的就应该使用多个 Pod。

image-1655176527064

Nodes 与 Pods

Pod 是运行在 Node 上的。一个 Node 是 Kubernetes 上的工作节点,它可能是虚拟出来的节点或者是物理节点,这取决于集群的部署方式。每个 Node 节点都被 Master 节点所管理,一个 Node 节点可以运行多个 Pod,在集群中,Kubernetes Master 节点通过 Node 节点自动管理 Pods。

每个 Nodes 运行至少需要两个组件:

image-1655176534877

3.操作 Pod

主要介绍如何直接手动操作 Pod,虽然在实际的生产过程中,我们常常使用的是部署(Deployment)来管理 Pod,但是了解这些操作可以帮助我们更好的理解 Pod。

3.1 创建 Pod

Pod 和其他 Kubernetes 资源通过向 Kubernetes REST API 提供 JSON 或 YAML 描述文件来创建,由于 YAML 格式具有更好的可读性,kubectl 在请求 API 时会从配置文件中读取 YAML 格式内容并转换为 JSON 格式后再发送给 API Server。

编写 YAML 描述文件

编写一个 YAML 文件有 4 个重要的组成部分:

/home/shiyanlou 目录下新建 nginx-manual.yaml 文件,并向该文件中写入如下内容:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-manual
spec:
  containers:
    - image: registry.cn-hangzhou.aliyuncs.com/chenshi-kubernetes/nginx:1.9.1
      name: nginx
      ports:
        - containerPort: 80
          protocol: TCP

上面的这个 YAML 文件定义了我们要创建一个名为 nginx-manual 的 Pod,这个 Pod 中运行了一个基于 nginx:1.9.1 镜像构建的名为 nginx 的容器,默认监听在 80 端口。

大家在编写的时候如果不清楚有哪些可用属性,可以使用 kubectl explain 来查看:

# 查看 pods 有哪些属性
kubectl explain pods
# 查看具体的属性
kubectl explain pod.spec

执行创建

使用如下命令可以创建 Pod:

$ kubectl create -f nginx-manual.yaml
pod/nginx-manual created

在 Pod 列表中查看新创建的 Pod:

$ kubectl get pods
NAME           READY     STATUS              RESTARTS   AGE
nginx-manual   0/1       ContainerCreating   0          4s

$ kubectl get pods
NAME           READY     STATUS    RESTARTS   AGE
nginx-manual   1/1       Running   0          16s

可以看到这个 Pod 已经创建成功并在运行中。

Pod 在整个生命周期中存在各种状态,常见的状态如下所示:

状态 描述
Pending API Server 已经创建该 Pod,但在 Pod 内还有一个或多个容器的镜像没有创建,包括正在下载镜像的过程
Running Pod 内所有容器均已创建,且至少有一个容器处于运行状态、正在启动状态或正在重启状态
Succeeded Pod 内所有容器均成功执行后退出,且不会再重启
Failed Pod 内所有容器均已退出,但至少有一个容器退出为失败状态
Unknown 由于某种原因无法获取该 Pod 的状态,可能由于网络通信不畅导致的

另外 Pod 的重启策略(RestartPolicy)有 3 种,分别为:

查看运行中的 Pod 完整定义

查看一个 Pod 的 YAML 描述文件:

kubectl get pod nginx-manual -o yaml
# 也可以查看 json 格式的
# kubectl get pod nginx-manual -o json

结果如下所示:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: 2019-08-04T08:56:24Z
  name: nginx-manual
  namespace: default
  resourceVersion: '1384'
  selfLink: /api/v1/namespaces/default/pods/nginx-manual
  uid: bcf146de-b695-11e9-b712-ce9ce5f54bfa
spec:
  containers:
    - image: registry.cn-hangzhou.aliyuncs.com/chenshi-kubernetes/nginx:1.9.1
      imagePullPolicy: IfNotPresent
      name: nginx
      ports:
        - containerPort: 80
          protocol: TCP
      resources: {}
      terminationMessagePath: /dev/termination-log
      terminationMessagePolicy: File
      volumeMounts:
        - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
          name: default-token-ttqxg
          readOnly: true
  dnsPolicy: ClusterFirst
  nodeName: kube-node-1
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 30
  tolerations:
    - effect: NoExecute
      key: node.kubernetes.io/not-ready
      operator: Exists
      tolerationSeconds: 300
  volumes:
    - name: default-token-ttqxg
      secret:
        defaultMode: 420
        secretName: default-token-ttqxg
status:
  conditions:
    - lastProbeTime: null
      lastTransitionTime: 2019-08-04T08:56:24Z
      status: 'True'
      type: Initialized
    - lastProbeTime: null
      lastTransitionTime: 2019-08-04T08:56:34Z
      status: 'True'
      type: Ready
    - lastProbeTime: null
      lastTransitionTime: 2019-08-04T08:56:24Z
      status: 'True'
      type: PodScheduled
  containerStatuses:
    - containerID: docker://111b01507bed8b5b752529e778ee79895ebe60a5d73244f3ac84362634e85360
      image: registry.cn-hangzhou.aliyuncs.com/chenshi-kubernetes/nginx:1.9.1
      imageID: docker-pullable://registry.cn-hangzhou.aliyuncs.com/chenshi-kubernetes/nginx@sha256:a42a428525996f3a84d466ee628a074cac568e0e8c99b5d6f7398be342337039
      lastState: {}
      name: nginx
      ready: true
      restartCount: 0
      state:
        running:
          startedAt: 2019-08-04T08:56:34Z
  hostIP: 10.192.0.3
  phase: Running
  podIP: 10.244.2.3
  qosClass: BestEffort
  startTime: 2019-08-04T08:56:24Z

这个 Pod 是处于运行状态,最终获取到的信息会比较多,可以看到前面我们定义的 4 个部分都展示了出来,这里额外还显示了一个部分:

注意:status 是运行的 Pod 在查询时刻的资源状态,所以在编写 YAML 文件的时候不需要这个属性。

查看日志

如果 Pod 上只包含一个容器,想要查看该容器的日志,就可以通过如下命令查看日志:

kubectl logs nginx-manual

如果 Pod 上包含多个容器,在查询具体容器的日志时,需要指定参数 -c 容器名 进行查询,比如:

kubectl logs nginx-manual -c nginx

需要注意的是:每天或是每次日志文件达到 10M 大小之后,容器日志会自动轮替,kubectl logs 命令只显示最后一次轮替后的日志。

测试 Pod 端口是否可用

Pod 运行之后想要测试(调试)一下是否可用,怎么办呢?可以使用 kubectl port-forward 命令来执行端口转发,将本地的 8080 端口转发到 nginx-manual 的 80 端口,执行如下命令:

$ kubectl port-forward nginx-manual 8080:80
Forwarding from 127.0.0.1:8080 -> 80

使用 curl 命令向 pod 发送 HTTP 请求,新开一个标签,执行:

$ curl http://localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

3.2 标签

当集群中的 Pod 数量越来越多时,想要有效的区分和管理这些 Pod 就成为了一个问题。可以通过标签来组织 Pod 和所有其他 Kubernetes 资源对象。通过标签划分组,这样就可以对属于某个组的所有 Pod 进行操作,而不需要单独为某个 Pod 执行操作。

标签是可以附加在 Kubernetes 资源对象的任意键值对,通过标签选择器可以选择具有该确切标签的资源。标签的 key 是唯一的,一个资源可以拥有多个标签。创建资源时可以将标签附加在资源上,也可以在现有的资源上添加标签或修改标签值。

创建时指定标签

我们尝试创建一个带有两个标签的新 Pod,在 /home/shiyanlou 目录下新建 nginx-manual-with-labels.yaml 文件,并向其中写入如下代码:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-manual-v2
  labels:
    creation_method: manual
    rel: beta
spec:
  containers:
    - image: registry.cn-hangzhou.aliyuncs.com/chenshi-kubernetes/nginx:1.9.1
      name: nginx
      ports:
        - containerPort: 80
          protocol: TCP

然后执行如下命令创建 Pod:

$ kubectl create -f nginx-manual-with-labels.yaml
pod/nginx-manual-v2 created

查看 Pod 的标签可以使用 --show-labels 选项:

$ kubectl get pod --show-labels
NAME              READY     STATUS    RESTARTS   AGE       LABELS
nginx-manual      1/1       Running   0          14m       <none>
nginx-manual-v2   1/1       Running   0          33s       creation_method=manual,rel=beta

如果想要查看某个具体的标签可以使用 -L 选项指定:

$ kubectl get pod -L creation_method,rel
NAME              READY     STATUS    RESTARTS   AGE       CREATION_METHOD   REL
nginx-manual      1/1       Running   0          15m
nginx-manual-v2   1/1       Running   0          1m        manual            beta

修改标签

标签也可以在现有的 Pod 上进行添加和修改。

先尝试向 nginx-manual 这个 Pod 添加一个标签,执行如下命令:

$ kubectl label pod nginx-manual creation_method=manual
pod/nginx-manual labeled

然后将 nginx-manual-v2 这个 Pod 上的 rel=beta 修改为 rel=stable,需要添加 --overwrite 选项,执行如下命令:

$ kubectl label pod nginx-manual-v2 rel=stable --overwrite
pod/nginx-manual-v2 labeled

现在来查看更新后的标签:

$ kubectl get pod -L creation_method,rel
NAME              READY     STATUS    RESTARTS   AGE       CREATION_METHOD   REL
nginx-manual      1/1       Running   0          18m       manual
nginx-manual-v2   1/1       Running   0          3m        manual            stable

3.3 标签选择器

标签通常与标签选择器结合在一起使用,标签选择器可以选择标记有特定标签的 Pod 子集,并对这些 Pod 执行操作。

标签选择器对于标签的使用一般有如下 3 种方式:

想要筛选出所有手动创建的 Pod:

$ kubectl get pod -l creation_method=manual
NAME              READY     STATUS    RESTARTS   AGE
nginx-manual      1/1       Running   0          18m
nginx-manual-v2   1/1       Running   0          4m

列出含有 rel 标签的所有 Pod:

$ kubectl get pod -l rel
NAME              READY     STATUS    RESTARTS   AGE
nginx-manual-v2   1/1       Running   0          4m

列出不含有 rel 标签的所有 Pod:

$ kubectl get pod -l '!rel'
NAME           READY     STATUS    RESTARTS   AGE
nginx-manual   1/1       Running   0          7m

还有一些其它的使用方式:

# 列出含有 creation_method 标签,但是其值不能为 manual 的 Pod
kubectl get pod -l 'creation_method!=manual'
# 列出含有 rel 标签,且其值为 beta 或是 stable 的 Pod
kubectl get pod -l 'rel in (beta,stable)'
# 列出含有 rel 标签,且其值不为 beta 和 stable 的 Pod
kubectl get pod -l 'rel notin (beta,stable)'
# 列出含有标签 creation_method=manual 和 rel=stable 的 Pod
kubectl get pod -l creation_method=manual,rel=stable

约束 Pod 调度

在 Kubernetes 中,Pod 通常都是随机调度到工作节点上的,这也是 Kubernetes 集群中正确的工作方式。但是在某些特定的情况下,我们会指定 Pod 的调度,这种需求不太常见,这里大家仅作为了解即可。

有的时候一个集群中的硬件基础设施会不一样,比如机器学习的环境中,有的使用 CPU 环境,有的是 GPU 环境,这两种环境的硬件不一样,会导致对应的计算速度也不同。如果想要将 Pod 调度到 GPU 的工作节点上,可以对相应的节点打上标签,然后在编写 yaml 文件时使用 nodeSelector 指定对应的节点标签。

首先查看环境中有几个节点:

$ kubectl get nodes
NAME          STATUS    ROLES     AGE       VERSION
kube-master   Ready     master    8d        v1.15.0
kube-node-1   Ready     <none>    8d        v1.15.0
kube-node-2   Ready     <none>    8d        v1.15.0

任意选择一个节点打上标签:

$ kubectl label node kube-node-1 gpu=true
node/kube-node-1 labeled

现在查看是否打上标签:

$ kubectl get nodes -l gpu=true
NAME          STATUS    ROLES     AGE       VERSION
kube-node-1   Ready     <none>    8d        v1.15.0

新建一个名为 nginx-gpu 的 Pod,在 /home/shiyanlou 目录下新建 nginx-gpu.yaml 文件,并向其中写入如下代码:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-gpu
spec:
  nodeSelector:
    gpu: 'true'
  containers:
    - image: registry.cn-hangzhou.aliyuncs.com/chenshi-kubernetes/nginx:1.9.1
      name: nginx
      ports:
        - containerPort: 80
          protocol: TCP

其中的 nodeSelector 字段表示在创建 Pod 时调度器只在包含 gpu=true 的工作节点中选择。

然后执行创建:

$ kubectl create -f nginx-gpu.yaml
pod/nginx-gpu created

3.4 命名空间

由于标签和 Pod 是多对多的关系,使用标签可以将众多的 Pod 进行分组,这些组中的 Pod 有交叉重复的可能;使用命名空间就可以将对象分隔成完全独立且不重叠的组。Kubernetes 的命名空间为对象名称提供了一个作用域。对象名称只需要在一个命名空间内保持唯一,不同的命名空间可以包含同名的对象。

查看集群中所有的命名空间:

$ kubectl get namespaces
NAME          STATUS    AGE
default       Active    8d
kube-node-lease Active   8d
kube-public   Active    8d
kube-system   Active    8d

我们在前面的操作中没有指定命名空间,那么就一直都是在 default 这个命名空间下进行的 Pod 创建等操作。

可以查看一下其他命名空间中的 Pod:

$ kubectl get pod -n kube-system
NAME                                  READY     STATUS    RESTARTS   AGE
coredns-76dc8f6bf5-rdp2j              1/1       Running   0          14m
etcd-kube-master                      1/1       Running   6          13m
kube-apiserver-kube-master            1/1       Running   6          14m
kube-controller-manager-kube-master   1/1       Running   6          13m
kube-proxy-6kj44                      1/1       Running   0          14m
kube-proxy-n5bbk                      1/1       Running   0          14m
kube-proxy-ptd2d                      1/1       Running   0          14m
kube-scheduler-kube-master            1/1       Running   6          14m
kubernetes-dashboard-5ff478f859-2mkkh   1/1     Running   0          14m

可以看到 kube-system 命名空间下的 Pod 都是和 Kubernetes 系统相关的。

现在来尝试新建一个命名空间,在 /home/shiyanlou 目录下新建 test1-namespace.yaml 文件并向其中写入如下代码:

apiVersion: v1
kind: Namespace
metadata:
  name: test1-namespace

然后执行创建:

$ kubectl create -f test1-namespace.yaml
namespace/test1-namespace created

当然也可以使用命令直接创建新的命名空间:

$ kubectl create namespace test2-namespace
namespace/test2-namespace created

如果想要在新的命名空间中创建 Pod,有两种方式:

比如我们使用前面的 nginx-manual.yaml 文件在 test1-namespace 命名空间中新建 Pod:

$ kubectl create -f nginx-manual.yaml -n test1-namespace
pod/nginx-manual created

这样我们就有两个同名的 Pod 分别运行在两个不同的命名空间内。

3.5 删除及更新 Pod

可以使用名称删除 Pod:

$ kubectl delete pod nginx-manual
pod "nginx-manual" deleted

也可以使用标签选择器删除 Pod:

$ kubectl delete pod -l creation_method=manual
pod "nginx-manual-v2" deleted

还可以通过删除整个命名空间的方式来删除 Pod(命名空间不存在的话,其中的 Pod 会自动被删除):

$ kubectl delete ns test1-namespace
namespace "test1-namespace" deleted

如果想要更新 Pod,可以在修改 Pod 的配置文件之后执行 kubectl replace -f xxx.yaml --force 强制更新 Pod 的各种配置属性。

4.副本集(RS)、后台支撑服务集(DaemonSet)、任务(Job)

使用前面的方法创建 Pod,如果该 Pod 所在的工作节点挂掉了,那么这个节点上的 Pod 会丢失,并且不会重新建立。

在实际的生产中,我们希望我们的部署可以自动运行、保持健康、工作节点挂掉之后 Pod 会自动迁移到新的节点,所以几乎不会手动创建 Pod,而是使用副本集 ReplicaSet 或部署 Deployment 来创建并管理 Pod。

4.1 副本集(RS)

副本集 ReplicaSet 是一种 Kubernetes 资源对象,它可以保证 Pod 始终处于运行状态,如果 Pod 因任何原因消失,ReplicaSet 会注意到缺少了 Pod 并创建新的 Pod 进行替代。

ReplicaSet 主要用于创建和管理一个 Pod 的多个副本(replicas),核心标准是确保 Pod 的数量始终与其标签选择器匹配,它会持续监控正在运行的 Pod 列表,并保证带有对应标签的 Pod 数量与期望相符,如果运行的 Pod 太少会根据 Pod 模板创建新的副本;如果运行的 Pod 太多就会删除多余的副本。

因此 ReplicaSet 由 3 个部分组成:

上述的 3 个部分都可以随时修改,只有副本个数的变更会影响到现有的 Pod,更改标签选择器和 Pod 模板对现有的 Pod 没有影响。如果更改标签选择器会使现有的 Pod 脱离 ReplicaSet 的范围,ReplicaSet 会停止关注它们;如果更改模板,只会对新创建的 Pod 应用新的模板,并不会影响现在存在的 Pod。

使用 ReplicaSet 有以下 3 大好处:

注意:这里的标签选择器可以匹配如下的标签:

现在我们来创建一个 ReplicaSet,在 /home/shiyanlou 目录下新建 nginx-replicaset.yaml 文件并向其中写入如下代码:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: registry.cn-hangzhou.aliyuncs.com/chenshi-kubernetes/nginx:1.9.1

执行创建:

$ kubectl create -f nginx-replicaset.yaml
replicaset.apps/nginx created

查看创建的 Pods 和 ReplicaSet:

$ kubectl get pods
NAME          READY     STATUS    RESTARTS   AGE
nginx-45h47   1/1       Running   0          48s
nginx-gpu     1/1       Running   0          17m
nginx-pp6xw   1/1       Running   0          48s
nginx-vf2rg   1/1       Running   0          48s

$ kubectl get rs
NAME      DESIRED   CURRENT   READY     AGE
nginx     3         3         3         1m

查看详细的描述:

$ kubectl describe rs
Name:         nginx
Namespace:    default
Selector:     app=nginx
Labels:       app=nginx
Annotations:  <none>
Replicas:     3 current / 3 desired
Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        registry.cn-hangzhou.aliyuncs.com/chenshi-kubernetes/nginx:1.9.1
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  2m    replicaset-controller  Created pod: nginx-pp6xw
  Normal  SuccessfulCreate  2m    replicaset-controller  Created pod: nginx-45h47
  Normal  SuccessfulCreate  2m    replicaset-controller  Created pod: nginx-vf2rg

除了使用 matchLabels 属性外,还可以使用功能更加强大的 matchExpressions 属性,在这个属性中可以使用表达式,使标签匹配能够更加丰富。现在我们来改写上面的标签表达式:

selector:
  matchExpressions:
    - key: app
      operator: In
      values:
        - nginx

使用 matchExpressions 属性一般包含 3 个部分:一个 key、一个 operator(运算符)、以及可能还有一个 values 列表(这取决于运算符)。

operator 运算符一般有 4 种:

如果指定了多个表达式或者同时混合使用了 matchLabels 和 matchExpressions 属性,,这些表达式必须都为 true 才能和 Pod 进行匹配。

最后也可以删除定义的 ReplicaSet:

$ kubectl delete rs nginx
replicaset.extensions "nginx" deleted

在删除 nginx ReplicaSet 时也会删除对应匹配的所有 Pod。

4.2 后台支撑服务集(DaemonSet)

ReplicaSet 用于在 Kubernetes 集群上运行部署特定数量的 Pod,如果你希望在集群的每个 Node 上都运行某个 Pod,这个时候就需要使用到 DaemonSet 了,当有节点加入集群时,会为它们新增一个 Pod。当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。通常这种情况下这种 Pod 都是执行系统级别与基础结构相关的操作,比如网络路由、存储、日志或是监控等。

DaemonSet 的使用情况分为两种:

在每个节点上运行一个 Pod

对于采用 kubeadm-dind 部署方式而言,后面创建 Pod 运行 MySQL 会不成功,会有相关报错如下所示:ERROR: mysqld failed whilmysqld: error while loading shared libraries: libpthread.so.0: cannot stat shared object: Permission denied,主要原因是在 dind 环境中 MySQL 的权限问题,执行如下命令可以暂时在开发环境中解决这个问题:

sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/
sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld

/home/shiyanlou 目录下新建 mysql-ds.yaml,并向其中写入如下代码:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: mysql-ds
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:5.7
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: '123456'

然后执行创建:

$ docker pull mysql:5.7

$ kubectl create -f mysql-ds.yaml
daemonset.apps/mysql-ds created

观察 Pod 是否分布到了每个节点上:

$ kubectl get nodes
NAME          STATUS    ROLES     AGE       VERSION
kube-master   Ready     master    8d        v1.15.0
kube-node-1   Ready     <none>    8d        v1.15.0
kube-node-2   Ready     <none>    8d        v1.15.0

$ kubectl get pods -o wide
NAME             READY   STATUS    RESTARTS   AGE     IP            NODE          NOMINATED NODE   READINESS GATES
mysql-ds-fmtcr   1/1     Running   0          5m50s   10.244.2.11   kube-node-1   <none>           <none>
mysql-ds-mkwn2   1/1     Running   0          5m50s   10.244.3.9    kube-node-2   <none>           <none>
nginx-gpu        1/1     Running   0          52m     10.244.2.5    kube-node-1   <none>           <none>

在特定的节点上运行一个 Pod

如果想要使用 DaemonSet 只在特定的节点上运行 Pod,只需要在模板中的 nodeSelector 属性中指明即可。

环境中有两个节点,分别名为 kube-node-1 和 kube-node-2,我们先将 kube-node-1 打上标签 disk1=ssd1:

$ kubectl label node kube-node-1 disk1=ssd1
node/kube-node-1 labeled

然后在 /home/shiyanlou 目录下新建 tomcat-ssd.yaml,并向其中写入如下代码:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: tomcat-ssd
spec:
  selector:
    matchLabels:
      app: tomcat
  template:
    metadata:
      labels:
        app: tomcat
    spec:
      nodeSelector:
        disk1: ssd1
      containers:
        - name: tomcat
          image: kubeguide/tomcat-app:v1

执行创建并观察新建的 ds 和 Pod:

$ kubectl create -f tomcat-ssd.yaml
daemonset.apps/tomcat-ssd created
$ kubectl get ds
NAME          DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
mysql-ds      2         2         2         2            2           <none>          9m
tomcat-ssd    1         1         1         1            1           disk1=ssd1      37s
$ kubectl get pod
NAME                READY     STATUS             RESTARTS   AGE
mysql-ds-fmtcr      1/1       Running            6          10m
mysql-ds-mkwn2      1/1       Running            6          10m
nginx-gpu           1/1       Running            0          58m
tomcat-ssd-sqswr    1/1       Running            0          1m

那么如果 kube-node-1 这个节点上的标签打错了,我们将其修改为 disk1=hdd,使用 DaemonSet 创建的 Pod 还存在吗?让我们试试:

$ kubectl label node kube-node-1 disk1=hdd --overwrite
node/kube-node-1 labeled

然后运行在节点上的 Pod:

$ kubectel get pod
NAME             READY     STATUS             RESTARTS   AGE
mysql-ds-fmtcr   1/1       Running            9          27m
mysql-ds-mkwn2   1/1       Running            9          27m
nginx-gpu        1/1       Running            0          1h

可以看到该 Pod 已经终止了。

4.3 任务(Job)

前面介绍的 ReplicaSet 和 DaemonSet 监控管理的 Pod 会持续不断的运行任务,这样的任务是没有完成态的,如果其中的进程退出了 Pod 会重新启动。如果我们想要执行一个可完成的任务,当进程终止后 Pod 不再重新启动,那么就需要使用任务(Job)。

当节点发生故障的时候,该节点上由 Job 管理的 Pod 会按照 ReplicaSet 的 Pod 的方式,重新安排到其它节点;如果进程异常退出,可以将 Job 配置为重新启动容器。

现在我们来模拟一个任务,有一个构建在 busybox 上的容器,该容器会调用 sleep 命令休息两分钟,然后就正常退出。

/home/shiyanlou 目录下新建 sleep.yaml 文件并向其中写入如下代码:

apiVersion: batch/v1
kind: Job
metadata:
  name: batch-job
spec:
  template:
    metadata:
      labels:
        app: batch-job
    spec:
      restartPolicy: OnFailure
      containers:
        - name: main
          image: luksa/batch-job

其中的 restartPolicy 属性定义了当容器中的进程运行结束时 Kubernetes 会如何做,默认的值为 Always,表示的是重新启动并运行;所以在使用 Job 时我们必须修改为 OnFailure 或是 Never。

执行创建:

$ kubectl create -f sleep.yaml
job.batch/batch-job created

查看 Job 和 Pod:

$ kubectl get jobs
NAME        COMPLETIONS   DURATION   AGE
batch-job   0/1           17s        17s

$ kubectl get po
NAME              READY     STATUS             RESTARTS   AGE
batch-job-zknt4   1/1       Running            0          1m
mysql-ds-fmtcr    1/1       Running            0          30m
mysql-ds-mkwn2    1/1       Running            0          30m
nginx-gpu         1/1       Running            0          1h

等待两分钟,当任务执行完成后再重看一次:

$ kubectl get pod
NAME              READY     STATUS             RESTARTS   AGE
batch-job-zknt4   0/1       Completed          0          4m
mysql-ds-fmtcr    1/1       Running            0          33m
mysql-ds-mkwn2    1/1       Running            0          33m
nginx-gpu         1/1       Running            0          1h

$ kubectl get job
NAME        COMPLETIONS   DURATION   AGE
batch-job   1/1           2m8s       7m17s

如果你想要一项任务按顺序依次执行 10 次,可以使用 completions 属性:

spec:
  completions: 10
  template: ...

如果你想要一项任务总共运行 10 次,每次可以并行执行 3 个 Pod,可以使用 parallelism 属性:

spec:
  completions: 10
  parallelism: 3
  template: ...

5.实验总结

在这个实验中,我们主要围绕 Kubernetes 中的最小核心概念 Pod 进行了详细的讲解,其中包括 Pod 简介、创建 Pod 和删除 Pod,还有使用命名空间(Namespace) 对不同的 Pod 进行隔离。对 Pod 进行操作就涉及到一个很重要的概念:标签和标签选择器,正是它们使我们可以很轻松的选择出所需要的 Pod。

然后还介绍了对 Pod 进行管理的副本集(RS)、后台支撑服务集(DaemonSet)、以及任务(Job),这是 Kubernetes 中非常重要的工作机制,这样才能够实现自动托管 Pod,保证任务可以连续正常的运行。