第5章-Pod
原创
本博客原创文章,转载请注明出处
- name: 原创
desc: 本博客原创文章,转载请注明出处
bgColor: '#F0DFB1'
textColor: '#1078E6'
2
3
4
# 第5章-Pod
本章要点 |
---|
查看、创建、删除及操作 Pod |
初始化 Pod |
静态 Pod |
# 11. 查看Pod
1. 查看当前有多少 Pod(默认只列出当前命名空间里的 Pod):
kubectl get pods
2. 列出指定命名空间里的 Pod:
kubectl get pods -n kube-system
3. 列出所有命名空间里的 Pod:
kubectl get pods -A
# 完整写法
kubectl get pods --all-namespaces
2
3
4
以上几个命令在之前的章节中已经使用过,下面介绍其它一些常用的标志符。
4. 列出 Pod 的同时显示这些 Pod 的基础信息:
kubectl get pods -o wide
从图中可以看到,在添加标志符-o wide
后多出了几列信息。这些信息反馈了当前 Pod 运行在哪台主机上、运行在该主机上的哪个网段中等。
当你想知道一个 Pod 位于何处时,该标志符非常有用。
5. 只列出 Pod 的名称:
kubectl get pods -o name
如果你只想获取 Pod 的名称,而不想看到其它的运行信息,则可以使用以上命令。
同时还可以通过管道符|
以及命令wc -l
来统计 Pod 的数量,这非常方便。
6. 显示 Pod 的标签信息:
kubectl get pods --show-labels
从图中可以看到,这些 Pod 都具有不同的标签,不同的标签通过逗号来分隔。通过识别这些标签,你可以快速判断出某个 Pod 的作用、位置等信息,例如标签tier=control-plane
表明这个 Pod 是运行在 master 节点上的。
7. 列出含有特定标签的 Pod:
kubectl get pods -l <键>=<值>
# 多个标签之间通过逗号 , 分隔
kubectl get pods -l <键>=<值>,<键2>=<值2>
2
3
4
看,标签的作用这不就来了吗?
8. 列出 Pod 的工作负载(该命令在第 2 章中已经介绍过):
kubectl top pods
9. 以 JSON 或 yaml 的格式输出 Pod 的详细运行信息:
kubectl get pods -o json
kubectl get pods -o yaml
# 数据量可能过大, 建议配合输出重定向来使用
kubectl get pods -o json > result.json
2
3
4
5
这里我添加了-A
和-o json
标志符,以 JSON 格式输出所有命名空间里的 Pod 的运行信息。由于数据量过大,所以我将其保存到了外部文件当中。
以下动图演示了这条命令:
从图中可以看到,仅仅 14 个 Pod 的运行信息就有将近五千行之多。
# 22. 创建Pod
之前查看的那些 Pod 都是集群自带的,你一定很想创建一个属于你自己的 Pod 吧?
Pod 和容器一样,也需要依靠镜像来创建,所以我们先下载几个镜像:
# 由于网络原因, 下载可能会很慢, 需要耐心等待
ctr -n k8s.io i pull docker.io/library/nginx:latest
ctr -n k8s.io i pull docker.io/library/nginx:1.9
ctr -n k8s.io i pull docker.io/library/busybox:latest
ctr -n k8s.io i pull docker.io/library/alpine:latest
2
3
4
5
官方的提醒
在生产环境中部署容器时,应避免使用:latest
标记,因为很难跟踪正在运行的镜像版本,也很难正确回滚。
前面说过,ctr 是安装 containerd 时自带的一款命令行工具,可用于管理容器镜像。同时 ctr 具有命名空间概念,与 kubernetes 有关的镜像都需要放在k8s.io
命名空间当中。
完成全部下载之后,你应该已经拥有了四个来自 docker.io 存储库的镜像。
ctr -n k8s.io i ls | grep library
# 2.12.1 通过命令行创建Pod
我们先来看看创建 Pod 的命令语法:
# 语法
kubectl run <名称> --image <镜像>
kubectl run <名称> --image <镜像> --image-pull-policy=<镜像下载策略>
2
3
你可能会注意到--image-pull-policy
标志符,它指定了创建 Pod 时的镜像下载策略,共有三种策略:
IfNotPresent
:如果本地没有才去下载镜像。如果本地存在相应的镜像,则使用本地镜像来创建 Pod;如果本地没有镜像,则尝试下载该镜像,然后再创建 PodAlways
:总是下载镜像。不管本地有没有镜像,都会去下载镜像,然后用所下载的镜像来创建 PodNever
:从不下载镜像。只当本地存在镜像的时候,才创建 Pod
笔记
一些选项必须通过等于号=
来指定,不能使用空格。
默认的下载策略一般为Always
,我们刚刚已经下载过镜像了,所以这里手动指定策略为IfNotPresent
,以免二次下载镜像。
创建一个名为 “pod1” 的 Pod,同时指定镜像下载策略为 “IfNotPresent”:
# 嫌命令太长的话可以通过 Tab 键自动补全, 该功能在第 2 章中已经设置过了
kubectl run pod1 --image nginx --image-pull-policy=IfNotPresent
2
等待一会后,你就可以看到一个新鲜出炉的 Pod,它被调度到了 worker02 节点上运行。
我们来创建第二个 Pod,这次的操作不太一样:
# 创建命名空间 ns1
kubectl create ns ns1
# 创建 Pod 并将其命名空间指定为 ns1
kubectl run pod2 --image nginx --image-pull-policy=IfNotPresent -n ns1
2
3
4
5
创建 pod2 之后我们查看一下,你会发现还是只有 pod1 的信息,那 pod2 呢?创建失败了吗?
我们刚刚位于命名空间default
当中,而kubectl get pods
命令默认只会列出当前命名空间中的 Pod,自然看不到命名空间ns1
中的 Pod 啦。
这次我们为命令加上-n ns1
:
kubectl get pods -n ns1
这次顺利看到了 pod2。
笔记
如果没有指定-n
选项,那么你当前所处的命名空间是哪个,创建的 Pod 就会在哪个命名空间中运行。
# 2.22.2 通过yaml文件创建Pod
kubernetes 通过 yaml(.yaml
)文件来定义和创建各种集群资源。在第 2 章中我们配置并优化了 vim 编辑器,这将有助于你编写 yaml 文件。
我们先创建一个目录,把相关的 yaml 文件都放在该目录中。
mkdir pod-yaml
cd pod-yaml
2
然后,你可以通过以下命令快速生成一个用于创建 Pod 的 yaml 文件:
--dry-run=client
:试运行,在本地模拟创建一个 Pod,并不会真的创建-o yaml
:以 yaml 格式导出该 Pod 的所有配置项
kubectl run <名称> --image <镜像> --dry-run=client -o yaml > pod.yaml
vim pod.yaml
2
3
导出文件后,你可通过 vim 来打开该文件。
打开文件之后,你将看到以下内容:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod-y
name: pod-y
spec:
containers:
- image: nginx
name: pod-y
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 2.2.1apiVersion参数
该参数用于指定 Pod 的 API 版本,一般是固定的v1
。
apiVersion: v1
你还记得kubectl api-versions
命令吗?它列出了 k8s 里所有可用的 apiVersion 参数值。
# 2.2.2kind参数
该参数用于指定当前 yaml 文件所创建的 k8s 资源类型。参数值采用驼峰命名法,英文首字母需要大写,否则会创建失败。
kind: Pod
资源类型和 apiVersion 的值是对应的,不同的资源都有不同的 apiVersion 来搭配,例如 Pod 资源必须对应 v1 版本。
# 2.2.3metadata参数
该参数用于指定当前资源的元数据信息。
metadata:
creationTimestamp: null
labels:
run: pod-y
name: pod-y
2
3
4
5
metadata.creationTimestamp
是一个时间戳,表示创建此资源时的服务器时间。它以 RFC3339 形式表示,并采用 UTC。它可以指定为 null(空)。
metadata.labels
是一个对象,用于定义资源的多个标签,定义标签的格式为<key>: <value>
。这就是之前通过命令kubectl get pods --show-labels
所看到的那些标签。
metadata.name
用于定义资源的名称。
# 2.2.4spec参数
该参数定义了 Pod 中的容器以及各种策略。
spec:
containers:
- image: nginx
name: pod-y
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
2
3
4
5
6
7
spec.containers
是一个数组 + 对象的复合结构,用于定义单个 Pod 中的多个容器。每个对象的第一个参数都需要以符号-
开头,表示这是一个新容器的开始。
spec.containers.image
定义容器所使用的镜像。
spec.containers.name
定义容器名称,该名称与metadata.name
是不同的。一个是 Pod 名称,一个是 Pod 中某个容器的名称。
spec.containers.resources
定义容器资源限制,这将在后面的内容中介绍。
spec.dnsPolicy
定义 DNS 策略。
spec.restartPolicy
定义 Pod 重启策略,在这里为 Always(遇到故障之后,总是重启)。
# 2.2.52.2.5 应用yaml并创建Pod
在创建 Pod 之前先打开文件,在原有的条目下方添加镜像下载策略:
imagePullPolicy: IfNotPresent
此外,你也可以在执行命令的时候添加--image-pull-policy
选项,这样镜像下载策略会一同被导出至 yaml 文件当中。
运行以下命令,从一个文件中创建 Pod:
kubectl apply -f <文件名>
成功创建了一个名为 pod-y 的 Pod。
# 2.32.3 关于镜像下载策略
在命令行中创建 Pod 时,可以通过标志符--image-pull-policy
来指定镜像下载策略。通过 yaml 文件创建 Pod 时,可以通过参数spec.containers.imagePullPolicy
来指定镜像下载策略。
通过前面的介绍,得知镜像下载策略有三个值:IfNotPresent
、Always
和 Never
。默认的策略一般为 Always,为什么要说一般呢?网上有很多文章都说 Always 是默认的下载策略,但这并不完全正确。
在官方文档 (opens new window)中详细记录了有关于 “默认镜像下载策略” 的信息。当满足某个特定条件时,你的 k8s 会自动设置一个合适的下载策略。
笔记
当你省略imagePullPolicy
字段的情况下,如果:
- 没有为容器镜像指定标签,自动设置为
Always
- 容器镜像的标签为
:latest
,自动设置为Always
- 为容器镜像指定了摘要,自动设置为
IfNotPresent
- 为容器镜像指定了非
:latest
的标签,自动设置为IfNotPresent
两种 Always 的情况,两种 IfNotPresent 的情况。相信你现在对默认的镜像下载策略有了一个初步了解。
更加详细的信息可参阅官方文档 (opens new window)。
# 33. 小练习
我们来做个小练习:
- 通过 yaml 文件创建一个名为 pod-abc 的 Pod
- 镜像为 nginx
- 镜像下载策略为 IfNotPresent
- 命名空间为 ns1
- 添加两个标签:abc=xyz 和 hello=world
kubectl run pod-abc --image nginx --image-pull-policy=IfNotPresent -n ns1 -l abc=xyz,hello=world --dry-run=client -o yaml > abc.yaml
运行命令之后,将会获得以下文件内容:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
abc: xyz
hello: world
name: pod-abc
namespace: ns1
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-abc
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
然后,运行以下命令创建该 Pod:
kubectl apply -f abc.yaml
在命名空间 ns1 中可以看到刚刚创建的 pod-abc,还有之前创建的 pod2。
pod-abc 具有两个标签,都是我们手动指定的。我们之前没有为 pod2 打过标签,但 pod2 却有一个标签run=pod2
。这说明,如果你没有为 Pod 指定标签,则 kubernetes 会自动为其加上一个格式为run=<名称>
的默认标签。
# 44. 删除Pod
从开始到现在,我们一共创建了四个 Pod:pod1、pod2、pod-y 和 pod-abc。其中 pod1 和 pod2 都是通过命令行创建的,而 pod-y 和 pod-abc 都是通过 yaml 文件的方式创建的。
删除命令的语法:
kubectl delete pods <名称>
# 强制删除
kubectl delete pods <名称> --force
2
3
4
1. 删除 pod1:
kubectl delete pods pod1
一次简单的删除。此外,你还可以通过添加--force
来强制删除某个 Pod,以加快删除速度。
2. 同时删除命名空间 ns1 中的两个 Pod:
# 多个 Pod 之间通过空格来分隔
kubectl delete pods -n ns1 pod-2 pod-abc
2
我当前位于default
命名空间中,如果想要删除 pod-2 和 pod-abc,则必须使用-n
指定命名空间。
可以看到,第一次我没有指定命名空间,所以删除失败了,因为当前命名空间中没有这两个 Pod。
现在只剩下 pod-y 了,这个 Pod 是通过 yaml 文件pod.yaml
创建的。那么问题来了,我们能否基于 yaml 文件来删除某个 Pod 呢?当然可以!
3. 通过 yaml 文件删除某个资源:
kubectl delete -f <文件名>
但是 yaml 文件中的kind
和metadata
参数不能有太大的偏差,否则 k8s 可能找不到对应的资源。
4. 删除具有某个特定标签的多个 Pod:
之前的 Pod 都删光了,再创建四个 Pod,分别打上不同的标签:
kubectl run pod1 --image nginx --image-pull-policy=IfNotPresent -l abc=123
kubectl run pod2 --image nginx --image-pull-policy=IfNotPresent -l xyz=123
kubectl run pod3 --image nginx --image-pull-policy=IfNotPresent -l xyz=456
kubectl run pod4 --image nginx --image-pull-policy=IfNotPresent -l xyz=456
2
3
4
创建成功。
然后运行以下命令,删除具有标签xyz=456
的所有 Pod:
kubectl delete pods -l xyz=456
感受到标签的便捷性了吧。
# 55. 操作Pod
现在我们来对 Pod 进行一些具体的操作。不过在那之前,我们先生成一个 Pod 的 yaml 文件:
kubectl run ac-pod --image nginx --image-pull-policy=IfNotPresent --dry-run=client -o yaml > ac-pod.yaml
文件内容如下:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: ac-pod
name: ac-pod
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: ac-pod
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
前面提到过,一个 Pod 里可以有多个容器,怎么实现呢?
我们编辑这个文件的spec.containers
参数,在原有的基础上往 Pod 里再添加一个容器:
...
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: ac1
resources: {}
- name: ac2
image: nginx
imagePullPolicy: IfNotPresent
resources: {}
...
2
3
4
5
6
7
8
9
10
11
12
前面介绍过spec.containers
参数,它是一个数组 + 对象的复合结构,每个符号-
都表示一个新容器的开始。
这里有两个-
说明有两个容器,第一个容器以参数image
开始,第二个容器以参数name
开始。参数没有书写顺序要求,只要保持在符号-
之内即可。
此外,如果同一个 Pod 中有多个容器,则这些容器的名称不能重复,否则会冲突,导致 Pod 创建失败。所以我这里将两个容器的name
分别设置为了ac1
和ac2
。
完整的 yaml 文件内容如下:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: ac-pod
name: ac-pod
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: ac1
resources: {}
- name: ac2
image: nginx
imagePullPolicy: IfNotPresent
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
通过以上 yaml 文件创建 Pod:
kubectl apply -f ac-pod.yaml
创建成功,这个 Pod 在READY
一栏中显示了2/2
,表示这个 Pod 里有两个容器,且容器运行正常。
如果显示1/2
,表示 Pod 里有两个容器,但是只有一个容器运行正常。
你可能会看到 Pod 状态一直处于 “Error”(错误)之中,别担心,这是演示目的之一。
# 5.15.1 查看Pod的日志信息
通过以下命令可以查看 Pod 中某个容器的日志信息(命令行输出信息):
kubectl logs <Pod名称>
查看成功,这是容器内部的一些初始化信息。
同时在最上方有一行提示 “默认容器为 ac1,共有 ac1, ac2”。这说明,如果单个 Pod 中有多个容器,则logs
命令默认只会查看第一个容器中的命令行输出。
如果想要查看其它容器中的命令行输出,则需要指定-c
选项:
# 查看Pod中指定容器的命令行输出
kubectl logs <Pod名称> -c <容器名称>
kubectl logs ac-pod -c ac2
2
3
4
通过-c
指定容器名称为 ac2,成功看到了容器 ac2 中的命令行输出。其中包含大量的错误信息,提示 “绑定地址的 80 端口失败”。
在 docker 中,每个容器可能都有一个独立的 IP 地址。但是在 k8s 中,每个 Pod 拥有一个独立的 IP 地址,而 Pod 中的多个容器则会共用同一个 Pod 的 IP 地址。
由于单个 Pod 中的多个容器会共用一个 IP 地址,而我们创建的两个容器又都是nginx
镜像,该镜像默认监听的端口为80
。两个容器都想使用这个 IP 的 80 端口,这造成了冲突,导致最终只有一个容器可以正常运行。
# 5.25.2 在Pod中执行命令
# 5.2.15.2.1 在已有的Pod中执行命令
与 docker 类似,我们可以通过exec
命令在 Pod 的某个容器中执行命令。
kubectl exec <Pod名称> -- <命令>
# 在指定容器中执行命令
kubectl exec <Pod名称> -c <容器名称> -- <命令>
2
3
4
就像这样:
# 在 ac-pod 中的 ac1 容器里执行 ls -l 命令
kubectl exec ac-pod -c ac1 -- ls -l
2
你可能会很疑惑,为什么 k8s 要用--
来分隔要执行的命令?我也很疑惑,但这是固定语法。
笔记
符号--
的两边必须有空格。
那么,命令kubectl exec
能否像 docker 那样,获取一个容器的 shell 终端呢?
当然!和 docker 一样,我们可以通过-it
参数以及bash
命令来获取一个容器的 shell 交互终端。
-i
:获取容器的标准输入-t
:获取一个伪终端
kubectl exec -it ac-pod -c ac1 -- bash
获取 ac1 容器的终端之后,你就可以在 ac1 中尽情地执行命令。最后,你可以通过命令exit
或者快捷键Ctrl+D
来退出容器终端。
此外,如果不指定-c
选项,则默认会获得第一个容器的 shell 终端,这和logs
命令是一样的。
# 5.2.25.2.2 在创建Pod时执行命令
虽然通过kubectl exec
命令可以在现有的某个 Pod 中执行命令,但如果我想在创建 Pod 时执行命令呢?
和 exec 命令一样,我们可以在创建 Pod 时加上--
符号,后面跟上想要执行的命令:
kubectl run e-pod --image nginx --image-pull-policy=IfNotPresent -- echo "Hello k8s"
然后通过 logs 命令查看 Pod 命令行输出,可以看到所打印的字符 “Hello k8s”。
此外,该 Pod 的运行状态为Completed
(完成),你可能会感到疑惑,为什么这个 Pod 的运行状态会是 Completed 呢?这个问题将会在下一章节中进行解答。
在模拟创建 Pod 的时候,你同样可以执行命令,并将命令一同导出至 yaml 文件中:
kubectl run e-pod2 --image nginx --image-pull-policy=IfNotPresent --dry-run=client -o yaml -- sh -c echo "Hello k8s" > hello.yaml
打开hello.yaml
文件之后,你将看到以下内容:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: e-pod2
name: e-pod2
spec:
containers:
- args:
- sh
- -c
- echo
- Hello k8s
image: nginx
imagePullPolicy: IfNotPresent
name: e-pod2
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
可以看到,参数spec.containers.args
定义了一个容器在启动时将要执行的命令。
args
看起来可能有点突兀,其实它可以使用command
参数来代替:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: e-pod2
name: e-pod2
spec:
containers:
- command: ['sh', '-c', 'echo "Hello k8s"']
image: nginx
imagePullPolicy: IfNotPresent
name: e-pod2
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
换成command
之后看得是不是更直观了点?完整的命令为sh -c echo "Hello k8s"
,意思是通过脚本解释器 sh 来执行 echo 命令打印一个字符串。
然后创建该 Pod:
kubectl apply -f hello.yaml
查看命令行输出,可以看到由 echo 打印出来的字符。
# 5.35.3 Pod文件的拷贝
我们先通过exec
命令在容器 ac1 中创建一个目录:
kubectl exec ac-pod -c ac1 -- mkdir /opt/test
# 5.3.15.3.1 从物理机到容器
语法:
kubectl cp <物理机文件路径> <Pod名称>:<文件路径>
# 指定容器, 不指定的话默认拷贝至第一个容器
kubectl cp <物理机文件路径> <Pod名称>:<文件路径> -c <容器名称>
2
3
4
尝试将物理机上的/etc/hosts
文件拷贝到 ac-pod 里的容器 ac1 的/opt/test
目录当中:
kubectl cp /etc/hosts ac-pod:/opt/test
# 查看
kubectl exec ac-pod -c ac1 -- ls /opt/test
kubectl exec ac-pod -c ac1 -- cat /opt/test/hosts
2
3
4
5
拷贝成功,在容器 ac1 的对应目录中可以看到来自物理机的 hosts 文件。
你也可以将物理机上的一个目录拷贝至容器中。
# 5.3.25.3.2 从容器到物理机
只需要将物理机和 Pod 的位置调换一下即可:
kubectl cp <Pod名称>:<文件路径> <物理机文件路径>
# 指定从哪个容器中拷贝
kubectl cp <Pod名称>:<文件路径> <物理机文件路径> -c <容器名称>
2
3
4
将容器 ac1 中的/etc/passwd
文件拷贝至物理机的当前目录下:
kubectl cp ac-pod:/etc/passwd ./passwd
成功。
值得一提的是,从容器中拷贝文件 到 物理机中时,你必须在物理机的路径中指定文件名。如果不指定物理机上的文件名,则拷贝会失败:
# 失败, 必须在物理机路径中指定文件名
kubectl cp -c ac1 ac-pod:/etc/passwd ./
# 成功
kubectl cp -c ac1 ac-pod:/etc/passwd ./abc
2
3
4
5
但如果你拷贝的是一个文件夹,可以不指定目录名。
笔记
使用kubectl cp
命令从容器里拷贝文件至物理机时,必须在物理机路径中指定文件名。
# 5.45.4 查看Pod详细运行信息
通过该命令可以列出某个 Pod 的详细运行信息:
kubectl describe pods <Pod名称>
可以看到这个 Pod 的名称、所处的命名空间、运行在哪个节点上。
还可以看到 Pod 中的容器信息,例如容器 ac1 的容器编号、镜像编号、容器运行状态等。
# 5.55.5 为Pod设置和删除标签
在列出 Pod 的同时,我们可以添加--show-labels
选项以显示这些 Pod 的标签,那么如何为 Pod 设置标签呢?
# 5.5.15.5.1 为已有的Pod设置标签
语法:
kubectl label pods <Pod名称> <键>=<值>
# 多个标签之间使用空格分隔
kubectl label pods <Pod名称> <键1>=<值1> <键2>=<值2> ...
# 通过 减号- 来删除标签
kubectl label pods <Pod名称> <键>-
2
3
4
5
6
7
为这个 Pod 设置标签status=error
(状态=错误),表示该 Pod 运行错误:
kubectl label pods ac-pod status=error
标签设置成功。
同时设置多个标签:
kubectl label pods ac-pod abc=123 xyz=456
设置成功。
注意,在生产环境中为集群资源设置标签时,标签应该具有意义,避免设置无意义标签。像此处的标签abc=123
和xyz=456
就是一个反面示例。
# 5.5.25.5.2 在创建Pod时设置标签
语法:
kubectl run <Pod名称> --labels <键>=<值>
# 多个标签之间使用逗号分隔
kubectl run <Pod名称> --labels <键1>=<值1>,<键2>=<值2>
2
3
4
创建 Pod 时通过--labels
选项来为其设置标签。需要注意的是,在一个选项中设置多个标签时,你应该通过逗号,
来分隔这些标签,这与kubectl label pods
命令(通过空格分隔)不太一样。
使用 yaml 文件创建 Pod 时,你也可以通过metadata.labels
参数来为 Pod 设置标签。
metadata:
name: <Pod名称>
labels:
<标签1>: <值1>
<标签2>: <值2>
...
2
3
4
5
6
# 5.5.35.5.3 删除已有的标签
很简单,同一个命令,只需要在对应的标签 “键” 后面添加减号-
即可:
kubectl label pods <Pod名称> <键>-
# 例如
kubectl label pods ac-pod abc-
2
3
4
如果你想同时删除多个标签,则用空格分隔:
kubectl label pods ac-pod xyz- run-
# 66. 初始化Pod
初始化 Pod 也可以叫作 Pod 中的初始化容器。
# 6.16.1 初始化容器概念
举个例子:
- 现在有一辆车,这辆车想过河到对岸去,但是没有桥
- 然后来了一个施工队,这个施工队在车辆过河之前,先帮忙把桥修好,然后这辆车才能过河
- 如果在施工期间发生了任何闪失,导致修桥计划终止,那这辆车就过不了河了
然后,你可以这样理解初始化容器:
- 某个 Pod 里有一个容器,这个容器想执行某些操作,但是运行环境不满足条件
- 然后有一个初始化容器,在后续的容器启动之前,初始化容器先把环境配置好,之后的容器就可以正常启动和运行了
- 如果在初始化期间发生了任何致命错误,导致初始化容器终止,那后续的容器就都不会启动了
简单来讲,初始化容器会先进行一些准备工作,为后续容器的正常运行奠定基础。
# 6.26.2 创建一个初始化容器
假设现在有一个需求,如果某个节点想要运行容器,则该节点的内核参数vm.swappiness
必须为40
,否则容器会运行失败。
通过以下命令可以查询一台主机上的vm.swappiness
内核参数值:
cat /proc/sys/vm/swappiness
可以看到,节点 worker01 和 worker02 的内核参数vm.swappiness
都为30
,不满足运行条件。虽然我们可以手动修改这个参数,但这很繁琐。
worker01:
worker02:
能否在容器运行之前,先自动把当前主机的内核参数vm.swappiness
调整为预期的值?这时候就可以用到初始化容器。
先通过以下命令生成一个 Pod 的 yaml 文件:
kubectl run i-pod --image nginx --image-pull-policy=IfNotPresent --dry-run=client -o yaml > init.yaml
然后我们打开这个文件,将其修改为以下内容:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: i-pod
name: i-pod
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: c
resources: {}
initContainers:
- image: alpine
imagePullPolicy: IfNotPresent
name: init-c
command: ["/bin/sh", "-c", "/sbin/sysctl -w vm.swappiness=40"]
securityContext:
privileged: true
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
这个 Pod 中包含两个容器,一个是位于spec.containers
参数下的普通容器c1
,另一个是位于spec.initContainers
参数下的初始化容器init-c
。容器镜像分别为 nginx 和 alpine。
在初始化容器init-c
启动的时候,通过参数command
执行命令/sbin/sysctl -w vm.swappiness=40
,这条命令可以修改 Pod 所处物理机上的内核参数vm.swappiness
为 40。
此外,由于安全策略的存在,容器不允许修改物理机的内核参数。所以这里还添加了securityContext
选项,并通过privileged: true
赋予了初始化容器特权权限,使其拥有足够的权限来修改物理机的内核参数。
创建初始化 Pod:
kubectl apply -f init.yaml
查看 Pod 状态,此时容器的准备状态为READY: 0/1
,说明容器尚未启动。而运行状态为STATUS: Init:0/1
,说明有一个初始化容器已经启动,正处于初始化运行状态。
等待一段时间后,容器运行状态变为Running
,说明初始化成功,普通容器正常启动并运行了。
该 Pod 运行在 worker02 节点上。
先看下节点 worker01 上的内核参数,还是 30 没有变化。
再看下 worker02 节点,由于刚刚的 Pod 在 worker02 上运行,所以初始化容器将主机上的内核参数vm.swappiness
修改为了 40。
初始化容器成功配置了运行环境。
# 6.36.3 遇到错误的初始化容器
前面的例子提到过,如果在初始化期间发生了任何致命错误,导致初始化容器终止,那后续的容器就都不会启动了。我们来验证一下这个说法。
还是使用 6.2 中的 yaml 文件,但是我故意把/sbin/sysctl
写错成了/sbin/sysctyyl
:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: i-pod2
name: i-pod2
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: c
resources: {}
initContainers:
- image: alpine
imagePullPolicy: IfNotPresent
name: init-c
command: ["/bin/sh", "-c", "/sbin/sysctyyl -w vm.swappiness=40"]
securityContext:
privileged: true
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
将以上内容保存至文件init2.yaml
中,然后创建 Pod:
# 保存
cat <<EOF > init2.yaml
...文件内容
EOF
# 创建Pod
kubectl apply -f init2.yaml
2
3
4
5
6
7
然后可以看到 Pod 的运行状态为Init:Error
,说明初始化容器遇到错误。同时由于spec.restartPolicy
参数设置为了Always
,所以该 Pod 会不断尝试重启,直到不再产生错误为止,但这种重启是徒劳的。
这验证了之前的说法,桥都没修好,车怎么过?
前面提到过,一个 Pod 可以有多个容器,在spec.containers
条目下添加参数即可。
那可不可以有多个初始化容器呢?当然可以,书写参数的语法和普通容器一样,只不过添加参数的位置变成了spec.initContainers
而已:
apiVersion: v1
kind: Pod
metadata:
labels:
run: i-pod3
name: i-pod3
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: c
initContainers:
- image: alpine
imagePullPolicy: IfNotPresent
name: init-c1
command: ["/bin/sh", "-c", "echo 123"]
- image: alpine
imagePullPolicy: IfNotPresent
name: init-c2
command: ["/bin/sh", "-c", "echo 456456"]
- image: alpine
imagePullPolicy: IfNotPresent
name: init-c3
command: ["/bin/sh", "-c", "echo 789789789"]
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
如果存在多个初始化容器,它们会如何运行?
例如,以上 Pod 拥有init-c1
、init-c2
和init-c3
共三个初始化容器,这三个初始化容器会按定义的顺序依次执行,当这三个初始化容器都完成运行之后,普通容器c
才会正常启动。
如果某一个初始化容器出现错误,则整个 Pod 都不会运行。例如,当init-c1
完成运行之后,如果init-c2
遇到了致命错误,则后续的初始化容器init-c3
和普通容器c
都将不会启动,普通容器不运行,则 Pod 也同样不会运行。
# 77. 静态Pod
# 7.17.1 静态Pod概念
在 master 节点的/etc/kubernetes/manifests/
目录下,总是会看到以下几个 yaml 文件:
这些 yaml 文件的名称是不是很眼熟?在命名空间kube-system
中,也能看到四个同名的 Pod,它们是用于运行 master 节点的核心 Pod。
只有当这些 Pod 运行了,master 和其余的 node 节点才能运行和建立联系。当节点与节点之间都联系起来时,就形成了一个集群。换句话说,这四个 Pod 是用于启动集群的核心。
但是现在遇到了一个问题:
- 如果想运行 Pod,则需要依靠集群内的 master 节点来创建和调度 Pod
- 如果想启动 master 节点,又必须先运行这些核心 Pod
核心 Pod 没运行,那 master 就无法运行,集群也无法运行。集群没启动,怎么创建和调度 Pod 呢?
这时候就需要静态 Pod 了,这是一种不需要 master 节点即可自主运行的 Pod。
一般情况下,需要依靠 master 节点来创建和调度 Pod。这类 Pod 可以称其为动态 Pod(依赖 master 创建)。
而有些 Pod 则不需要依赖 master 节点,当 k8s 的主服务kubelet
启动时,这些 Pod 也会跟随服务自动创建和运行。这类 Pod 可以称其为静态 Pod(不依赖 master,启动 k8s 时自动创建)。
什么?你问我kubelet
是啥?请回去看 “第 1 章的 2.1 节”。
# 7.27.2 创建一个静态Pod
# 7.2.17.2.1 通过yaml文件创建静态Pod
静态 Pod 纯粹依靠 yaml 文件来创建,无法通过kubectl run
命令来创建。
但你依然可以通过该命令来导出一个 yaml 文件模板:
kubectl run static-pod --image nginx --image-pull-policy=IfNotPresent --dry-run=client -o yaml > static-pod.yaml
cp static-pod.yaml /etc/kubernetes/manifests/
2
3
通过以上命令生成一个 Pod 的 yaml 文件,然后将其拷贝到默认的静态 Pod 目录/etc/kubernetes/manifests/
中。
可以看到,在拷贝 yaml 文件之前只有i-pod
这一个 Pod,在拷贝之后,立马生成了一个新的名为static-pod-<主机名>
的 Pod。
这正是由我们的 yaml 创建出来的一个静态 Pod,它的名称后面总会加上一个-<主机名>
作为标识。
# 7.2.27.2.2 通过yaml文件删除静态Pod
静态 Pod 是无法通过命令kubectl delete pods
来删除的,我们可以做个实验。
删除刚刚创建的静态 Pod:
kubectl delete pods static-pod-www.k10.com
从图中可以看到,运行命令后提示成功删除了该 Pod。随后发现 Pod 的状态为 “Pending”,在该状态停留一会之后,Pod 的状态又变回了 “Running” 继续运行。
刚刚的 Pod 确实已经被删除了,但是由于 yaml 文件还存在,在删除之后的短时间内,这个 yaml 文件又被解析并创建了一个新的、一模一样的 Pod。
既然无法通过命令删除静态 Pod,那就直接找准源头,删除它的 yaml 文件:
rm -rf /etc/kubernetes/manifests/static-pod.yaml
删除源文件后,由该文件创建出来的静态 Pod 也会自动停止并删除。
# 7.37.3 修改静态Pod存储路径
存放静态 Pod 的默认路径为/etc/kubernetes/manifests/
,有两种方式可以修改存放路径。
# 7.3.17.3.1 通过kubelet服务文件修改
我拿 master 节点来做以下操作。
- 查看
kubelet
服务状态,找到服务文件的存储目录:
systemctl status kubelet
- 在目录下找到
kubelet
的服务文件10-kubeadm.conf
,然后打开它:
ls -l /usr/lib/systemd/system/kubelet.service.d/
vim /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
2
3
- 打开文件后,找到以下配置选项:
# 任意一个Environment
Environment="..."
2
- 在该选项的最后边添加参数,修改静态 Pod 存储路径:
--pod-manifest-path=<目录>
# 例如
--pod-manifest-path=/opt/change-service
2
3
4
- 创建对应的存储目录,然后重启
kubelet
服务:
mkdir /opt/change-service
systemctl daemon-reload
systemctl restart kubelet
2
3
4
重启成功后就完成了路径的修改。
然后你会发现,集群失灵了?服务kubelet
的运行状态是正常的,但无论运行什么命令都毫无作用。
是配置错了吗?
其实配置没错,反而非常对。
前面说过,集群的运行依靠 master 节点,master 节点的运行依靠默认路径/etc/kubernetes/manifests/
下的核心静态 Pod。
由于刚刚修改了静态 Pod 的默认路径,导致 master 的核心组件无法启动,集群自然也无法运行。
你可以将 master 所需的静态 Pod yaml 文件拷贝到新的路径下,从而解决这个问题。但一般情况下,我们都不会去修改 master 节点上的默认静态 Pod 存储目录。
# 7.3.27.3.2 通过k8s配置文件修改
既然 master 节点上有静态 Pod,那 worker 上面有没有静态 Pod 呢?——有,而且可以随意修改存储路径。
- 切换至 worker01 节点,打开同样的
kubelet
服务文件:
ls -l /usr/lib/systemd/system/kubelet.service.d/
vim /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
2
3
- 打开文件后,即可看到 k8s 配置文件的存储位置。
--config=/var/lib/kubelet/config.yaml
- 打开配置文件后,在文件最底部可以看到静态 Pod 的默认存储路径:
staticPodPath: /etc/kubernetes/manifests
- 你可以通过
#
将原来的条目注释,防止原配置丢失。然后另起一行用于修改静态 Pod 存储路径:
- 一样的创建存储目录,一样的重启
kubelet
服务:
mkdir /opt/change-conf
systemctl daemon-reload
systemctl restart kubelet
2
3
4
重启服务之后,在目录opt/change-conf
下放置一个简单的 yaml 文件用于测试:
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: test
2
3
4
5
6
7
8
9
然后回到 master 节点上进行查看,发现 worker01 节点成功创建了一个名为test-www.k11.com
的静态 Pod。
# 88. 小结
本章对 Pod 做了一个初步的了解,通过本章的学习,你应该掌握以下技能:
- 使用
kubectl get
和kubectl top
命令以及一系列参数查看 Pod - 通过命令行或 yaml 文件的方式创建 Pod
- 通过命令行或 yaml 文件的方式删除单个/多个 Pod
- 通过
logs
、exec
、cp
、describe
和label
等子命令操作 Pod - 创建初始化 Pod
- 修改静态 Pod 存储路径,通过 yaml 创建静态 Pod
如果你已经掌握了以上内容,就试着去完成一些练习(练习题-3)吧!