Loading

Pod

三、Pod

1 基础介绍

我们在上一章成功搭建了k8s集群并运行了一个应用,你可能想通过一个命令显示所有正在运行的容器,就像docker ps一样,但这并不是Kubernetes的工作。Kubernetes不直接处理单个容器,而是通过pod管理一组容器。

一个pod可以包含多个和单个容器。pod将容器绑定在一起,并将它们作为一个单元进行管理。这些容器彼此是紧密相关的,一个pod中的容器共享相同的linux命名空间、IP和网络接口。

列出pod命令

kubectl get pods
# 返回结果
NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-9b8d7cb9b-8z7ff   1/1     Running   1          5h

可以看到pod已经处于运行状态。如果想查看pod的详细信息,使用

kubectl describe pod [pod名]

你可能想知道应用运行在哪个节点上,其实这并不重要,k8s会自动调度一个可供运行pod的节点,可以使用-o wide选项显示出更多信息:

kubectl get pod -o wide

为何需要使用pod,为什么不直接用容器?一个容器运行多个进程和多个容器运行一个进程到底有什么区别?原因是多个进程组成的应用,进程之间的通信等都要求它们运行在同一台机器上,尽管容器可以运行多个进程,但并不合理。每个容器一般只运行一个进程(主进程产生子进程除外),如果一个容器运行了多个不相关的进程,那么容器就要保证它们之间的通信、运行、日志等运行,这不是我们愿意看到的。k8s和docker期望使用容器的方式是每个进程运行在自己的容器中。基于此,我们就需要另一种结构将容器绑定在一起,并将它们作为一个基础单元管理,这就是产生pod的原因。如果不加配置,容器之间是完全隔离的,而k8s通过配置让pod内的所有容器可以共享相同的linux命名空间、IP和网络接口。

因此,需要注意的是,同一个pod中的容器运行的进程不能绑定相同的端口,否则会导致冲突。这仅限于同一个pod,pod与pod之间则永远不会遇到端口冲突。另外,容器可以通过localhost与同一pod的其他容器进行通信。

还有一个概念是扁平化网络结构:集群中的所有pod都在同一个共享网络地址中,一个pod可以通过另一个pod的ip地址相互访问。

2 资源清单

首先是一些名词的解释:

  1. 资源是什么?对象是什么?

    k8s中所有的内容都抽象为资源,资源实例化之后,叫做对象,对象是持久化的实体。 Kubernetes 使用这些实体去表示整个集群的状态。比如

    • 哪些容器在运行(以及在哪些节点上)
    • 可以被应用使用的资源
    • 关于应用运行时表现的策略,比如重启策略、升级策略,以及容错策略
  2. 对象规约(Spec)与状态(Status)是什么?

    几乎每个 Kubernetes 对象包含这两个字段。

    spec 描述你希望对象所具有的特征:期望状态(Desired State),比如你希望应用有两个副本,容器挂掉之后不需要重启等等。期望状态需要你创建对象时设置。

    status 描述了对象的当前状态(Current State),不需要创建时设置,k8s每时每刻都在监视着这个状态,以使它达到期望状态。

  3. 什么是资源清单?

    前面已经说过,我们如果想要指定一些期望,就需要在.yaml文件中为 kubectl 提供这些信息。这样的文件我们一般称为资源清单。

2.1 资源类型

先来看看k8s中都有哪些资源。

名称空间级别:仅在此名称空间下生效

  • Pod: k8s中最小单元
  • ReplicaSet:调度器,通过标签控制 pod 的副本数目
  • Deployment:控制器,通过控制 rs 的创建来创建 pod
  • StatefulSet:为有状态服务创建的管理器
  • DaemonSet:可以在每个节点运行 pod 主键
  • Job:为批处理而生
  • 服务发现及负载均衡型资源(ServiceDiscoveryLoadBalance):Service、Ingress、…
  • 配置与存储型资源:Volume(存储卷)、CSI(容器存储接口,可以扩展各种各样的第三方存储卷)
  • 特殊类型的存储卷:
    • ConfigMap(当配置中心来使用的资源类型):通过他可以创建一些配置文件,达到热更新
    • Secret(保存敏感数据):加密方案存储数据,可以用它存储一些密码、秘钥等
    • DownwardAPI(把外部环境中的信息输出给容器):下载文件的接口,可以下载、上传

