pod详解

pod详解

  • 在生产环境中,使用的是pod的更加高级的对象,不建议直接使用pod

  • 应该使用的是pod的高级对象

1、pod和命名空间

  • pod是k8s中最小的,最基本的部署和调度单元

    • 一个pod里面封装了一个或者多个容器,相当于是一个进程

    • 一个pod多个容器,共享网络命名空间(共享ip和端口空间)

    • pod单独创建的话,是一个临时性的,删除就没有了,如果是控制器的话,删除pod,系统会再次创建一个pod

  • 默认创建的pod都是在default空间下的

  • 不同命名空间下的pod是隔离的,但是还是可以通过服务发现(svc)进行通信(服务名.命名空间.svc.cluster.local)

创建一个pod,里面运行nginx,httpd 2个容器,看是否能运行

[root@master01 ~]# cat web1.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: web
  name: web
spec:
  containers:
  - image: httpd
    name: h1  # h1容器
  - image: nginx 
    name: n1  # n1容器
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}


# 发现pod是error的状态
[root@master01 ~]# kubectl get pod 
NAME   READY   STATUS   RESTARTS     AGE
web    1/2     Error    1 (5s ago)   11s

# 查看日志
# 发现n1容器无法监听80端口,所以失败了
# -c 是选择一个容器进行查看(有多个容器的情况)

[root@master01 ~]# kubectl logs web -c n1
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2026/03/25 10:09:24 [emerg] 1#1: bind() to 0.0.0.0:80 failed (98: Address already in use)


  • 为什么会失败了?

    • 一个pod里面的多个容器都是共享这个网络命名空间的

    • 共享同一个pod的ip地址,可以进入2个容器,查看ip地址都是一样的

    • 同一个网络端口,不能绑定相同的端口

  • 可以这样理解

    • 每一个pod都相当于是一个主机

    • 在里面运行多个容器,会占用这个主机的端口,不能使用相同

  • default/pod1 和 default/pod2 2个pod是互不干扰的,2个都是独立的虚拟空间(抽象)

img

2、pod的创建过程

img

  • 用户提交创建pod请求

  • api-server处理用户请求,先存储pod数据到etcd中,写入成功,api-server将成功的信息返回给客户端

  • scheduler通过watch机制监听到有新的pod后,会对多个主机进行打分,选择一个节点调度到上面去

  • scheduler将成功的调度到节点的信息写回到api-server,api-server将其写入到etcd中进行永久存储

  • kubelet接管pod创建,调用容器运行时创建,运行在目标节点上的kubelet监听pod资源变化

  • 创建成功后kubelet返回信息给api-server,然后存入到etcd中

  • 就是这样反复的一个写入过程

3、pod的创建的方式2种

1、命令行创建

# kubectl run pod_name image=
[root@master01 ~]# kubectl run pod1 --image=nginx 
pod/pod1 created
[root@master01 ~]# kubectl get pod 
NAME   READY   STATUS    RESTARTS   AGE
pod1   1/1     Running   0          3s

# 会自动的注入一个标签
[root@master01 ~]# kubectl get pod --show-labels 
NAME   READY   STATUS    RESTARTS   AGE   LABELS
pod1   1/1     Running   0          13s   run=pod1

2、yaml文件创建

# 将配置信息输出到一个yaml文件里面
# --dry-run=client 不会向api server 发送请求,只是在客户端验证并输出结果,通常是生成配置模板
# 然后再次基础上面修改配置文件(不然一个一个手敲,太麻烦了)

[root@master01 ~]# kubectl run pod2 --image=nginx --image-pull-policy IfNotPresent --dry-run=client -o yaml > pod2.yml

[root@master01 ~]# cat pod2.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod2
  name: pod2
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod2
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

  • apply命令,非常好用,如果后续修改了yml文件,也可以使用apply,

  • 但是如果修改了一些pod的名字这些的话,使用apply就会报错,就需要删除,然后使用apply创建即可

