第4章 Pod--Kubernetes的基本单位

Pod是Kubernetes中的==基本单位==。容器本身并不会直接分配到主机上,而会封装到名为Pod的对象中。

Pod通常表示单个应用程序,由一个或多个关系紧密的容器构成,这些容器拥有同样的生命周期,作为一个整体一起编排到Node上。

这些容器共享环境、存储卷(volume)和IP空间。尽管Pod基于一个或多个容器,但应将Pod视作一个单一的整体、单独的应用程序。Kubernetes以Pod为最小单位进行调度、伸缩并共享资源、管理生命周期。

1. Pod基本操作

1. 创建Pod

部署Pod

定义模版文件:

1
vim examplepod.yml

填入:

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: examplepod
spec:
containers:
- name: examplepod-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['echo "Hello Kubernetes!"; sleep 3600']
  • apiVersion表示使用的API版本。v1表示使用Kubernetes API的稳定版本

  • kind表示要创建的资源对象,这里使用关键字Pod。

  • metadata表示该资源对象的元数据。一个资源对象可拥有多个元数据,其中一项是name,它表示当前资源的名称。

  • spec表示该资源对象的具体设置。其中containers表示容器的集合,这里只设置了一个容器,该容器的属性如下。

    • name:要创建的容器名称。

    • image:容器的镜像地址。

    • imagePullPolicy:镜像的下载策略,支持3种

      imagePullPolicy,如下所示。

      • Always:不管镜像是否存在都会进行一次拉取。
      • Never:不管镜像是否存在都不会进行拉取。
      • IfNotPresent:只有镜像不存在时,才会进行拉取。
    • command:容器的启动命令列表(不配置的话,使用镜像内部的命令)。

    • args:启动参数列表(在本例中是输出文字“HelloKubernetes!”并休眠3600s)

运行以下命令,通过模板创建Pod。

1
kubectl apply -f examplepod.yml

image-20220804215147389


*提示:apply是一种声明式对象配置命令。这里应用了之前创建的模板,-f参数表示使用文件名作为参数。相比命令式对象管理,apply既便于跟踪,又具备很好的可读性。本书将统一使用声明式对象配置来管理资源。*


创建成功后,可通过以下命令查询当前运行的所有Pod。

1
kubectl get pod

image-20220804215230213

2. 查询Pod

​ Pod创建后,最常用的功能就是查询。可以用以下命令查询Pod的状态

1
kubectl get pod {Pod名称}

image-20220804215302364

还可以在查询命令中带上参数-w,以对Pod状态进行持续监控

​ 只要Pod发生了变化,就会在控制台中输出相应信息。命令如下。

1
kubectl get pod {Pod名称} -w

image-20220804215324072

​ 另外,还可以在查询命令中带上-o wide参数,输出Pod的更多概要信息(如调度到哪台机器上,Pod本身的虚拟IP等信息)。命令如下。

1
kubectl get pod {Pod名称} -o wide

image-20220804215453630

​ get命令除了可以显示简要的运行信息外,还可以输出完整信息。它支持多种格式的输出,如可以用yaml和Json方式输出,命令如下。

1
2
kubectl get pod examplepod --output yaml
kubectl get pod examplepod --output json

一般情况下,如果要查询Pod更详细的信息(包括状态、生命周期和执行情况等),除了将其输出为yaml或json格式,还可以用describe命令查看详情,格式如下。

1
kubectl describe pods {Pod名称}

结果:

