Pod 基本用法

107 阅读10分钟

一、前言

Kubernetes 让 Pod 去编排处理容器,然后把 Pod 作为应用调度部署的最小单位。

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

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

  • 运行一个单独的容器:这是最常用的方式;在这种情况下,可以认为一个 Pod 封装了一个单独的容器,Kubernetes 是对 pod 进行管理,而不是直接对容器进行管理。
  • 运行耦合度高的多个容器:Pod 也可能包含多个容器,这些容器之间关联紧密需要共享资源。将多个容器添加到单个 Pod 的主要原因是应用可能由一个主进程和一个或多个辅助进程组成。在实践中,把一个进程放到一个容器中运行是最好的。

Nodes 与 Pods 关系:

  • Pod 是运行在 Node 上的。
  • 一个 Node 是 Kubernetes 上的工作节点,它可能是虚拟出来的节点或者是物理节点,这取决于集群的部署方式。

每个 Node 节点都被 Master 节点所管理,一个 Node 节点可以运行多个 Pod,在集群中,Kubernetes Master 节点通过 Node 节点自动管理 Pods。

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

  • Kubelet:负责 Master 和 Node 之间的通信,用于管理节点上的 Pod。
  • Docker:负责从仓库拉取容器镜像,运行应用等。

二、基本用法

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

主要学习点有:

  1. 创建 Pod 和删除 Pod:手动操作。

  2. 命名空间 (Namespace) 对不同的 Pod 进行隔离。

  3. 标签和标签选择器:可以很轻松的选择出所需要的 Pod。

  4. 副本集 (RS)、后台支撑服务集 (DaemonSet)、任务 (Job):自动托管 Pod,保证任务可以连续正常的运行。

(1)创建/删除 Pod

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

  • apiVersion: YAML 描述文件所使用的 Kubernetes API 版本,对于 pod 而言是固定值:v1
  • kind: Kubernetes 对象/资源类型,对于 pod 而言是固定值:Pod
  • metadata: Pod 元数据(名称、命名空间、标签、注解等)
  • spec: Pod 规格/内容(容器列表、存储卷等)
    • ports: 列出容器对外暴露的端口,和 Docker 的 -p 参数有点像。
    • imagePullPolicy: 指定镜像的拉取策略,可以是 Always/Never/IfNotPresent,一般默认是 IfNotPresent,也就是说只有本地不存在才会远程拉取镜像,可以减少网络消耗。
    • env: 定义 Pod 的环境变量,和 Dockerfile 里的 ENV 指令有点类似,但它是运行时指定的,更加灵活可配置。
    • command: 定义容器启动时要执行的命令,相当于 Dockerfile 里的 ENTRYPOINT 指令。
    • args: 它是 command 运行时的参数,相当于 Dockerfile 里的 CMD 指令,这两个命令和 Docker 的含义不同,要特别注意。
# 查看 pods 有哪些属性
$ kubectl explain pods

# 查看具体的属性
$ kubectl explain pod.spec

用 YAML 代码就描述了一个简单的 Pod,名字是“busy-pod”,再附加上一些标签:

apiVersion: v1
kind: Pod
metadata:
  name: busy-pod
  labels:
    owner: donald
    env: demo
spec:
  containers:
  - image: busybox:latest
    name: busy
    imagePullPolicy: IfNotPresent
    env:
      - name: os
        value: "ubuntu"
      - name: debug
        value: "on"
    command:
      - /bin/echo
    args:
      - "$(os), $(debug)"

创建/删除 Pod 操作:

# 1. 指定 YAML 文件创建 Pod
$ kubectl apply -f busy-pod.yml
pod/busy-pod created

# 2.1 根据 YAML 文件删除 Pod
$ kubectl delete -f busy-pod.yml

# 2.2 根据名称删除 Pod
# 因为在 YAML 里定义了“name”字段,所以也可以在删除的时候直接指定名字来删除:
$ kubectl delete pod busy-pod
pod "busy-pod" deleted


