04.yaml和Kubernetes Pod精讲

2026-04-21

复习和预习

昨天课堂内容

  1. Node and Cluster
  2. Namespace and Contexts
  3. Pod Basic

课前复习

默写。

今天课堂内容

  1. yaml 格式
  2. Pod 管理
  3. Volume

yaml 格式

yaml格式只使用空格缩进,对于空格的数量没有强制要求,正常使用2个空格

基本规则:

  • 同一级别的元素,使用相同的缩进。
  • 对于子项目,使用比父项目更多的缩进。
  • 增加空白行,提高可读性。

yaml 示例

vim设置为粘贴模式(set paste),排版不会错乱。

playbook.yaml 内容如下:

yaml 复制代码
# yaml格式起始行,一般不省略
---

# Playbook中第一个play
# play具有属性:name,hosts,become,tasks,缩进一致
# name属性,用于简要描述play
- name: debploy WebSite
  
  # hosts属性,用于定义要在哪个受管理节点执行
  hosts: webs
  
  # tasks属性,用于描述play中任务,属性是列表格式
  tasks:
    
    # 第一个任务
    # 任务具有属性:涵name和模块名等。
    # name属性,用于简要描述任务
    - name: latest version of httpd and firewalld installed
      # 指明模块名,也就是要执行的任务
      yum:
        # 指定要操作的rpm包名称
        name:
          # rpm包名称是-开头的列表格式,或者逗号分隔的列表格式
          - httpd
          - firewalld
        
        # 定义软件包的状态,lastet代表升级为最新版本
        state: latest
    
    # 第二个任务
    - name: prepare index.html
      # copy 模块,用于将content属性值写入到目标文件
      copy:
        content: "Welcome to Laoma WebSite!\n"
        dest: /var/www/html/index.html
    
    # 第三个任务
    - name: enable and start httpd
      # service模块,用于启用并启动httpd服务
      service:
        name: httpd
        enabled: true
        state: started

    # 第四个任务
    - name: enable and start firewalld
      # service模块,用于启用并启动firewalld服务
      service:
        name: firewalld
        enabled: true
        state: started

    # 第五个任务
    - name: firewalld permits access to httpd service
      # firewalld,用于放行http服务
      firewalld:
        service: http
        permanent: true
        state: enabled
        immediate: yes

# Playbook中第二个play,-开头表示列表
- name: Test WebSite
  hosts: localhost
  become: no
  tasks:
    - name: connect to intranet web server
      # uri模块,用于测试网站是否可以访问
      uri:
        url: http://{{item}}
      loop:
        - node1
        - node2

# yaml格式结束行,一般省略
...

YAML 注释

