Pod中的初始化容器:Init Containers
initContainers实现理论前提:同一个Pod内的容器共享 网络、volume等资源
Init Containers
在Kubernetes中,init容器是在同一个Pod中的其他容器之前启动和执行的容器。它的目的是为Pod上托管的主应用程序执行初始化逻辑。例如,创建必要的用户帐户、执行数据库迁移、创建数据库模式等等。
Init Containers设计注意事项
• 它们总是比Pod里的其他容器先执行。因此,它们不应该包含需要很长时间才能完成的复杂逻辑。启动脚本通常很小而且简洁。如果我们发现在init容器中添加了太多的逻辑,那就应该考虑将它的一部分移到应用程序容器本身。
• Init容器按顺序启动和执行。除非成功完成其前置容器,否则不会调用init容器。因此,如果启动任务很长,可以考虑将其分成若干步骤,每个步骤都由init容器处理,以便知道哪些步骤失败。
• 如果任何init容器失败,整个Pod将重新启动(除非将restartPolicy设置为Never)。重新启动Pod意味着重新执行所有容器,包括任何init容器。因此,我们可能需要确保启动逻辑能够容忍多次执行而不会导致重复。例如,如果数据库迁移已经完成,那么应该忽略再次执行迁移命令。
• 在一个或多个依赖项可用之前,init容器是延迟应用程序初始化的一个很好的候选者。例如,如果我们的应用程序依赖于一个施加了API请求速率限制的API,可能需要等待一段时间才能从该API接收响应。在应用程序容器中实现此逻辑可能很复杂;因为它需要与运行状况和准备状态探测相结合。一种更简单的方法是创建一个init容器,该容器等待API准备好后才能成功退出。只有在init容器成功完成其工作之后,应用程序容器才会启动。
• Init容器不能像应用程序容器那样使用liveness和readiness探针。原因是它们注定要成功启动后退出,就像Jobs和CronJobs的行为一样。
• 同一个Pod内的所有容器共享相同的卷和网络。我们可以使用此特性在应用程序及其init容器之间共享数据。
Init Containers的“请求”和“限制”行为
正如我们刚刚讨论的,init容器总是在同一个Pod上的其他应用程序容器之前启动。因此,调度程序为init容器的资源和限制提供了更高的优先级。这种行为必须被彻底考虑,因为它可能会导致不期望的结果。例如,如果我们有一个init容器和一个应用程序容器,并且将init容器的资源和限制设置为高于应用程序容器的资源和限制,那么只有在存在满足init容器要求的可用节点时,才会调度整个Pod。换句话说,即使有一个未使用的节点可以运行应用程序容器,如果init容器具有该节点可以处理的更高的资源先决条件,那么Pod也不会部署到该节点。因此,在定义init容器的请求和限制时,应该尽可能严格。作为最佳实践,除非绝对需要,否则不要将这些参数设置为高于应用程序容器的值。
应用场景1: 代码升级发布
以java项目war包 + tomcat为例, 如果我们总是将 代码+tomcat打包为镜像一起发布,那么这个镜像相对就会比较到,而我们项目更新往往时只是代码部分,如果这样频繁更新发布那么每次大镜像传输非常消耗资源和时间。因此可以考虑将 war 单独作为一个镜像 实现为 initContainers,tomcat运行平台单独为一个 container。
使用deployment加InitContainer实现代码的升级发布以及版本回退
k8s的集群我的上个博文有介绍了详细的安装步骤,这里就不提及安装过程了
首先将Tomcat的代码准备好,这里我准备了两个代码,一个是Tomcat的初始页面代码,一个是zrlog项目的代码
然后在代码目录下编辑Dockerfile文件,将两个代码都打包成镜像
这个是初始页面的代码
[root@server151 test]# ls
Dockerfile ROOT.war
[root@server151 test]# cat Dockerfile
FROM alpine
WORKDIR /code
COPY ./ROOT.war /tmp
[root@server151 test]# docker build -t tomcat:v0 .
这个是zrlog项目的代码
[root@server151 web]# ls
Dockerfile ROOT.war
[root@server151 web]# cat Dockerfile
FROM alpine
WORKDIR /code
COPY ./ROOT.war /tmp
[root@server151 web]# docker build -t zrlog:v1 .
然后将两个镜像都推送到我们的私有镜像仓库
推送镜像的过程我就不赘述了,我的上一篇博文详细的写了私有镜像仓库的创建和使用,忘记了的可以去看看
代码准备好以后,就可以去我们的k8s构建deployment了
先准备一个空目录,然后进入这个空目录
用kubectl创建一个tomcat的deployment模板,这样就不用我们完全手写了,只需要修改部分代码
[root@server153 test]# kubectl create deployment tomcat-deploy --image tomcat --dry-run=server -o yaml > tomcat-deploy.yaml
[root@server153 test]# ls
tomcat-deploy.yaml
然后将模板文件修改成我们需要的
[root@server153 test]# cat tomcat-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: tomcat-deployname: tomcat-deploynamespace: default
spec:progressDeadlineSeconds: 600replicas: 1revisionHistoryLimit: 10selector:matchLabels:app: tomcat-deploystrategy:rollingUpdate:maxSurge: 25%maxUnavailable: 25%type: RollingUpdatetemplate:metadata:creationTimestamp: nulllabels:app: tomcat-deployspec:#创建init容器initContainers:#代码镜像- image: www.test.com/mytest/tomcat:v0#init容器名字name: init#将代码复制到匿名数据卷command: ["cp","-R","/tmp/ROOT.war","/www"]#将匿名数据卷挂载到容器中的/www目录下volumeMounts:- mountPath: /wwwname: tomcat-volume#创建tomcat容器containers:- image: oxnme/tomcat#重启策略imagePullPolicy: Alwaysname: tomcatterminationMessagePath: /dev/termination-logterminationMessagePolicy: File#将数据卷挂载到tomcat的代码目录下volumeMounts:- mountPath: /usr/local/tomcat/webapps/name: tomcat-volumednsPolicy: ClusterFirstrestartPolicy: AlwaysschedulerName: default-schedulerterminationGracePeriodSeconds: 10#创建匿名数据卷volumes:- name: tomcat-volumeemptyDir: {}
然后开始构建我们的deployment资源
[root@server153 test]# kubectl apply -f tomcat-deploy.yaml
deployment.apps/tomcat-deploy created
注意看运行结果后面的结尾的是created
因为我们的还没有配置service,只能在本机访问,外网是不能访问的
所以下个nginx做代理转发查看结果
先看看刚才创建pod的ip
[root@server153 test]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tomcat-deploy-8457d967b5-ltwd6 1/1 Running 0 5m45s 10.2.1.36 server154 <none> <none>
然后下载nginx
[root@server153 test]# yum install nginx -y
[root@server153 test]# vim /etc/nginx/nginx.conf
启动nginx后去浏览器查看情况
[root@server153 test]# systemctl start nginx.service
可以看到是可以正常访问到的
现在我们去模拟代码升级,用另一个代码镜像替换
继续修改我们的模板文件
修改好以后更新我们的deployment资源
[root@server153 test]# kubectl apply -f tomcat-deploy.yaml
deployment.apps/tomcat-deploy configured
注意看运行结果的结尾,configured,说明我们是更新了配置文件而已,不像刚才一样是创建
[root@server153 test]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tomcat-deploy-555479bdf4-hw29f 1/1 Running 0 14s 10.2.1.37 server154 <none> <none>
这里看我们pod的名称和ip已经换了,说明已经完成了更新,原来的pod删除了,起了新的pod
ip变了我们也得去nginx修改代理的地址
[root@server153 test]# vim /etc/nginx/nginx.conf
重启nginx后去浏览器访问
[root@server153 test]# systemctl restart nginx.service
可以看到我们的代码已经更新成另一个项目的了
说明我们的代码升级完成了
deployment的强大可不仅仅在代码升级,还能回滚
在新版本代码上线的时候,如何出现问题可以快速回滚到上一个版本
刚才我们不是更新了一次代码,我们看看现在deployment的历史版本情况
[root@server153 test]# kubectl rollout history deployment tomcat-deploy
deployment.apps/tomcat-deploy
REVISION CHANGE-CAUSE
1 <none>
2 <none>
可以看到有两个版本,我们查看第一个版本的详细情况
我只截取部分,可以看到第一个版本用的镜像
[root@server153 test]# kubectl rollout history deployment/tomcat-deploy --revision=1
deployment.apps/tomcat-deploy with revision #1
Pod Template:Labels: app=tomcat-deploypod-template-hash=8457d967b5Init Containers:init:Image: www.test.com/mytest/tomcat:v0
我们再看看第二个版本
[root@server153 test]# kubectl rollout history deployment/tomcat-deploy --revision=2
deployment.apps/tomcat-deploy with revision #2
Pod Template:Labels: app=tomcat-deploypod-template-hash=555479bdf4Init Containers:init:Image: www.test.com/mytest/zrlog:v1
可以看到都是对应得上的
比如现在我们刚更新的版本出了问题,我么要回滚到上一个版本
[root@server153 test]# kubectl rollout undo deployment/tomcat-deploy --to-revision=1
deployment.apps/tomcat-deploy rolled back
然后看看pod的情况,可以看到我们的旧版本的pod正在起来
在旧版本pod没起来之前新版本的pod不会直接停止,这样保证了业务能正常运行
[root@server153 test]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tomcat-deploy-555479bdf4-hw29f 1/1 Running 0 21m 10.2.1.37 server154 <none> <none>
tomcat-deploy-8457d967b5-5gdwt 0/1 PodInitializing 0 21s 10.2.1.38 server154 <none> <none>
[root@server153 test]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tomcat-deploy-8457d967b5-5gdwt 1/1 Running 0 116s 10.2.1.38 server154 <none> <none>
过一会再看可以看到已经回到我们的旧版本pod了,pod的ip变是正常的
然后我们再修改nginx的代理配置
[root@server153 test]# vim /etc/nginx/nginx.conf
重启nginx,然后去浏览器访问
[root@server153 test]# systemctl restart nginx.service
可以看到又回到原来版本的代码了
证明我们的代码版本升级或者回滚都没有问题
可以看出来deploym的确是非常强大的
希望对大家有帮助