4、yaml中的pod


[root@master01 ~]# cat pod2.yml 
apiVersion: v1  # 版本
kind: Pod  # 类型
metadata:  # pod元数据
  creationTimestamp: null
  labels:  # 标签
    run: pod2
  name: pod2  # pod的名字
spec:   # 描述信息
  containers:   # pod包含的容器
  - image: nginx   # 第一个容器使用的镜像,多个容器使用-
    imagePullPolicy: IfNotPresent  # 镜像拉取策略
    name: pod2  # 容器的名字
    resources: {}
  dnsPolicy: ClusterFirst  # dns策略
  restartPolicy: Always  # pod重启策略
status: {}

5、镜像的拉取策略

  • Always 每次都会联网,从网上下载最新的镜像,不管本地有没有镜像,每次找的都是latest

  • Never 从来不联网,在本地寻找镜像

  • IfNotPresent 先从本地进行拉取,如果没有的话,就联网去网上寻找

6、pod的基本操作

1、端口映射

  • 我们创建一个web容器,集群之间可以访问到内容,但是在集群外面就访问不到了

  • 因此的话我们就需要映射端口

1、hostPort

  • 直接映射到宿主机上面,hostPort
[root@master01 ~]# cat pod2.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod2
  name: pod2
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod2
    ports:
      - containerPort: 80  # 容器监听的端口
        hostPort: 8080  # 直接映射到被调度的节点上的8080端口
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

  • 客户端访问 节点ip+映射到节点的端口,换了一个节点的话,访问就换节点的ip

  • 上面这种很少用,不推荐使用,如果再次运行了一个pod,使用的也是hostPort的话,也是8080端口,那么就出现了端口占用,无法调度了

2、NodePort

  • 后面会有这个NodePort这种方式(最常见的),后面讲svc的时候,会详细说明的

    • 这个的话,就是任意节点的ip+映射的端口都能访问到

    • 映射到宿主机上面端口,所有节点都在监听,同意了入口

    • 高可用,某个节点宕机了,其他节点也可提供服务

3、hostnetwork

  • 直接使用宿主机的ip地址,调度在哪一个节点上,就是哪一个ip

  • kube-system空间下,pod就是这样的

[root@master01 ~]# cat pod2.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod2
  name: pod2
spec:
  hostNetwork: true
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod2
    resources: {}

# 发现ip为节点的ip
[root@master01 ~]# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP              NODE   NOMINATED NODE   READINESS GATES
pod2   1/1     Running   0          4s    192.168.50.22   node   <none>           <none>



2、环境变量

  • 为pod注入环境变量
[root@master01 ~]# cat m1.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: m1
  name: m1
spec:
  containers:
  - image: mysql
    name: m1
    ports:
      - containerPort: 3306
        hostPort: 3306
    env:
      - name: MYSQL_ROOT_PASSWORD   # root密码
        value: "123"
      - name: MYSQL_DATABASE  # 创建一个数据库
        value: "qwe"
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}


# 直接登录数据库
[root@master01 ~]# mysql -u root -p123 -h 192.168.50.22 -P3306

3、配置dns

  • dnsPolicy字段

    • default,继承 Pod 运行节点的 DNS 配置,也就是这个宿主机的dns

    • ClusterFirst: 优先使用集群 DNS(也就是coredns),失败则使用宿主机 DNS(默认)

    • none: 忽略集群 DNS,使用自定义 DNS 配置

    • clusterfirstwithhostnet 如果一个容器使用的ip是宿主机的ip,默认策略使用集群的dns

# 创建出来的pod默认使用的dns是ClusterFirst,也就是使用集群的dns
[root@master01 ~]# kubectl run b1 --image=busybox -- sleep 3600
pod/b1 created

/ # cat /etc/resolv.conf 
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

1、default策略


[root@master01 ~]# cat b2.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: b2
  name: b2