在 YAML中, 编号或井号符号(#)右侧的所有内容都是注释。如果注释的左侧有内容, 请在该编号符号的前面加一个空格。注释可用于提高可读性。

示例:

yaml 复制代码
# This is YAML comment
Some data  # This is also a YAML comment

YAML 单行字符串

YAML中的字符串通常不需要放在引号里,即使字符串中包含空格。

字符串也可以用双引号或单引号括起。

yaml 复制代码
this is a string
'this is another string'
"this is yet another a string"

YAML 多行字符串

  • 可以使用竖线(I)字符表示,保留字符串中的换行字符。

    示例:

    yaml 复制代码
    ---
    - name: test string
      hosts: node1
      tasks:
        - name: test string
          # 用户显示变量值或者字符串
          debug:
            msg: |
              Example Company
              123 Main Street
              Atlanta, GA 30303
  • 也可以使用大于号(>)字符表示换行字符。执行时换行符使用空格代替,并且行内的引导空白将被删除。

    示例:

    yaml 复制代码
    ---
    - name: test string
      hosts: node1
      tasks:
        - name: test string
          # 用户显示变量值或者字符串
          debug:
            msg: >
              This is an example
              of a long string,
              that will become
              a single sentence once folded.

    这种方法通常用于将很长的字符串在空格字符处断行,使它们跨占多行来提高可读性。

YAML 字典

一组键值对的集合,又称为映射(mapping)和哈希(hashes)。

以缩进块的形式编写键值对集合,如下方所示:user属性是字典格式,是多个键值对集合。

yaml 复制代码
user:
  name: laoma 
  uid: 1088
  state: absent

字典也可以使用以花括号括起的内联块格式编写,如下方所示:

复制代码
user: {name: laoma, uid: 1088, state: absent}

大多数情形中应避免内联块格式,其可读性较差。不过,当playbook中包含角色列表时,使用这种语法,更加容易区分play中包含的角色和传递给角色的变量。

某些 playbook 可能使用较旧的简写(shorthand)格式,通过将模块的键值对放在与模块名称相同的行上来定义任务。

示例:

yaml 复制代码
- name: shorhand form
  user: name=laoma uid=1088 state=absent

普通格式:

yaml 复制代码
- name: shorhand form
  user:
    name: laoma 
    uid: 1088
    state: absent

两者格式总结:

  • 通常您应避免简写格式,而使用普通格式。
  • 普通格式的行数较多,更容易操作。任务的关键字垂直堆叠,更容易区分。 阅读play时,您的眼睛直接向下扫视,左右运动较少。
  • 普通格式是原生的YAML,现代文本编辑器中的语法突出显示工具可以识别,简写形式则不支持。
  • 可能会在文档和他人提供的旧playbook中看到这种语法,而且这种语法仍然可以发挥作用。

YAML 列表

一组按次序排列的值,又称为序列(sequence)和数组(array)。

以缩进块的形式编写的键值对集合,如下方所示:

yaml 复制代码
- name: latest version of httpd and firewalld installed
  yum:
    name:
      - httpd
      - firewalld
    state: latest
- name: test html page is installed
  copy:
    content: "Welcome to the example.com intranet!\n"
    dest: /var/www/html/index.html

以上有两个任务,每个任务都是多个键值对描述。其中yum模块操作的软件包是一个简单的名称列表。

内联格式:

复制代码
name: [httpd, firewalld]

尽量避免内联格式。

k8s中pod定义示例

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: wordpress
  name: wordpress
  namespace: pods
spec:
  containers:
  - image: wordpress
    imagePullPolicy: Always
    name: wordpress
  nodeName: worker31.laoma.cloud
  restartPolicy: Always
  schedulerName: default-scheduler
  serviceAccount: default
  serviceAccountName: default
  volumes:
  - name: kube-api-access
    projected:
      defaultMode: 420
      sources:

使用vimdiff工具编写并进行比较。

快捷键:切换窗口ctrl+w+w

全部保存退出::wqa

Kubernetes Pod

多个容器指定网络 --network host,容器跟宿主机公用网络。

nginx:使用宿主机80端口

mysql:使用宿主机3306端口

多个容器指定 -v /data:/data,容器跟宿主机公用网络。

环境准备

bash 复制代码
[root@master30 ~]# kubectl create ns pods
[root@master30 ~]# kubectl config set-context --current --namespace pods

kubectl api-resources

bash 复制代码
[root@master30 ~]# kubectl api-resources |egrep 'NAMESPACED|namespace|pod|nodes'
NAME                      SHORTNAMES   APIVERSION                  NAMESPACED   KIND
namespaces                ns           v1                          false        Namespace
nodes                     no           v1                          false        Node
pods                      po           v1                          true         Pod
podtemplates                           v1                          true         PodTemplate
horizontalpodautoscalers  hpa          autoscaling/v2              true         HorizontalPodAutoscaler
caliconodestatuses                     crd.projectcalico.org/v1    false        CalicoNodeStatus
poddisruptionbudgets      pdb          policy/v1                   true         PodDisruptionBudget
csinodes                               storage.k8s.io/v1           false        CSINode

多容器 pod

示例文件:blog.yaml

bash 复制代码
[root@master30 ~]# vim pod-blog.yaml
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: bbs
  labels:
    run: bbs
spec:
  containers:
  - image: mysql:latest
    imagePullPolicy: IfNotPresent
    name: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "123"
    - name: MYSQL_USER
      value: tom
    - name: MYSQL_PASSWORD
      value: "123"
    - name: MYSQL_DATABASE
      value: bbs
    ports:
    - containerPort: 3306
      name: mysql
      protocol: TCP
  - image: wordpress:latest
    imagePullPolicy: IfNotPresent
    name: wordpress
    env:
    - name: WORDPRESS_DB_USER
      value: tom
    - name: WORDPRESS_DB_PASSWORD
      value: "123"
    - name: WORDPRESS_DB_NAME
      value: bbs
    - name: WORDPRESS_DB_HOST
      value: "127.0.0.1"
    ports:
    - containerPort: 80
      name: wordpress
      protocol: TCP
      hostPort: 80
bash 复制代码
[root@master30 ~]# kubectl apply -f pod-blog.yaml

[root@master30 ~]# kubectl get pods -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP              NODE               NOMINATED NODE   READINESS GATES
bbs    2/2     Running   0          10m   10.224.225.68   worker32.laoma.cloud   <none>           <none>

# 多容器pod中执行命令,通过-c指定容器
[root@master30 ~]# kubectl exec bbs -c wordpress -- hostname
bbs
[root@master30 ~]# kubectl cp /etc/hosts bbs:/new-hosts -c wordpress
[root@master30 ~]# kubectl exec bbs -c wordpress -- ls /new-hosts
/new-hosts

# 创建 svc
[root@master30 ~]# kubectl expose pod bbs --type NodePort
[root@master30 ~]# kubectl get svc bbs
NAME   TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)                       AGE
bbs    NodePort   10.98.5.216   <none>        3306:32590/TCP,80:30576/TCP   118s

