0%

Kubernetes容器

Kubernetes中容器的相关

一、容器

容器镜像

在 Kubernetes 的 Pod 中使用容器镜像之前,您必须将其推送到一个镜像仓库(或者使用仓库中已经有的容器镜像)。在 Kubernetes 的 Pod 定义中定义容器时,必须指定容器所使用的镜像,容器中的 image 字段支持与 docker 命令一样的语法,包括私有镜像仓库和标签。

例如:my-registry.example.com:5000/example/web-example:v1.0.1 由如下几个部分组成:

my-registry.example.com:5000/example/web-example:v1.0.1

  • my-registry.example.com:registry 地址
  • :5000:registry 端口
  • example:repository 名字
  • web-example:image 名字
  • v1.0.1:image 标签

如果使用 hub.dokcer.com Registry 中的镜像,可以省略 registry 地址和 registry 端口。

例如:nginx:latesteipwork/kuboard

更新镜像

Kubernetes中,默认的镜像抓取策略是 IfNotPresent,使用此策略,kubelet在发现本机有镜像的情况下,不会向镜像仓库抓取镜像。如果想每次启动 Pod 时,都强制从镜像仓库抓取镜像,可以尝试如下方式:

  • 设置 container 中的 imagePullPolicyAlways
  • 省略 imagePullPolicy 字段,并使用 :latest tag 的镜像
  • 省略 imagePullPolicy 字段和镜像的 tag
  • 激活 AlwaysPullImages 管理控制器

imagePullPolicy 字段和 image tag的可能取值将影响到 kubelet 如何抓取镜像:

  • imagePullPolicy: IfNotPresent 仅在节点上没有该镜像时,从镜像仓库抓取
  • imagePullPolicy: Always 每次启动 Pod 时,从镜像仓库抓取
  • imagePullPolicy 未填写,镜像 tag 为 :latest 或者未填写,则同 Always 每次启动 Pod 时,从镜像仓库抓取
  • imagePullPolicy 未填写,镜像 tag 已填写但不是 :latest,则同 IfNotPresent 仅在节点上没有该镜像时,从镜像仓库抓取
  • imagePullPolicy: Never,Kubernetes 假设本地存在该镜像,并且不会尝试从镜像仓库抓取镜像

二、容器的环境变量

Kubernetes为容器提供了一系列重要的资源:

  • 由镜像、一个或多个数据卷合并组成的文件系统
  • 容器自身的信息
  • 集群中其他重要对象的信息

集群的信息

在容器创建时,集群中所有的 Service 的连接信息将以环境变量的形式注入到容器中。例如,已创建了一个名为 Foo 的 Service,此时再创建任何容器时,该容器将包含如下环境变量:

1
2
FOO_SERVICE_HOST = <Service的ClusterIP>
FOO_SERVICE_PORT = <Service的端口>

三、容器生命周期

容器钩子