spec:
  containers:
  - image: busybox
    name: b2
    resources: {} 
    command: ["sleep","3600"]  # 数组的形式,"" 包含一个命令
  dnsPolicy: Default   # pod的dns策略

# 使用的是节点的dns
/ # cat /etc/resolv.conf 
nameserver 119.29.29.29

2、ClusterFirstWithHostNet

  • 使用宿主机的网络,dns策略就是集群的dns

[root@master01 ~]# cat b2.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: b2
  name: b2
spec:
  hostNetwork: true  # 使用宿主机网络
  containers:
  - image: busybox
    name: b2
    resources: {}
    command: ["sleep","3600"]
  dnsPolicy: ClusterFirstWithHostNet   # dns策略
  restartPolicy: Always


# 使用的是集群的dns
/ # cat /etc/resolv.conf 
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

3、None(自定义dns)


[root@master01 ~]# cat b2.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: b2
  name: b2
spec:
  containers:
  - image: busybox
    name: b2
    resources: {}
    command: ["sleep","3600"]
  dnsPolicy: None  # 自定义dns
  dnsConfig:  # 自定义dns配置
    nameservers:
    - 114.114.114.114
  restartPolicy: Always

/ # cat /etc/resolv.conf 
nameserver 114.114.114.114

4、重启策略

  • restartPolicy

    • Always: 容器退出后,总是重启

    • OnFailure: 容器异常退出时,重启,job,cronjob这些

    • Never:从不重启

5、pod的持久化存储

  • 就是创建了一个pod后,写入数据后,删除后里面的数据就没了,下次创建pod的时候,里面就没有数据了

  • 有了持久化目录后,再次创建容器指定这个目录,里面就有数据了

  • 后面会详细讲解持久化存储的

[root@master01 test]# cat b1.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: b1
  name: b1
spec:
  volumes:
  - name: timefile  # 注意挂载的名字需要一样,这样才能对应起来
    hostPath:
      path: /etc/localtime
  containers:
  - image: busybox
    name: b1
    resources: {}
    command: ["sleep","3600"]
    volumeMounts:
    - name: timefile  # 将节点上面的/etc/localtime挂载到容器里面
      readOnly: true
      mountPath: /etc/localtime
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

  • 但是了上面的持久化存储有一种缺陷,就是这个pod被删除的话,再次调度到其他节点上面,就没有这个文件,就需要自己创建出来

  • hostPath 适用的场景是每个节点都有这个文件

7、pod健康检查

  • 检查的是容器的状态,因此的话就是在containers字段里面

1、探针参数和方式

  • 三个探针基本上都是共用参数的

  • 是有kubelet发起的

initialDelaySeconds  # 容器启动后,延迟多少秒才开始探测, 这个作用就是为了先让容器启动一段时间,然后开始探测  默认值为0

periodSeconds  # 每隔多少秒后执行一次探测, 默认值为 10s

timeoutSeconds  # 每次探测的超时时间 就是执行一次探测后,没有反应的超时时间 默认值为 1s

successThreshold # 连续成功多少次,标记为成功, 默认值为 1

failureThreshold  # 连续失败多少次,标记为失败 默认值为 3次

  • 探测的方式有三种
  - httpget 发送其http Get 请求

  - tcpSocket 尝试建立tcp连接,成功则健康

  - exec 在容器内部执行命令,退出码为0成功,这个很少用的

2、Startup Probe

  • 判断容器内的应用是否已启动成功

  • 就是容器启动的阶段

  • 失败的话,重启容器,重启就相当于是杀掉这个容器

  • 这个优先级是最高的,执行期间会暂停禁用liveness和readiness探针

  • 应用启动慢的应用,适合这java应用,初始化时长的服务,避免因为启动慢被其他探针误杀

  • startupprobe 1.16出来的,启动阶段探测,探测失败,探测退出,成功则这个阶段结束

3、Liveness Probe(存活探针)

  • 判断容器是否存活,失败的话,重启容器,重启就相当于是杀掉容器