数据库访问测试

bash 复制代码
[root@master30 ~]# kubectl exec -it bbs -c mysql -- mysql -utom -p123
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

浏览器访问:http://10.1.8.30:30576/

pod 关键属性

bash 复制代码
[root@master30 ~]# kubectl explain pod | grep '^  [a-zA-Z]'
  apiVersion	<string>
  kind	<string>
  metadata	<ObjectMeta>
  spec	<PodSpec>
  status	<PodStatus>

pod.metadata

bash 复制代码
[root@master30 ~]# kubectl explain pod.metadata | grep '^  [a-zA-Z]'
   annotations	<map[string]string>
   clusterName	<string>
   creationTimestamp	<string>
   deletionGracePeriodSeconds	<integer>
   deletionTimestamp	<string>
   finalizers	<[]string>
   generateName	<string>
   generation	<integer>
   labels	<map[string]string>
   managedFields	<[]Object>
   name	<string>
   namespace	<string>
   ownerReferences	<[]Object>
   resourceVersion	<string>
   selfLink	<string>
   uid	<string>

需要关注的属性:labels,name,namespace。

pod.spec

bash 复制代码
[root@master30 ~]# kubectl explain pod.spec | grep '^  [a-zA-Z]'
   activeDeadlineSeconds	<integer>
   affinity	<Object>
   automountServiceAccountToken	<boolean>
   containers	<[]Object> -required-
   dnsConfig	<Object>
   dnsPolicy	<string>
   enableServiceLinks	<boolean>
   ephemeralContainers	<[]Object>
   hostAliases	<[]Object>
   hostIPC	<boolean>
   hostNetwork	<boolean>
   hostPID	<boolean>
   hostname	<string>
   imagePullSecrets	<[]Object>
   initContainers	<[]Object>
   nodeName	<string>
   nodeSelector	<map[string]string>
   overhead	<map[string]string>
   preemptionPolicy	<string>
   priority	<integer>
   priorityClassName	<string>
   readinessGates	<[]Object>
   restartPolicy	<string>
   runtimeClassName	<string>
   schedulerName	<string>
   securityContext	<Object>
   serviceAccount	<string>
   serviceAccountName	<string>
   setHostnameAsFQDN	<boolean>
   shareProcessNamespace	<boolean>
   subdomain	<string>
   terminationGracePeriodSeconds	<integer>
   tolerations	<[]Object>
   topologySpreadConstraints	<[]Object>
   volumes	<[]Object>

