深入解读核心资源Pod

发布时间 2023-07-04 22:34:15作者: 休耕

一、Pod 概念

Pod是Kubernetes中最小的调度单位,是一组容器的集合,Pod中的容器共享网络和存储资源,Pod中的容器可以通过localhost进行通信,Pod中的容器可以通过共享的Volume进行数据共享。

k8s 是通过定义一个 Pod 的资源,然后再 Pod 里面运行容器,容器需要指定一个镜像,这样就可以用来运行具体的服务。

pod架构

Pod 需要调度到k8s集群的工作节点来运行,具体的调度过程是由 kube-scheduler 组件来完成的,kube-scheduler 会根据 Pod 的资源需求和节点的资源容量,选择一个合适的节点来运行 Pod。

1、Pod 如何管理多个容器

Pod 中可以同时运行多个容器。

  • 同一个Pod中的容器会自动分配到同一个工作节点上
  • 同一个Pod中的容器共享资源、网络环境
  • 同一个Pod中的容器可以通过localhost进行通信
  • 同一个Pod中的容器总是被同时调度

只有当你的容器需要紧密配合协作的时候才考虑用这种模式。如果你的容器需要独立扩展,或者你的容器需要被不同的团队管理,那么你应该把它们放到不同的 Pod 中。

一些 Pod 有 init 容器和应用容器,init 容器会先于应用容器启动,init 容器会一直运行直到它们成功完成为止。如果 init 容器失败,Pod 会被重新启动,直到 init 容器成功为止。

2、Pod 网络

Pod 是有 IP 地址的,每个 pod 都被分配唯一的 IP 地址(IP地址靠网络插件calico、flannel、canal、weave等实现),Pod 中的容器共享网络命名空间,包括IP地址和网络端口。

  • Pod 内部的容器可以通过 localhost 相互通信。
  • Pod 中的容器也可以通过网络插件calico 与其他节点上的 Pod 进行通信。

3、Pod 存储

创建 Pod 的时候可以指定挂载的存储卷。

  • Pod 中的所有容器都可以访问这个存储卷,允许容器之间共享数据
  • 这个存储卷的生命周期与 Pod 的生命周期相同
  • Pod 只要挂载持久化数据卷,重启后数据不会丢失
  • Pod 可以挂载多个存储卷

4、Pod 工作方式

在K8S中,所有的资源都可以使用一个 yaml 文件来创建,Pod 也不例外,下面是一个 Pod 的 yaml 文件示例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx-container
    image: nginx
    ports:
    - containerPort: 80

或者使用 kubectl run 命令来创建 Pod:

kubectl run nginx-pod --image=nginx --restart=Never --port=80

Pod 的 yaml 文件中,有几个重要的字段:

  • apiVersion:指定 Pod 的 API 版本,一般都是 v1
  • kind:指定资源类型,Pod 的类型是 Pod
  • metadata:指定 Pod 的元数据,包括 Pod 的名称、标签等
  • spec:指定 Pod 的规格,包括 Pod 中的容器、存储卷等

Pod 的 yaml 文件中,spec 字段是最重要的,它包含了 Pod 中的容器、存储卷等信息,下面是 spec 字段的详细介绍:

spec:
  containers: # 容器列表
  - name: nginx-container # 容器名称
    image: nginx # 容器镜像
    ports: # 容器端口列表
    - containerPort: 80 # 容器端口
  restartPolicy: Never # Pod 重启策略

Pod 的 yaml 文件中,spec 字段中的 containers 字段是一个容器列表,可以在一个 Pod 中运行多个容器,这些容器会被同时调度到同一个工作节点上。

Pod 的 yaml 文件中,spec 字段中的 restartPolicy 字段是 Pod 的重启策略,它有三个取值:

  • Always:Pod 一旦终止就立即重启,用于部署应用
  • OnFailure:Pod 只有在非零退出码时才重启,用于批处理任务
  • Never:Pod 不重启,用于静态 Web 网站

Pod 的 yaml 文件中,spec 字段中的 nodeName 字段是 Pod 的调度节点,如果指定了 nodeName 字段,那么 Pod 就会被调度到指定的节点上,如果没有指定 nodeName 字段,那么 Pod 就会被调度到一个合适的节点上。

(1)自主式 Pod

自主式 Pod 是指没有被任何控制器管理的 Pod,它的生命周期由用户手动管理,一般用于测试和开发环境。示例如下:

[root@master tomcat-test]# vim pod-tomcat.yaml
apiVersion: v1
kind: Pod
metadata:
  name: tomcat-test
  namespace: default
  labels:
    app: tomcat
spec:
  containers:
  - name: tomcat-java
    ports:
    - containerPort: 8080
    image: xianchao/tomcat-8.5-jre8:v1
    imagePullPolicy: IfNotPresent

