本文主要是提供了 Init 容器的概览 1,它是一种专用的容器,在Pod 内的应用容器启动之前运行,并包括一些应用镜像中不存在的实用工具和安装脚本。

一、理解 Init 容器

Pod 可以包含多个容器,应用运行在这些容器里面,同时 Pod 也可以有一个或多个先于应用容器启动的 Init 容器。
Init 容器与普通的容器非常像,除了如下两点:
1、它们总是运行到完成。
2、每个都必须在下一个启动之前成功完成
如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止。然而,如果 Pod 对应的 restartPolicy 值为 Never,它不会重新启动。指定容器为 Init 容器,需要在 Pod 的 spec 中添加 initContainers 字段, 该字段內以Container 类型对象数组的形式组织,和应用的 containers 数组同级相邻。 Init 容器的状态在 status.initContainerStatuses 字段中以容器状态数组的格式返回(类似 status.containerStatuses 字段)。

  • 与普通容器的不同之处

Init 容器支持应用容器的全部字段和特性,包括资源限制、数据卷和安全设置。 然而,Init 容器对资源请求和限制的处理稍有不同,在下面 资源 处有说明。
同时 Init 容器不支持 Readiness Probe,因为它们必须在 Pod 就绪之前运行完成。
如果为一个 Pod 指定了多个 Init 容器,这些容器会按顺序逐个运行。每个 Init 容器必须运行成功,下一个才能够运行。当所有的 Init 容器运行完成时,Kubernetes 才会为 Pod 初始化应用容器并像平常一样运行。

二、Init 容器能做什么【使用场景】

因为 Init 容器具有与应用容器分离的单独镜像,其启动相关代码具有如下优势

Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码。例如,没有必要仅为了在安装过程中使用类似 sed、 awk、 python 或 dig 这样的工具而去FROM 一个镜像来生成一个新的镜像。

Init 容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低。
应用镜像的创建者和部署者可以各自独立工作,而没有必要联合构建一个单独的应用镜像。

Init 容器能以不同于Pod内应用容器的文件系统视图运行。因此,Init容器可具有访问 Secrets 的权限,而应用容器不能够访问。

由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。一旦前置条件满足,Pod内的所有的应用容器会并行启动。

利用initContainer发布更新应用,通过initContainer下载一个demo4shl.war文件,放在它内部的目录下。而此目录是通过volumes定义的一个空目录,这个目录在后边tomcatContainer上被挂载到了/usr/local/tomcat/webapps,这样就把war包动态放入tomcat的webapps中部署,好处是不用每次代码变更时都对tomcat重新build,在镜像间实现了解耦。

参考网上文章案例2
将glusterfs存储同时挂载到initContainer和container的指定目录上,如:/var/data/
在initContainer中拉取资源放到/var/data/,也就推到了分布式存储glusterfs上。

三、话不多说,来实操 Init 容器

下面的例子定义了一个具有 1 个 Init 容器的简单 Pod,一旦getwar Init容器启动完成,Pod 将启动spec区域中的sidecartomcat应用容器。

initContainers作用是:通过command命令下载一个demo4shl.war文件,放在它内部的/app目录下。而/app目录是通过volumes定义的一个空目录,这个目录在后边tomcat镜像上被挂载到了/usr/local/tomcat/webapps,这样就把war包动态放入tomcat的webapps中部署
利用initContainer发布代码的好处是,不用每次代码变更时都对tomcat重新build,在镜像间实现了解耦.

1.用docker启用个nginx将war包放在上面

#将/Users/honglei/dev/docker/war目录挂载到/usr/share/nginx/html
#访问端口是88,使用的镜像是nginx:latest
docker run --name servc-nginx --privileged=true -v /Users/honglei/dev/docker/war:/usr/share/nginx/html  -d -p 88:80 nginx:latest

#验证服务
wget http://localhost:88/demo4shl.war

2.k8s中创建initContainers和containers