重点关注:containers、nodeName、volumes等。

pod.spec.containers
bash 复制代码
[root@master30 ~]# kubectl explain pod.spec.containers | grep '^   [a-zA-Z]'
   args	<[]string>
   command	<[]string>
   env	<[]Object>
   envFrom	<[]Object>
   image	<string>
   imagePullPolicy	<string>
   lifecycle	<Object>
   livenessProbe	<Object>
   name	<string> -required-
   ports	<[]Object>
   readinessProbe	<Object>
   resources	<Object>
   securityContext	<Object>
   startupProbe	<Object>
   stdin	<boolean>
   stdinOnce	<boolean>
   terminationMessagePath	<string>
   terminationMessagePolicy	<string>
   tty	<boolean>
   volumeDevices	<[]Object>
   volumeMounts	<[]Object>
   workingDir	<string>

重点关注:command、env、image、imagePullPolicy、volumeMounts等。

pod.spec.containers.ImagePullPolicy
  • Always,总是从仓库下载镜像。
  • Never,只使用本地镜像,不下载。
  • IfNotPresent,优先使用本地镜像,如果没有才从仓库下载镜像。
pod lifecycle

容器的运行状态取决于容器中的进程。

pod的运行状态取决于pod中所有容器的状态。

Pod and Container status
  • ContainerCreating 正在创建
  • Running 正在运行
  • Completed 运行完成
  • Error 运行错误
  • CrashLoopBackOff 重新创建
  • ErrImagePull 获取镜像错误
  • ImagePullBackOff 重新获取镜像

container states

  • Waiting: 等待某个条件满足变成Running状态,例如下载镜像,更新secrets等。 通过describe pod查看message和reason详细信息。

  • Running: 容器正在运行,没有问题。同时记录Running开始时间。

  • Terminated: 容器运行完成,也有可能是运行失败终止。

实验1:pod中包含1个容器,验证pod状态

监控命令: watch -n 1 kubectl get pod

示例1:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  restartPolicy: Never
  containers:
  - name: busybox
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 10']

command命令可以改写如下:

yaml 复制代码
command:
- sh
- -c
- echo OK! && sleep 10

或者

复制代码
args:
- sh
- -c
- echo OK! && sleep 10

观察pod状态为:ContainerCreating-->Running-->Completed

示例2:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  restartPolicy: Never
  containers:
  - name: busybox
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echoxxx Hello Kubernetes! && sleep 5']

实验2:pod中包含2个容器,验证pod状态

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  restartPolicy: Never
  containers:
  - name: busybox1
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 5']
  - name: busybox2
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 10']

观察pod状态为:ContainerCreating-->Running-->NotReady-->Completed

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  restartPolicy: Never
  containers:
  - name: busybox1
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echoxx Hello Kubernetes! && sleep 5']
  - name: busybox2
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 20']

观察pod状态为:ContainerCreating-->Error

pod.spec.restartPolicy

示例:

yaml 复制代码
  restartPolicy: Never
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo The app is running! && sleep 5'] 

restartPolicy,针对pod中所有容器生效。

  • Always ,除了 Running 状态,其他状态总是重启,默认值
  • OnFailure,失败了才重启。
  • Never,从不重启。

实验1:验证pod中含有单个容器重启策略。

更改pod重启策略,直接apply会报错,需要删除重建。