# 导入镜像,且发送文件给node
[root@master tomcat-test]# scp xianchao-tomcat.tar.gz root@10.104.43.118:/root/
# 将镜像导入到本地
[root@master tomcat-test]# docker load -i xianchao-tomcat.tar.gz
[root@node ~]# docker load -i xianchao-tomcat.tar.gz
df64d3292fd6: Loading layer [==================================================>]  4.672MB/4.672MB
0c3170905795: Loading layer [==================================================>]  3.584kB/3.584kB
9bca1faaa73e: Loading layer [==================================================>]  79.44MB/79.44MB
e927085edc33: Loading layer [==================================================>]   2.56kB/2.56kB
e5f8376fd9dc: Loading layer [==================================================>]  27.08MB/27.08MB
e82a3681bb38: Loading layer [==================================================>]  2.048kB/2.048kB
Loaded image: xianchao/tomcat-8.5-jre8:v1

# 查看镜像
[root@master tomcat-test]# docker images 
REPOSITORY                          TAG            IMAGE ID       CREATED         SIZE
xianchao/tomcat-8.5-jre8            v1             4ac473a3dd92   4 years ago     108MB

# 更新资源清单文件
[root@master tomcat-test]# kubectl apply -f pod-tomcat.yaml
pod/tomcat-test created

# 查看 Pod 是否创建成功
[root@master tomcat-test]# kubectl get pods -o wide -l app=tomcat
NAME          READY   STATUS        IP          NODE   
tomcat-test   1/1     Running     10.244.1.13   node   
# 查看 Pod 详情
[root@master tomcat-test]# kubectl describe -f pod-tomcat.yaml

# 注意:
# 自主式Pod是可以删除的,而且一旦被删除是彻底删除。
# 这在生产环境具有非常大风险,因此要应该选择使用控制器管理。
[root@master tomcat-test]# kubectl delete -f pod-tomcat.yaml 
pod "tomcat-test" deleted
[root@master tomcat-test]# kubectl get pods -o wide -l app=tomcat
No resources found in default namespace.

(2)控制器管理的 Pod

常见的管理 Pod 的控制器:Replicaset、Deployment、Job、CronJob、Daemonset、Statefulset。

控制器管理的 Pod 可以确保 Pod 始终维持在指定的副本数运行。通过 Deployment 管理 pod 案例:

# 上传xianchao-nginx.tar.gz 上传到node节点
[root@node ~]# ls
anaconda-ks.cfg  install.sh  original-ks.cfg  xianchao-nginx.tar.gz

# 创建资源清单文件
[root@master ~]# mkdir nginx-test && cd nginx-test
[root@master nginx-test]# vi nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test
  labels:
    app: nginx-deploy
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

# 更新资源清单文件
[root@master nginx-test]# kubectl apply -f nginx-deploy.yaml 
deployment.apps/nginx-test created

# 查看deployment
[root@master nginx-test]# kubectl get deploy -l app=nginx-deploy
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
nginx-test   2/2     2            2           87s

# 查看replicaset
[root@master nginx-test]# kubectl get rs -l app=nginx
NAME                    DESIRED   CURRENT   READY   AGE
nginx-test-854985cc6f   2         2         2       2m11s

# 查看pod
[root@master nginx-test]# kubectl get pods -o wide -l app=nginx
NAME                          READY   STATUS    RESTARTS   AGE     IP             NODE     NOMINATED NODE   READINESS GATES
nginx-test-854985cc6f-9qnnj   1/1     Running   0          2m56s   10.244.1.18    node     <none>           <none>
nginx-test-854985cc6f-fw55w   1/1     Running   0          2m56s   10.244.0.250   master   <none>           <none>

# 查看deployment详情
[root@master nginx-test]# kubectl describe deploy nginx-test

# 删除nginx-test-854985cc6f-9qnnj这个Pod
[root@master nginx-test]# kubectl delete pod nginx-test-854985cc6f-9qnnj
pod "nginx-test-854985cc6f-9qnnj" deleted
# 发现重新创建了一个新的pod
[root@master nginx-test]# kubectl get pods -o wide -l app=nginx
NAME                          READY   STATUS    RESTARTS   AGE     IP             NODE     NOMINATED NODE   READINESS GATES
nginx-test-854985cc6f-dfm5b   1/1     Running   0          6s      10.244.1.19    node     <none>           <none>
nginx-test-854985cc6f-fw55w   1/1     Running   0          4m10s   10.244.0.250   master   <none>           <none>

由此可见,通过 deployment 管理的 pod,当 pod 被删除后,会自动创建一个新的 pod,保证 pod 的副本数始终维持在指定副本数量。

二、如何创建一个Pod资源

