【Kubernetes系列三】资源配置清单
1.资源配置清单
资源配置清单主要由apiVersion、kind、metadata、spec、status五个核心字段组成
kubernetes api参考文档
查看pod资源的一级字段
~]# kubectl explain pods #查看一级字段帮助文档
查看pod资源的spec对象支持嵌套使用的二级字段
~]# kubectl explain pods.spec
查看pod资源的三级字段
~]# kubectl explain pods.spec.containers
以现有活动对象的清单为模版,可以更快生成目标资源的配置文件
~]# kubectl get deployment nginx-app -o yaml --export
--export: 省略输出由系统生成的信息
2.kubectl命令与资源管理
资源的管理操作可简单归结为增删改查,kubectl提供了一系列的子命令,如create、delete、patch、apply、replace、edit、get等
具体子命令列表,参考书第58页
3.资源配置清单字段(pods资源管理)
各工作节点负责运行pod对象,pod核心功能在于运行容器
(1) 镜像及其获取策略
资源配置清单中spec > containers > image > imagePullPolicy 配置拉取镜像策略
Always: 镜像标签为'latest'或不存在时总是从指定的仓库中获取镜像
IfNotPresent:仅当本地镜像缺失方才从目标仓库下载镜像
Nerver:禁止CNG仓库下载镜像,即仅使用本地镜像
有些镜像仓库服务器需要认证才能使用,认证需要在相关节点交互执行docker login命令进行,要么将认证信息定义专有的secret资源,并配置pod通过'imagePullSecretes'字段调用认证信息完成
(2) 暴露端口
hostPort和NodePort类型的Service对象暴露端口的方式不同,NodePort是通过所有节点暴露容器服务,而hostPort则是经由pod对象所在的节点IP地址来进行。
containerPort
name
protocol:端口相关的协议,值为tcp或udp,默认为tcp
资源配置清单示例:
apiVersion: v1
kind: Pod
metadata:
name: pod-example
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
protocol:TCP
(3) 传递参数
apiVersion: v1
kind: Pod
metadata:
name: pod-with-custom-command
spec:
containers:
- name: myapp
image: alpine:latest
command: ["/bin/sh"]
args: ["-c","while true; do sleep 30; done"]
(4) 环境变量
向pod对象中的容器环境变量传递数据方法有两种:env和envFrom,这里介绍第一种
name <string>: 环境变量的名称
value <string>:传递给环境变量的值
apiVersion: v1
kind: Pod
metadata:
name: pod-with-env
spec:
containers:
- name: filebeat
image: ikubernetes/filebeat:5.6.5-alpine
env:
- name: REDIS_HOST
value: db.ilinux.io:6379
- name: LOG_LEVEL
value: info
(5) 共享节点的网络名称空间
想要共享名称空间所有资源,pod内的多个容器必须部署在同一个节点,仅需设置spec.hostNetwork的属性为true即可创建共享节点网络名称空间的pod对象,配置清单如下所示
~]# cat pod-use-hostnetwork.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-use-hostnetwork
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
hostNetwork: true
~]# kubectl apply -f pod-use-hostnetwork.yaml
~]# kubectl exec -it pod-use-hostnetwork -- sh
/ # ifconfig
可以看到网卡和宿主机一样
(6) pod对象的安全上下文
pod对象的安全上下文用于设定pod或容器的权限和访问控制功能,常用属性包括以下几方面
-
基于用户id和组id控制访问对象时的权限。
-
以特权或非特权的方式运行。
-
通过Linux Capabilities为其提供部分特权。
-
基于Seccomp过滤进行的系统调用。
-
基于SELinux的安全标签
-
是否能够进行权限升级。
pod对象的安全上下文定义在spec.securityContext字段中,而容器的安全上下文则定义在spec.containers[].securityContext字段中,且二者可嵌套使用的字段还有所不同。
示例:
~]# cat pod-with-securitycontext.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-with-securitycontext
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh","-c","sleep 86400"]
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
~]# kubectl apply -f pod-with-securitycontext.yaml
~]# kubectl exec pod-with-securitycontext -- ps aux
PID USER TIME COMMAND
1 1000 0:00 sleep 86400
6 1000 0:00 ps aux
另外,可设置的安全上下文属性还有fsGroup、seLinuxOptions、supplementalGroups、sysctls、capabilities和privileged等。
(7) 标签
创建资源时,可直接在其metadata中嵌套使用'labels'字段定义要附加的标签项
示例:
~]# vi pod-with-labels.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-with-labels
labels:
env:qa
tier:frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
基于此资源清单创建定义的pod对象后,即可在'kubectl get pods'中额外查看显示对象的标签信息
~]# kubectl apply -f pod-with-labels.yaml
~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
pod-use-hostnetwork 1/1 Running 0 40h <none>
pod-with-labels 1/1 Running 0 21s env=qa,tier=frontend
标签较多时,可以用标签过滤符合指定标签键名的pod
~]# kubectl get pods -L env,tier
NAME READY STATUS RESTARTS AGE ENV TIER
pod-use-hostnetwork 1/1 Running 0 40h
pod-with-labels 1/1 Running 0 117s qa frontend
为已有pod添加标签
~]# kubectl label pods/pod-use-hostnetwork env=production
为已有pod修改标签
~]# kubectl label pods/pod-with-labels env=testing --overwrite
4.标签选择器
标签选择器用于表达标签的查询条件或选择标准,k8s api目前支持两个选择器:基于等值关系(equality-based)以及基于集合关系(set-based)。例如,env=production和env!=qa是基于等值关系的选择器,而tier in (frontend,backend)则是基于集合关系的选择器。另外,使用标签选择器时还将遵循以下逻辑:
-
同时指定的多个选择器之间的逻辑关系为“and”操作
-
使用空值的标签选择器意味着每个资源对象都被选中
-
空的标签选择器将无法选出任何资源
基于等值关系的标签选择器的可用操作符有“==”、“=”和“!=”三种,其中前两种表示等值关系,最后一个表示不等关系。使用"kubectl get"命令的“-l”选项能够指定使用标签选择器
例如:-l指定使用的标签选择器,显示键名env的值不为qa,同时键名tier的值为frontend的所有pod对象
~]# kubectl get pods -l 'env!=qa,tier=frontend' -L env,tier
NAME READY STATUS RESTARTS AGE ENV TIER
pod-with-labels 1/1 Running 0 23m testing frontend
基于集合关系的标签选择器支持in、notin和exists三种操作符,他们的使用格式及意义如下:
KEY in (value1,value2,...):指定的键名的值存在于给定的列表中即满足条件
KEY notin (VALUE1,VALUE2,...):指定的键名的值不存在于给定的列表中即满足条件
KEY:所有存在此键名标签的资源
!KEY:所有不存在此键名标签的资源
例如:显示标签键名为env的值为production或dev的所有pod对象
~]# kubectl get pods -l "env in (production,dev)" -L env
NAME READY STATUS RESTARTS AGE ENV
pod-use-hostnetwork 1/1 Running 0 40h production
例如: 列出标签键名env的值为produciton或dev,且不存在键名为tier的标签的所有pod对象
~]# kubectl get pods -l 'env in (production,dev),!tier' -L env,tier
NAME READY STATUS RESTARTS AGE ENV TIER
pod-use-hostnetwork 1/1 Running 0 40h production
ps:为了避免shell解释器解析感叹号,必须要为此类表达式使用单引号
此外,k8s的诸多资源对象必须以标签选择器的方式关联到pod资源对象,它们在spec字段中嵌套使用嵌套的selector字段,通过matchLabels来指定标签选择器,有的甚至还支持使用matchExpressions构造复杂的标签选择机制。
matchLabels:通过直接给定键值对来指定标签选择器
matchExpresstions:基于表达式指定的标签选择器列表。使用Exists或DostNotExist时,其values必须为空
示例:
selector:
matchLabels:
component: redis
matchExpresstions:
- {key: tier, operator: In,values: [cache]}
- {key: environment, operator: Exists,values}
5.节点选择器
pod节点选择器是标签选择器的一种应用,它能够让pod对象基于集群中工作节点的标签来挑选倾向运行的目标节点。
kubernetes的kube-scheduler守护进程负责在各工作节点中基于系统资源的可用性等标签挑选一个来运行待创建的pod对象,默认的调度器是default-scheduler。k8s可将所有工作节点上的各系统资源抽象成资源池统一分配使用,因此用户无须关系pod对象的具体运行位置也能良好工作。pod对象的spec.nodeSelector可用于定义节点标签选择器,用户事先为特定部分的node资源对象设定好标签,而后配置pod对象通过节点标签选择器进行匹配检测,从而完成节点亲和性调度。
示例:为节点添加'disktype=ssh'的标签
~]# kubectl label nodes node01.ilinux.io disktype=ssd
node/node01.ilinux.io labeled
如果某Pod资源需要调度到具有SSD设备的节点上,那么只需要为其使用spec.nodeSelector标签选择器即可
示例:
apiVersion: v1
kind: Pod
metadata:
name: pod-with-nodeselector
labels:
env:testing
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
nodeSelector:
disktype: ssd
除了上述使用标签选择节点,还可以直接使用spec.nodeName字段直接指定目标节点
6.pod对象生命周期
(1) pod的相位
无论是手动创建还是通过控制器创建pod,pod对象总是应该处于其生命进程中以下几个相位之一:
pending:apiserver创建了pod资源对象并存入etcd中,但它尚未被调度完成或者仍处于下载镜像的过程中
running:pod已经被调度至某节点,并且所有容器都已经被kubelet创建完成
succeeded:pod中的所有容器都已经成功终止并且不会被重启
failed:所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态或已经被系统终止。
unknown:apiserver无法正常获取到pod对象的状态信息,通常是由于其无法于所在工作节点的kubelet通信所致。
(2) pod的创建过程
pod是k8s的基础单元,以下为一个pod资源对象的典型创建过程:
1)、用户通过kubectl或其他api客户端提交pod spec给api server
2)、api server尝试着将pod对象的相关信息存入etcd中,待写入操作执行完成,api server即会返回确认信息至客户端。
3)、api server开始反映etcd中的状态变化
4)、所有的k8s组件均使用watch机制来跟踪检查api server上的相关变动
5)、kube-scheduler通过其watch觉察到api server创建了新的pod对象但尚未绑定至任何工作节点
6)、kube-scheduler为pod对象挑选一个工作节点并将结果信息更新至api server
7)、调度结果信息由api server更新至etcd,而且api server也开始反映此pod对象的调度结果
8)、pod被调度到目标工作节点上的kubelet尝试在当前节点上调用docker启动容器,并将容器的结果状态回送至api server
9)、api server将pod状态信息存入etcd中
10)、在etcd确认写入操作成功完成后,api server将确认信息发送至相关的kubelet,时间将通过它被接受。
(3) pod生命周期中的重要行为
初始化容器
初始化容器即应用程序的主容器启动之前要运行的容器,常用于为主容器执行一些预置操作,它们具有两种典型特征
1)初始化容器必须运行完成直至结束,若某初始化容器运行失败,那么k8s需要重启它直到成功完成
2)每个初始化容器都必须按定义的顺序串行运行
有不少场景都需要在应用容器启动之前进行部分初始化操作,例如,等待其他相关联组件服务可用、基于环境变量或配置模板为应用程序生成配置文件、从配置中心获取配置等。初始化容器的典型应用需求具体包含如下几种。
1)用于运行特定的工具程序,出于安全等反面的原因,这些程序不适于包含在主容器镜像中
2)提供主容器镜像中不具备的工具程序或自定义代码
3)为容器镜像的构建和部署人员提供了分离、独立工作的途径,使得它们不必协同起来制作单个镜像文件
4)初始化容器和主容器处于不同的文件系统视图中,因此可以分别安全地使用敏感数据,例如secrets资源
5)初始化容器要先于应用容器串行启动并运行完成,因此可用于延后应用容器的启动直至其依赖的条件得到满足
pod资源的spec.initContainers字段以列表的形式定义可用的初始容器,其嵌套可用字段类似于spec.containers。
示列:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: ikubernetes/myapp:v1
initContainers:
- name: init-something
image: busybox
command: ['sh','-c','sleep 10']
生命周期钩子函数
容器生命周期钩子使它能够感知其自身生命周期管理中的事件,并在相应的时刻到来时运行由用户指定的处理程序代码。k8s为容器提供了两种生命周期钩子:
postStart:于容器创建完成之后立即运行的钩子处理器(handler),不过k8s无法确保它一定会于容器中的entrypoint之前运行
preStop:于容器终止操作之前立即运行的钩子处理器,它以同步的方式调用,因此在其完成之前会阻塞删除容器的操作调用。
钩子处理器的实现方法由Exec和HTTP两种,前一种在钩子事件触发时直接在当前容器中运行由用户定义的命令,后一种则是在当前容器中向某url发起http请求。postStart和preStop处理器定义在spec.lifecycle嵌套字段中。
示列:
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: ikubernetes/myapp:v1
lifecycle:
postStart:
exec:
command: ["/bin/sh","-c","echo 'lifecycle hooks handler' > /usr/share/nginx/html/test/html"]
7.容器探测
容器探测时pod对象生命周期中的一项重要的日常任务,它是kubelet对容器周期性执行的健康状态诊断,诊断操作由容器的处理器进行定义。k8s支持三种处理器用于pod探测:
ExecAction:在容器中执行一个命令,并根据其返回的状态码进行诊断的操作称为Exec探测,状态码为0表示成功,否则即为不健康状态
TCPSocketAction:通过与容器的某TCP端口尝试建立连接进行诊断,端口能够成功打开即为正常,否则为不健康状态。
HTTPGetAction:通过向容器IP地址的某指定端口的指定path发起HTTP GET请求进行诊断,响应码为2或3时即为成功。
任何一种探测方式都可能存在三种结果:success、failure、unknown
kubelet可在活动容器上执行两种类型的检测:
存活性检测:用于判定容器是否处于运行状态,一旦此类检测未通过,kubelet将杀死容器并根据restartPolicy决定是否将其重启;未定义存活性检测的容器的默认状态未success
就绪性检测:用于判断容器是否准备就绪并可对外提供服务;未通过检测的容器意味着尚未准备就绪,端点控制器会将其IP从所有匹配到此pod对象的service对象的端点列表中移除;检测通过之后,会再次将其IP添加至端点列表中。
8.容器的重启策略
容器程序发生奔溃或容器申请超出限制的资源等原因都可能会导致pod对象的终止,此时是否应该重建该pod对象则取决于其重启策略(restartPolicy)属性的定义:
1、Always:但凡pod对象终止就将其重启,此为默认设定
2、OnFailure:尽在pod对象出现错误时方才将其重启
3、Never:从不重启。
restartPolicy适用于pod对象中的所有容器,而且它仅用于控制在同一节点上重新启动pod对象的相关容器。首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作的延迟时长以此为10s、20s、40s、80s、160s和300s,300s是最大延迟时长。事实上,一旦绑定到一个节点,pod对象将永远不会重新绑定到另一个节点,它要么被重启,要么终止,直到节点发生故障或被删除。
9.pod的终止过程
当用户提交删除请求之后,系统就会进行强制删除操作的宽限期倒计时,并将TERM信息发送给pod对象的每个容器中的主进程。宽限期倒计时结束后,这些进程将收到强制终止的KILL信号,pod对象随即也将由api server删除,如果在等待进程终止的过程中,kubelet或容器管理器发生了重启,那么终止操作会重新获得一个满额的删除宽限期并重新执行删除操作。
一个典型的pod对象终止流程具体如下:
1)用户发送删除pod对象的命令
2)api服务器中的pod对象会随着时间的推移而更新,在宽限期内(默认30s),pod被视为dead
3)将pod标记为terminating状态
4)与第三步同时运行,kubelet在监控到pod对象转为terminating状态的同时启动pod关闭过程
5)与第三步同时运行,端点控制器监控到pod对象的关闭行为时将其从所有匹配到此端点的service资源的端点列表中移除
6)如果当前pod对象定义了preStop钩子处理器,则在其标记为terminating后即会以同步的方式启动执行;若宽限期结束后,preStop仍未执行结束,则第二步会被重新执行并额外获取一个时长为2s的小宽限期
7)pod对象中的容器进程收到TERM信号
8)宽限期结束后,若存在任何一个仍在运行的进程,那么pod对象即会收到SIGKILL信号
9)kubelet请求api server将此pod资源的宽限期设置为0从而完成删除操作,它变得对用户不再可见。
默认情况下,所有删除操作的宽限期都是30s,不过,kubectl delete命令可以使用“--grace-period=