1
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
[root@iZbp1f3y0av6g5d4zhb3uoZ examplepod]# kubectl get pod examplepod --output  yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"examplepod","namespace":"cert-manager"},"spec":{"containers":[{"args":["echo \"Hello Kubernetes!\"; sleep 3600"],"command":["sh","-c"],"image":"busybox","imagePullPolicy":"IfNotPresent","name":"examplepod-container"}]}}
creationTimestamp: "2022-08-04T13:32:13Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
f:spec:
f:containers:
k:{"name":"examplepod-container"}:
.: {}
f:args: {}
f:command: {}
f:image: {}
f:imagePullPolicy: {}
f:name: {}
f:resources: {}
f:terminationMessagePath: {}
f:terminationMessagePolicy: {}
f:dnsPolicy: {}
f:enableServiceLinks: {}
f:restartPolicy: {}
f:schedulerName: {}
f:securityContext: {}
f:terminationGracePeriodSeconds: {}
manager: kubectl-client-side-apply
operation: Update
time: "2022-08-04T13:32:13Z"
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:status:
f:conditions:
k:{"type":"ContainersReady"}:
.: {}
f:lastProbeTime: {}
f:lastTransitionTime: {}
f:status: {}
f:type: {}
k:{"type":"Initialized"}:
.: {}
f:lastProbeTime: {}
f:lastTransitionTime: {}
f:status: {}
f:type: {}
k:{"type":"Ready"}:
.: {}
f:lastProbeTime: {}
f:lastTransitionTime: {}
f:status: {}
f:type: {}
f:containerStatuses: {}
f:hostIP: {}
f:phase: {}
f:podIP: {}
f:podIPs:
.: {}
k:{"ip":"10.42.2.25"}:
.: {}
f:ip: {}
f:startTime: {}
manager: k3s
operation: Update
time: "2022-08-04T13:32:39Z"
name: examplepod
namespace: cert-manager
resourceVersion: "82803826"
uid: be9afea5-2764-4a01-ba9e-33f13082e7d0
spec:
containers:
- args:
- echo "Hello Kubernetes!"; sleep 3600
command:
- sh
- -c
image: busybox
imagePullPolicy: IfNotPresent
name: examplepod-container
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-kj5lt
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: izbp113w9axywnhpyk1525z
preemptionPolicy: PreemptLowerPriority
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: default
serviceAccountName: default
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- name: default-token-kj5lt
secret:
defaultMode: 420
secretName: default-token-kj5lt
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2022-08-04T13:32:13Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2022-08-04T13:32:39Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2022-08-04T13:32:39Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2022-08-04T13:32:13Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: containerd://7500b2e8b6f2e0932250d129685edaa17ed84e50aa575bfd5e34ff8203ce2b6f
image: docker.io/library/busybox:latest
imageID: docker.io/library/busybox@sha256:ef320ff10026a50cf5f0213d35537ce0041ac1d96e9b7800bafd8bc9eff6c693
lastState: {}
name: examplepod-container
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2022-08-04T13:32:38Z"
hostIP: 172.19.230.67
phase: Running
podIP: 10.42.2.25
podIPs:
- ip: 10.42.2.25
qosClass: BestEffort
startTime: "2022-08-04T13:32:13Z"

该命令会输出比较全面的信息,包括资源的基本信息、容器信息、准备情况、存储卷信息及相关的事件列表。在资源部署时如果遇到问题,可以使用此命令查看详情,分析部署错误的原因。

如果要查询Pod本身输出的日志信息,还可以使用logs命令,格式如下。

1
kubectl logs {Pod名称}

image-20220804220238729

3. 修改Pod

​ 可以用replace命令来修改原先设置的Pod属性,命令格式如下:

1
kubectl replace -f {pod模板路径}

修改之前示例中定义的Pod,使它输出“Hello Kubernetesreplaced!”。先打开examplepod.yml文件。

1
vim examplepod.yml

在文件中填入如下内容并保存。

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: examplepod
spec:
containers:
- name: examplepod-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['echo "Hello Kubernetes replaced!"; sleep 3600']

提示:Pod有很多属性无法修改,比如containers的image属性,spec下的activeDeadline Seconds、tolerations属性等。如果一定要修改,则需要加上—force参数,相当于重新创建Pod,命令如下。


1
kubectl replace -f {pod模板路径} --force

image-20220804221306895

image-20220804221403636

4. 删除Pod

Pod的删除非常简单,只要执行以下命令即可

1
kubectl delete pod {Pod名称}

image-20220804221552127

另外,还可以基于模板文件删除资源,如以下命令所示:

1
kubectl delete -f {模板文件名称}

2. Pod模板详解