第一个yaml验证Always/OnFailure/Never重启策略

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  restartPolicy: Always/OnFailure/Never
  containers:
  - name: busybox
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 5']

第二个yaml验证OnFailure

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  restartPolicy: OnFailure
  containers:
  - name: busybox
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echoxxx Hello Kubernetes! && sleep 5']

第三个yaml验证 Never 重启策略

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  restartPolicy: Never
  containers:
  - name: busybox
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echoxxx Hello Kubernetes! && sleep 5']

实验2:验证pod中含有多个容器重启策略。

验证结果:只要有一个容器满足重启策略条件,就会重启。

示例1:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  restartPolicy: Always/OnFailure/Never
  containers:
  - name: busybox1
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 5']
  - name: busybox2
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 5']

使用Always重启策略时,当容器busybox1状态为Completed时,容器busybox2还未创建成功,此时会根据重启策略重启pod。当然也可以将容器的生命周期延长,sleep 5改为sleep 50,运行50s之后还会重启pod。

使用OnFailure/Never重启策略就不会出现这种情况。

示例2:第二个command是错误的。

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  restartPolicy: Always/OnFailure/Never
  containers:
  - name: busybox1
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 5']
  - name: busybox2
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echoxxx Hello Kubernetes! && sleep 5']

Always和OnFailure重启策略总是会重启pod;Never重启策略就不会重启pod。

Init Containers(自学)

一个pod可以有多个容器在其中运行应用程序,也可以有一个或多个initContainers。initContainers中容器状态必须是complete,containers容器才能运行。

如果pod的initContainers fail,kubernetes根据restartpolicy重启pod,直到initContainers状态为complete。

如果pod中initContainers有多个容器,那么会按顺序创建和执行。按顺序执行的initContainers必须成功才能执行下一个initContainers。所有init全部成功执行完成后,才开始执行pod中常规容器。

使用场景:

1、等待某个服务创建成功。

复制代码
for i in {1..100}; do sleep 1; if dig myservice; then exit 0; fi; done; exit 1

2、等待固定时间后再运行app容器。

复制代码
sleep 60

3、Clone a git repository into a volume

示例1:

bash 复制代码
root@master30:~/pods# vim pod-myapp.yaml
yaml 复制代码
# myservice
---
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376

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

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

首先创建2个service,然后创建pod,观察pod状态变化。

示例2:

bash 复制代码
root@master30:~/pods# vim pod-myapp.yaml
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  volumes:
  - name: workdir
    emptyDir: {}
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
    volumeMounts:
    - name: workdir
      mountPath: "/work-dir"
  initContainers:
  - name: init-poda
    image: busybox
    command: ['sh', '-c', 'touch /work-dir/aa.txt && sleep 10']
    volumeMounts:
    - name: workdir
      mountPath: "/work-dir"

在app容器中查看文件

bash 复制代码
[root@master30 ~]# kubectl exec myapp-pod -c myapp-container -- ls /xx

pod的状态变化:Init:0/1->PodInitializing->Running

将初始化容器command变更为以下内容再测试:

复制代码
command: ['sh', '-c', 'touchxx /work-dir/aa.txt']

pod的状态变化:

Init:0/1->Init:Error->Init:CrashLoopBackOff,然后Init:Error->Init:CrashLoopBackOff循环。

静态 pod

**问题:**正常情况下,pod 由 master 节点统一管理。那么 master 上的kube-apiserver、kube-scheduler、kube-controller-manager等pod又是由谁来管理的呢?

答案kubelet 服务。master节点上组件以静态Pod方式运行,由本地kubelet进行管理。它们不能通过API Server进行管理,无法与ReplicationController、Deployment或DaemonSet进行关联,并且kubelet也无法对其健康检查。

我们来分析下 kubelet.service 配置。

