文章目录
主要详细介绍Pod资源的各种配置(即yaml文件)和原理。
1. Pod结构和定义
1) Pod结构
Pause容器,这是每个Pod都会有的一个根容器,它的作用有两个:
- 可以以它为依据,评估整个Pod的健康状态(Pod的寿命与Pause相同,而与用户容器无关);
- 可以在根容器上设置IP地址,其它容器都此IP(Pod IP),以实现Pod内部的网络通信(Pod的之间的通信采用虚拟二层网络技术来实现,当前环境用的是Flannel)。
summary:知道pod里有1根容器(Pause)+n个用户容器,可通过Pause容器判断健康状态、可以做IP共享。
2) Pod定义
Pod资源清单(yaml配置文件的编写,以下为常用的参数):
apiVersion: v1 #必选,版本号,例如v1
kind: Pod #必选,资源类型,例如 Pod
metadata: #必选,元数据
name: string #必选,Pod名称
namespace: string #Pod所属的命名空间,默认为"default"
labels: #自定义标签列表
- name: string
spec: #必选,Pod中容器的详细定义
containers: #必选,Pod中容器列表
- name: string #必选,容器名称
image: string #必选,容器的镜像名称
imagePullPolicy: [ Always|Never|IfNotPresent ] #获取镜像的策略
command: [string] #容器的启动命令列表,如不指定,使用打包时使用的启动命令
args: [string] #容器的启动命令参数列表
workingDir: string #容器的工作目录
volumeMounts: #挂载到容器内部的存储卷配置
- name: string #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
mountPath: string #存储卷在容器内mount的绝对路径,应少于512字符
readOnly: boolean #是否为只读模式
ports: #需要暴露的端口库号列表
- name: string #端口的名称
containerPort: int #容器需要监听的端口号
hostPort: int #容器所在主机需要监听的端口号,默认与Container相同
protocol: string #端口协议,支持TCP和UDP,默认TCP
env: #容器运行前需设置的环境变量列表
- name: string #环境变量名称
value: string #环境变量的值
resources: #资源限制和请求的设置
limits: #资源限制的设置
cpu: string #Cpu的限制,单位为core数,将用于docker run --cpu-shares参数
memory: string #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
requests: #资源请求的设置
cpu: string #Cpu请求,容器启动的初始可用数量
memory: string #内存请求,容器启动的初始可用数量
lifecycle: #生命周期钩子
postStart: #容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启
preStop: #容器终止前执行此钩子,无论结果如何,容器都会终止
livenessProbe: #对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器
exec: #对Pod容器内检查方式设置为exec方式
command: [string] #exec方式需要制定的命令或脚本
httpGet: #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
path: string
port: number
host: string
scheme: string
HttpHeaders:
- name: string
value: string
tcpSocket: #对Pod内个容器健康检查方式设置为tcpSocket方式
port: number
initialDelaySeconds: 0 #容器启动完成后首次探测的时间,单位为秒
timeoutSeconds: 0 #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
periodSeconds: 0 #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
successThreshold: 0
failureThreshold: 0
securityContext:
privileged: false
restartPolicy: [Always | Never | OnFailure] #Pod的重启策略
nodeName: <string> #设置NodeName表示将该Pod调度到指定到名称的node节点上
nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上
imagePullSecrets: #Pull镜像时使用的secret名称,以key:secretkey格式指定
- name: string
hostNetwork: false #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
volumes: #在该pod上定义共享存储卷列表
- name: string #共享存储卷名称 (volumes类型有很多种)
emptyDir: {} #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
hostPath: string #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
path: string #Pod所在宿主机的目录,将被用于同期中mount的目录
secret: #类型为secret的存储卷,挂载集群与定义的secret对象到容器内部
scretname: string
items:
- key: string
path: string
configMap: #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
name: string
items:
- key: string
path: string
可以按照层级结构来学习pod.yaml的编写,如第一层:apiVersion / kind / metadata / spec …;第一层之下又有第二层、第三层…。还可以通过如下命令查看每种资源的可配置项:
kubectl explain 资源类型 # 查看某种资源可配置的一级属性
# 例如
kubectl explain pod
- kubernetes中基本所有资源的一级属性都是一样的,主要包含5部分:
apiVersion <string>
: 版本,由kubernetes内部定义,版本号必须可以用 kubectl api-versions 查询到;
kind <string>
:类型,由kubernetes内部定义,版本号必须可以用 kubectl api-resources 查询到;
metadata <Object>
: 元数据,主要是资源标识和说明,常用的有name、namespace、labels等;常用的如下:
spec <Object>
: 描述,这是配置中最重要的一部分,里面是对各种资源配置的详细描述;status <Object>
: 状态信息,里面的内容不需要定义,由kubernetes自动生成,可以通过此字段查看pod(等资源)的状态。例如使用如下命令查看一个pod的状态:
kubectl get pods -n test-ns
kubectl get pod nginx-deploy-8446569dd6-4f45j -o yaml -n test-ns
查看某种资源属性的子属性:
kubectl explain 资源类型.属性 # 查看某种资源属性的子属性
# 例如
kubectl explain pod.spec
- 在上面的一级属性中,spec是重点,其子属性为:
-
containers <[]Object>
:容器列表,用于定义容器的详细信息。是一个对象列表因为一个pod中可以运行多个容器; -
nodeName <String>
:指定调度节点,根据nodeName的值将pod调度到指定的Node节点上; -
nodeSelector <map[]>
:根据NodeSelector中定义的信息选择将该Pod调度到包含这些label的Node上; -
hostNetwork <boolean>
:是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络。如果使用宿主机网络,则需要保证端口不要重复,因此一般不使用宿主机IP;
-
volumes <[]Object>
:存储卷,用于定义Pod上面挂在的存储信息; -
restartPolicy <string>
: 重启策略,表示Pod在遇到故障的时候的处理策略(重启?删除?不管?)。
-
2. Pod配置
这一节主要来研究 pod.spec.containers 属性,这也是pod配置中最为关键的一项配置。
常用参数:
FIELDS:
name <string> # 容器名称
image <string> # 容器需要的镜像地址
imagePullPolicy <string> # 镜像拉取策略(本地拉取?远程仓库拉取?...)
command <[]string> # 容器的启动命令列表,如不指定,使用打包时使用的启动命令
args <[]string> # 容器的启动命令需要的参数列表
env <[]Object> # 容器环境变量的配置
ports <[]Object> # 容器需要暴露的端口号列表
resources <Object> # 资源限制和资源请求的设置
1) 基本配置:创建一个yaml文件test-container-base.yaml:
# test-container-base.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-conta-base
namespace: test-ns
labels:
user: Cherry
spec:
containers:
- name: nginx-base
image: nginx:1.17.1
- name: busybox-base
image: busybox:1.30
上面定义了一个比较简单Pod的配置,里面有两个容器:
- nginx:用1.17.1版本的nginx镜像创建(nginx是一个轻量级web容器);
- busybox:用1.30版本的busybox镜像创建(busybox是一个小巧的linux命令集合)。
然后运行如下命令:
kubectl apply -f test-container-base.yaml
查看一下pod中的详情信息:
kubectl describe pod test-conta-base -n test-ns
因此只有一个容器成功启动,另一个有问题(后续讲解问题——启动命令部分)。
2) 镜像拉取策略
imagePullPolicy用于设置镜像拉取策略,kubernetes支持配置三种拉取策略:
kubectl explain pod.spec.containers
- Always:总是从远程仓库拉取镜像(一直远程下载);
- IfNotPresent:本地有则使用本地镜像,本地没有则从远程仓库拉取镜像(本地有就本地,本地没有就远程下载)。
- Never:只使用本地镜像,从不去远程仓库拉取,本地没有就报错(一直使用本地)。
imagePullPolicy的默认值取决于镜像的tag,如果镜像tag为具体版本号,默认策略是:IfNotPresent;如果镜像tag为:latest(最新版本) ,默认策略是always。在实际使用中一般不会使用镜像的最新版本,为了稳定会为其指定版本号,因此常用的imagePullPolicy默认值就是IfNotPresent。
创建如下yaml文件:
# test-image-pullpolicy.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-image-pullpolicy # 此name不准使用大写
namespace: test-ns
spec:
containers:
- name: nginx
image: nginx:1.17.2
imagePullPolicy: Never
- name: busybox
image: busybox:1.30
查看一下本地已经拉取的镜像:
docker images
可以看到,三个节点上均没有上述yaml文件指定的nginx:1.17.2镜像,同时镜像拉去策略为Nerver(只使用本地镜像,从不去远程仓库拉取),那这样的话运行结果是什么?
运行如下命令创建Pod:
kubectl apply -f test-image-pullpolicy.yaml
发现pod没有启动成功,查看出了什么错误:
kubectl describe pod test-image-pullpolicy -n test-ns
可以看到镜像不存在。初始将这个pod调度到了node2上,查看node2上的docker images:没有nginx:1.17.2
若想使其运行成功,则只需修改镜像拉去策略为IfNotPresent:
vim test-image-pullpolicy.yaml
# 修改如下部分:
imagePullPolicy: IfNotPresent
发现修改文件之后不能直接使用kubectl apply -f命令,因为仅当框框中的spec的值发生改变时才会更新,此处修改imagePullPolicy相当于没有改变。因此需要先删除pod,再重新apply。
kubectl delete -f test-image-pullpolicy.yaml
kubectl apply -f test-image-pullpolicy.yaml
虽然还没有运行成功(先不管,此处只关注镜像的拉取),查看一下pod中的状况:
此时可以看到,这个pod被调度到了node2上,且nginx:1.17.2已经被成功拉取,查看一下node2节点上的docker镜像:
3) 启动命令
在前面的案例中,一直有一个问题没有解决,就是的busybox容器一直没有成功运行(1/2:只运行了nginx容器,busybox容器没有成功运行)
那么到底是什么原因导致这个容器的故障呢?原来busybox并不是一个程序,而是类似于一个工具类的集合,即busybox里面并没有一个进程在运行,而都是一些小的命令,kubernetes集群启动管理后,它会自动关闭(因为容器中没有一个进程在占据着他,因此一启动就结束了)。解决方法就是让其一直在运行(例如在busybox中运行一个死循环),这就用到了command配置。
创建test-pod-command.yaml文件,内容如下:
# test-pod-command.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pod-command
namespace: test-ns
spec:
containers:
- name: nginx
image: nginx:1.17.1
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T)>> /tmp/hello.txt; sleep 3; done;"]
command:用于在pod中的容器初始化完毕之后运行一个命令。
- “/bin/sh”,“-c”, 使用sh执行命令;
- touch /tmp/hello.txt; 创建一个/tmp/hello.txt 文件;
- while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done; 每隔3秒向文件中写入当前时间。
显示nginx与busybox容器都运行成功了。查看/tmp/hello.txt是否有内容,即上述command指令是否正常执行:首先要进入容器:
# kubectl exec pod名称 -n 命名空间 -it -c 容器名称 /bin/sh # 在容器内部执行命令
kubectl exec test-pod-command -n test-ns -it -c busybox /bin/sh
# exec:执行,在docker中有同样的命令
# -n:指定命名空间
# -it:以交互式方式执行
# -c:指定容器,当这个颇多、中只有一个容器时可以不写
- 【说明】:
通过上面发现command已经可以完成启动命令和传递参数的功能,为什么这里还要提供一个args选项,用于传递参数呢?这其实跟docker有点关系,kubernetes中的command、args两项其实是实现覆盖Dockerfile中ENTRYPOINT的功能:
- 如果command和args均没有写,那么用Dockerfile的配置;
- 如果command写了,但args没有写,那么Dockerfile默认的配置会被忽略,执行输入的command;
- 如果command没写,但args写了,那么Dockerfile中配置的ENTRYPOINT的命令会被执行,使用当前args的参数(相当于使用Dockerfile命令,但为其配置新的参数);
- 如果command和args都写了,那么Dockerfile的配置被忽略,执行command并追加上args参数。
4) 环境变量
env参数:环境变量,用于在pod中的容器设置环境变量。数组类型,一个环境变量就是一个键值对。
创建test-pod-env.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: test-pod-env
namespace: test-ns
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","while true;do /bin/echo $(date +%T);sleep 60; done;"]
env: # 设置环境变量列表
- name: "username"
value: "admin"
- name: "password"
value: "123456"
首先创建pod:
kubectl apply -f test-pod-env.yaml -n test-ns
然后进入容器,查看环境变量:
kubectl exec test-pod-env -n test-ns -it /bin/sh
这种方式不是很推荐,推荐将这些配置单独存储在配置文件中(在后面介绍)。
5) 端口设置
端口的设置对应pod.spec.containers.ports=容器需要暴露的端口号列表,查看ports的子选项:
hostIP、hostPort解释:
编写一个测试案例test-pod-ports.yaml:
apiVersion: v1
kind: Pod
metadata:
name: test-pod-ports
namespace: test-ns
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports: # 设置容器暴露的端口列表
- name: nginx-port
containerPort: 80
protocol: TCP
访问容器中的程序需要使用的是 podIP:containerPort。
6) 资源配额
容器中的程序要运行,肯定是要占用一定资源的,比如cpu和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量资源,导致其它容器无法运行(一个节点上的资源总额是一定的,例如内存,若A容器忽然“发疯”,开始大量消耗内存,如果未对其进行限制,则势必会导致BCDE…等其他容器无法正常运行)。针对这种情况,kubernetes提供了对内存和cpu的资源进行配额的机制,这种机制主要通过resources选项实现,他有两个子选项:可以通过下面两个选项设置资源的上下限。
- limits:用于限制运行时容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启;
- requests :用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动。
编写一个测试案例(针对CPU和内存),创建test-pod-resources.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pod-resources
namespace: test-ns
spec:
containers:
- name: nginx
image: nginx:1.17.1
resources: # 资源配额
limits: # 限制资源(上限)
cpu: "2" # CPU限制,单位是core数
memory: "10Gi" # 内存限制
requests: # 请求资源(下限)
cpu: "1" # CPU限制,单位是core数
memory: "10Mi" # 内存限制
- cpu:core数,可以为整数或小数;
- memory: 内存大小,可以使用Gi、Mi、G、M等形式。
运行成功了,即当前的环境满足配置文件中的条件了。若将pod的资源下限改为10Gi还能运行成功吗:
requests: # 请求资源(下限)
cpu: "1" # CPU限制,单位是core数
memory: "10Gi" # 内存限制
首先删除原pod:
kubectl delete -f test-pod-resources.yaml
然后修改test-pod-resources.yaml中的上述配置信息,重新创建pod:
可以看到,新创建的pod没有运行成功,处于Pending状态。查看详细信息:
显示三个节点上没有可以满足内存需求的,因此没有调度部署成功。
参考:B站大学