集群级别:整个集群中都可用

  • Namespace:名称空间
  • Node:工作节点
  • Role
  • ClusterRole
  • RoleBinding
  • ClusterRoleBinding

元数据类型:

  • HPA
  • PodTemplate(pod模板)
  • LimitRange(资源限制)

2.2 必须存在的属性

这些属性必须有。

参数名 字段类型 说明
apiVersion String 这里是指的是k8s API的版本,目前基本上是v1,可以用 kubectl api-versions 或者 kubectl explain pod 命令查询
kind String 这里指的是yaml文件定义的资源类型和角色,比如: Pod
metadata Object 元数据对象,固定值就写metadata
metadata.name String 元数据对象的名字,这里由我们编写,比如命名Pod的名字
metadata.namespace String 元数据对象的命名空间,由我们自身定义
metadata.labels map[string]string 键值数据,常被用作挑选条件
spec Object 详细定义对象,固定值就写Spec
spec.containers[] List 这里是Spec对象的容器列表定义,是个列表
spec.containers[].name String 这里定义容器的名字
spec.containers[].image String 这里定义要用到的镜像名称,如果镜像的标签是 latest,每次使用该镜像都会从远程下载

总结一下就是资源清单 .yaml 文件中,必须配置如下字段:

  • apiVersion - 创建该对象所使用的 Kubernetes API 的版本
  • kind - 想要创建的对象的类别
  • metadata - 帮助唯一性标识对象的一些数据
  • spec - 你所期望的该对象的状态,对每个 Kubernetes 对象来说它是不同的

2.3 pod的资源清单

pod或其他k8s资源通常是通过json或者yaml文件创建的,当然也可以通过前面演示的命令行kubectl run创建,但是命令行只能配置一些有限的属性。

除了2.2列出的必须要有的属性,还有一些其他属性。

下面展示的是pod资源的资源清单

apiVersion: v1   #必选,版本号,例如v1
kind: Pod        #必选,资源类型,例如Pod
metadata:        #必选,元数据
  name: string     #必选,Pod名称
  namespace: string   #Pod所属的命名空间,默认为“default”
  labels:             #Pod自定义的标签列表
    - name: string    #标签名和值
