在前面的文章中,我们介绍了容器技术,他的本质是操作系统上的一个进程,那么打包的容器镜像其实就是一个安装包,类似于windows操作系统中的exe文件,那容器所在的kubernete集群其实就是一个操作系统。
容器和操作系统中进程一样,并不是独立的运行,而是和其他容器之间有着关联的关系。假如我们有3个进程,因为相互之间的关系需要部署在同一台宿主机上,这3个进程每个需要分配1G内存,但是有2个宿主机,A机器有3G内存,B机器有2.5G内存,如果不能统一调度,其中2个进程调度到B机器后,因为内存不足,第3个进程启动失败。而如果我们使用kubernete中的pod,pod会对3个容器统一管理和调度,这样就会直接选择A宿主机。
调度在一个pod的中的容器,一般具有某些关联关系,比如共享Linux namespace,共享volume,通过localhost进行通信。像Tomcat和它所依赖的war包,就是这样的一个例子。在pod的,实现这个功能,依靠的是一个infra容器,这个容器使用的镜像是k8s.gcr.io/pause,只有不到200k,它的状态永远是pause,作用是启动后控制namespace,pod中的其他容器启动后就可以加入当前这个namespace中,这样pod中的容器共享一份namespace,就可以通过localhost进行通信了。如下图所示:
因此,infra容器是一个基础容器,它控制着namespace和整个pod的生命周期。
下面我们看一个yaml文件,Tomcat和war在同一个pod的不同容器中
apiVersion: v1 kind: Pod metadata: name: javaweb-2 spec: initContainers: - image: zjj2006forever/spingboot-rabbitmq name: war command: ["cp", "/spingboot-rabbitmq.war", "/app"] volumeMounts: - mountPath: /app name: app-volume containers: - image: tomcat:jdk8-openjdk-slim name: tomcat command: ["sh","-c","/root/apache-tomcat-8.5.54-v2/bin/start.sh"] volumeMounts: - mountPath: /root/apache-tomcat-8.5.54-v2/webapps name: app-volume ports: - containerPort: 8080 hostPort: 8001 volumes: - name: app-volume emptyDir: {}
上面的yaml文件中,定义了一个pod,这个pod中有2个容器,其中一个是运行标准Tomcat8镜像,一个是存放Tomcat下的war包,这儿使用的是我之前做的一个springboot-rabbitmq的镜像。同时我们也看到war包所在的容器类型是initContainer,这个镜像的特点是会比spec.containers中的容器先启动。而且,这2个容器都挂载了一个叫app-volume的volume,这个volume的路径是/app,war包容器启动时会把war包copy到/app目录下,这样Tomcat容器的/root/apache-tomcat-8.5.54-v2/webapps下就可以看到这个war包了。这就是kubernete容器编排的高明之处。如果没有pod,那就是两种处理方式,要不就是在Tomcat容器下启动一个war包容器,这种方式不方便升级war包;要不就是把war包放在宿主机上,挂载在容器种,但这种情况不适合分布式场景。
上面这个配置,叫做sidecar模式,也就是在pod中启动一个辅助容器来配合主容器进程的工作,上面的war容器就是一个sidecar。sidecar有很多场景可以应用,比如pod中可以启动一个sidecar来收集日志。