bash 复制代码
[root@master30 ~]# systemctl status kubelet.service |cat
● kubelet.service - kubelet: The Kubernetes Node Agent
     Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; preset: enabled)
    Drop-In: /usr/lib/systemd/system/kubelet.service.d
             └─10-kubeadm.conf
     Active: active (running) since Sun 2024-07-14 09:52:08 UTC; 1h 7min ago
       Docs: https://kubernetes.io/docs/
   Main PID: 7489 (kubelet)
      Tasks: 11 (limit: 4557)
     Memory: 39.0M (peak: 39.9M)
        CPU: 1min 45.913s
     CGroup: /system.slice/kubelet.service
             └─7489 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock --pod-infra-container-image=registry.k8s.io/pause:3.9
......

[root@master30 ~]# cat /usr/lib/systemd/system/kubelet.service
[Unit]
Description=kubelet: The Kubernetes Node Agent
Documentation=https://kubernetes.io/docs/
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/usr/bin/kubelet
Restart=always
StartLimitInterval=0
RestartSec=10

[Install]
WantedBy=multi-user.target

# kubelet.service服务启动参数通过 Drop-In 文件 10-kubeadm.conf 配置
[root@master30 ~]# cat /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf 
# Note: This dropin only works with kubeadm and kubelet v1.11+
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
# This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
# This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use
# the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file.
EnvironmentFile=-/etc/default/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS

# 以下参数定义了kubelet配置文件位置
# KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml
# 该文件中的 staticPodPath 参数定义了一个目录
# kubelet会定期扫描该目录中pod文件,并根据目标中文件变动创建和删除pod
[root@master30 ~]# grep staticPodPath /var/lib/kubelet/config.yaml
staticPodPath: /etc/kubernetes/manifests

[root@master30 ~]# ls -1 /etc/kubernetes/manifests
etcd.yaml
kube-apiserver.yaml
kube-controller-manager.yaml
kube-scheduler.yaml

**注意:**不要修改 staticPodPath 参数配置,否则需要将集群组件4个yaml文件也要复制过去。

我们还可以通过参数 --pod-manifest-path设置,该目录下所有 pod 也是静态 pod。