spec:      #必选,Pod中容器的详细定义
  containers:   #必选,Pod中的容器列表
    - name: string  #必选,容器名字
      image: string   #必选,要用到的容器的镜像名称
      imagePullPolicy: [Always | Never | IfNotPresent]  #镜像的下载策略。默认值是Always。建议改为IfNOtPresent
                                           # (1)Always:每次都尝试从远程仓库拉取镜像
                                           # (2)Never:仅使用本地镜像
                                           # (3)IfNOtPresent:如果本地有镜像就使用本地镜像,没有就拉取远程镜像。
      command: [string]  #指定容器启动时的命令,因为是列表可以指定多个,不指定则使用镜像打包时使用的启动命令。
      args: [string]  #容器启动的命令参数列表
      workingDir: string  #容器的工作目录
      volumeMounts:   #挂载到容器内部的存储卷配置
        - name: string  #引用Pod定义的共享存储卷的名称,需用volumes[]部分定义的卷名
          mountPath: string  #存储卷在容器内部mount的绝对路径,应少于512字符
          readOnly: boolean   #是否只读模式,默认为false读写模式
      ports:  #指定需要暴露的端口号列表
        - name: string   #端口的名称
          containerPort: int   #容器监听的端口号
          hostPort: int     #容器所在主机需要监听的端口号,默认与containerPort相同
          protocol: string    #端口协议,支持TCP和UDP,默认TCP
      env:     #容器运行前需设置的环境变量的列表
        - name: string    #环境变量名称
          value: string    #环境变量的值
      resource:   #资源限制和请求的设置(从这里开始就是限制容器使用资源的上限)
        limits:   #资源限制的设置
          cpu: string  #CPU的限制,单位为core数,将用于docker run --cpu-shares参数
          memory: string  #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
        requests: #资源请求的设置
          cpu: string   #CPU请求,容器启动的初始可用数量
          memory: string  #内存请求,容器启动的初始可用数量
      lifecycle:  #生命周期钩子
        postStart: #容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启
        preStop:  #容器终止前执行此钩子,无论结果如何,容器都会终止
      livenessProbe: #对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器
        exec: #对Pod容器内检查方式设置为exec方式
          command: [string] #exec方式需要制定的命令或脚本
        httpGet:  #对Pod内各个容器健康检查方式设置为HttpGet,需要制定Path、port
          path: string
          port: number
          host: string
          scheme: string
          HttpHeaders:
            - name: string
              value: string
        tcpSocket:   #对pod内各个容器健康检查方式设置为tcpSocket方式
          port: number  
        initialDelaySeconds: 0  #容器启动完成后首次探测的时间,单位为秒
        timeoutSeconds: 0  #对容器健康检查探测等待响应的超时时间,单位秒
        periodSeconds: 0 #对容器监控检查的定期探测时间设置,单位秒,默认1秒
        successThreshold: 0
        failureThreshold: 0
        sucurityContext:
          privileged: false
      restartPolicy: [Always|Never|OnFailure]  #对Pod的重启策略。默认值为 Always。
      						 #1.Always: Pod 一旦终止运行,则无论容器是如何终止的,Kubelet服务都将重启它。
                             #2.OnFailure:只有Pod以非零退出码终止时,kubelet才会重启该容器。如果容器正常结束〔退出码为0),则kubelet将不会重启它。
                             #3.Never:Pod终止后,kubelet将退出码报给Master,不会重启该Pod。
      nodeName: <string> #设置NodeName表示将该Pod调度到指定名称的node节点上
      nodeSelector: object  #设置NodeSelector表示将该pod调度到包含这个label的Node上
      imagePullSecurets: #pull镜像时使用的secret名称,以key:secretKey格式指定
        - name: string
      hostNetwork: false  #是否使用主机网络模式。默认值为false。
                          #false表示不使用宿主机网络
                          #设置true表示使用宿主机网络,不使用docker网桥,设置了true将无法在同一台宿主机上启动第二个副本。
      volumes:  #在该pod上定义共享存储卷列表
        - name: string  #共享存储卷名称
          emptyDir: {} #类型为emptyDir的存储卷,与pod同生命周期的一个临时目录,为空值
          hostPath: string  #类型为hostPath的存储卷,表示挂载pod所在宿主机目录
           path: string   #pod所在宿主机的目录,将被用于同期中mount的目录
          secret:   #类型为secret存储卷,挂载集群与定义的secret对象到容器内部
            scretname: string
            items:
             - key: string
               path: string
          configMap:   #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
            name: string
            items:
             - key: string
               path: string

这里并不会详细解释每个属性的细节,因此如果想了解更多,强烈建议官方文档查看所有的Kubernetes API,文档上包含了所有资源的资源清单以及详细解释。你也可以通过kubectl explain [资源类型]命令可以查看API。

你可以通过命令kubectl get pod -o yaml来查看当前运行的pod的资源清单,

3 通过资源清单创建pod

接下来尝试通过yaml文件创建一个pod。你可以在任意目录下定义这个文件vim createpod.yaml

apiVersion: v1
kind: Pod # 这里必须是大写P
metadata:
  name: mypod
spec:
  containers:
  - image: hub.example.com/library/helloworld
    name: test

接下来创建pod

kubectl apply -f createpod.yaml

查看pod运行状态

kubectl get pod
kubectl describe pod mypod

image

可以看到已经运行成功了。

补充说明,通过文件创建pod有两个命令:

kubectl create:创建资源,如果再次运行该命令,则会抛出错误。

kubectl apply:将配置应用于资源,如果该资源不存在就创建,如果存在则更新配置,如果再次运行该命令,不会抛出错误。