在这里插入图片描述
直接粗暴形式创建,输入 YAML内容到k8s界面
注意:1. initContainers的包下载地址,需要配置自己的ip+端口
注意:2. 检测指定资源是否存在,以防重复拉取

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sidecartomcat
spec:
  selector:
    matchLabels:
      app: sidecartomcat
  template:
    metadata:
      labels:
        app: sidecartomcat
    spec:
      initContainers: #初始化容器执行完,会下载一个demo4shl.war文件映射到emptyDir{},而主容器也是和spec.volumes里的emptyDir{}进行映射,所以tomcat容器的/usr/local/tomcat/webapps`目录下会映射demo4shl.war文件
      - name: getwar
        image: busybox:latest #用shell的if语法判断,检测指定资源是否存在,以防重复拉取
        command: ['sh', '-c', 'if [ ! -f "/app/demo4shl.war" ]; then wget -O /app/demo4shl.war http://192.168.3.2:88/demo4shl.war; fi']  
        volumeMounts:
          - mountPath: /app
            name: app-volume
      containers:
      - name: sidecartomcat
        image: tomcat:latest
        volumeMounts: #将指定的卷mount到容器指定的位置
          - mountPath: /usr/local/tomcat/webapps
            name: app-volume
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 8080
      volumes: #指的是Pod中的卷
        - name: app-volume
          emptyDir: {} #容器目录是emptyDir{},这个就相当于一个共享卷,是一个临时的目录,生命周期等同于Pod的生命周期

直接粗暴形式创建service,输入 YAML内容到k8s界面,
注意:type: NodePort,指定端口为31986(要是未占用的端口)

kind: Service
apiVersion: v1
metadata:
  name: sidecartomcat
  namespace: default
spec:
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
      nodePort: 31986
  selector:
    app: sidecartomcat
  type: NodePort

四、效果展示

在这里插入图片描述

在这里插入图片描述

五、特别注意几点

# 1. 查看container执行顺序:

通过命令可以看出app Container的启动时在init Container启动并成功完成后:
kubectl describe po sidecartomcat-65d9469b85-t82tm 

2.Pod重启,会导致initContainer重新执行,检测initContainer的操作or资源,以防重复操作or拉取

这里的解决办法是用shell的if语法判断,检测指定资源是否存在,以防重复拉取
command: [‘sh’, ‘-c’, ‘if [ ! -f “/app/demo4shl.war” ]; then wget -O /app/demo4shl.war http://192.168.3.2:88/demo4shl.war; fi’]

3.Pod 重启的原因

用户更新 Pod 的 Spec 导致 Init 容器镜像发生改变。Init 容器镜像的变更会引起 Pod 重启. 应用容器镜像的变更仅会重启应用容器。
Pod 的基础设施容器 (译者注:如 pause 容器) 被重启。 这种情况不多见,必须由具备 root 权限访问 Node 的人员来完成。
当 restartPolicy 设置为 Always,Pod 中所有容器会终止而强制重启,由于垃圾收集导致 Init 容器的完成记录丢失。
你可以在Pod的规格信息中与containers数组同级的位置指定 Init 容器。

4.注意点

在Pod上使用activeDeadlineSeconds,在容器上使用livenessProbe,这样能够避免Init容器一直失败,这就为Init容器活跃设置了一个期限。在Pod中的每个app和Init容器的名称必须唯一,与任何其它容器共享同一个名称,会在验证时抛出错误。
在Pod启动过程中,Init容器会按顺序在网络和数据卷初始化之后启动。每个容器必须在下一个容器启动之前成功退出。如果由于运行时或失败退出,导致容器启动失败,它会根据Pod的restartPolicy指定的策略进行重试,在所有的Init容器没有成功之前,
Pod将不会变成Ready状态,如果Pod重启,所有Init容器必须重新执行。
因为Init容器可能会被重启、重试或者重新执行,所以Init容器的代码应该是幂等的(即任意多次执行所产生的影响与一次执行的影响相同)。特别地,被写到EmptyDirs中文件的代码,应该对输出文件可能已经存在做好准备。

5.initContainer的镜像用法:

initContainers:
  - image: registry-vpc.cn-qingdao.aliyuncs.com/***demo:latest
    name: myapp
    command: ["cp","-r","/usr/local/app/.","/app"]

  1. 官方中文文档 ↩︎

  2. 网上文章案例 ↩︎

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