4、readinessProbe(就绪探针)

  • 判断容器是否就绪,能否接收外部的请求

  • 失败的话,则不能对外提供访问

5、探针案例

  • 一般在生产中三个探针都是结合使用的
[root@master01 test]# cat n1.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: n1
  name: n1
spec:
  restartPolicy: Always
  containers:
  - image: nginx
    name: n1
    ports:
      - containerPort: 80  # 容器监听的端口
    startupProbe:  # 启动探针,判断容器是否启动了
      periodSeconds: 5  # 每隔5秒开始探测
      timeoutSeconds: 1  # 超时时间为1s
      successThreshold: 1  # 成功1次就标记成功
      failureThreshold: 6  # 练习失败6次标记失败,过30s才算失败
      tcpSocket: # tcp连接的方式
        port: 80  # 与80进行连接
    readinessProbe:  # 就绪探针,判断容器是否能够接收流量
      periodSeconds: 2
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 3  # 连续失败3次,标记为失败
      httpGet:   # 发送请求探测
        path: /  # curl localhost:80/ 进行探测
        port: 80 
    livenessProbe:   # 存活探针
      periodSeconds: 2 
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 3
      httpGet:   # 也是发送http请求探测
        path: /
        port: 80


[root@master01 test]# kubectl get pod 
NAME   READY   STATUS    RESTARTS   AGE
n1     1/1     Running   0          10m

  • 启动探针失败的案例
# 修改监听的端口为8080

    startupProbe:  # 在运行启动探针的时候,其他探针都暂停,这个优先级是最高的
      periodSeconds: 5
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 6
      tcpSocket:
        port: 8080  # 8080端口  



# 查看pod详细信息,发现无法监听8080端口,启动探针失败了
  Warning  Unhealthy  1s (x4 over 16s)  kubelet            Startup probe failed: dial tcp 10.246.73.153:8080: connect: connection refused

# pod重启了,相当于是被kill了
[root@master01 ~]# kubectl get pod -w 
NAME   READY   STATUS    RESTARTS   AGE
n1     0/1     Running   0          7s

n1     0/1     Running   1 (4s ago)   39s
  • 就绪探针失败
# 修改为8080端口
    readinessProbe:
      periodSeconds: 2
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 3
      httpGet:
        path: /
        port: 8080

# 一直无法就绪,无法对外提供服务
[root@master01 test]# kubectl get pod 
NAME   READY   STATUS    RESTARTS   AGE
n1     0/1     Running   0          41s


# 查看pod详细信息,发现这个就绪探测失败了
  Warning  Unhealthy  1s (x4 over 3s)  kubelet            Readiness probe failed: Get "http://10.246.73.154:8080/": dial tcp 10.246.73.154:8080: connect: connection refused

  • 存活探针失败案例
    livenessProbe:
      periodSeconds: 2
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 3
      httpGet:
        path: /
        port: 8080


# 可以发现pod启动了,也是就绪的,但是了隔一段时间就会重启,这个就是存活探针
[root@master01 ~]# kubectl get pod -w 
NAME   READY   STATUS    RESTARTS   AGE
n1     1/1     Running   0          7s
n1     0/1     Running   1 (3s ago)   13s
n1     0/1     Running   1 (5s ago)   15s
n1     1/1     Running   1 (6s ago)   16s
n1     0/1     Running   2 (3s ago)   23s
n1     0/1     Running   2 (5s ago)   25s
n1     1/1     Running   2 (5s ago)   25s

# 查看pod详细信息

  Warning  Unhealthy  101s (x9 over 2m5s)   kubelet            Liveness probe failed: Get "http://10.246.73.155:8080/": dial tcp 10.246.73.155:8080: connect: connection refused

8、静态pod,就是一个配置文件

  • 在一个目录下面的文件,会自动的创建