1
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
apiVersion: v1 #版本,必填,v1代表稳定版本
kind: pod #类型,必填,Pod
metadata: #元数据,表示资源的标识信息
name: String #元数据,必填,Pod的名字
namespace: String #元数据,Pod的命名空间
labels: #元数据,标签列表
- key: value #元数据,可定义多个标签的键/值对
annotations: #元数据,自定义注解列表
- key: value #元数据,可定义多个注解的键/值对
spec: #Pod中容器的详细定义,必填
containers: #Pod中的容器列表,必填,可以有多个容器
- name: String #容器名称,必填
image: String #容器中的镜像地址,必填
imagePullPolicy: [Always|Never|IfNotPresent]#获取镜像的策略,Always表示下载镜像;
#IfNotPresent表示优先使用本地镜像,否则下载镜像;Never表示仅使用本地镜像
command: [String] #容器的启动命令列表(不配置的话,使用镜像内部的命令)
args: [String] #启动命令参数列表
workingDir: String #容器的工作目录
volumeMounts: #挂载到容器内部的存储卷设置
- name: String #为了引用Pod定义的共享存储卷的名称,要用volumes[]部分定义的卷名
mountPath: String #存储卷在容器内挂载的绝对路径,应少于512个字符
readOnly: boolean #是否为只读模式
ports: #容器需要暴露的端口号列表
- name: String #端口名称
containerPort: int #容器要暴露的端口
hostPort: int #容器所在主机监听的端口(把容器暴露的端口映射到宿主机的端口)
protocol: String #端口协议,支持TCP和UDP,默认为TCP
env: #容器运行前要设置的环境变量列表
- name: String #环境变量名称
value: String #环境变量值
resources: #资源限制和请求的设置
limits: #资源限制的设置
cpu: String #CPU的限制,单位为CPU内核数。将用于docker run--cpu-quota 参数,
#也可以使用小数,例如0.1,0.1等价于表达式100m,表示100milicpu
memory: String #内存限制,单位可以为
MiB/GiB/MB/GB(1MiB=1024×1024B,#1MB=1000×1000B),将用于docker run --memory参数 requests: #资源请求的设置
cpu: String #CPU请求,容器启动时的初始可用数量,将用于
docker run --cpu-shares参数
memory: String #内存请求,容器启动时的初始可用数量
livenessProbe: #Pod内容器健康检查的设置,当探测几次无响应后将自动重启该容器,
#检查方法有exec、httpGet和tcpSocket,对一个容器只要设置一种方法即可
exec: #通过exec方式来检查Pod内各容器的健康状况
command: [String] #exec方式需要指定的命令或脚本
httpGet: #通过httpGet方式来检查Pod中各容器的健康状况,需要指定path、port
path: String
port: number
host: String
scheme: String
httpHeaders:
- name: String
value: String
tcpSocket: #通过tcpSocket检查Pod中各容器的健康状况
port: number
initialDelaySeconds: 0 #容器启动完成后,首次探测的时间(单位为秒)
timeoutSeconds: 0 #对容器进行健康检查时探测等待响应的超时时间
(单位为秒,默认为1s)
periodSeconds: 0 #对容器监控检查的定期探测时间设置(单位为秒),默认10s一次
successThreshold: 0
failureThreshold: 0
securityContext: #安全配置
privileged: false
restartPolicy: [Always|Never|OnFailure]#Pod的重启策略,Always表示不管以何种方式终止
#运行,kubelet都将重启;OnFailure表示只有Pod以非0码退出才重启;Never表示不再重启该Pod
nodeSelector: object #节点选择,设置nodeSelector表示将该Pod调度到包含这个标签的
#节点上,以key:value格式来指定
imagePullSecrets: #拉取镜像时使用的secret名称,以key:
secretkey格式指定
- name: String
hostNetwork: false #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
volumes: #在该Pod上定义共享存储卷列表
- name: String #共享存储卷名称
emptyDir: {} #类型为emptyDir的存储卷,与Pod有相同生命周期的一个临时目录,为空值
hostPath: #类型为hostPath的存储卷,将会挂载Pod所在宿主机的目录
path: string #Pod所在宿主机的目录,该目录将在容器中挂载
secret: #类型为secret的存储卷,在容器内部挂载集群中
预定义的secret对象
secretName: String
items:
- key: String
path: String
configMap: #类型为configMap的存储卷,挂载预定义的
configMap对象到容器内部
name: String
items:
- key: String
path: String

还可以使用$ kubectl explain pod命令详细查看Pod资源所支持的所有字段的详细说明

image-20220804222023988

可以看到图中列出了5个字段,分别是apiVersion、kind、metadata、spec、status。如果要进一步查看每个字段的详情,例如,对于spec字段可以使用命令$ kubectl explainpod.spec进行查看,

image-20220804222153635

如果要了解一个正在运行的Pod的配置,可以通过以下命令来获取。

1
kubectl get pod {pod名称} -o yaml

3. Pod与容器

1. Pod创建容器的方式

之前描述的Pod模板Docker-Compose配置非常相似,但Pod模板涉及其他部署参数的设定,相对更复杂。

先排除与容器无关的配置参数,在模板的Containers部分,指明容器的部署方式。在部署过程中,会转换成对应的容器运行时(containerruntime)命令,例如,对于Docker,会转换成类似于Docker run的命令。

​ 在最开始的例子中,yml文件内容如下。

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: examplepod
spec:
containers:
- name: examplepod-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['echo "Hello Kubernetes!"; sleep 3600']

在Kubernetes将Pod调度到某个节点后,kubelet会调用容器运行时(本例中为Docker),执行如下所示的命令。

1
docker run --name examplepod-container busybox sh -c 'echo"Hello Kubernetes!"; sleep 3600'

提示:command和args设置会分别覆盖原Docker镜像中定义的EntryPoint与CMD,在使用时请务必注意以下规则。


  • 如果没有在模板中提供command或args,则使用Docker镜像中定义的默认值运行
  • 如果在模板中提供了command,但未提供args,则仅使用提供的command。Docker镜像中定义的默认的EntryPoint和默认的命令都将被忽略。
  • 如果只提供了args,则Docker镜像中定义的默认的EntryPoint将与所提供的args组合到一起运行。
  • 如果只提供了args,则Docker镜像中定义的默认的EntryPoint将与所提供的args组合到一起运行。
  • 如果同时提供了command和args,Docker镜像中定义的默认的EntryPoint和命令都将被忽略。所提供的command和args将会组合到一起运行。

同样,在Pod模板的Container设置中的各项信息,在运行时都会转换为类似的容器命令来执行。Container的基础信息的设置如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
containers: #Pod中的容器列表,必填,可以有多个容器
- name: String #容器的名称,必填
image: String #容器中的镜像地址,必填
imagePullPolicy: [Always|Never|IfNotPresent]#获取镜像的策略。
Always表示下载镜像;
#IfNotPresent表示优先使用本地镜像,否则下载镜像;Never表示仅使用本地镜

command: [String] #容器的启动命令列表(不配置的话,使用镜像内部的
命令)
args: [String] #启动命令参数列表
workingDir: String #容器的工作目录
volumeMounts: #挂载到容器内部的存储卷设置
- name: String #为了引用Pod定义的共享存储卷的名称,要用
volumes[]部分定义的卷名
mountPath: String #存储卷在容器内挂载的绝对路径,应少于512个字符
readOnly: boolean #是否为只读模式
ports: #容器需要暴露的端口号列表
- name: String #端口名称
containerPort: int #容器要暴露的端口
hostPort: int #容器所在主机监听的端口(把容器暴露的端口映射到
宿主机的端口)
protocol: String #端口协议,支持TCP和UDP,默认为TCP
env: #容器运行前要设置的环境变量列表
- name: String #环境变量名称
value: String #环境变量值

