pod只是逻辑上的概念,可以把pod看成一个容器,容器内跑的是一个或多个容器实体(docker容器)。Pod资源spec.containers
对象说明
pod.spec.containers <[]Object>
字段:
name <string> 必选字段,表明容器名称
image <string> 镜像地址
imagePullPolicy <string> 镜像拉取策略,Always, Never, IfNotPresent 其中之一,当镜像的tag为latest时,默认为Always
ports <[]Object> 暴露端口,是说明性信息,并不代表容器是否真正暴露端口,只要容器监听某个端口,集群内就能访问相应的服务
containerPort <integer> 必选项,容器暴露的端口号
name <string> 起一个名称
command <[]string> 容器中执行的命令
args <[]string> 容器中为执行命令传递的参数,如果要引用变量使用“$(VAR_NAM)”,使用“$$(VAR_NAM)”为变量逃逸
说明:镜像中可能会有CMD和ENTRYPOINT两个字段,镜像中的ENTRYPOINT相当于k8s中的command,镜像中的CMD相当于k8s中的args,如果定义资源清单时也定义了command和args字段,这4个字段混合使用会产生不同的场景。官方已给了详细的说明:https://kubernetes.io/zh/docs/tasks/inject-data-application/define-command-argument-container/
标签与标签选择器
官方文档参考: https://kubernetes.io/zh/docs/concepts/overview/working-with-objects/labels/
标签是附加到 Kubernetes 对象(比如 Pods)上的键值对。标签旨在用于指定对用户有意义且相关的对象的标识属性,但不直接对核心系统有语义含义。 标签可以用于组织和选择对象。标签可以在创建时附加到对象,随后可以随时添加和修改。
标签可以在多个维度上进行定义,如
"release" : "stable", "release" : "canary" # 以发布的版本为维度
"environment" : "dev", "environment" : "qa", "environment" : "production" # 发环境为维度
"tier" : "frontend", "tier" : "backend", "tier" : "cache" # 以层次为维度
"partition" : "customerA", "partition" : "customerB" # 以分区为维度
"track" : "daily", "track" : "weekly"
标签语法和字符集
标签 是键值对。有效的标签键有两个段:可选的前缀和名称,用斜杠(/
)分隔。名称段是必需的,必须小于等于 63 个字符,以字母数字字符([a-z0-9A-Z]
)开头和结尾,带有破折号(-
),下划线(_
),点( .
)和之间的字母数字。
有效标签值必须为 63 个字符或更少,并且必须为空或以字母数字字符([a-z0-9A-Z]
)开头和结尾,中间可以包含破折号(-
)、下划线(_
)、点(.
)和字母或数字。
标签相关命令
显示标签
在k8s中使用kubectl get
来获取某种资源时,在最后加上--show-lables
选项就能显示该种资源的标签信息,如
$ kubectl get pods --show-lables
$ kubectl get nodes --show-lables
过滤标签
-L, --label-columns=[]
显示某个资源指定标签的值,没有就为空
-l, --selector=‘‘
过滤出指定的标签的资源
k8s@node01:~$ kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
myapp-dep-c988cf69-2dps2 1/1 Running 0 5h33m app=myapp-dep,pod-template-hash=c988cf69
myapp-dep-c988cf69-hjm8j 1/1 Running 0 5h33m app=myapp-dep,pod-template-hash=c988cf69
myapp-dep-c988cf69-j56hp 1/1 Running 0 5h33m app=myapp-dep,pod-template-hash=c988cf69
mynginx-deployment-646959f957-jqq67 1/1 Running 0 5h33m app=mynginx-deployment,pod-template-hash=646959f957
pod-demo 2/2 Running 0 48s app=myapp,tier=frontend
k8s@node01:~$ kubectl get pods -L tier,app
NAME READY STATUS RESTARTS AGE TIER APP
myapp-dep-c988cf69-2dps2 1/1 Running 0 5h36m myapp-dep
myapp-dep-c988cf69-hjm8j 1/1 Running 0 5h36m myapp-dep
myapp-dep-c988cf69-j56hp 1/1 Running 0 5h36m myapp-dep
mynginx-deployment-646959f957-jqq67 1/1 Running 0 5h36m mynginx-deployment
pod-demo 2/2 Running 0 3m58s frontend myapp
k8s@node01:~$ kubectl get pods -l tier
NAME READY STATUS RESTARTS AGE
pod-demo 2/2 Running 0 4m12s
打标签
k8s@node01:~$ kubectl label pods pod-demo release=canary # 给pod-demo这个pod对象打标签
pod/pod-demo labeled
k8s@node01:~$ kubectl get pod pod-demo --show-labels
NAME READY STATUS RESTARTS AGE LABELS
pod-demo 2/2 Running 0 7m52s app=myapp,release=canary,tier=frontend
# 对已有标签重新打标签需要加了“--overwrite”选项
k8s@node01:~$ kubectl label pods pod-demo release=stable --overwrite
pod/pod-demo labeled
k8s@node01:~$ kubectl get pod pod-demo --show-labels
NAME READY STATUS RESTARTS AGE LABELS
pod-demo 2/2 Running 0 10m app=myapp,release=stable,tier=frontend
标签选择器分类
基于等值关系的标签选择器
操作符: =
或==
表示相等,!=
表示不相等
k8s@node01:~$ kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
myapp-dep-c988cf69-2dps2 1/1 Running 0 5h52m app=myapp-dep,pod-template-hash=c988cf69,release=canary
myapp-dep-c988cf69-hjm8j 1/1 Running 0 5h52m app=myapp-dep,pod-template-hash=c988cf69
myapp-dep-c988cf69-j56hp 1/1 Running 0 5h52m app=myapp-dep,pod-template-hash=c988cf69
mynginx-deployment-646959f957-jqq67 1/1 Running 0 5h52m app=mynginx-deployment,pod-template-hash=646959f957,release=canary
pod-demo 2/2 Running 0 19m app=myapp,release=stable,tier=frontend
k8s@node01:~$ kubectl get pods -l release==canary --show-labels
NAME READY STATUS RESTARTS AGE LABELS
myapp-dep-c988cf69-2dps2 1/1 Running 0 5h53m app=myapp-dep,pod-template-hash=c988cf69,release=canary
mynginx-deployment-646959f957-jqq67 1/1 Running 0 5h53m app=mynginx-deployment,pod-template-hash=646959f957,release=canary
k8s@node01:~$ kubectl get pods -l release=canary,app=myapp-dep --show-labels # 多个条件
NAME READY STATUS RESTARTS AGE LABELS
myapp-dep-c988cf69-2dps2 1/1 Running 0 5h52m app=myapp-dep,pod-template-hash=c988cf69,release=canary
k8s@node01:~$ kubectl get pods -l app!=myapp-dep --show-labels
NAME READY STATUS RESTARTS AGE LABELS
mynginx-deployment-646959f957-jqq67 1/1 Running 0 5h53m app=mynginx-deployment,pod-template-hash=646959f957,release=canary
pod-demo 2/2 Running 0 20m app=myapp,release=stable,tier=frontend
基于集合关系的标签选择器
操作符号: in
,notin
,!
k8s@node01:~$ kubectl get pods -l "release in (canary, release, beta)" --show-labels
NAME READY STATUS RESTARTS AGE LABELS
myapp-dep-c988cf69-2dps2 1/1 Running 0 5h59m app=myapp-dep,pod-template-hash=c988cf69,release=canary
mynginx-deployment-646959f957-jqq67 1/1 Running 0 5h59m app=mynginx-deployment,pod-template-hash=646959f957,release=canary
k8s@node01:~$ kubectl get pods -l "release notin (canary, release, beta)" --show-labels
NAME READY STATUS RESTARTS AGE LABELS
myapp-dep-c988cf69-hjm8j 1/1 Running 0 5h59m app=myapp-dep,pod-template-hash=c988cf69
myapp-dep-c988cf69-j56hp 1/1 Running 0 5h59m app=myapp-dep,pod-template-hash=c988cf69
pod-demo 2/2 Running 0 27m app=myapp,release=stable,tier=frontend
k8s@node01:~$ kubectl get pods -l "release" --show-labels # 显示有 release 标签的资源
NAME READY STATUS RESTARTS AGE LABELS
myapp-dep-c988cf69-2dps2 1/1 Running 0 6h3m app=myapp-dep,pod-template-hash=c988cf69,release=canary
mynginx-deployment-646959f957-jqq67 1/1 Running 0 6h3m app=mynginx-deployment,pod-template-hash=646959f957,release=canary
pod-demo 2/2 Running 0 31m app=myapp,release=stable,tier=frontend
k8s@node01:~$ kubectl get pods -l !"release" --show-labels # 不支持显示没有 release 标签的资源
-su: !"release": event not found
资源清单定义标签选择器
许多资源支持内嵌字段定义其使用的标签选择器
matchLable: 直接给定键值
mathExpressions: 基于给定的表达式来定义使用的标签选择器,格式为 {key: "KEY", operator: "OPERATOR", values: [VAL1, VAL2, ...]},表示 KEY这个键基于 [VAL1, VAL2, ...]这些值做 OPERATOR 比较。operator常用操作符号一般为In,NotIn
,values必须为非空列表,Exists, NotExists
,values必须为空列表。
pod的节点标签选择器
在pods.spec
下有字段nodeSelector <map[string]string>
,表示pod运行节点的倾向性,比如k8s集群内的工作节点配置不相同,一些节点有ssd磁盘,有些节点有比较大的内存或多颗cpu,这样可以针对各个节点的特点打上标签,然后在定义pod时使用节点选择器让pod更有倾向运行在打有相应标签的节点上。
在pods.spec
下有字段nodeName <string>
,表示让pod运行在指定的节点上
元数据annotations字段
pod.annotations
定义pod的注解信息。
annotations <map[string]string>
与label不同的地方在于它不能用于挑选资源对象,仅用于为对象提供元数据
。
综合以上,对pods-demo.yaml文件进行修改
k8s@node01:~/my_manifests$ cat pods-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-demo
namespace: default
labels:
app: myapp
tier: frontend
annotations:
abc.com/created-by: "cluster admin"
spec:
containers:
- name: app
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
- name: bbox
image: busybox:latest
imagePullPolicy: IfNotPresent
command:
- "/bin/sh"
- "-c"
- "sleep 3600"
nodeName: node03
pod生命周期
pod创建过程
1. 通过kubectl向apiserver发送创建Pod资源的请求
2. apiserver把请求创建资源的目标状态保存在etcd中,apiserver请求scheduler进行调度,并把调度的结果(调度在哪个节点上)保存在pod信息相关etcd中
3. apiserver通知目标工作节点的kubelet有新的资源创建请求,并拿到相应的资源清单,根据清单在当前节点创建资源,并把相应的创建的状态信息发送回apiserver
pod状态:
Pending: 挂起状态,调度未完成
Running: 正常运行状态
Failed: 运行失败
Succeeded: 成功
Unknown: 工作节点kubelet出现故障,apiserver无法获取状态信息
Pod生命周期中的重要行为:
-
初始化容器,在创建Pod时需要先做容器的初始化操作
-
容器探测
探测有两种类型: liveness probe 存活性探测,用于探测容器是否处于存活状态 readiness probe 就绪性探测,用于探测容器中的程序是否能够正常提供服务 每种探测类型都支持3种探针: 1. ExecAction, 执行自定义的命令 2. TCPSockerAction, 向指定的tcp端口发请求 3. HTTPGetAction, 向指定的http服务发请求
pod的重启策略
pod.spec
下有字段restartPolicy
restartPolicy <string>
One of Always, OnFailure, Never. Default to Always. More info:
https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy
Always: 总是重启,其内部实现延时重启策略,如:第一次异常立刻重启,第二次重启就得等30秒,第三次重启就得等更长的时间,最多等待300秒重启
OnFailure:状态为错误时重启
Never:不管是什么原因永不重启
pod终止过程
kubectl delete -f FILE
命令向apiserver发送任务,apiserver通知目标工作节点的kubelet对相应pod资源进行终止操作,先向pod中的容器发送terminal(15)信号,并有一个默认30秒的宽限期,如果能正常终止就终止pod,再如宽限期过后pod还无法终止,则再发送 kill(9)信号强制终止。
livenessProbe探测
使用kubectl explain pod.spec.containers.livenessProbe
查看容器的存活性探测的帮助信息
livenessProbe <Object>
FIELDS:
exec <Object> exec类型探针
httpGet <Object> http类型探针
tcpSocket <Object> tcp类型探针
failureThreshold <integer> 探测几次才判断为失败,默认为3次
periodSeconds <integer> 每一次探测间隔时长,默认为10s
successThreshold <integer> 判断为成功的次数,默认为1次
timeoutSeconds <integer> 每一次探测的超时时间,默认为1s
initialDelaySeconds <integer> 容器启动后开始做探测等待时长,无默认值
exec探针
k8s@node01:~/my_manifests$ cat liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec-pod
namespace: default
spec:
containers:
- name: liveness-exec-container
image: busybox:latest
imagePullPolicy: IfNotPresent # 镜像tag为latest时镜像拉取策略都为Always
command: ["/bin/sh", "-c", "touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 3600"]
livenessProbe:
exec:
command: ["test", "-e", "/tmp/healthy"]
initialDelaySeconds: 1
periodSeconds: 3
# 应用yaml
k8s@node01:~/my_manifests$ kubectl create -f liveness-exec.yaml
pod/liveness-exec-pod created
# 容器启动后等待1秒开始进行探测,前30秒探测为正常
k8s@node01:~/my_manifests$ kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness-exec-pod 1/1 Running 0 21s
# /tmp/healthy文件被删除后,探测失败,经过3个周期探测失败后,pod的默认restartPolicy为Always,容器会重启
k8s@node01:~/my_manifests$ kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness-exec-pod 1/1 Running 2 2m42s
# 当容器被重启次数过多时,状态为 CrashLoopBackOff
k8s@node01:~/my_manifests$ kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness-exec-pod 0/1 CrashLoopBackOff 5 7m16s
httpGet探针
k8s@node01:~/my_manifests$ cat liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
namespace: default
spec:
containers:
- name: liveness-httpget-container
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: myapp-http
containerPort: 80
livenessProbe:
httpGet:
port: myapp-http # 可引用暴露端口的名称
path: /
scheme: HTTP # 可省略,默认使用HTTP
initialDelaySeconds: 1
periodSeconds: 3
k8s@node01:~/my_manifests$ kubectl create -f liveness-httpget.yaml
pod/liveness-httpget-pod created
# pod处于Running状态,正常
k8s@node01:~/my_manifests$ kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness-httpget-pod 1/1 Running 0 2m36s
# 连接至pod中的容器,手动删除主页文件
k8s@node01:~/my_manifests$ kubectl exec -it liveness-httpget-pod -- /bin/sh
/ # rm -f /usr/share/nginx/html/
50x.html index.html
/ # rm -f /usr/share/nginx/html/index.html
/ # command terminated with exit code 137 # 主机文件被删除后,httpget探测失败,容器被重启,退出容器连接
# pod被重启1次
k8s@node01:~/my_manifests$ kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness-httpget-pod 1/1 Running 1 4m33s
tcpSocket探针
k8s@node01:~/my_manifests$ cat liveness-tcpsocket.yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-tcpsokcet-pod
namespace: default
spec:
containers:
- name: liveness-tcpsokcet-container
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: myapp-http
containerPort: 80
livenessProbe:
tcpSocket:
port: myapp-http # 可引用暴露端口的名称
initialDelaySeconds: 1
periodSeconds: 3
readingProbe探测
为什么要做就绪性探测?
service是对外提供访问的固定端点,对其下的pods会进行负载调度,如果一个pod下运行了两个容器,现在因负载太大需要扩容为3个容器,第3个容器被创建成功后就立刻会被pod所关联进来进行调度,而容器内运行的程序可能需要一定的时间才能接受请求服务,而此时会有一部分流量被service分配到未就绪
的第3个容器上,请求到该容器上的请求就会出问题,所以需要做就绪性探测,只有探测正常后service才分配流量到新的容器上。
readinessProbe探测同样支持exec, httpGet, tcpSocket
三种探针进行探针,定义格式也一样。
k8s@node01:~/my_manifests$ cat readiness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
namespace: default
spec:
containers:
- name: readiness-httpget-container
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: myapp-http
containerPort: 80
readinessProbe:
httpGet:
port: myapp-http # 可引用暴露端口的名称
path: /
scheme: HTTP # 可省略,默认使用HTTP
initialDelaySeconds: 1
periodSeconds: 3
k8s@node01:~/my_manifests$ kubectl create -f readiness-httpget.yaml
pod/readiness-httpget-pod created
k8s@node01:~/my_manifests$ kubectl get pods
NAME READY STATUS RESTARTS AGE
readiness-httpget-pod 1/1 Running 0 11s
# 连接到Pod删除主页
k8s@node01:~/my_manifests$ kubectl exec -it readiness-httpget-pod -- /bin/sh
/ # mv /usr/share/nginx/html/index.html /tmp/
/ #
# 删除主页后,REAY中就绪为0,容器主进程仍然在运行,容器不会被重启;如果再把主页还原,就绪状态就恢复
# k8s@node01:~/my_manifests$ kubectl get pods
NAME READY STATUS RESTARTS AGE
readiness-httpget-pod 0/1 Running 0 2m7s
lifecycle-生命周期勾子
pod中的容器在被启动后(postStart)
可以立刻执行一些操作,当该操作执行成功后才接着做接下来的流程;pod中的容器在结束前preStop
也可以先执行一此收尾操作
,收尾操作完成后才结束pod的生命周期。
k8s@node01:~/my_manifests$ kubectl explain pod.spec.containers.lifecycle
说明:
RESOURCE: lifecycle <Object>
FIELDS:
postStart <Object>
preStop <Object>
postStart
和preStop
都支持exec, httpGet和tcpSocket
三种类型的探针。
k8s@node01:~/my_manifests$ cat lifecycle-poststart.yaml
apiVersion: v1
kind: Pod
metadata:
name: lifecycele-poststart-pod
namespace: default
spec:
containers:
- name: busybox-container
image: busybox:latest
command: ["/bin/sh", "-c", "sleep 3600"]
lifecycle:
postStart:
exec:
command: [‘/bin/sh‘, "-c", "mkdir /tmp/lifecycle"]
k8s@node01:~/my_manifests$ kubectl get pods
NAME READY STATUS RESTARTS AGE
lifecycele-poststart-pod 1/1 Running 0 10s
# 检查 /tmp/lifecycle 目录是否存在
k8s@node01:~/my_manifests$ kubectl exec -it lifecycele-poststart-pod -- ls /tmp
lifecycle
注意:
postStart
中执行的操作与容器的command
中执行的程序不要有强依赖性,如:容器的command
运行httpd服务,并把家目录指向postStart
中创建的一个目录,这样容器运行httpd命令时会出错,也就是容器是先执行自己的command
中的命令,再运行postStart
中定义的操作。