Kubernetes中为容器提供了两个 hook(钩子函数):

  • PostStart

    此钩子函数在容器创建后将立刻执行。但是,并不能保证该钩子函数在容器的 ENTRYPOINT 之前执行。该钩子函数没有输入参数。

  • PreStop

    此钩子函数在容器被 terminate(终止)之前执行,例如:

    • 通过接口调用删除容器所在 Pod
    • 某些管理事件的发生:健康检查失败、资源紧缺等

    如果容器已经被关闭或者进入了 completed 状态,preStop 钩子函数的调用将失败。该函数的执行是同步的,即,kubernetes 将在该函数完成执行之后才删除容器。该钩子函数没有输入参数。

    Hook handler的实现

    容器只要实现并注册 hook handler 便可以使用钩子函数。Kubernetes 中,容器可以实现两种类型的 hook handler:

    • Exec - 在容器的名称空进和 cgroups 中执行一个指定的命令,例如 pre-stop.sh。该命令所消耗的 CPU、内存等资源,将计入容器可以使用的资源限制。
    • HTTP - 向容器的指定端口发送一个 HTTP 请求

    Hook handler的执行

    当容器的生命周期事件发生时,Kubernetes 在容器中执行该钩子函数注册的 handler。

    对于 Pod 而言,hook handler 的调用是同步的。即,如果是 PostStart hook,容器的 ENTRYPOINT 和 hook 是同时出发的,然而如果 hook 执行的时间过长或者挂起了,容器将不能进入到 Running 状态。

    PreStop hook 的行为与此相似。如果 hook 在执行过程中挂起了,Pod phase 将停留在 Terminating 的状态,并且在 terminationGracePeriodSeconds 超时之后,Pod被删除。如果 PostStart 或者 PreStop hook 执行失败,则 Kubernetes 将 kill(杀掉)该容器。

    用户应该使其 hook handler 越轻量级越好。例如,对于长时间运行的任务,在停止容器前,调用 PreStop 钩子函数,以保存当时的计算状态和数据。

    Hook触发的保证

    Hook 将至少被触发一次,即,当指定事件 PostStartPreStop 发生时,hook 有可能被多次触发。hook handler 的实现需要保证即使多次触发,执行也不会出错。

    通常来说,hook 实际值被触发一次。例如:如果 HTTP hook 的服务端已经停机,或者因为网络的问题不能接收到请求,请求将不会被再次发送。在极少数的情况下, 触发两次 hook 的事情会发生。例如,如果 kueblet 在触发 hook 的过程中重启了,该 hook 将在 Kubelet 重启后被再次触发。

    调试 hook handler

    Hook handler 的日志并没有在 Pod 的 events 中发布。如果 handler 因为某些原因失败了,kubernetes 将广播一个事件 PostStart hook 发送 FailedPreStopHook 事件。 可以执行命令 kubectl describe pod $(pod_name) 以查看这些事件。

四、容器生命周期事件处理

Kubernetes 中支持容器的 postStart 和 preStop 事件,本文阐述了如何向容器添加生命周期事件处理程序(handler)。

  • postStart 容器启动时,Kubernetes 立刻发送 postStart 事件,但不确保对应的 handler 是否能在容器的 EntryPoint 之前执行
  • preStop 容器停止前,Kubernetes 发送 preStop 事件

定义postStart和preStop处理程序

创建一个包含单一容器的 Pod,并为该容器关联 postStart 和 preStop 处理程序(handler)。Pod 的yaml文件定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]

在该例子中,请注意:

  • postStart 命令向 usr/share/message 文件写入了一行文字
  • preStop 命令优雅地关闭了 nginx

如果容器碰到问题,被 Kubernetes 关闭,这个操作是非常有帮助的,可以使得您的程序在关闭前执行必要的清理任务

  • 创建pod
1
2
3
4
5
[root@k8s-master k8s-yamls]# kubectl apply -f lifecycle-demo.yaml 
pod/lifecycle-demo created
[root@k8s-master k8s-yamls]# kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
default lifecycle-demo 1/1 Running 0 50s
  • 进入容器的命令行终端:
1
2
3
[root@k8s-master k8s-yamls]# kubectl exec -it lifecycle-demo -- /bin/bash
root@lifecycle-demo:/# cat /usr/share/message
Hello from the postStart handler

总结

Kubernetes 在容器启动后立刻发送 postStart 事件,但是并不能确保 postStart 事件处理程序在容器的 EntryPoint 之前执行。

postStart 事件处理程序相对于容器中的进程来说是异步的(同时执行),然而,Kubernetes 在管理容器时,将一直等到 postStart 事件处理程序结束之后,才会将容器的状态标记为 Running。

Kubernetes 在决定关闭容器时,立刻发送 preStop 事件,并且,将一直等到 preStop 事件处理程序结束或者 Pod 的 --grace-period 超时,才删除容器。

注意: Kubernetes 只在 Pod Teminated 状态时才发送 preStop 事件,这意味着,如果 Pod 已经进入了 Completed 状态, preStop 事件处理程序将不会被调用