1.1.volumeMounts配置信息

容器运行时通常会提供一些机制来将存储附加到容器上。例如,Docker有两种容器机制:一种是数据卷(data volume),它可以将容器内的文件或目录映射到宿主机上的文件或目录中,其命令格式为

1
$docker run -v /{主机的目录}:/{映射到容器的目录} {镜像名称}

另一种是数据卷容器(data volume container),不过其本质使用的还是数据卷,这种容器一般用在一组相关的容器中,用于专门处理数据存储以供其他容器挂载

不管是数据卷还是数据卷容器,其存留时间通常超过其他容器的生命周期。由于生命周期不同步,因此实现起来非常缺乏灵活性

为了解决这些问题,Kubernetes在数据卷的基础上,又新增加了一套自己的存储卷(volume)抽象机制。该机制不仅允许Pod中的所有容器方便地共享数据,还允许存储卷与Pod中的其他容器保持完全一致的生命周期

下面是一个简单的示例,说明如何对容器创建数据卷及存储卷,以实现数据共享。

  1. 首先,创建examplepodforvolumemount.yml文件。
1
$ vim examplepodforvolumemount.yml
  1. 填入以下内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: v1
kind: Pod
metadata:
name: examplepodforvolumemount
spec:
containers:
- name: containerforwrite
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['echo "test data!" > /write_dir/data; sleep 3600']
volumeMounts:
- name: filedata
mountPath: /write_dir
- name: containerforread
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['cat /read_dir/data; sleep 3600']
volumeMounts:
- name: filedata
mountPath: /read_dir
volumes:
- name: filedata
emptyDir: {}