k8s创建pod流程:
k8s创建pod流程

Pod 是 Kubernetes 中最基本的部署调度单位,可以包含 container,逻辑上表示某种应用的一个实例。如:一个web站点应用由前端、后端、数据库构建而成,这个站点的所有容器可以放在一个 Pod 中,也可以将前端、后端、数据库分别放在不同的 Pod 中,这取决于用户的实际需求。

1、创建 pod 流程

创建 pod 流程

第一步:用户通过 kubectl 命令或者 API Server 发送创建 Pod 的请求。
第二步:apiserver 接收到请求后,将 yaml 文件转换成 json 格式,然后将 json 格式的 Pod 对象存储到 etcd 中。
第三步:apiserver 将 Pod 对象转发给控制器管理器。调度器使用调度算法为 Pod 选择合适的 Node 节点,将node信息给apiserver,apiserver将node信息存储到etcd中。

  • 调度器的调度算法:
    • 1、过滤器:过滤掉不符合要求的节点,如:资源不足、污点、亲和性等。
    • 2、优选器:对符合要求的节点进行打分,选择分数最高的节点。
      第四步:apiserver 通过 watch 机制,调用 kebelet,指定 pod 信息,调用 docker api 在 node 节点上创建 pod。
      第五步:创建完成后反馈给 kubelet,kubelet 又将pod的状态信息反馈给 apiserver,apiserver 将 pod 的状态信息存储到 etcd 中。

2、资源清单yaml文件书写技巧

yaml文件书写技巧:

  • 1、yaml文件中的空格不能随意添加,否则会报错。
  • 2、yaml文件中的缩进必须是两个空格,不能是tab键。
  • 3、yaml文件中的注释必须是#号开头。
  • 4、yaml文件中的字符串必须加上引号,否则会报错。
  • 5、yaml文件中的字符串如果有特殊字符,必须加上单引号或者双引号,否则会报错。
  • 6、yaml文件中的字符串如果有多行,必须使用|或者>,否则会报错。

编写yaml文件遇到字段含义不明确时,可以使用kubectl explain命令查看字段的含义。

(1)kubectl explain 命令

kubelet explain 命令可以查看资源清单文件中的字段的含义。

# kubelet explain 语法
[root@master ~]# kubectl explain -h
List the fields for supported resources.   # 列出支持的资源的字段

 This command describes the fields associated with each supported API resource. Fields are identified via a simple JSONPath identifier:  # 该命令描述与每个支持的API资源相关联的字段。字段通过简单的JSONPath标识符进行标识:

  <type>.<fieldName>[.<fieldName>]
  
 Add the --recursive flag to display all of the fields at once without descriptions. Information about each field is retrieved from the server in OpenAPI format.    # 添加--recursive标志以一次显示所有字段而无需描述。有关每个字段的信息以OpenAPI格式从服务器检索。

Use "kubectl api-resources" for a complete list of supported resources. # 使用“kubectl api-resources”获取支持的资源的完整列表。

Examples:
  # 获取资源及其字段的文档
  kubectl explain pods
  
  # 获取资源的特定字段的文档
  kubectl explain pods.spec.containers

Options:
    --api-version='': 获取特定API版本(API组/版本)的不同解释
    --recursive=false: 打印字段的字段(目前只有1级深)

查看pod的字段,示例如下所示:

[root@master ~]# kubectl explain pods
KIND:     Pod    # 资源类型
VERSION:  v1     # 资源版本

DESCRIPTION:     # 描述
    Pod是可以在主机上运行的容器集合。此资源由客户端创建并安排到主机上。

FIELDS:          # 字段
   apiVersion	<string>    # api版本
     APIVersion定义此对象的版本化模式。服务器应将已识别的模式转换为最新的内部值,并且可以拒绝未识别的值。

   kind	<string>          # 字符串类型的值,代表要创建的资源类型
     Kind是表示此对象表示的REST资源的字符串值.服务器可以从客户端提交请求的端点推断此值。不能更新。在CamelCase中。

   metadata	<Object>      # metadata是对象,定义元数据属性信息
     标准对象的元数据

   spec	<Object>          # 制定了定义pod的规格,里面包含容器的信息
     Pod的期望行为的规范。 

   status	<Object>        # 状态,不可修改,定义pod时不需要填写
     最近观察到的Pod的状态。这些数据可能不是最新的。状态字段,由系统填充,只读。

查看pod.metadata的字段,示例如下所示:

[root@master ~]# kubectl explain pods.metadata
KIND:     Pod        # 资源类型
VERSION:  v1         # 资源版本
RESOURCE: metadata <Object>   # 资源为 metadata 对象<object>
DESCRIPTION:
     标准对象的元数据
     ObjectMeta是所有持久化资源必须具有的元数据,其中包括用户必须创建的所有对象。