bash 复制代码
[root@master30 ~]# kubelet --help|grep pod-manifest-path
      --pod-manifest-path string    Path to the directory containing static pod files to run, or the path to a single static pod file. Files starting with dots will be ignored. (DEPRECATED: This parameter should be set via the config file specified by the Kubelet's --config flag. See https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/ for more information.)

思考

1. kubernetes 中 pod 和 容器区别?

答:

容器是运行时概念真正跑应用的进程环境

kubernetes 中 Pod:是 K8s 独有的概念 ,是 K8s 中最小调度、管理、自愈单元,是容器的"外壳 + 运行环境"

  • 可以为pod额外配置:健康检查、重启策略、资源配额等。
  • pod 有 独立 IP,内部容器共享IP。
  • pod 有 独立存储:内部容器共享存储。
  • **一个 Pod 里可以有 1 个或多个容器,**pod中所有容器:
    • 同住一个房间 ,共用网络、存储
    • 一起被调度到同一台机器,一起销毁。
特性 容器 Pod
K8s 最小调度单位 ❌ 不是 ✅ 是
独立 IP ❌ 无 ✅ 有
多容器支持 ❌ 不支持 ✅ 天然支持
网络/存储共享 ❌ 不能 ✅ 内部容器可共享
生命周期管理 ❌ 弱 ✅ 完整(重启、自愈)
属于谁 运行时(Docker/containerd) Kubernetes

2. kubernetes 为什么直接管理 pod 而不是容器?

  1. 容器太"原子",不适合直接调度。K8s 需要一个能直接被调度、能独立运行、有完整身份 的对象--Pod
  • 容器:只是一个进程/运行环境(Docker/containerd)。

  • Pod:是容器的封装 + 网络/存储/配置/生命周期的统一抽象 。K8s 调度、扩缩容、自愈、服务发现、监控......全都以 Pod 为单位

  1. Pod 支持"多容器协同"(最关键设计)。

    很多场景必须多个容器一起跑、共享资源

    • 业务容器 + Sidecar(日志、监控、代理)
    • 业务容器 + InitContainer(初始化)
    • 业务容器 + 网络/安全代理容器

    它们需要:

    • 共享 Network Namespace(同一个 IP、端口空间)
    • 共享 Volume
    • 一起调度到同一台机器
  2. Pod 提供统一的生命周期与自愈

    K8s 要做:

    • 重启失败容器
    • 替换崩溃节点上的实例
    • 滚动更新、回滚
    • 扩缩容

    这些行为必须作用在一个稳定、标准、独立的单元上:

    • 如果直接管容器,多容器应用会乱
    • 以 Pod 为单位,管理语义清晰、一致
  3. 解耦底层容器运行时。

    Pod 屏蔽了底层实现:

    • Docker
    • containerd
    • cri-o
    • 其他 CRI 运行时

    K8s 只跟 Pod/CRI 打交道,不绑定某一种容器技术。

3. 解释 pod 中 pause 容器作用

在 Kubernetes 里,每个 Pod 都会自动创建一个 pause 容器(也叫 infra 容器) ,它是 Pod 里第一个启动、最后退出的容器,作用非常关键。

  1. pause 容器是干嘛的?

    pause 容器就是为了"占坑",把 Pod 的网络和命名空间先 hold 住。

  2. 它的 3 个核心作用

    创建并持有 Pod 的 Linux Namespace

    Pod 里所有容器要共享:

    • Network namespace(同一个 IP、端口)
    • PID namespace(可选)
    • IPC namespace

    这些共享空间必须由一个"永远不死"的容器来持有 ,否则共享空间会消失。这个容器就是 pause

    让 Pod 生命周期独立于业务容器

    • 业务容器挂了 → 重启
    • pause 容器不挂 → Pod 就不会消失
    • 网络、IP、存储挂载都能保持不变

    如果没有 pause:业务容器一退出,整个 Pod 的网络就没了,重启也没用。

    实现多容器共享网络

    nginx、业务容器、sidecar 都加入 pause 的 network namespace:

    • 共享同一个 IP
    • 可以用 127.0.0.1 互相访问
    • 端口不能冲突
  3. 超形象比喻

    • pause = 房东
    • Pod = 房子
    • 业务容器 = 租客

    房东(pause)先占好房子(网络/命名空间),租客(业务容器)才能住进来。租客换了一波又一波,房子一直都在。

  4. 一个 Pod 内部结构(最标准)

    bash 复制代码
    Pod
    ├── pause 容器(infra)------ 永远第一个启动
    │    持有:网络命名空间、IP、IPC
    └── 业务容器 1、2、3...
         都加入 pause 的命名空间
  5. 最精炼总结

    pause 容器 = Pod 的基石

    • 只负责 holding namespace
    • 保证 Pod 网络、IP、生命周期稳定

环境清理

bash 复制代码
[root@master30 ~]# kubectl delete ns pods

Kubernetes Volume

学习参考:

环境准备

bash 复制代码
[root@master30 ~]# kubectl create ns storage
[root@master30 ~]# kubectl config set-context --current --namespace storage

Volume 类型

Kubernetes支持Volume类型有:

  • emptyDir
  • hostPath
  • gcePersistentDisk
  • awsElasticBlockStore
  • nfs
  • iscsi
  • fc (fibre channel)
  • flocker
  • glusterfs
  • rbd
  • cephfs
  • gitRepo
  • secret
  • persistentVolumeClaim
  • downwardAPI
  • projected
  • azureFileVolume
  • azureDisk
  • vsphereVolume
  • Quobyte
  • PortworxVolume
  • ScaleIO
  • StorageOS
  • local

emptyDir

默认情况下,当Pod分配到Node上时,将会创建emptyDir,只要Node上的Pod一直运行,Volume就会一直存。当Pod(不管任何原因)从Node上被删除时,emptyDir也同时会删除,存储的数据也将永久删除。

**实验:**准备一个包含2个容器的pod,使用emptyDir。

bash 复制代码
[root@master30 ~]# vim pod-emptyDir.yaml
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  volumes:
  - name: datavolume
    emptyDir: {} 
  containers:
  - name: busybox1
    image: docker.io/library/busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data
      name: datavolume
  - name: busybox2
    image: docker.io/library/busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
    volumeMounts:
    - mountPath: /data
      name: datavolume

配置说明:

  • spec.volumes:定义卷,是默认卷类型。

  • spec.containers.volumeMounts:引用卷

bash 复制代码
# 创建pod
[root@master30 ~]# kubectl apply -f pod-emptyDir.yaml
pod/busybox created

# 获取容器ID
[root@master30 ~]# kubectl describe pod busybox |egrep -o 'Container ID.*//.{12}'
Container ID:  containerd://07d189383321
Container ID:  containerd://bb02c680ca8a

# 获取容器所在节点
[root@master30 ~]# kubectl describe pod busybox |grep Node:
Node:             worker32.laoma.cloud/10.1.8.32

# 登录到node查看容器挂载情况
[root@worker32 ~]# crictl inspect 07d189383321|grep datavolume
        "hostPath": "/var/lib/kubelet/pods/8417656f-4b6c-4b2a-a2f3-f4a248c4c043/volumes/kubernetes.io~empty-dir/datavolume",
          "host_path": "/var/lib/kubelet/pods/8417656f-4b6c-4b2a-a2f3-f4a248c4c043/volumes/kubernetes.io~empty-dir/datavolume"
          "source": "/var/lib/kubelet/pods/8417656f-4b6c-4b2a-a2f3-f4a248c4c043/volumes/kubernetes.io~empty-dir/datavolume",

# 创建数据
[root@master30 ~]# kubectl exec busybox -c busybox1 -- touch /data/b1-f1
[root@master30 ~]# kubectl exec busybox -c busybox2 -- ls /data
b1-f1

# node上查看数据
[root@worker32 ~]# ls /var/lib/kubelet/pods/8417656f-4b6c-4b2a-a2f3-f4a248c4c043/volumes/kubernetes.io~empty-dir/datavolume
b1-f1

# 删除pod,验证emptyDir
[root@master30 ~]# kubectl delete pod busybox --force
[root@worker32 ~]# ls /var/lib/kubelet/pods/8417656f-4b6c-4b2a-a2f3-f4a248c4c043/volumes/kubernetes.io~empty-dir/datavolume
ls: cannot access '/var/lib/kubelet/pods/8417656f-4b6c-4b2a-a2f3-f4a248c4c043/volumes/kubernetes.io~empty-dir/datavolume': No such file or directory

# 容器删除后,需要等待一些时间,临时卷数据等待才会删除。
相关推荐
没有口袋啦2 小时前
基于K8s+Karmada的混合多云容器平台项目(Ubuntu22.04)(还在持续打磨中)
云原生·容器·kubernetes
她叫我大水龙2 小时前
Docker 安装和常用命令
运维·docker·容器
风翼靓崽2 小时前
记一次k8s pod的CrashLoopBackOff错误状态
云原生·容器·kubernetes
dualven_in_csdn2 小时前
【docker】docker下如何使用宿主主机的GPU
运维·docker·容器
cyber_两只龙宝2 小时前
【Oracle】Oracle之SQL的集合运算符
linux·运维·数据库·sql·云原生·oracle
菱玖2 小时前
K8s集群部署与应用运维实战
运维·容器·kubernetes
Elastic 中国社区官方博客3 小时前
自动化可靠性:自愈型企业的架构
运维·elasticsearch·搜索引擎·云原生·架构·自动化·serverless
喜欢流萤吖~3 小时前
微服务的统一大门:SpringCloud Gateway
微服务·云原生·架构