Vert.x Docker

翻译:Ranger Tsao

简介

Docker 是一个可以将应用部署在其中的轻量级、隔离的容器。应用程序并行运行在隔离的 Linux 容器中。如果从未使用过 Docker ,可以根据官方教程,轻松入门

Vert.x 提供两个 Docker 镜像给开发人员运行部署程序,分别是:

  • vertx/vertx3 基础镜像,需要进行一些扩展才可以运行程序
  • vertx/vertx3-exec 给宿主系统提供 vertx 命令行

这两个 Docker 镜像均发布在 Docker Hub 中。

现在公开镜像的仓库已经更名为 Docker Store

本指南将介绍如何使用这 Docker 镜像,以及如何使用 Maven 自动生成 Docker 镜像,生成 Fabric8 元数据并使用 fat jar

基础镜像

vertx/vertx3 能让开发人员轻松的在 Docker 容器中运行 Vert.x 应用。为实现该目的,开发人员须通过创建自定义 Dockerfile 的形式,继承 vertx/vertx3 。这样便可以通过容器中的 vertx 命令启动应用程序。

在 Docker 容器中部署 JavaScript Verticle

编写一个简单的 JS Verticle ,代码如下:

hello-verticle.js

  1. vertx.createHttpServer().requestHandler(function (request) {
  2. request.response().end("Hello world");
  3. }).listen(8080);

在同一目录,创建一个 Dockerfile ,代码如下:

Dockerfile

  1. # 继承 vert.x 镜像 (1)
  2. FROM vertx/vertx3
  3. # 设置要部署的 verticle 名字 (2)
  4. ENV VERTICLE_NAME hello-verticle.js
  5. # 设置 verticles 存放路径 (3)
  6. ENV VERTICLE_HOME /usr/verticles
  7. EXPOSE 8080
  8. # 拷贝 verticle 至容器中 (4)
  9. COPY $VERTICLE_NAME $VERTICLE_HOME/
  10. # 启动 verticle (5)
  11. WORKDIR $VERTICLE_HOME
  12. ENTRYPOINT ["sh", "-c"]
  13. CMD ["exec vertx run $VERTICLE_NAME -cp $VERTICLE_HOME/*"]

步骤说明:

  1. 继承 vert.x 镜像
  2. 设置要部署的 verticle 名字
  3. 设置 verticles 存放路径
  4. 拷贝 verticle 至容器中
  5. 启动 verticle

接下来就可以构建 Docker 镜像了:

  1. docker build -t sample/vertx-javascript .

最后执行 docker run -t -i -p 8080:8080 sample/vertx-javascript 便可以启动应用了。

这时候可以打开浏览器验证成果了,在浏览器输入地址 http://localhost:8080 (如果是 docker 引擎是 boot2docker 的话,则输入 http://192.168.59.103:8080 )。

‘-t -i’ 参数意味着是互动模式。按 CTRL + C 可以停止容器。点击 Docker run reference 查阅 Docker run 命令详细信息。

可能已经注意到 Dockerfile 中的 EXPOSE 8080 和运行命令中的 -p 8080:8080 。第一个是可选的信息,告诉应用程序想要监听8080端口。第二个是强制性的,并指示码头工具将8080端口从主机转发到容器的8080端口。

可能还注意到复杂且令人费解的应用程序的启动方式。不是直接调用 vertx,而是与 exec 一起使用 sh -csh -c 是转向Docker限制,而不是在 CMD 中扩展变量。关于 Docker 构建器文档的更多细节,查阅文档exec 是使 vertx 命令进程替换 shell ,以便获取pid 1并接收信号,比如在运行 docker stop 时,获取 SIGTERM 。如果没有 exec ,那么 shell会随着 vertx 命令进程一直运行,而且 vertx 命令难以得到信号,从而阻止正常关闭。

在 Docker 容器中部署 Groovy Verticle

同部署 JavaScript Verticle,区别在与 Verticle 代码及文件后缀名。在上述例子中,hello_verticle.js 在此则是 hello_verticle.groovy ,代码如下:

hello-verticle.groovy

  1. vertx.createHttpServer().requestHandler({ request ->
  2. request.response().end("Groovy world")
  3. }).listen(8080)

注意在后续编写 Dockerfile 时 引入的 Verticle 名字 则是 hello_verticle.groovy

在 Docker 容器中部署 Ruby Verticle

同部署 JavaScript Verticle,区别在与 Verticle 代码及文件后缀名。在上述例子中,hello_verticle.js 在此则是 hello_verticle.rb ,代码如下:

hello-verticle.rb

  1. $vertx.create_http_server().request_handler() { |request|
  2. request.response().end("A ruby world full of gems")
  3. }.listen(8080)