FIELDS:              # 字段
   annotations	<map[string]string>     # annotations是注解,map类型表示对应值是键值对
     注释是存储在资源中的非结构化键值映射,可以由外部工具设置以存储和检索任意元数据。它们不可查询,并且在修改对象时应该保留。

   creationTimestamp	<string>
     创建时间戳是表示创建此对象时的服务器时间的时间戳。不能保证在单独操作中以发生之前的顺序设置它。客户端可能不会设置此值。它以RFC3339形式表示,并且处于UTC中。可以设置为任何时间,但一旦设置,就不能更改。只读。

   deletionGracePeriodSeconds	<integer>
      此对象在从系统中删除之前允许优雅终止的秒数。仅在设置deletionTimestamp时设置。可能只缩短。只读。

   deletionTimestamp	<string>  # 删除时间戳
     删除时间戳是表示删除此对象时的服务器时间的时间戳。如果未设置,则此对象尚未删除。只读。

   finalizers	<[]string>   # finalizers是终结器,[]string表示对应值是字符串数组
     终结器是在删除对象时运行的一组非同步任务。在删除对象时,系统会阻止对该对象的修改,直到所有终结器都已完成。终结器可能会以任何顺序运行。系统不会保证它们将在删除操作完成之前运行,或者将在删除操作完成之后运行。终结器可能会在删除操作完成之前或之后失败。终结器是在删除操作完成之前运行的,因此它们可以在删除操作完成之前阻止对象的删除。失败的终结器可能会在下一次删除操作中重新运行。终结器通常用于将群集外部的资源与Kubernetes对象关联起来,以便在删除对象时清理这些资源。

   generateName	<string>
     生成名称是服务器使用的可选前缀,仅在未提供名称字段时生成唯一名称。如果使用此字段,则返回给客户端的名称将与传递的名称不同。此值还将与唯一后缀组合。提供的值具有与名称字段相同的验证规则,并且可能会被所需的后缀的长度截断,以使该值在服务器上唯一。  

   generation	<integer>
     表示所需状态的特定生成的序列号。由系统填充,只读。

   labels	<map[string]string>
     标签是一组键值对,用于组织和分类对象. 可以与复制控制器和服务的选择器匹配。

   managedFields	<[]Object>
     管理字段将工作流ID和版本映射到由该工作流管理的字段集。这主要用于内部事务处理,用户通常不需要设置或了解此字段。工作流可以是用户的名称,控制器的名称,或者特定应用程序路径的名称,例如“ci-cd”。字段集始终在工作流修改对象时使用的版本中。

   name	<string>
     名称必须在命名空间内唯一。在创建资源时是必需的,尽管某些资源可能允许客户端自动请求生成适当的名称。名称主要用于创建幂等性和配置定义。不能更新。

   namespace	<string>
     命名空间定义了每个名称必须唯一的空间。空命名空间等同于“默认”命名空间,但“默认”是规范表示。并非所有对象都必须限定于命名空间 - 这些对象的此字段的值将为空。

   ownerReferences	<[]Object>
     依赖的对象列表。如果列表中的所有对象都已删除,则将对此对象进行垃圾回收。如果此对象由控制器管理,则列表中的条目将指向此控制器,其中控制器字段设置为true。不能有多个管理控制器。

   resourceVersion	<string>
     资源版本,用于乐观锁,客户端不需要修改,只读

   selfLink	<string>
     selfLink是一个遗留的只读字段,不再由系统填充。

   uid	<string>
     UID是此对象的时间和空间唯一值。通常由服务器在成功创建资源时生成,并且不允许在PUT操作上更改。客户端可以使用此值确定是否已替换服务器上的对象。不能更新。更多信息请参见类型元数据。

3、通过资源清单文件创建pod

[root@master tomcat-test]# vi pod-tomcat.yaml
apiVersion: v1
kind: Pod
metadata:
  name: tomcat-test
  namespace: default
  labels:
    app: tomcat-pod-first
spec:
  containers:
  - name: tomcat-first
    image: tomcat:8.5.51
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8080

# 更新资源清单文件
[root@master tomcat-test]# kubectl apply -f pod-tomcat.yaml

# 查看pod是否创建成功
[root@master tomcat-test]# kubectl get pods -o wide -l app=tomcat-pod-first

# 查看pod日志
[root@master tomcat-test]# kubectl logs tomcat-test

# 进入刚刚创建的Pod
[root@master tomcat-test]# kubectl exec -it tomcat-test /bin/bash

# 架设pod有多个容器时,可以指定容器名称
[root@master tomcat-test]# kubectl exec -it tomcat-test -c tomcat-first /bin/bash

4、通过命令行创建pod

# Kubectl run语法