在本例中,我们创建了两个容器。一个是containerforwrite,它向数据卷写入数据,会向/write_dir/data文件写入"testdata!"文本。容器内的数据卷地址/write_dir,它引用的存储卷filedata

另一个容器是containerforreadTE会从/read_dir/data文件中读取文本,并将其输出到控制台(后续可以通过日志查询方式读取输出到控制台的文本)。容器内的数据卷地址为/read_dir,它引用的存储卷为filedata

本例中还创建了一个存储卷,其名称为filedata,这个名称会被容器设置中的数据卷所引用

存储卷的类型是emptyDir它是最基础的类型,表示纯净的空目录,其生命周期和所属的Pod完全一致(后续章节会讲解更多的种类)。对于例子中的两个容器,虽然数据卷地址不同(一个是/write_dir,一个是/read_dir),但因为它们都是映射到同一个空目录下的,所以本质上仍在同一个文件夹内进行操作。

执行以下命令,创建Pod。

1
$ kubectl apply -f examplepodforvolumemount.yml

image-20220812150036626

通过以下命令,查看Pod的运行情况,READY 2/2表示两个容器都已成功运行。

1
$ kubectl get pods examplepodforvolumemount

image-20220812150142353

此时可以通过logs命令,查看Pod中containerforread容器的日志

1
$ kubectl logs examplepodforvolumemount containerforread

image-20220812150253756

此处已经可以看到containerforwrite写入的数据内容了。

1.2 ports配置信息

容器运行时通常会提供一些机制以将容器端口暴露出来,并映射到主机的端口上,以便其他人能通过“主机IP:端口”访问容器所提供的服务,例如,Docker的命令$ docker run -p {宿主机端口}:{容器端口} {镜像名称}。同样,Pod模板中也提供了这个功能。为了通过例子进行演示,首先,创建examplepodforport.yml文件。

1
$ vim examplepodforport.yml

在文件中填入以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Pod
metadata:
name: examplepodforport
spec:
containers:
- name: containerfornginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- name: portfoxnginx
containerPort: 80
hostPort: 8081
protocol: TCP

​ 在本例中,Nginx镜像中默认定义的对外提供服务的端口为80。通过containerPort属性,我们将80端口暴露出来,再通过hostPort属性将其映射到宿主机的端口8081上,以便通过“主机IP:端口”访问容器所提供的服务,其中protocol为端口协议支持TCP和UDP,默认为TCP

​ 执行以下命令,创建Pod。

1
$ kubectl apply -f examplepodforport.yml

image-20220812163407488

通过以下命令,查看Pod的运行情况,直到状态变为Running。

1
$ kubectl get pods examplepodforport

image-20220812163813405

Pod创建完成后,执行以下命令,查看Pod具体被分配到哪台Node上

1
$ kubectl describe pods examplepodforport

image-20220812164520644

Pod被分配到了izbp113w9axywnhpyk1525z/172.19.230.67

1
Node:         izbp113w9axywnhpyk1525z/172.19.230.67

使用wget检验端口是否开通

image-20220812172604700


注意:以上案例仅为了说明Kubernetes是如何创建容器的,这种类似于Docker直接映射到主机端口的方式,在Kubernetes中强烈不推荐

Pod只是一个运行服务的实例,随时可能在一个Node上停止,而在另一个Node上以新的IP地址启动新的Pod,因此它不能以稳定的IP地址和端口号提供服务。若要稳定地提供服务,则需要服务发现和负载均衡能力。


3.env配置信息

容器运行时通常还会提供一些机制来输入可动态配置的一些环境变量,以供容器中的应用程序使用。如在Docker中,配置环境变量的命令为$ docker run --env {变量1}={值1} --env {变量2}={值2} ... {镜像名称}。同样,Pod模板中也提供了这个功能。

首先,创建examplepodforenv.yml文件。

1
$ vim examplepodforenv.yml

填入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: examplepodforenv
spec:
containers:
- name: containerforenv
image: busybox
imagePullPolicy: IfNotPresent
env:
- name: parameter1
value: "good morning!"
- name: parameter2
value: "good night!"
command: ['sh','-c']
args: ['echo "${parameter1} ${parameter2}"; sleep 3600']

在模板中定义了一个名为containerforenv的容器,向它传入了两个环境变量:其中一个名为parameter1,值为goodmorning!;另一个变量名为parameter2,值为good night!。在本例中,将通过在容器中执行命令的方式,将传入的两个环境变量拼接到一起并输出到日志

执行以下命令,创建Pod