二者是声明式管理命令式管理的区别,各有利弊,至于使用哪个需要具体分析。你可以在官方文档查看对象管理的更多信息。

4 查看应用运行日志

docker容器运行时,想查看运行日志可以通过以下命令

docker log 容器id

在k8s上如何获取呢?可以通过ssh连接到pod正在运行的节点,然后运行上面的命令查看日志;k8s为我们提供了更简便的方法:

kubectl logs Pod名称

比如我们要查看刚刚创建的pod的日志(其实是容器的日志),只需要在master节点运行

[root@k8s-master ~]# kubectl logs mypod
2021-12-15 22:57:56 Listening on port 8080

这里的2021-12-15 22:57:56 Listening on port 8080就是容器运行的日志,它提示我们这个容器正在监听8080端口,我们来验证一下。首先查看pod运行在哪个节点,以及它的ip

[root@k8s-master ~]# kubectl get pod -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP           NODE         NOMINATED NODE   READINESS GATES
mypod   1/1     Running   0          27m   10.244.2.3   k8s-node-1   <none>           <none>

可以看到pod运行在node-1节点,接下来尝试访问这个ip

curl 10.244.2.3:8080

结果如下
image

由于我们的pod内只有一个容器,所以不需要指定即可查看日志。如果pod内运行了多个容器,我们需要使用-c指定容器名称查看对应容器的日志:

kubectl logs pod名称 -c 容器名称

需要注意:我们只能获取当前存在的pod日志,当一个pod被删除时,对应的logs也会被删除。如果想要将日志信息持久化,需要进行额外配置,这里暂时不深入研究。

5 pod阶段

pod的创建起始于 Pending 阶段,如果至少其中有一个主要容器正常启动,则进入 Running,之后取决于 Pod 中是否有容器以 失败状态结束而进入 Succeeded 或者 Failed 阶段。可以看出pod和容器一样都是临时的实体,一个生命周期结束后,pod就死亡了。任何pod一旦完成分配,就不会再被重新调度到其他节点,如果pod死亡,k8s会删除它然后重新创建一个。

pod的status字段是一个PodStatus对象,其中包含一个 phase 字段。

下面是phase可能的值:

取值 描述
Pending(悬决) Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间,
Running(运行中) Pod 已经绑定到了某个节点,Pod 中所有的容器都已被创建。至少有一个容器仍在运行,或者正处于启动或重启状态。
Succeeded(成功) Pod 中的所有容器都已成功终止,并且不会再重启。
Failed(失败) Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止。
Unknown(未知) 因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。

如果某节点死掉或者与集群中其他节点失联,Kubernetes会实施一种策略,将失去的节点上运行的所有 Pod 的phase设置为 Failed

6 pod的生命周期

我们一般将pod对象从创建至终止这段时间范围称为pod的生命周期,它主要包含以下的过程:
image

下面来分别看看生命周期中的不同阶段pod做了什么。

6.0 基础容器

其实在init c容器创建之前,首先会创建一个基础容器运行着/pause命令,它的目的是保存所有的名称空间,后创建的容器都使用这个名称空间,以及网络和数据卷初始化。

6.1 初始化容器

基础容器创建好后,开始创建初始化容器(init c)。初始化容器是在pod的主容器(main container)启动之前要运行的容器,主要是做一些主容器的前置工作,它具有以下特征:

  1. 初始化容器必须运行完成直至结束,若某初始化容器运行失败,那么k8s需要重启它直到成功完成
  2. 初始化容器必须按照定义的顺序执行,当仅当前一个执行成功之后,后面的init才能运行。如果某容器因为容器运行时的原因无法启动,或以错误状态退出,kubelet 会根据 Pod 的 restartPolicy 策略进行重试。 然而,如果 Pod 的 restartPolicy 设置为 "Always",Init 容器失败时会使用 restartPolicy 的 "OnFailure" 策略。
  3. 在所有的 Init 容器没有成功之前,Pod 将不会变成 Ready 状态。
  4. 如果 Pod 重启,所有 Init 容器必须重新执行。