# 标准输出流信息展示给我们看,在这里就会显示出预设的两个环境变量的值:
$ kubectl logs busy-pod
ubuntu, on

# 其他删除 Pod 操作:
# 2.3 使用标签选择器删除 Pod:
$ kubectl delete pod -l creation_method=manual
# 2.4 通过删除整个命名空间的方式来删除 Pod
$ kubectl delete ns test1-namespace

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

使用命令 kubectl get pod 可以查看 Pod 列表和运行状态:

$ kubectl get pod
NAME       READY   STATUS      RESTARTS      AGE
busy-pod   0/1     Completed   3 (36s ago)   61s


# 使用命令 kubectl describe 来检查它的详细状态,它在调试排错时很有用:
$ kubectl describe pod busy-pod
Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  80s                default-scheduler  Successfully assigned default/busy-pod to minikube
  Normal   Pulling    79s                kubelet            Pulling image "busybox:latest"
  Normal   Pulled     72s                kubelet            Successfully pulled image "busybox:latest" in 7.487183003s
  Normal   Created    29s (x4 over 72s)  kubelet            Created container busy
  Normal   Started    29s (x4 over 72s)  kubelet            Started container busy
  Normal   Pulled     29s (x3 over 71s)  kubelet            Container image "busybox:latest" already present on machine
  Warning  BackOff    1s (x7 over 70s)   kubelet            Back-off restarting failed container

因为 busy-pod,因为它只执行了一条 echo 命令就退出了,而Kubernetes 默认会重启 Pod,所以就会进入一个反复停止 - 启动的循环错误状态

(2)标签/标签选择器

当集群中的 Pod 数量越来越多时,想要有效的区分和管理这些 Pod 就成为了一个问题。

可以通过标签来组织 Pod 和所有其他 Kubernetes 资源对象。通过标签划分组,这样就可以对属于某个组的所有 Pod 进行操作,而不需要单独为某个 Pod 执行操作。

  • 标签的 key 是唯一的,一个资源可以拥有多个标签。
  • 创建资源时可以将标签附加在资源上,也可以在现有的资源上添加标签或修改标签值。

1. 标签基本操作

在对应目录下创建 YMAL 文件: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

# 创建 Pod
$ kubectl create -f nginx-manual-with-labels.yaml

# 查看 Pod 的标签可以使用 --show-labels 选项:
$ kubectl get pod --show-labels
NAME              READY   STATUS    RESTARTS   AGE   LABELS
nginx-manual-v2   1/1     Running   0          16m   creation_method=manual,rel=beta

# 如果想要查看某个具体的标签可以使用 -L 选项指定:
$ kubectl get pod -L creation_method,rel
NAME              READY   STATUS    RESTARTS   AGE   CREATION_METHOD   REL
nginx-manual-v2   1/1     Running   0          16m   manual            beta

执行如下命令: 修改标签

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

# 1. 添加标签
# 向 nginx-manual-v2 这个 Pod 添加一个标签
$ kubectl label pod nginx-manual-v2 env=demo
pod/nginx-manual-v2 labeled

# 再次查看:
$ kubectl get pod --show-labels
NAME              READY   STATUS    RESTARTS   AGE   LABELS
nginx-manual-v2   1/1     Running   0          18m   creation_method=manual,env=demo,rel=beta


# 2. 修改标签
# 将 nginx-manual-v2 这个 Pod 上的 rel=beta 修改为 rel=stable
# 需要添加 --overwrite 选项
$ kubectl label pod nginx-manual-v2 rel=stable --overwrite

# 查看更新后的标签:
$ kubectl get pod -L creation_method,rel
NAME              READY   STATUS    RESTARTS   AGE   CREATION_METHOD   REL
nginx-manual-v2   1/1     Running   0          29m   manual            stable


