【Kubernetes 005】容器生命周期子Init容器

上一节我们成功创建了自己的第一个pod,但是这背后到底发生了什么,又有什么可以被我们自定义的还不知道。这一节我们一起来看看一个容器从被创建到被销毁经历的生命周期,并详细学习下其中的初始化过程:Init阶段。

我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

容器的生命周期

下面这个图很好的说明了在敲完命令kubectl apply -f pod.yaml之后经历的过程
1-lifecycle.png

  1. 首先kubectl和api server交互,然后到etcd,再到对应node的kubelet
  2. 然后kubelet和node上的CRI,也就是docker交互,开始图中得初始化流程
  3. 首先创建好pod内容器公用得pause网络栈,然后进行一系列得Init C流程
  4. 初始化工作完成后每个容器都会有自己的start初始化脚本要跑
  5. 可以设定一个探针,在初始化脚本一段时间后检测容器是否可提供服务,如果可以提供服务显示为running状态。也就是上面得readiness状态
  6. 同时还有另一个探针,不停检测容器是否有异常,并根据restartPolicy决定是否要重启。也就是上面得Liveness状态
  7. 最后在销毁容器之前,还可以运行一段结尾的stop脚本

下面我们就用实例对这些流程一个个的深入了解一下。这一节先看看第一个步骤Init C。

Init C

Init C是专门用作初始化的容器,其具有如下两个特点:

  • Init容器总是运行到成功退出为止,如果Init容器失败,k8s会不停重启该Pod,直到成功为止
  • 每个Init容器必须在上一个Init容器成功完成后才能运行

注意,如果restartPolicy是never的话,Init容器失败不会重新启动。

因为Init是区别于业务容器的单独容器,所以其可以完成如下工作:

  • 可以安装某些可以被业务容器使用的工具,这些工具如果封装在业务镜像中会增加业务镜像的冗余
  • 对业务容器的代码进行分离为创建和部署两阶段,将创建阶段放入Init容器中
  • Init容器属于Linux命名空间,相对业务容器具有更高的文件系统权限,例如Secret权限。敏感文件处理在Init阶段完成使得业务容器安全性更高
  • 针对某些有严重顺序依赖的两个Pod来说,可以再需要后启动的Pod的Init中去做判断,一直等到先启动的Pod成功完成,再退出Init,启动后一个Pod

说了这么多,下面来实际上手操作看看。

Init容器实际操作

下面所有操作的源码都被托管在github:
https://github.com/Victor2Code/centos-k8s-init/tree/master/test%20yaml/Pod%20Lifecycle%20-%20Init

下面创建一个带Init容器的yaml配置文件test-init-main.yaml如下

apiVersion: v1
kind: Pod
metadata:
  name: test-init-main
  labels:
    app: myapp
    version: v1