因为 Init 容器具有与应用容器分离的单独镜像,其启动相关代码具有如下优势:

  • Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码。例如,没有必要仅为了在安装过程中使用类似 sedawkpythondig 这样的工具而去 FROM 一个镜像来生成一个新的镜像。
  • Init 容器可以安全地运行这些工具,在init容器中使用,而不是在主容器中使用,以避免这些工具导致应用镜像的安全性降低。
  • 主容器启动时可能需要前置代码,构建这些代码的工具只在开始时运行,因此可以分离到init c中,没必要在主容器一直运行。
  • Init 容器能以不同于 Pod 内应用容器的文件系统视图运行。因此,Init 容器可以访问应用容器不能访问的 Secret 的权限。(可以类比为init创建时sudo提权,而创建完运行时没有权限访问隐私内容)
  • 由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器 提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。 一旦前置条件满足,Pod 内的所有的应用容器会并行启动。

下面的例子定义了一个具有 2 个 Init 容器的简单 Pod。 第一个等待 myservice 启动, 第二个等待 mydb 启动。 一旦这两个Init容器都启动完成,Pod 将启动 spec 中的应用容器。

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers: # 定义初始化容器
  - name: init-myservice
    image: busybox
    command: ['sh', '-c', "until nslookup myservice.; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox
    command: ['sh', '-c', "until nslookup mydb; do echo waiting for mydb; sleep 2; done"]

你通过运行下面的命令启动 Pod:

kubectl apply -f myapp.yaml

输出类似于:

pod/myapp-pod created

使用下面的命令检查其状态:

kubectl get -f myapp.yaml

输出类似于:

NAME        READY     STATUS     RESTARTS   AGE
myapp-pod   0/1       Init:0/2   0          6m

由于没有启动这两个服务,Init 容器将会等待至发现名称为 mydbmyservice 的 Service。如下为创建这些 Service 的配置文件:

---
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377

创建 mydbmyservice 服务的命令:

kubectl create -f services.yaml

这样你将能看到这些 Init 容器执行完毕。随后 my-app 的 Pod 进入 Running 状态:

NAME        READY     STATUS    RESTARTS   AGE
myapp-pod   1/1       Running   0          9m

6.2 PostStart和Pre Stop

post start是在主容器被创建之后立即被执行的回调钩子。

pre stop是在容器被终止之前的回调钩子,在用来停止容器的 TERM 信号被发出之前,回调必须执行结束。 Pod 的终止宽限周期在 PreStop回调被执行之前即开始计数,所以无论回调函数的执行结果如何,容器最终都会在 Pod 的终止宽限期内被终止。没有参数会被传递给处理程序。

下面的例子展示了资源清单同时使用postStart 和 preStop :

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]

在上述配置文件中,你可以看到 postStart 命令在容器的 /usr/share 目录下写入文件 message。 命令 preStop 负责优雅地终止 nginx 服务。当因为失效而导致容器终止时,这一处理方式很有用。

6.3 探针

探针是由 kubelet 对容器执行的定期诊断。要执行诊断,kubelet 调用由容器实现的 Handler (处理程序)。有三种类型的处理程序:

  • ExecAction: 在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功,其他任何返回码都认为失败。
  • TCPSocketAction: 对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开能被连接,则诊断被认为是成功的,否则失败。
  • HTTPGetAction: 对容器的 IP 地址上指定端口和路径执行 HTTP Get 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。

每次探测都将获得以下三种结果之一:

  • Success(成功):容器通过了诊断。
  • Failure(失败):容器未通过诊断。
  • Unknown(未知):诊断失败,因此不会采取任何行动。

针对运行中的容器,kubelet可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:

  • livenessProbe(存活检测):指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定动作。如果容器不提供存活探针, 则默认状态为 Success
  • readinessProbe(就绪检测):指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success

下面的例子展示了资源清单同时使用就绪和存活探测器:

apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: test
    image: k8s.gcr.io/busybox
    # 就绪探测
    # kubelet 会在容器启动 5 秒后发送第一个就绪探测。 这会尝试连接 busybox 容器的 8080 端口。 如果探测成功,这个 Pod 会被标记为就绪状态,kubelet 将继续每隔 10 秒运行一次检测。
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5 # initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 5 秒
      periodSeconds: 10 # periodSeconds 字段指定了 kubelet 应该每 10 秒执行一次探测
    # 存活探测
    # kubelet 会在容器启动 5 秒后进行第一次存活探测, kubelet 会向容器内运行的服务80端口发送一个 HTTP GET 请求来执行探测。 如果服务器上 /index.html 响应的状态码大于等于 200 且小于 400,则 kubelet 认为容器是健康存活的。 否则 kubelet 会杀死这个容器并且重新启动它。
    livenessProbe:
      httpGet:
        port: 80
        path: /index.html
      initialDelaySeconds: 5 # initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 5 秒。
      periodSeconds: 5 # periodSeconds 字段指定了 kubelet 每隔 5 秒执行一次存活探测

探针有很多配置字段,可以使用这些字段精确的控制存活和就绪检测的行为:

  • initialDelaySeconds:容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认是 0 秒,最小值是 0。
  • periodSeconds:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。
  • timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。
  • successThreshold:探测器在失败后,被视为成功的最小连续成功数。默认值是 1。 存活和启动探测的这个值必须是 1。最小值是 1。
  • failureThreshold:当探测失败时,Kubernetes 的重试次数。 存活探测情况下的放弃就意味着重新启动容器。 就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。

6.4 pod的终止

关于pod的终止详情,请查阅官方文档

7 通过标签组织pod

7.1 介绍标签

标签是一种组织pod和其他k8s资源的方式。标签是可以附加到资源的任意键值对,一个资源可以拥有多个标签(当然标签的key不能重复)。一般在创建资源时会附加一个标签,在之后也可以再添加其他标签,或者修改现有标签的值。

举个例子,通过给pod添加标签,可以得到一个更组织化的系统,以便我们理解。每个pod都标有两个标签:

  • app,它指定pod属于哪个应用、组件或微服务。
  • rel,它显示在pod中运行的应用程序版本是stable(稳定版)、beta(测试版)还是canary(尝鲜体验版)。

如下图所示, 通过添加这两个标签基本上可以将pod组织为两个维度(基于应用的横向维度和基于版本的纵向维度)。
image

7.2 创建pod时添加标签

有如下资源清单:

apiVersion: vl
kind: Pod
metadata:
  name: kubia-manual-v2
  labels:  # 创建两个标签
    creation_method: manual
    env: prod
spec:
  containers:
  - image: luksa/kubia
    name: kubia
    ports:
  - containerPort: 8080
    protocol: TCP

根据这个清单创建的pod,就会有两个标签。

kubectl get pod --show-labels
# 返回结果
NAME             READY   STATUS    RESTARTS   AGE         LABLES
kubia-manual-v2   1/1   Running       0       3m   creation_method=manual,env=prod

如果你只对某些标签感兴趣, 可以使用-L选项指定它们并将它们分别显示在自己的列中:

kubectl get pod -L creation_method,env
# 返回结果
NAME             READY   STATUS    RESTARTS   AGE    CREATION METHOD     ENV
kubia-manual-v2   1/1   Running       0       3m         manual          prod

7.3 修改现有pod的标签

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

kubia-manual-v2上的env =prod标签更改为env=debug

kubectl label pod kubia-manual-v2 env=debug --overwrite
# 在更改现有标签时, 需要使用--overwrite选项

也可以为它添加一个标签:

kubectl label pod kubia-manual-v2 ver=v1
# 添加一个没有的标签时可以不用--overwrite

再次查看:

kubectl get pod -L creation_method,env,ver
# 返回结果
NAME             READY   STATUS    RESTARTS   AGE    CREATION METHOD     ENV     VER
kubia-manual-v2   1/1   Running       0       7m         manual          debug    v1

7.4 通过标签选择器来筛选资源

我们一旦定义好标签,就相当于为这个资源做了分类。接下来就可以通过标签选择器筛选出特定标签的资源。