# 3. 使用标签选择器删除 Pod:
$ kubectl delete pod -l creation_method=manual
2. 标签选择器基本操作

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

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

  • 包含或是不包含使用特定键的标签
  • 包含具有特定键和值的标签
  • 包含有特定键,值可以为任意值的标签
# 1. 筛选出所有手动创建的 Pod, 标签 creation_method 值为 manual
$ kubectl get pod -l creation_method=manual
NAME              READY   STATUS    RESTARTS   AGE
nginx-manual-v2   1/1     Running   0          31m

# 2. 列出含有 rel 标签的所有 Pod:
NAME              READY   STATUS    RESTARTS   AGE
nginx-manual-v2   1/1     Running   0          38m

# 3. 列出不含有 rel 标签的所有 Pod:
$ kubectl get pod -l '!rel'
No resources found in default namespace.


# 4. 其他
# 列出含有 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

# 任意选择一个节点打上标签:
$ kubectl label node kubernetes-worker gpu=true

# 现在查看是否打上标签:
$ kubectl get nodes -l gpu=true

新建一个名为 nginx-gpu 的 Pod,新建 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

$ kubectl get pod -o wide

(3)命名空间(Namespace

由于标签和 Pod 是多对多的关系,使用标签可以将众多的 Pod 进行分组,这些组中的 Pod 有交叉重复的可能;使用命名空间就可以将对象分隔成完全独立且不重叠的组。

  • Kubernetes 的命名空间为对象名称提供了一个作用域。
  • 对象名称只需要在一个命名空间内保持唯一,不同的命名空间可以包含同名的对象。

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

# namespaces 可以缩写为 ns
$ kubectl get ns
NAME                   STATUS   AGE
default                Active   6d1h
kube-node-lease        Active   6d1h
kube-public            Active   6d1h
kube-system            Active   6d1h
kubernetes-dashboard   Active   6d1h

Tips:操作中没有指定命名空间,那么默认在 default 这个命名空间下进行的 Pod 创建等操作。

查看 kube-system命名空间下的 Pod

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

# 可以查看一下其他命名空间中的 Pod:
$ kubectl get pod -n kube-system
NAME                               READY   STATUS    RESTARTS   AGE
coredns-7f8cbcb969-5vggj           1/1     Running   0          6d1h
etcd-minikube                      1/1     Running   0          6d1h
kube-apiserver-minikube            1/1     Running   0          6d1h
kube-controller-manager-minikube   1/1     Running   0          6d1h
kube-proxy-jlkd8                   1/1     Running   0          6d1h
kube-scheduler-minikube            1/1     Running   0          6d1h
metrics-server-c9fb666df-lpcfb     1/1     Running   0          6d
storage-provisioner                1/1     Running   0          6d1h

基本操作

尝试新建一个命名空间,新建 test1-namespace.yaml 文件:

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

执行如下命令:

# 1. 执行创建
$ kubectl create -f test1-namespace.yaml

# 直接使用命令创建新的命名空间:
$ kubectl create namespace test2-namespace

# 2. 查看
$ kubectl get ns

# 3. 删除命名空间:同时会删除 Pod
$ kubectl delete ns test1-namespace

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

  • 在 yaml 文件的 metadata 字段中添加 namespace: test1-namespace 属性
  • 创建 Pod 时在命令中使用参数 -n 指定命名空间
# 比如使用前面的 nginx-manual.yaml 文件在 test1-namespace 命名空间中新建 Pod:
$ kubectl create -f nginx-manual.yaml -n test1-namespace

(4)副本集 (RS

如果 Pod 所在的工作节点挂掉了,那么这个节点上的 Pod 会丢失,并且不会重新建立。

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

副本集 ReplicaSet 是一种 Kubernetes 资源对象:

  • 它可以保证 Pod 始终处于运行状态,如果 Pod 因任何原因消失,ReplicaSet 会注意到缺少了 Pod 并创建新的 Pod 进行替代。
  • 主要用于创建和管理一个 Pod 的多个副本(replicas)。

核心标准:是确保 Pod 的数量始终与其标签选择器匹配,它会持续监控正在运行的 Pod 列表,并保证带有对应标签的 Pod 数量与期望相符:

  • 如果运行的 Pod 太少会根据 Pod 模板创建新的副本;

  • 如果运行的 Pod 太多就会删除多余的副本。

此 ReplicaSet 由 3 个部分组成:

  • 标签选择器(label selector): 确定 ReplicaSet 作用域中有哪些 Pod

  • 副本个数(replica count): 指定应运行的 Pod 数量

  • Pod 模板(pod template): 用于创建新的 Pod 副本

下面来实操

创建一个 ReplicaSet,新建 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

然后执行如下命令:

# 1. 执行创建
$ kubectl create -f nginx-replicaset.yaml
replicaset.apps/nginx created

# 2. 查看创建的 Pods 和 ReplicaSet:
$ kubectl get pods
NAME              READY   STATUS    RESTARTS   AGE
nginx-2xlvx       1/1     Running   0          22s
nginx-dlpgj       1/1     Running   0          22s
nginx-manual-v2   1/1     Running   0          176m
nginx-vv6zz       1/1     Running   0          22s

$ kubectl get rs
NAME    DESIRED   CURRENT   READY   AGE
nginx   3         3         3       22s

# 3. 查看详细的描述:
$ kubectl describe rs

# 4. 删除定义的 ReplicaSet
# 在删除 nginx ReplicaSet 时也会删除对应匹配的所有 Pod
$ kubectl delete rs nginx
replicaset.apps "nginx" deleted

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

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

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

operator 运算符一般有 4 种:

  • In: 标签的值必须与其中一个指定的 values 匹配
  • NotIn: 标签的值必须与指定的 values 不匹配
  • Exists: Pod 必须包含一个指定名称的标签(值不重要),这时候就不需要指定 values
  • DoesNotExist: Pod 不能够包含有指定名称的标签,这时候也不需要指定 values

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

(5)后台支持服务集(DaemonSet

ReplicaSet 用于在 Kubernetes 集群上运行部署特定数量的 Pod。

如果你希望在集群的每个 Node 上都运行某个 Pod,这个时候就需要使用到 DaemonSet 了:

  1. 当有节点加入集群时,会为它们新增一个 Pod。
  2. 当有节点从集群移除时,这些 Pod 也会被回收。
  3. 删除 DaemonSet 将会删除它创建的所有 Pod。

通常这种情况下这种 Pod 都是执行系统级别与基础结构相关的操作,比如网络路由、存储、日志或是监控等。

DaemonSet 的使用情况主要分为两种:

  • 在每个节点上运行一个 Pod

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

1. 在每个节点上运行一个 Pod

新建 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"

然后执行命令:

$ kubectl create -f mysql-ds.yaml

# 观察 Pod 是否分布到了每个节点上:
$ kubectl get nodes

$ kubectl get pods -o wide

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

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

举个例子:

  • 环境中有两个节点,分别名为 kubernetes-worker 和 kubernetes-worker2
  • 先将 kubernetes-worker 打上标签 disk1=ssd1:
$ kubectl label node kubernetes-worker disk1=ssd1

新建 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

执行如下命令:

# 创建:
$ kubectl create -f tomcat-ssd.yaml

# 观察:
$ kubectl get pod -o wide

# 修改标签
$ kubectl label node kubernetes-worker disk1=hdd --overwrite

(6)任务 Job/CronJob

如果我们想要执行一个可完成的任务,当进程终止后 Pod 不再重新启动,那么就需要使用任务 (Job)。

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

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

新建 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

然后执行如下命令:

# 1. 创建
$ kubectl create -f sleep.yaml

# 2. 查看 Job 和 Pod:
$ kubectl get job
$ kubectl get pod

# 等待两分钟,当任务执行完成后再重看一次:
$ kubectl get pod
$ kubectl get job

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

spec:
  completions: 10
  template: ...

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

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


开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4 天,点击查看活动详情