注意在后续编写 Dockerfile 时 引入的 Verticle 名字 则是 hello_verticle.rb

在 Docker 容器中部署 Java Verticle

同样,部署 Java Verticle 与前面的例子依然差不多,区别在于将 verticle jar 文件复制到容器。先看 Java 代码:

io.vertx.sample.hello.HelloVerticle

  1. package io.vertx.sample.hello;
  2. import io.vertx.core.AbstractVerticle;
  3. public class HelloVerticle extends AbstractVerticle {
  4. @Override
  5. public void start() throws Exception {
  6. vertx.createHttpServer().requestHandler(request -> {
  7. request.response().end("Hello Java world");
  8. }).listen(8080);
  9. }
  10. }

显而易见,本 verticle 被打包到 target/hello-verticle-1.0.-SNAPSHOT.jar 文件中。所以 Dockerfile 需要复制这个文件,同时也必须告知 vertx 需要执行的 verticle 类名:

Dockerfile

  1. # Extend vert.x image
  2. FROM vertx/vertx3
  3. # (1)
  4. ENV VERTICLE_NAME io.vertx.sample.hello.HelloVerticle
  5. ENV VERTICLE_FILE target/hello-verticle-1.0-SNAPSHOT.jar
  6. # Set the location of the verticles
  7. ENV VERTICLE_HOME /usr/verticles
  8. EXPOSE 8080
  9. # Copy your verticle to the container (2)
  10. COPY $VERTICLE_FILE $VERTICLE_HOME/
  11. # Launch the verticle
  12. WORKDIR $VERTICLE_HOME
  13. ENTRYPOINT ["sh", "-c"]
  14. CMD ["exec vertx run $VERTICLE_NAME -cp $VERTICLE_HOME/*"]
  1. 同前面的例子不一样,需要设置 verticle 类名及对应的文件名
  2. 拷贝 jar 文件至 $VERTICLE_HOME

同理,执行命令依然没有什么变化:

  1. > docker build -t sample/vertx-java .
  2. ....
  3. > docker run -t -i -p 8080:8080 sample/vertx-java

配置

在前面所提到的 Dockerfile 并没有对 Vert.x 进行相关配置。下面的几个章节,从 5 个方面着重介绍如何配置。

配置 JVM 参数

可以通过设置 JAVA_OPTS 环境变量来配置 Java 虚拟机,代码如下:

  1. ENV JAVA_OPTS "-Dfoo=bar"

配置 vertx 参数

可以使用 VERTX_OPTS 环境变量配置 Vert.x 特定的系统变量:

  1. ENV VERTX_OPTS "-Dvertx.options.eventLoopPoolSize=26 -Dvertx.options.deployment.worker=true"

Classpath

使用 Vert.x 命令的 -cp 参数或 设置 CLASSPATH 环境变量来配置应用程序的类路径:

  1. ENV CLASSPATH "/usr/verticles/libs/foo.jar:/usr/verticles/libs/bar.jar:"

日志

要配置 logging.properties 文件(自定义 JUL 日志记录器),请设置 VERTX_JUL_CONFIG 环境变量:

  1. COPY ./logging.properties $VERTICLE_HOME/ (1)
  2. ENV VERTX_JUL_CONFIG $VERTICLE_HOME/logging.properties (2)
  1. 拷贝 logging.properties 日志配置文件
  2. 设置 VERTX_JUL_CONFIG 环境变量

集群