1
$ kubectl apply -f examplepodforenv.yml

运行以下命令,查看Pod的运行情况,直到状态变为Running

1
$ kubectl get pods examplepodforenv

image-20220812193426614

通过以下命令,查看Pod中输出的日志。

1
$ kubectl logs examplepodforenv

可以看到两个环境变量的值成功拼接到一起并输出到日志中

image-20220812193508164

2. Pod组织容器的方式

Pod的设计初衷在于同时运行多个共同协作的进程(作为容器来运行)。Pod中的各个容器总是作为一个整体,同时调度到某台Node上。容器之间可以共享资源、网络环境和依赖,并拥有相同的生命周期。

当然,在同一个Pod中同时运行和管理多个容器,是一种相对高级的用法,只在容器必须要紧密配合进行协作的时候才使用此模式。

1. 容器如何组成一个Pod

Pod只是一种抽象,并不是一个真正的物理实体,表示一组相关容器的逻辑划分。每个Pod都包含一个或一组密切相关的业务容器,除此之外,每个Pod都还有一个称为“根容器”的特殊Pause容器

image-20220812194430776

Pause容器其实属于Kubernetes的一部分。在一组容器作为一个单位的情况下,很难对整个容器组进行判断,如一个容器挂载了能代表整个Pod都挂载了吗?

如果引入一个和业务无关的Pause容器,用它作为Pod的根容器,用它的状态代表整组容器的状态,便能解决该问题。

另外,Pod中的所有容器都共享Pause容器的IP地址及其挂载的存储卷,这样也简化了容器之间的通信和数据共享问题。另外,Pause容器还在Pod中担任Linux命名空间共享的基础,为各个容器启用pid命名空间,开启init进程。

例如,对于本章最开始的操作示例,创建Pod后可以登录对应的Node,使用以下命令查看创建的容器。

1
$ docker ps

Pod中的容器可以使用Pod所提供的两种共享资源——存储和网络。

1) 存储

在Pod中,可以指定一个或多个共享存储卷。Pod中的所有容器都可以访问共享存储卷,从而让这些容器共享数据。存储卷也可以用来持久化Pod中的存储资源,以防容器重启后文件丢失。

image-20220812204444677

2)网络

每个Pod都分配了唯一的IP地址。Pod中的每个容器都共享网络命名空间,包括IP地址和网络端口。Pod内部的容器可以使用localhost互相通信。当Pod中的容器与Pod外部进行通信时,还必须共享网络资源(如使用端口映射)。

Docker和Kubernetes在网络空间上的差异

image-20220812215352573

要查看Pod的IP,可以使用以下命令

1
$ kubectl get pod my-app --template={{.status.podIP}}

image-20220813111719382

2.Pod之间如何通信

Docker其实一开始没有考虑多主机互连的网络解决方案。在实际的业务场景中,组件之间的管理十分复杂,应用部署的粒度更加细小。Kubernetes使用其独有的网络模型去解决这些问题。

Pod之间的通信主要涉及两个方面:

1)同一个Node上Pod之间的通信

因为同一个Node上的Pod使用的都是相同的Docker网桥,所以它们天然支持通信。

image-20220813111911317

每一个Pod都有一个全局IP地址,同一个Node内不同Pod之间可以直接采用对方Pod的IP地址通信,而且不需要使用其他发现机制。因为它们都是通过veth连接在同一个docker0网桥上的,其IP地址都是从docker0网桥上动态获取的,并关联在同一个docker0网桥上地址段也相同,所以它们之间能直接通信。

2)跨Node的Pod之间的通信

要实现跨Node的Pod之间的通信,首先需要保证的是Pod的IP地址所有Node上都是全局唯一的。这其实并不复杂,因为Pod的IP地址是由Docker 网桥分配的,所以可以将不同Node机器上的Docker网桥配置成不同的IP网段来实现这个功能。

然后需要在容器集群中创建一个覆盖网络来连接各个机器。目前可以通过第三方网络插件来覆盖网络。

Flannel会配置Docker网桥(即docker0),通过修改Docker的启动参数bip来实现这一点。通过这种方式,集群中各台机器的Docker网桥就得到了全局唯一的IP网段,它所创建的容器自然也拥有全局唯一的IP。


第4章 Pod--Kubernetes的基本单位
http://example.com/2022/07/30/第4章 Pod--Kubernetes的基本单位/
Author
WYX
Posted on
July 30, 2022
Licensed under