标签选择器根据资源的以下条件来选择资源:

  • 包含(或不包含)使用特定键的标签
  • 包含具有特定键和值的标签
  • 包含具有特定键的标签, 但其值与我们指定的不同

举例:

kubectl get pod -l creation_method=manual  # 筛选出creation_method标签值为manual的所有pod

kubectl get pod -l env   # 只有具有env标签,就把这些pod筛选出来

kubectl get pod -l '!env'   # 筛选出没有env标签的pod,注意要用单引号括起来转义,因为bash中感叹号!有特殊含义

kubectl get pod -l 'creation_method=!manual' # 筛选出creation_method标签值不为manual的所有pod

kubectl get pod -l 'env in (prod,debug)' # 筛选出具有env标签,且env标签值为prod或者debug的pod

kubectl get pod -l 'env notin (prod,debug)' # 筛选出具有env标签,且env标签值不为prod或者debug的pod

kubectl get pod -l creation_method=manual,env=prod # 通过逗号,使用多个条件筛选,与逻辑,只有全都匹配成功才算成功

8 名称空间

标签可以将pod或其他资源组织成组,但是每个对象可以有多个标签,所以这些组内的资源可能是重叠的。而且,如果没有指定标签,我们kubectl get时会返回所有的对象。

我们可以使用名称空间(命名空间)对资源进行分组。这种分组方式可以将对象分割成完全独立且不重叠的组,你操作时,只在一个名称空间下操作,其他名称空间的资源是看不到的。

首先, 查看一下集群中的所有名称空间

kubectl get namespaces
kubectl get ns # 简写

返回如下结果:

NAME              STATUS   AGE
default           Active   2d
kube-node-lease   Active   2d
kube-public       Active   2d
kube-system       Active   2d

我们之前只在default命名空间中进行操作。当使用kubectl get命令列出资源时,我们从未明确指定命名空间,因此kubectl总是默认为
default命名空间,只显示该命名空间下的对象。但从列表中我们可以看到还存在其他的命名空间,接下来我们看看这些命名空间下都有什么资源:

kubectl get pod --namespace kube-system  # 可以使用 -n 代替 --namespace
# 返回结果如下
NAME                                 READY   STATUS    RESTARTS   AGE
coredns-5c98db65d4-dl6fz             1/1     Running   4          2d
coredns-5c98db65d4-lwv77             1/1     Running   4          2d
etcd-k8s-master                      1/1     Running   3          2d
kube-apiserver-k8s-master            1/1     Running   3          2d
kube-controller-manager-k8s-master   1/1     Running   4          2d
kube-flannel-ds-h2bvx                1/1     Running   3          46h
kube-flannel-ds-ptqd6                1/1     Running   3          47h
kube-flannel-ds-tp584                1/1     Running   3          46h
kube-proxy-hd68r                     1/1     Running   3          46h
kube-proxy-lqslr                     1/1     Running   3          2d
kube-proxy-q2xld                     1/1     Running   3          46h
kube-scheduler-k8s-master            1/1     Running   5          2d

顾名思义,kube-system名称空间下运行着一些与k8s系统运行有关的资源。多亏有了名称空间!不然的话,如果它们都在默认的命名空间中,同时与我们自己创建的资源混合在一起,那么我们很难区分这些资源属于哪里,并且也可能会无意中删除一些系统资源。

就像是c++中的namespace一样,k8s的名称空间也起着类似的效果,它隔离一组独立的资源集合,形成了一个作用域。除了隔离资源,命名空间还可用于仅允许某些用户访问某些特定资源,甚至限制单个用户可用的计算资源数量。

8.1 创建名称空间

命名空间是一种和其他资源一样的Kubernetes资源,因此可以通过将yaml文件提交到Kubernetes API服务器来创建该资源:

# custom-namespace.yaml
apiVersion: v1
kind: Namespace  # 之前我们创建的资源是Pod,实际上还有很多资源,比如这里的Namespace
metadata:
  name: custom-namespace  # 名称空间的名字

