Torres-tao  

背景

现在有一个Java Web应用的WAR包,它需要被放在Tomcat的webapps目录下运行起来。

假设,现在只能使用Docker来做这件事情,那么该如何处理这个组合关系呢?

  • 一种方法是,把WAR包直接放在Tomcat镜像的webapps目录下,做成一个新的镜像运行起来。可是,这时候,如果要更新WAR包的内容,或者要升级Tomcat镜像,就要重新制作一个新的发布镜像,非常麻烦。
  • 另外一个方法是,你压根不管WAR包,永远只发布一个Tomcat容器。不过,这个容器的webapps目录,就必须声明一个hostPath类型的Volume,从而把宿主机上的WAR包挂载进Tomcat容器当中运行起来。不过,这样就必须要解决一个问题,即:如何让每一台宿主机,都预先准备好这个存储有WAR包的目录呢?这样看来,你只能独立维护一套分布式存储系统了。

有了Pod之后,这样的问题就很容易解决了。我们可以把WAR包和Tomcat分别做成镜像,然后把它们作为一个Pod里的两个容器“组合”在一起。该Pod的配置文件如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: javaweb-2
spec:
  initContainers:
    - name: war
      image: geektime/sample:v2
      imagePullPolicy: IfNotPresent
      command: ["cp", "/sample.war", "/app"]
      volumeMounts:
        - name: app-volume
          mountPath: /app
  containers:
    - name: tomcat
      image: geektime/tomcat:7.0
      imagePullPolicy: IfNotPresent
      command: ["sh", "-c", "/root/apache-tomcat-7.0.42-v2/bin/start.sh"]
      volumeMounts:
        - name: app-volume
          mountPath: ,"/root/apache-tomcat-7.0.42-v2/webapps
      ports:
        - containerPort: 8080
          hostPort: 8001
  volumes:
    - name: app-volume
      emptyDir: {}

在这个Pod中,定义了两个容器,第一个容器使用的镜像是geektime/sample:v2,这个镜像里只有一个WAR包(sample.war)放在根目录下;而第二个容器使用的则是一个标准的Tomcat镜像。

可以看到,WAR包容器的类型不是一个普通容器,而是一个Init Container类型的容器。

在Pod中,所有Init Container定义的容器,都会比spec.containers定义的用户容器先启动。并且,Init Container容器会按顺序逐一启动,而直到它们都启动并且退出了,用户容器才会启动。

因此,这个Init Container类型的WAR包容器启动后,我们执行命令cp /sample.war /app,把应用的WAR包拷贝到/app目录下,然后退出。同时,这个/app目录,挂载了一个名叫app-volume的Volume。

接下来就是关键点了。Tomcat容器,同样声明了挂载app-volume到自己的webapps目录下。

所以,等Tomcat容器启动时,它的webapps目录下就一定会存在sample.war文件:这个文件正是WAR包容器启动时拷贝到这个Volume里面的,而这个Volume是被这两个容器共享的。

这样子,就用一种“组合”方式,解决了WAR包与Tomcat容器之间耦合关系的问题。

这种所谓的“组合”操作,正是容器设计模式里最常用的一种模式,它的名字叫:sidecar。

sidecar指的就是我们可以在一个Pod中,启动一个辅助容器,来完成一些独立于主进程(主容器)之外的工作。

比如,在上述的Pod中,Tomcat容器是我们要使用的主容器,而WAR包容器的存在,只是为了给它提供一个WAR包而已。所以我们用Init Container的方式优先运行WAR包容器,扮演了一个sidecar的角色。

posted on 2022-07-06 11:11  雷子锅  阅读(61)  评论(0编辑  收藏  举报