在K8S实战笔记–4中,我们对名称空间及其操作有了基础了解。本篇将对K8S的另一个重要概念–Label
(标签)做介绍。
1. Label
标签是用来标识K8S对象的一组附加在其上的键值对,通过标签我们可以方便地筛选或排除一组对象。借鉴资料中的话来讲,集群中的应用部署或是批处理的程序部署通常都是多维度的,为了实现对这些对象的管理,往往需要对某一特定维度的对象进行操作,而标签可以通过用户的意愿组织集群中的对象之间的结构,而不需要对集群进行修改。
在同一个对象之下标签的Key值必须唯一的。名称方面,标签名不得多于63个字符且必须由字母或数字开头或结尾,可以包含字母、数字、-
、_
、.
;标签前缀是可选的,必须以DNS子域名的方式指定,例如:kubernetes.io
,后用/
将其与标签名分隔。通常情况下,若不使用标签前缀,那么该标签的Key将被视为专属于用户的,在K8S的系统组件向对象添加标签时,必须指定前缀。在标签值方面,若标签值不为空,则其长度不得多于63个字符且必须由字母或数字开头或结尾,可以包含字母、数字、-
、_
、.
。
标签选择器可以用来选择一组对象(标签并不能唯一标识一个对象),APIServer支持基于等式与基于集合的标签选择方式:
- 基于等式的标签选择方式:在这种选择方式下可以使用
=
、==
、!=
三种操作符来进行选择,前两个的含义是一样的,都代表相等,第三种代表不等。选择条件可以通过,
叠加,例如date=day1,name!=build
代表选择date
值为day1且name
值不为build的对象。 - 基于集合的标签选择方式:这种选择器可以同时选择一组对象。支持的操作符有:
in
、notin
、exists
。具体的使用方法为:- 选择
date
包含有值为day1、day2、day3的标签:date in (day1, day2, day3)
- 选择
name
值不为build、pipline的标签:name notin (build, pipline)
- 选择所有包含test的标签:
test
- 选择所有不包含test的标签:
!test
这种标签选择器也支持使用,
分隔以同时叠加选择,相同意义上的选择条件在这两种选择方式之间是等价的。
- 选择
在我们查看集群内的对象时,我们可以通过参数-l
来约束选择条件。例如:
kubectl get pods -l `date in (day1, day2, day3)`
基于等式的选择方式不需要加单引号。在K8S中,Job
、Deployment
、ReplicaSet
、DaemonSet
同时支持基于等式与基于集合的选择方式。与Label相对,还有一种annotation
(注解),注解不能用来选择对象,通常,注解包含的是如下一些信息:
- build信息、release信息、Docker镜像信息等,例如时间戳、release id号、PR号、镜像hash值、docker registry地址等。
- 日志库、监控库、分析库等资源库的地址信息。
- 程序调试工具信息,例如工具、版本号等。
- 团队等联系信息,例如电话号码、负责人名称、网址等。
2. 字段选择器
field selector
是可以根据一个或多个字段来选择一组对象的选择器,使用时,只需要在查询时使用参数--field-selector
来对对象中的字段进行选取即可。支持的操作符与基于等式的标签选择方式一样,也可以使用,
进行多个字段的叠加。但是,不同类型的对象,所支持选择的字段不一样,除了所有对象都支持的metadata.name
和metadata.namespace
,在命令行中使用了不支持的字段,会报错。字段选择器也支持跨多种资源类型使用。
3. 容器镜像
我们曾在DevOps实战笔记–2中3.2推送本地镜像至Harbor提到,在Docker中使用Harbor时,需要按照:Harbor的地址/Harbor中建立的项目名/镜像名称:版本号
对容器镜像命名来完成操作。在Kubernetes使用镜像时,我们也应当按照与Docker命令一样的语法来执行。若我们使用的是docker.hub.com
仓库中的镜像,我们则可以省略仓库地址或仓库端口,例如,直接使用nginx:last
来完成镜像的拉取。在Kubernetes中,默认的镜像拉取策略是IfNotPresent
,它代表着若本地存在目标容器的镜像,则不从远端仓库拉取镜像。若我们希望容器每次启动时,都默认从仓库拉取镜像,那么可以采取以下几种方式:
- 将容器镜像拉取策略
imagePullPolicy
设置为Always
; - 省略
imagePullPolicy
字段,并使用tag为latest
的镜像; - 直接将
imagePullPolicy
字段与tag省去。
需要注意的是,在imagePullPolicy
未填写且tag为一个确定值(不包括latest
)此时拉取策略同IfNotPresent
;若拉取策略为Never
,则集群假定本地存在镜像,不会尝试从远端拉取镜像。
4. 容器的钩子函数
在K8S实战笔记–3中4.容器生命周期我们了解到,在init容器启动成功后,Pod会进入主容器的启动。在主容器的启动和退出时,分别会执行启动和退出命令。这两条命令分别调用两个Hook(也就是钩子函数)PostStart
和PreStop
。PostStart
在容器创建后将立刻执行,PreStop
在容器被terminate之前执行,K8S将在此函数执行完成后才删除容器。在K8S中,为了使用Hook,需要注册Hook Handler
,通常有入下两种形式的handler:
Exec
:在容器的名称空间或是在cgroup(Control Group的缩写,由Linux内核提供,用于限制、记录和隔离进程组使用的物理资源)中执行一个指定的命令;HTTP
:向容器的指定端口发送一个HTTP请求。
K8S通过调用Hook,使得容器可以得知所在运行环境对其进行管理的生命周期事件,并可以相应这些事件,以执行对应的代码。在上述Hook运行时,若某一Hook运行失败,K8S就会杀死这个容器。在容器运行的过程中,Hook会被至少触发一次,当PostStart
或PreStop
执行时,Hook也可能被触发了多次,所以Hook Handler在实现时需要保证即使多次触发,也不会出现错误,同时,用户也应当使他们的Hook Handler越轻量级越好。Hook handler的日志并没有在 Pod 的 events 中发布。如果 handler 因为某些原因失败了,kubernetes 将广播一个事件 PostStart hook 发送 FailedPreStopHook 事件。可以执行命令kubectl describe pod
以查看这些事件。
那么如何为容器添加Handler呢?我们在编写Pod的YAML文件时,可以在容器的详细描述中添加lifecycle
字段来定义我们需要的处理程序。例如,我们为只运行单一容器的Pod添加Handler:
apiVersion: v1
kind: Pod
metadata:
name: nginx-demo
spec:
containers:
name: nginx-container
image: nginx:latest
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Executing command from postStart handler > /usr/local/message"]
我们使用kubectl apply
应用此YAML,待容器启动后,我们使用kubectl exec
获得容器内终端,可以在/usr/local/message
中查看到输出内容:
Executing command form postStart handler
one-container-per-pod
(一个Pod运行一个容器)是Pod最为常见的使用方式。Pod可以看作集群节点中运行的进程,进程的结束方式有两种:gracefull terminate
(优雅地终止)或kill
。在用户发起删除Pod的指令后,集群需要让用户知道Pod何时被删除并且确保删除Pod的指令能够被正常完成,执行如下操作:
- 记录强制终止前的等待时常;
- 向Pod中所有容器的主进程发送TERM信号;
- 等待超时后向超时容器主进程发送KILL信号;
- 删除APIServer中该Pod的记录。