可以提供自定义的 cluster.xml文件,并将其添加到类路径中。要从 $VERTICLE_HOME 中包含的所有文件构建动态类路径,可以使用:

  1. COPY ./cluster.xml $VERTICLE_HOME/
  2. # ...
  3. CMD [export CLASSPATH=`find $VERTICLE_HOME -printf '%p:' | sed 's/:$//'`; exec vertx run $VERTICLE_NAME"]

注意导出 CLASSPATH = ...; 部分在 CMD 指令中。它从 $VERTICLE_HOME 目录的内容构建 CLASSPATH 变量。这种技巧对于计算大型和动态类路径非常有用。

通过 Maven 构建镜像

在Maven构建过程中,有几个 Maven插件来构建 Docker 映像。此示例采用来自 Spotify 的 docker-maven-plugin

首先,同往常一样创建 Java 项目。源代码位于 src/main/java ,然后创建一个 src/main/docker 目录,并在其中创建一个 Dockerfile ,目录结构如下:

  1. .
  2. ├── pom.xml
  3. ├── src
  4. └── main
  5. ├── docker
  6. └── Dockerfile
  7. └── java
  8. └── io
  9. └── vertx
  10. └── example
  11. └── HelloWorldVerticle.java
  12. ├── target

然后在 pom.xml 文件增加相应的配置,代码如下:

  1. <groupId>com.spotify</groupId>
  2. <artifactId>docker-maven-plugin</artifactId>
  3. <version>0.2.8</version>
  4. <executions>
  5. <execution>
  6. <id>docker</id>
  7. <phase>package</phase>
  8. <goals>
  9. <goal>build</goal>
  10. </goals>
  11. </execution>
  12. </executions>
  13. <configuration>
  14. <dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>
  15. <!-- Configure the image name -->
  16. <imageName>sample/vertx-hello</imageName>
  17. <resources>
  18. <resource>
  19. <targetPath>/verticles</targetPath>
  20. <directory>${project.build.directory}</directory>
  21. <includes>
  22. <include>${project.artifactId}-${project.version}.jar</include>
  23. </includes>
  24. </resource>
  25. <!-- don't forget to also add all the dependencies required by your application -->
  26. </resources>
  27. </configuration>
  28. </plugin>

该插件会将列出的内容复制到 target/docker 中。所有的资源文件 都被复制到 targetPath 中。所以编辑 src /main/docker/Dockerfile 并添加以下内容:

  1. FROM vertx/vertx3
  2. ENV VERTICLE_HOME /usr/verticles
  3. ENV VERTICLE_NAME io.vertx.example.HelloWorldVerticle
  4. COPY ./verticles $VERTICLE_HOME
  5. ENTRYPOINT ["sh", "-c"]
  6. CMD ["exec vertx run $VERTICLE_NAME -cp $VERTICLE_HOME/*"]

这和上面的例子中的 Dockerfile 文件内容基本相同。稍微有点区别的是,插件已将文件拷贝至 Dockerfile 所在的目录中。

通过命令 mvn clean package 便可构建 Docker 镜像了。

构建 Fabric 8 平台使用的镜像

Fabric 8 是一个开源集成开发平台,为基于 Kubernetes 和 OpenShift V3 的微服务提供管理、持续发布、 iPaas 设施。可以在 Fabric 8 执行 包含 Vert.x 应用程序的 Docker 镜像。不过需要做一些额外工作,添加额外的元数据。在这个例子中,将使用 RolandHuß 的docker-maven-plugin

首先来看目录结构:

  1. ├── pom.xml
  2. ├── src
  3. └── main
  4. ├── docker
  5. └── assembly.xml
  6. └── java
  7. └── io
  8. └── vertx
  9. └── example
  10. └── HelloWorldVerticle.java
  11. └── target

与 Spotify 的 maven 插件不同,此插件以一个 assembly.xml 作为打包描述文件。该文件列出了需要复制到 docker 容器的所有文件,如:

  1. <assembly>
  2. <dependencySets>
  3. <dependencySet>
  4. <includes>
  5. <include>:${project.artifactId}</include>
  6. </includes>
  7. <outputDirectory>.</outputDirectory>
  8. </dependencySet>
  9. </dependencySets>
  10. </assembly>

Dockerfile在 其余部分的配置在 pom.xml 文件中进行配置:

  1. <plugin>
  2. <groupId>org.jolokia</groupId>
  3. <artifactId>docker-maven-plugin</artifactId>
  4. <version>0.11.5</version>
  5. <executions>
  6. <execution>
  7. <id>build</id>
  8. <phase>package</phase>
  9. <goals>
  10. <goal>build</goal>
  11. </goals>
  12. </execution>
  13. </executions>
  14. <configuration>
  15. <images>
  16. <image>
  17. <name>${docker.image}</name>
  18. <build>
  19. <from>vertx/vertx3</from>
  20. <tags>
  21. <tag>${project.version}</tag>
  22. </tags>
  23. <ports>
  24. <port>8080</port>
  25. </ports>
  26. <command>vertx run io.vertx.example.HelloWorldVerticle -cp
  27. /usr/verticles/${project.artifactId}-${project.version}.jar
  28. </command>
  29. <assembly>
  30. <mode>dir</mode>
  31. <basedir>/usr/verticles</basedir>
  32. <descriptor>assembly.xml</descriptor>
  33. </assembly>
  34. </build>
  35. </image>
  36. </images>
  37. </configuration>
  38. </plugin>

如需更精细地配置容器,请参考手册Dockerfile 中的所有指令都可以在插件中进行设置。

在上面的 pom.xml 文件使用名为 docker.image 的属性来设置镜像名称。不要忘记将它添加至 pom.xml 文件中

一旦有了这个配置,便需要 fabric8-maven-plugin 插件来生成 Fabric8 所需的元数据:

  1. <plugin>
  2. <groupId>io.fabric8</groupId>
  3. <artifactId>fabric8-maven-plugin</artifactId>
  4. <version>2.1.4</version>
  5. <executions>
  6. <execution>
  7. <id>json</id>
  8. <phase>generate-resources</phase>
  9. <goals>
  10. <goal>json</goal>
  11. </goals>
  12. </execution>
  13. <execution>
  14. <id>attach</id>
  15. <phase>package</phase>
  16. <goals>
  17. <goal>attach</goal>
  18. </goals>
  19. </execution>
  20. </executions>
  21. </plugin>

一旦设置,就可以使用:mvn clean package 指令来构建 docker 映像。它将创建 Fabric8 所需的 kubernates.json 文件。通过指令 docker push $DOCKER_REGISTRY/sample/vertx-hello 就可以将创建的镜像推送到由 Fabric8 提供的 Docker 注册表上。

同时不要忘记将 DOCKER_REGISTRY 的 URL 设置为指向由 Fabric8 管理的注册表。最后一步是应用它:

  1. mvn io.fabric8:fabric8-maven-plugin:2.1.4:apply

可执行镜像

vertx/vertx3-exec 镜像为其容器提供了 vertx 命令行功能,因此在机器上不需要安装 Vert.x ,只需要使用本 docker 镜像即可。

举个栗子:

  1. > docker run -i -t vertx/vertx3-exec -version
  2. 3.4.1

运行 verticle :

  1. docker run -i -t -p 8080:8080 \
  2. -v $PWD:/verticles vertx/vertx3-exec \
  3. run io.vertx.sample.RandomGeneratorVerticle \
  4. -cp /verticles/MY_VERTICLE.jar

自定义技术栈

vertx/vertx3-exec 镜像提供默认的完整的 Vert.x 堆栈。如果需要自定义此堆栈,并创建自己的 exec 映像。首先,创建一个 vertx-stack.json 文件:

  1. {
  2. "variables": {
  3. "vertx.version": "3.3.3"
  4. },
  5. "dependencies": [
  6. {
  7. "groupId": "io.vertx",
  8. "artifactId": "vertx-web",
  9. "version": "${vertx.version}",
  10. "included": true
  11. },
  12. {
  13. "groupId": "io.vertx",
  14. "artifactId": "vertx-lang-js",
  15. "version": "${vertx.version}",
  16. "included": true
  17. }
  18. ]
  19. }

可以在文件中列出所需的任何依赖关系,而不仅仅是 Vert.x 组件(有关详细信息,请参阅 Stack Manager 文档)。

最后编写 Dockerfile

  1. FROM vertx/vertx3-exec (1)
  2. COPY vertx-stack.json ${VERTX_HOME}/vertx-stack.json (2)
  3. RUN vertx resolve && rm -rf ${HOME}/.m2 (3)
  1. 继承 vertx/vertx3-exec 镜像
  2. 替换 vertx-stack.json
  3. 解析依赖

构建 Docker 镜像:

  1. docker build -t mycompany/my-vertx3-exec .

执行 Verticle :

  1. docker run -i -t -p 8080:8080 \
  2. -v $PWD:/verticles mycompany/my-vertx3-exec \
  3. run io.vertx.sample.RandomGeneratorVerticle \
  4. -cp /verticles/MY_VERTICLE.jar

部署 fat jar

可以将一个打包成 Fat jar 的 Vert.x 应用程序部署到 docker 容器中。为此,不需要 Vert.x 提供的镜像,直接使用基本的 Java 镜像即可。举个栗子:

  1. FROM openjdk:8-jre-alpine (1)
  2. ENV VERTICLE_FILE hello-verticle-fatjar-3.0.0-SNAPSHOT-fat.jar (2)
  3. # Set the location of the verticles
  4. ENV VERTICLE_HOME /usr/verticles
  5. EXPOSE 8080
  6. # Copy your fat jar to the container
  7. COPY target/$VERTICLE_FILE $VERTICLE_HOME/ (3)
  8. # Launch the verticle
  9. WORKDIR $VERTICLE_HOME
  10. ENTRYPOINT ["sh", "-c"]
  11. CMD ["exec java -jar $VERTICLE_FILE"] (4)
  1. 扩展 OpenJDK 8 镜像
  2. VERTICLE_FILE 设置为指向 fat jar
  3. 从 target 目录中复制 fat jar 。如果不使用 Maven ,请根据实际情况修改
  4. 使用 java 指令启动应用程序

它基本上是与以前的 Dockerfile 类似。区别在于,这次我们扩展的是 java:8 而不是 vertx/vertx3 镜像。 然后我们将 fat jar 复制到容器中,并使用 java 指令来启动。当然,上述所有配置设置仍然有效的。

最后构建并启动容器:

  1. > docker build -t sample/vertx-java-fat .
  2. ....
  3. > docker run -t -i -p 8080:8080 sample/vertx-java-fat

原文档更新于2017-03-15 15:54:14 CET