三、Pod详解
### 一、pod简介
Pod是Kubernetes最小的管理单位,一个Pod可以封装一个容器或多个容器。
一个Pod里的多个容器可以共享存储和网络,一个pod可以看作一个逻辑上的主机。
因为多个容器共享同一个network namespace,所以一个Pod内的多个容器之间可以通过localhost来进行通信,所需要注意的是不同容器要注意不要有端口冲突即可。
一个Pod里的多个容器可以共享存储卷,这个存储卷会被定义为Pod的一部分,并且可以挂载到该Pod里的所有容器的文件系统上。
二、pod分类
pod分为两类,不受控制器管理的自主式Pod,跟受控制器管理的Pod
-
自主式pod:没有副本控制器控制,删除自主式pod后不会重新创建
-
控制器管理的pod:控制器会按照定义的策略控制pod的数量,发现pod数量少了,会立即自动建立出来新的pod;一旦发现pod多了,也会自动杀死多余的Pod。
三、Pod创建
3.1 命令行创建pod
kubectl run nginx --image=nginx --replicas=3
- kubectl run:运行容器
- nginx:pod名
- --image:镜像名称
- --replicas:副本数
3.2 yaml文件创建pod
1.创建包含一个容器的pod
这里定义一个自主式pod,即没有控制器控制的pod,注意在生产环境中,都是使用控制器来控制pod的,这里仅作案例介绍使用
编辑名为pod_stress.yaml的文件,添加如下内容
apiVersion: v1 # api版本(不同版本语法有少量差异),这里为v1
kind: Pod # 资源类型为Pod
metadata: #定义该pod的元数据,即属性
name: memory-demo #pod的名称
spec: #定义规则
containers:
- name: demo #容器名
image: polinux/stress #镜像名
imagePullPolicy: IfNotPresent #拉取镜像规则,本地没有就从网络上拉取
command: ["stress"] #压力测试命令,让容器立即使用150M内存
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
polinux/stress这个镜像用于压力测试,在启动容器时传命令与参数就是相当于分配容器运行时需要的压力
imagePullPolicy:镜像拉取策略
Always : 只从网络镜像仓库中下载镜像
Never : 只使用本地镜像
IfNotPresent: 如果本地存在就直接使用, 不存在才从网络镜像仓库中下载
2.应用ymal文件
kubectl apply -f pod_stress.yaml
3.验证
kubectl get pod -o wide
kubectl describe pod memory-demo #查看pod详细信息
kubectl top pod
使用
kubectl top pod
查看使用资源信息,需要安装Metrics-server组件,安装方法看这里
4.删除pod
kubectl delete -f pod_stress.yaml
kubectl delete pod memory-demo
2.创建包含多个容器的pod
1, 准备yml文件
$ vim pod_Multiple.yml
apiVersion: v1
kind: Pod
metadata:
name: pod-multiple
spec:
containers:
- name: memory-demo-ctr-1 #容器名不能一样,因为他们在同一个pod中
image: polinux/stress
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "200Mi"
requests:
memory: "100Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
- name: memory-demo-ctr-2
image: polinux/stress
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "200Mi"
requests:
memory: "100Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
2.应用yaml文件
kubectl apply -f pod_Multiple.yml
3.验证
kubectl get pods -o wide
docker ps -a |grep stress
4.删除pod
kubectl delete -f pod_Multiple.yml
kubectl delect pod pod-multiple
3.3 yaml文件注解
1.完整的定义pod的yaml文件注解
apiVersion: v1 #必选,版本号,例如v1
kind: Pod #必选,Pod
metadata: #必选,元数据
name: string #必选,Pod名称
namespace: string #必选,Pod所属的命名空间
labels: #自定义标签
- name: string #自定义标签名字
annotations: #自定义注释列表
- name: string
spec: #必选,Pod中容器的详细定义
containers: #必选,Pod中容器列表
- name: string #必选,容器名称
image: string #必选,容器的镜像名称
imagePullPolicy: [Always | Never | IfNotPresent] #获取镜像的策略 Alawys表示下载镜像 IfnotPresent表示优先使用本地镜像,否则下载镜像,Nerver表示仅使用本地镜像
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 #内存清楚,容器启动的初始可用数量
livenessProbe: #对Pod内个容器健康检查的设置,当探测无响应几次后将自动重启该容器,检查方法有exec、httpGet和tcpSocket,对一个容器只需设置其中一种方法即可
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的重启策略,Always表示一旦不管以何种方式终止运行,kubelet都将重启,OnFailure表示只有Pod以非0退出码退出才重启,Nerver表示不再重启该Pod
nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上,以key:value的格式指定
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的存储卷,挂载集群与定义的secre对象到容器内部
scretname: string
items:
- key: string
path: string
configMap: #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
name: string
items:
- key: string
path: string
2.YAML格式查找帮助方法
查看目前使用的api版本
kubectl api-versions
3.查看yaml帮助
kubectl explain namespace
kubectl explain pod
kubectl explain pod.spec
kubectl explain pod.spec.containers
因为yaml文件是分层级的,所以查看pod下的spec定义就需要使用pod.spec来查看
四、pod调度
我们为了实现容器主机资源平衡使用, 可以使用约束把pod调度到指定的node节点上。
这里支持两种调度方式,nodeName 跟nodeSelector
-
nodeName:用于将pod调度到指定的node名称上
-
nodeSelector:用于将pod调度到匹配Label的node上
3.1 案例1: nodeName
1.编写YAML文件
编辑名为pod_nginx.yaml的文件,添加如下内容
apiVersion: v1 # api版本(不同版本语法有少量差异),这里为v1
kind: Pod # 资源类型为Pod
metadata: #定义该pod的元数据,即属性
name: nginx-pod #pod的名称
spec: #定义规则
nodeName: k8s-node01 #将该pod调度到k8s-node01节点上
containers:
- name: nginx #容器名
image: nginx:1.15-alpine #镜像名
imagePullPolicy: IfNotPresent #拉取镜像规则,本地没有就从网络上拉取
2.应用ymal文件
kubectl apply -f pod_nginx.yaml
3.验证
kubectl get pod -o wide
4.删除
kubectl delete -f pod_nginx.yaml
kubectl delete pod nginx-pod
3.2 案例2: nodeSelector
1.给节点打标签
kubectl label nodes k8s-node01 bussiness=ad
kubectl get nodes -L bussiness
2.编写yaml文件
编辑名为pod_nginx.yaml的文件,添加如下内容
apiVersion: v1 # api版本(不同版本语法有少量差异),这里为v1
kind: Pod # 资源类型为Pod
metadata: #定义该pod的元数据,即属性
name: nginx-pod #pod的名称
spec: #定义规则
nodeSelector:
bussiness: ad #将该pod调度到标签为bussiness: ad的节点上
containers:
- name: nginx #容器名
image: nginx:1.15-alpine #镜像名
imagePullPolicy: IfNotPresent #拉取镜像规则,本地没有就从网络上拉取
3.应用ymal文件
kubectl apply -f pod_nginx.yaml
3.验证
kubectl get pod -o wide
4.删除
kubectl delete -f pod_nginx.yaml
kubectl delete pod nginx-pod
五、pod的标签
为pod设置label,pod的label用于controller关联控制pod
kubectl get pods --show-labels
打标签
kubectl label pod memory-demo region=huanai zone=A env=test bussiness=game
搜索标签
kubectl get pods -l zone=A
kubectl get pods -l "zone in (A,B,C)"
删除标签
kubectl label pod memory-demo region- zone- env- bussiness-
kubectl get pods --show-labels
使用标签的key后面接-就能删除标签
六、pod资源限制
这里创建两个pod来对比观察限制效果
1.创建第一个pod的yaml文件
$ vim pod_stress01.yml
apiVersion: v1
kind: Namespace
metadata:
name: memory-test
---
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-01
namespace: memory-test
spec:
containers:
- name: memory-demo-ctr
image: polinux/stress
imagePullPolicy: IfNotPresent
resources:
limits: #限制使用内存200M
memory: "200Mi"
requests: #请求内存100M
memory: "100Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"] # 该命令作用为产生1个进程分配150M内存1秒后释放
-
这里创建了一个memory-test的命名空间,多个yaml写在一个文件中使用
---
分隔。 -
该pod限制最大使用内存200M,实际请求内存150M,该Pod是不会被杀死的
2.创建第二个pod的yaml文件
$ vim pod_stress02.yml
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-02
namespace: memory-test
spec:
containers:
- name: memory-demo-ctr
image: polinux/stress
imagePullPolicy: IfNotPresent
resources:
limits: #限制使用内存200M
memory: "200Mi"
requests: #请求内存100M
memory: "100Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]
限制使用最大内存200M,实际请求使用内存是250M,该Pod会被杀死。
3.应用yaml清单
kubectl apply -f pod_stress01.yml
kubectl apply -f pod_stress02.yml
4.验证
kubectl get pod -n memory-test -w
kubectl describe pod memory-demo-02 -n memory-test |tail -6
-
查看会发现memory-demo-02这个pod状态变为OOMKilled,因为它是内存不足所以显示Container被杀死;
-
这次创建pod使用了memory-test命名空间,查看pod跟删除pod都要指定memory-test命名空间
5.删除
删除命名空间,该命名空间里的pod都会被删除。
kubectl delete ns memory-test
kubectl get pod --all-namespaces #查看所有命名空间的pod,可以发现删除memory-test命名空间,该命名空间里的pod也被删除了
七、对pod里的容器进行操作
7.1 使用docker命令进入容器
1.先查看Pod在哪个节点上
kubectl get pods -o wide
2.查看容器名
docker ps
3.进入容器
docker exec -it k8s_xxxxx /bin/bash
- 使用docker进入容器较为繁琐。
7.2 使用kubectl进入容器
1.查看帮助
kubectl exec -h
2.不进入容器执行命令
#先查看pod名
kubectl get pods
#不进入容器执行命令,执行的命令跟pod名之间使用--隔开
kubectl exec pod1 -- date
#如果pod有多个容器,需要-c指定要操作的容器,否则默认找第一个容器
kubectl exec -it pod名 -c 容器名 -- touch /111
- 如果pod中有多个容器,不指定容器名默认为第1个容器
3.进入容器交互操作
格式为kubectl exec -it pod名 -c 容器名 -- /bin/bash
#进入容器,有些容器没有bash,就用sh
kubectl exec -it pod-multiple -- /bin/bash
#如果pod中有多个容器,-c指定进入容器的名字
kubectl exec -it pod-multiple -c memory-demo-ctr-2 -- /bin/bash
八、pod重启
1.有yaml文件的pod重启方式
kubectl replace --force -f xxx.yaml
2.没有yaml文件但使用Deployment 创建的pod重启方式
kubectl scale deployment esb-admin --replicas=0 -n {namespace}
kubectl scale deployment esb-admin --replicas=1 -n {namespace}
这里使用的是扩容缩容副本数的方式
参考资料:pod的重启
九、pod删除
kubectl delete pod pod{1..4} #如果pod命名为pod1,pod2,pod3,pod4则可使用该方法批量删除pod
kubectl delete pod xxx xxx xxx #删除多个pod用空格隔开
kubectl delete -f xxx.yaml #使用yaml文件删除
十、pod的生命周期
pod从创建到终止的过程就是pod的生命周期。
容器启动
-
pod中的容器在创建前,有初始化容器(init container)来进行初始化环境
-
初化完后,主容器(main container)开始启动
-
主容器启动后,有一个post start的操作(启动后的触发型操作,或者叫启动后钩子)
-
post start后,就开始做健康检查
- 第一个健康检查叫存活状态检查(liveness probe ),用来检查主容器存活状态的
- 第二个健康检查叫准备就绪检查(readyness probe),用来检查主容器是否启动就绪
容器终止
-
可以在容器终止前设置pre stop操作(终止前的触发型操作,或者叫终止前钩子)
-
当出现特殊情况不能正常销毁pod时,大概等待30秒会强制终止
-
终止容器后还可能会重启容器(视容器重启策略而定)
9.1 案例1:post-start
1, 编写YAML文件
主要用于定义环境变量时使用
$ vim pod-poststart.yml
apiVersion: v1
kind: Pod
metadata:
name: poststart
namespace: default
spec:
containers:
- name: poststart
image: nginx:1.15-alpine
imagePullPolicy: IfNotPresent
lifecycle: # 生命周期事件
postStart: # 在容器启动后就创建一个tz.html文件
exec:
command: ["mkdir","-p","/usr/share/nginx/html/tz.html"]
2, 应用YMAL文件
kubectl apply -f pod-poststart.yml
3, 验证
kubectl get pods -w
kubectl exec -it poststart -- ls /usr/share/nginx/html -l
9.2 案例2:pre-stop
容器终止前执行的命令
1, 编写YAML文件
$ vim pre-stop.yml
apiVersion: v1
kind: Pod
metadata:
name: prestop
namespace: default
spec:
containers:
- name: prestop
image: nginx:1.15-alpine
imagePullPolicy: IfNotPresent
lifecycle: # 生命周期事件
preStop: # 让容器终止前sleep 60000000秒
exec:
command: ["/bin/sh","-c","sleep 60000000"]
2, 应用YAML文件创建pod
kubectl apply -f pre-stop.yml
3, 删除pod验证
kubectl delete -f pre-stop.yml
会在删除这一步等待30s,此时验证是成功的,因为当出现特殊情况不能正常销毁pod时,大概等待30秒会强制终止。
十一、健康检查
健康检查用于当Pod启动时,检查容器是否启动,服务是否正常运行;
健康检查有两种方式,存活状态探测跟就绪型状态探测。
健康检查方式
健康检查方式 | 说明 |
---|---|
Liveness Probe | 存活状态探测,如检查未通过,则重启pod |
Readiness Probe | 就绪型探测,如检查未通过,会将容器STATUS设置为Notready;如果使用service来访问,流量不会转发给此种状态的pod(也就是处于Notready的容器运行的服务是访问不了的,不会将客户端请求发给该容器) |
探测方式
探测方式 | 说明 |
---|---|
Exec | 执行命令 |
HTTPGet | http请求某一个URL路径 |
TCP | tcp连接某一个端口 |
案例1: liveness-exec
使用命令探测容器是否正常。
1, 准备YAML文件
$ vim pod-liveness-exec.yml
apiVersion: v1
kind: Namespace
metadata:
name: test
--- #多个yaml文件使用---分隔
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec
namespace: test
spec:
containers:
- name: liveness
image: busybox
imagePullPolicy: IfNotPresent
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600 #创建healthy文件,等30秒后删除它
livenessProbe:
exec:
command:
- cat
- /tmp/healthy #检查是否有/tmp/healthy文件
initialDelaySeconds: 5 # pod启动后等5秒开始探测
periodSeconds: 5 #每5秒探测一次
这里创建了一个名为test的命名空间,多个yaml文件写成一个使用
---
分隔
2, 应用YAML文件
kubectl apply -f pod-liveness-exec.yml
3, 通过下面的命令观察
kubectl get pods -n test
kubectl describe pod liveness-exec -n test | tail -6
可以看到该容器重启了1次
容器重启策略
Always:表示容器挂了总是重启,这是默认策略
OnFailures:表容器状态为错误时才重启,也就是容器正常终止时才重启
Never:表示容器挂了不予重启
对于Always这种策略,容器只要挂了,就会立即重启,这样是很耗费资源的。所以Always重启策略是第一次容器挂了立即重启,如果再挂了就要延时10s重启,第三次挂了就等20s重启...... 依次类推,时间间隔会越来越长。
案例2: liveness-httpget
请求容器web服务检测容器是否正常。
1, 编写YMAL文件
$ vim pod-liveness-httpget.yml
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget
namespace: test
spec:
containers:
- name: liveness
image: nginx:1.15-alpine
imagePullPolicy: IfNotPresent
ports: # 指定容器端口,这一段不写也行,端口由镜像决定
- name: http # 自定义名称,不需要与下面的port: http对应
containerPort: 80 # 类似dockerfile里的expose 80
livenessProbe:
httpGet: # 使用httpGet方式
port: http # http协议,也可以直接写80端口
path: /index.html # 探测家目录下的index.html
initialDelaySeconds: 3 # 延迟3秒开始探测
periodSeconds: 5 # 每隔5s钟探测一次
2, 应用YAML文件
kubectl apply -f pod-liveness-httpget.yml
3, 验证查看
kubectl get pods -n test
kubectl describe pod livness-httpget -n test
4, 删除nginx里的主页文件
kubectl exec -it liveness-httpget -n test -- rm -rf /usr/share/nginx/html/index.html
#或者进入pod中的容器将html重命名,效果是一样的
kubectl exec -it liveness-httpget -n test -- /bin/sh
cd /usr/share/nginx/html/
mv index.html index.html.bak
5, 验证查看会发现,找不到主页,就重启了
kubectl describe pod liveness-httpget -n test | tail -6
kubectl get pods -n test #可以看到重启了
案例3: liveness-tcp
检查容器tcp端口是否正常。
1, 编写YAML文件
$ vim pod-liveness-tcp.yml
apiVersion: v1
kind: Pod
metadata:
name: liveness-tcp
namespace: default
spec:
containers:
- name: liveness
image: nginx:1.15-alpine
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
livenessProbe:
tcpSocket: # 使用tcp连接方式
port: 80 # 连接80端口进行探测
initialDelaySeconds: 3
periodSeconds: 5
2, 应用YAML文件创建pod
kubectl apply -f pod-liveness-tcp.yml
3, 查看验证
kubectl get pod
kubectl describe pod liveness-tcp
4, 交互关闭nginx
kubectl exec -it liveness-tcp -- /usr/sbin/nginx -s stop
5, 再次验证查看,发现pod开始重启
kubectl describe pod liveness-tcp
案例4: readiness-httpget
检查容器是否就绪,如果容器触发条件,就会从READY变为0/1,但不会重启容器,同时也不会走流量给该容器。
1, 编写YAML文件
$ vim pod-readiness-httpget.yml
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget
namespace: default
spec:
containers:
- name: readiness
image: nginx:1.15-alpine
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
readinessProbe: # 这里由liveness换成了readiness
httpGet:
port: http
path: /index.html
initialDelaySeconds: 3
periodSeconds: 5
2, 应用YAML文件
kubectl apply -f pod-readiness-httpget.yml
3, 验证查看
kubectl get pod -n default
4, 删除nginx主页
kubectl exec -it readiness-httpget -- rm -rf /usr/share/nginx/html/index.html
#进入容器删除,效果是一样的
kubectl exec -it readiness-httpget -n default -- /bin/sh
rm -rf /usr/share/nginx/html/index.html
5, 再次验证
kubectl get pod -n default
发现pod并没有重启容器,而是READY变为了0/1
6, 创建nginx主页文件再验证
kubectl exec -it readiness-httpget -- touch /usr/share/nginx/html/index.html
kubectl get pod
READY状态又为1/1了
案例5: readiness+liveness综合
1, 编写YAML文件
$ vim pod-readiness-liveiness.yml
apiVersion: v1
kind: Pod
metadata:
name: readiness-liveness-httpget
namespace: default
spec:
containers:
- name: readiness-liveness
image: nginx:1.15-alpine
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
livenessProbe:
httpGet:
port: http
path: /index.html
initialDelaySeconds: 5 #容器启动后5秒检测请求页面
periodSeconds: 5 #每隔5秒检测一次
readinessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 1 #容器启动1秒后开始检测80端口
periodSeconds: 3 #每个3秒检测一次
此时会先查看80端口是否通畅,然后再检查是否能请求到/index.html文件
2, 应用YAML文件
kubectl apply -f pod-readiness-liveiness.yml
3, 验证
kubectl get pod
#删除容器的index.html文件
kubectl exec -it readiness-liveness-httpget -- rm -rf /usr/share/nginx/html/index.html
#进入容器删除,效果一样
kubectl exec -it readiness-liveness-httpget -n default -- /bin/sh
rm -rf /usr/share/nginx/html/index.html
#发现容器已经重启了
kubectl get pod -w
#停止容器的nginx服务,pod变为未就绪状态,不会走流量,这样也访问不到index.html,触发livenessProbe,pod就会重启
kubectl exec -it readiness-liveness-httpget -- /usr/sbin/nginx -s stop
十二、参考资料
黑马Linux-k8s第二天视频