接下来将它提交到Kubernetes API Sever:

[root@k8s-master ~]# kubectl apply -f custom-namespace.yaml
namespace/custom-namespace created

虽然写出上面这样的文件并不困难,但这仍然是一件麻烦事。k8s提供了一个专用命令创建名称空间:

kubectl create namespace custom-namespace-2

这比编写创建文件快得多。此时再次查看会发现多个两个名称空间:
image

8.2 管理其他名称空间中的对象

如果想要在刚创建的名称空间中创建资源,可以选择在metadata宇段中添加一个namespace : custom-namespace属性,也可以用kubectl create/apply命令创建资源时指定命名空间:

kubectl create -f createpod.yaml -n custom-namespace

此时我们有两个同名的pod (mypod) 。一个在default命名空间中,另一个在custom-namespace中。
image

当你需要管理其他名称空间的对象时,需要-n指定,否则默认是default名称空间。

8.3 删除名称空间

删除命令如下:

kubectl delete namespace 名称空间的名字

9 停止和删除pod

到目前为止,我们己经创建了一些正在运行的pod,这些pod在不同的名称空间中,接下来我们尝试停止它们。

9.1 按名称删除pod

按名称删除mypod:

kubectl delete pod mypod

在删除pod的过程中,实际上我们在指示k8s终止该pod中的所有容器。k8s向进程发送一个SIGTERM信号并等待一定的秒数(默认30)使其正常关闭。如果超过这个时间,则通过SIGKILL终止该进程。

可以通过空格删除多个pod:

kubectl delete pod mypod1 mypod2 mypod3

9.2 使用标签选择器删除pod

我们还可以使用标签选择器来删除pod,为了演示效果,这里创建了两个pod,它们都有标签envcreation_method
image

kubectl delete pod -l env=prod  # 删除标签值为prod的所有pod

image

如果你的项目有成百上千个pod,使用标签删除一组pod比按名称一个一个删除快速得多。

9.3 通过删除整个命名空间来删除pod

我们之前只删除了默认名称空间的pod,但custom-namespace这个名称空间下还有pod,如果你不需要这些pod,也不需要这个名称空间,那就可以这样:

kubectl delete ns custom-namespace

9.4 删除名称空间中的所有pod但保留名称空间

如果想删除所有pod但保留名称空间,可以这样:

kubectl delete pod -n custom-namespace --all

9.5 删除名称空间中的所有资源

通过使用单个命令删除当前命名空间中的所有资源:

kubectl delete all --all

命令中的第一个all指定正在删除所有资源类型, 而--all选项指定将删除所有资源实例而不是按名称指定它们。

注意,使用all关键字删除所有内容并不是真的完全删除所有内容,一些资源会被保留下来(比如Secret)并且需要被明确指定删除。

另外,这个命令也会删除名为kubernetes的Service,但它应该会在几分钟后自动重新创建。

10 在运行的容器中远程执行命令

可以使用kubectl exec命令远程地在一个已经存在的pod容器上执行任何命令。这样就可以很方便地了解pod的内容、状态及环境。

命令如下:

kubectl exec POD [-c CONTAINER] -- COMMAND [args...]

选项说明:

  • POD:任意一个已经运行的pod名称,你可以用kubectl get pod命令列出所有的pod,并且选择其中一个作为exec命令的执行目标。
  • -c CONTAINER:可选参数,如果pod内运行多个容器,需要指定在哪个容器内执行命令
  • -- COMMAND [args...]:双横杠代表着kubectl命令项的结束,在两个横杠之后的内容是指在pod内部需要执行的命令。
  • -it:控制台输入发送到容器,并将标准输入控制台作为容器的控制台输入。

举例:

kubectl exec kubia-7nogl -- curl http://10.111.249.153  # 运行名为kubia-7nogl内的容器 执行curl命令
kubectl exec -it kubia-3inly bash # 运行名为kubia-3inly的pod内的容器,打开一个bash窗口用于在容器内执行命令
posted @ 2021-12-19 07:51  yyyz  阅读(395)  评论(0编辑  收藏  举报