spec:
  containers:
    - name: my-busybox
      image: busybox
      command: ['sh','-c','echo Main app is running && sleep 3600']
  initContainers:
    - name: init-myservice
      image: busybox
      command: ['sh','-c','until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
    - name: init-mydb
      image: busybox
      command: ['sh','-c','until nslookup mydb; do echo waiting for mydb; sleep 2; done;']

busybox是用在嵌入式中的轻量级linux系统,这里用busybox做为容器的镜像。containers下配置了一个主容器,功能只是启动后会打印一条记录,因为是从字符串获取shell命令,所以这里要用-c选项。initContainers下配置了两个Init容器,分别是等待myservice和mydb这两个服务起来后退出Init。service起来后,coreDNS里面就会有对应的解析IP,其余pod进行域名解析的话会自动获取到

shell命令的语法这里就不细说了,until do done的结构是直到until条件满足跳出循环

同时选择image的时候最好加上一个tag,而不要用默认的latest,因为latest的镜像每次都会去重新pull。即使是用的latest的image,也最好在本地打上自己的tag避免重复下载

运行kubectl apply -f test-init-main.yaml可以看到容器停在了Init阶段,并且两个Init容器只完成了0个

[root@k8s-master k8s-test]# kubectl apply -f test-init-main.yaml
pod/test-init-main created
[root@k8s-master k8s-test]# kubectl get pod -o wide
NAME                    READY   STATUS     RESTARTS   AGE   IP           NODE        NOMINATED NODE   READINESS GATES
curl-6bf6db5c4f-kljp4   1/1     Running    1          40h   10.244.1.2   k8s-node1   <none>           <none>
hellok8s                2/2     Running    0          21h   10.244.1.6   k8s-node1   <none>           <none>
test-init-main          0/1     Init:0/2   0          12s   10.244.1.8   k8s-node1   <none>           <none>

去看一下pod的详细信息

[root@k8s-master k8s-test]# kubectl describe pod test-init-main
Name:         test-init-main
Namespace:    default
Priority:     0
Node:         k8s-node1/172.29.56.176
Start Time:   Thu, 30 Apr 2020 09:44:01 +0800
Labels:       app=myapp
              version=v1
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"labels":{"app":"myapp","version":"v1"},"name":"test-init-main","namespace":"...
Status:       Pending
IP:           10.244.1.8
Init Containers:
  init-myservice:
    Container ID:  docker://58d01f2b5edd15b8f62c83386c2efa20bb1e95c9295b9807c258fb03a14c9486
    Image:         busybox
    Image ID:      docker-pullable://busybox@sha256:a8cf7ff6367c2afa2a90acd081b484cbded349a7076e7bdf37a05279f276bc12
    Port:          <none>
    Host Port:     <none>
    Command:
      sh
      -c
      until nslookup myservice; do echo waiting for myservice; sleep 2; done;
    State:          Running
      Started:      Thu, 30 Apr 2020 09:44:06 +0800
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-hln8x (ro)
  init-mydb:
    Container ID:  
    Image:         busybox
    Image ID:      
    Port:          <none>
    Host Port:     <none>
    Command:
      sh
      -c
      until nslookup mydb; do echo waiting for mydb; sleep 2; done;
    State:          Waiting
      Reason:       PodInitializing
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-hln8x (ro)
Containers:
  my-busybox:
    Container ID:  
    Image:         busybox
    Image ID:      
    Port:          <none>
    Host Port:     <none>
    Command:
      sh
      -c
      echo Main app is running && sleep 3600
    State:          Waiting
      Reason:       PodInitializing
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-hln8x (ro)
Conditions:
  Type              Status
  Initialized       False
  Ready             False
  ContainersReady   False
  PodScheduled      True
Volumes:
  default-token-hln8x:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-hln8x
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:          <none>

可以看到init-myservice这个Init容器还处于running状态,所以无法向下继续。

这时候我们通过yaml文件test-init-myservice.yaml去创建一个叫myservice的服务

apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 6666

这里是服务IP的80端口对应着容器的6666端口,这里先暂时不用管。确保服务正常起来

[root@k8s-master k8s-test]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP   43h
myservice    ClusterIP   10.107.244.119   <none>        80/TCP    6s

再去看pod的状态,发现第一个Init容器已经退出了

[root@k8s-master k8s-test]# kubectl get pod -o wide
NAME                    READY   STATUS     RESTARTS   AGE    IP           NODE        NOMINATED NODE   READINESS GATES
curl-6bf6db5c4f-kljp4   1/1     Running    1          42h    10.244.1.2   k8s-node1   <none>           <none>
hellok8s                2/2     Running    0          23h    10.244.1.6   k8s-node1   <none>           <none>
test-init-main          0/1     Init:1/2   0          115m   10.244.1.8   k8s-node1   <none>           <none>

同样再通过yaml文件test-init-mydb.yaml去创建mydb服务

apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 7777

同样确保服务已经起来

[root@k8s-master k8s-test]# kubectl apply -f test-init-mydb.yaml
service/mydb created
[root@k8s-master k8s-test]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP   44h
mydb         ClusterIP   10.106.146.185   <none>        80/TCP    27s
myservice    ClusterIP   10.107.244.119   <none>        80/TCP    56m

再看pod的状态,所有Init容器都已经退出,主容器处于running状态

[root@k8s-master k8s-test]# kubectl get pod -o wide
NAME                    READY   STATUS    RESTARTS   AGE    IP           NODE        NOMINATED NODE   READINESS GATES
curl-6bf6db5c4f-kljp4   1/1     Running   1          43h    10.244.1.2   k8s-node1   <none>           <none>
hellok8s                2/2     Running   0          24h    10.244.1.6   k8s-node1   <none>           <none>
test-init-main          1/1     Running   0          155m   10.244.1.8   k8s-node1   <none>           <none>

查看一下日志,正好是我们想要打印的内容

[root@k8s-master k8s-test]# kubectl logs test-init-main
Main app is running

Init容器注意事项

下面是Init容器的几个注意事项:

  • Init容器是在网络和数据卷初始化之后启动,所以Init容器并不是Pod的第一个容器。但是我们对网络和数据卷的初始化不能做任何操作
  • Init容器只能一个接一个串联执行,而不能并行
  • 在所有的Init容器成功之前,Pod将不会变为ready状态,也就意味着不会在service中进行聚集
  • 如果pod重启,所有的Init容器都会被重新执行一遍。这也就意味着Init容器多次执行的结果不能有差异
  • Pod已经在running的情况下可以用kubectl edit pod xxx去修改pod的一些属性。对Init容器只有修改image字段才会生效,之后会重启该pod
  • 在yaml文件中配置containers和initContainers的字段几乎一样,除了Init容器没有readinessProbe
  • 在yaml文件中配置的主容器和Init容器的名字必须要唯一不能重复

总结

这一节我们用实例学习了容器创建的第一个阶段,Init C。下一节我们来详细学习下探针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值