# 静态pod目录,将yml文件拷贝过去,会自动的创建pod
[root@master01 manifests]# ls
etcd.yaml            kube-controller-manager.yaml
kube-apiserver.yaml  kube-scheduler.yaml
[root@master01 manifests]# pwd
/etc/kubernetes/manifests

# 拷贝到哪一个节点上面去,就会运行在这个节点上面,不走调度器
[root@master01 manifests]# cp /root/test/n1.yml .

[root@master01 manifests]# kubectl get pod -o wide
NAME          READY   STATUS              RESTARTS   AGE   IP       NODE       NOMINATED NODE   READINESS GATES
n1-master01   0/1     ContainerCreating   0          98s   <none>   master01   <none>           <none>

9、pod状态

  • pending 等待中, 镜像正在拉取

  • running 运行中

  • succeeded 成功,所有容器都已经终止了,且不会重启,正常退出

  • failed 失败,pod所有容器都终止了,且至少有一个非0状态退出

  • unknown 未知 pod状态无法被kubelet获取,通常是节点失联

  • Completed 常见于 Job 或 initContainer,表示容器正常结束(类似于 Succeeded)

  • CrashLoopBackOff 容器反复启动后崩溃,kubelet 在退避等待重启

10、初始化容器

  • initcontainers 有自己的生命周期,为主容器准备数据的,先于主容器执行

  • 启动容器是有顺序的,启动前做什么,启动后做什么,删除容器之前做什么

  • yml文件里面的,就已经从上而下的执行就已经定义了

  • 实验

    • 一个pod里面的多个容器,存储是不共享的,临时存储

    • 准备一个临时目录

    • 初始化容器将网页数据写好

    • 主容器挂载即可

[root@master01 test]# cat n2.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: n2
  name: n2
spec:
  volumes:
  - name: webdir  # pod的临时存储目录
    emptyDir: {}
  initContainers:
  - name: initcontiner
    image: busybox
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: webdir
      mountPath: /etc/html/  # 挂载到这个目录
    command:
    - /bin/sh
    - -c
    - echo "welcome nginx" >> /etc/html/index.html # 任务就是写入这个文件即可,写入了一个文件
  containers:
  - image: nginx
    name: n2
    resources: {}
    ports:
    - containerPort: 80
    volumeMounts:
    - name: webdir
      mountPath: /usr/share/nginx/html/  # 挂载到网页目录下面
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}


# 访问
[root@master01 test]# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE    IP              NODE   NOMINATED NODE   READINESS GATES
n2     1/1     Running   0          3m3s   10.246.73.159   node   <none>           <none>
[root@master01 test]# curl 10.246.73.159
welcome nginx

11、资源配额

  • 设置pod中的容器资源配额,cpu,内存

  • requests 是启动容器最少需要的

  • limits 限制容器,最多的限制

[root@master01 test]# cat b1.yml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: b1
  name: b1
spec:
  volumes:
  - name: timefile
    hostPath:
      path: /etc/localtime
  containers:
  - image: busybox
    name: b1
    resources: {}
    command: ["sleep","3600"]
    volumeMounts:
    - name: timefile
      readOnly: true
      mountPath: /etc/localtime
    resources:
      requests:
        cpu: "100m"  # 0.1个cpu核心
        memory: "12Mi"  # 12mb内存
      limits:
        cpu: "200m"
        memory: "24Mi"
  dnsPolicy: ClusterFirst
  restartPolicy: Always

13、pod和控制器的区别

  • 为什么不用pod呢?缺点

    • pod删除后,就彻底没有了,没有自愈的能力

    • 没有滚动更新,更新的话,需要手动更新

    • 没有扩缩容的能力

  • 控制器

    • 通过标签管理多个副本数量

    • 有自愈的能力,pod被删除后,会自动的在创建pod出来

    • 弹性伸缩

  • 一般在生产中用的是控制器

posted @ 2026-03-25 20:07  乔的港口  阅读(5)  评论(0)    收藏  举报