在本文中,我们将重点介绍如何对 Spring Boot 应用程序进行 Docker 容器化以在独立的环境(即容器)中运行它。
此外,我们还会展示如何创建容器的集成,它们彼此依赖并在虚拟专用网络中彼此链接。我们还能看到如何通过单个命令进行统一管理。
那么我们先来创建一个运行在 Alpine Linux 的基于 Java 的轻量级基础映像。
基础镜像构建
我们将使用 Docker 独有的编译文件格式: Dockerfile。
Dockerfile 是一个面向行的批处理文件,包含建立一个镜像的命令。将这些命令放在一个文件中不是必须的,我们能够在命令行直接输入命令,但是一个文件会更简单。
所以,让我们写第一个 Dockerfile :
FROM:关键字 FROM,告诉 Docker 将给定的图像及其标记用作镜像的基础。如果此映像不在本地库中,则会对 DockerHub 或任何其他配置的远程仓库进行在线搜索。
MAINTAINER:MAINTAINER 通常是一个电子邮件地址,用于识别镜像的作者。
RUN:使用 RUN 命令,我们会在目标系统中执行一个 Shell 命令行。这里我们利用 Alpine Linux 的软件包管理器 Apk 安装 Java 8 OpenJDK。
COPY:最后的命令告诉 Docker 从本地文件系统中复制一些文件到镜像中的给定路径,特别是子文件夹到构建目录中。
要求:为了成功运行本教程,您必须从 Oracle 下载 Java 加密扩展(JCE)无限强度管理策略文件。只需将下载的归档文件解压缩到名为 files 的本地文件夹中。
为了最终能构建镜像并将其存储在本地库中,我们必须运行:
docker build --tag=alpine-java:base --rm=true .
注意:该-tag 选项将定义图像的名称,-rm =true 已成功建成后,将删除中间图像。此 Shell 命令中的最后一个字符是一个点,用作构建目录参数。
Docker 化独立的 Spring Boot
应用程序
作为我们可以容器化的应用的例子,我们从 Spring Cloud Configuration Tutorial 配置 Spring-cloud-config/server 文件。作为一个准备步骤,我们必须组装一个可运行的 Jar 文件并将其复制到我们的 Docker 构建目录中:
现在,我们将创建一个名为 Dockerfile.server 的 Dockerfile 文件,内容如下:
- FROM:作为我们的基础镜像,我们将采用在上一节中创建的可运行 Java 程序的 Alpine Linux。
- COPY:我们让 Docker 将我们的 Jar 文件复制到镜像中。
- ENV:该命令允许我们定义一些环境变量,这些变量将被容器中运行的应用程序所引用。在这里我们定义 Spring Boot 应用程序的配置,以便稍后将其传递给可执行的 Jar 文件。
- ENTRYPOINT / CMD:这是在容器启动时启动的可执行文件。我们必须将它们定义为 JSON-Array 格式的,因为我们将使用 ENTRYPOINT 与 CMD 结合的某些应用程序参数。
- VOLUME:因为我们的容器将在独立的环境中运行,没有直接的网络访问,所以我们必须为我们的配置库定义一个挂载点占位符。
- EXPOSE:这里我们告诉 Docker,我们的应用程序在哪个端口启动。当容器启动时,该端口将被发布到主机。
我们要从 Dockerfile 创建一个镜像,必须像之前一样运行 ‘docker build ‘:
但是,在我们从镜像中运行容器之前,我们必须创建一个用于安装的卷:
注意:虽然容器是不可变的,但是在应用程序退出后未提交到映像的情况下,存储在卷中的数据将在多个容器中持久存储。
最后我们可以在我们的镜像中运行容器。
- 首先,我们要命名我们的容器。如果没有,将选择默认的。
- 然后,我们必须将我们公开的端口(请参阅 Dockerfile)发布到我们主机上的端口。该值以 “host-port:container-port” 的形式给出。如果只提供了一个容器端口,将使用随机选择的主机端口。如果我们不写这个选项,容器将被完全隔离。
- Volume 选项可以访问任一主机上的一个目录(以绝对路径使用时)或先前创建的 Docker Volume(当时用 Volume-Name 时)。冒号后面的路径指定容器内的挂载点。
- 作为参数,我们必须告诉 Docker,要使用哪个镜像。在这里,我们必须从之前的“ docker build ”步骤给出的镜像名称。
- 一些更有用的选项:
- -it 启用交互模式并分配虚拟终端机
- -d 启动后从容器中分离
如果我们在分离模式下运行容器,我们可以检查其细节,停止并使用以下命令将其删除:
将互相依赖的应用程序进行容器化
Docker 命令和 Dockerfiles 特别适用于创建单个容器。但是,如果要在隔离的网络上运行微服务,容器管理将会变得非常混乱。
为了解决这个问题,Docker 提供了一个名为 Docker Compose 的工具。它具有自己的 YAML 格式的构建文件,更适合管理多个容器。例如:它能够在一个命令中启动或停止服务组合,或将多个服务的日志输出合并到一个虚拟终端机中。
我们来构建运行在不同 Docker 容器中的两个应用程序的例子。它们将彼此通信,并被视为主机系统的独立单元。我们将把 Spring Cloud 配置教程中描述的 spring-cloud-config / client 示例构建复制到我们的文件夹,就像我们之前使用的配置服务一样。
下面是我们的 Docker-compose.yml:
- Version:指定应使用哪种版本的格式。这是必填字段。这里我们使用较新版本,而旧版格式为‘1‘。
- Services:此键中的每个对象定义一个服务,也称为容器。这个选项是必填的。
- Build:如果指定,docker-compose 可以从 Dockerfile 构建镜像。
- Context:如果指定,就会生成 Dockerfile 中制定的目录。
- Dockerfile:如果指定,它会生成一个Dockerfile 中设置的备用名称。
- Image:在构建功能被使用时,告诉 Docker 它应该使用的镜像的名字。否则,它会在本地库或远程注册表中搜索此图像。
- Networks:这是命名网络名称的标识符。给定的名称值必须是在网络节点中存在的。
- Volumes:标识要使用的命名卷和挂载点,以冒号分隔。同样应该是在网络节点中存在的,卷名必须在单独的卷部分中定义。
- Links:将创建此服务和列出的服务之间的内部网络链接。此服务将连接到列出的服务,其中冒号之前的部分从服务节点中指定服务名称,冒号后面的部分是指定服务在外部端口上侦听的主机名。
- Depends_on:告诉 Docker 仅启动服务,如果列出的服务已成功启动。注意:这只在容器级别有用!关于启动从属应用程序的解决方法,请参阅 Config-client-entrypoint.sh。
- Logging:这里我们使用 ‘json-file‘ 驱动,这是默认驱动。可替换为其他日志驱动或使用 “none”。
- Networks:在本选项中,我们将指定可用于我们服务的网络。在这个例子中,我们让 docker-compose 创建一个对我们来说名为“桥接”的网络类型。如果选项 external 设置为 true,它将使用具有给定名称的现有选项。
- Volumes:这与网络部分相似。
在继续之前,我们将检查我们的构建文件以检查语法错误:
下面是我们的 Dockerfile.client 来构建配置客户端映像。它不同于 Dockerfile.server,因为我们还安装了 OpenBSD netcat(这在下一步需要),并使 Entrypoint 成为可执行文件:
这是我们的配置客户端服务定制的 Entrypoint。这里我们在一个循环中使用 netcat 来检查我们的配置服务器是否准备就绪。您必须注意,我们可以通过其链接名称而不是 IP 地址访问我们的配置服务器:
最后,我们可以构建我们的镜像,创建定义的容器并在一个命令中启动它们:
要停止容器,请将其从 Docker 中删除,并从中删除所连接的网络和卷,我们可以使用相反的命令:
Docker-compose 的一个很好的功能是扩容服务的能力。例如,我们可以告诉 Docker 启动一个容器配置服务器和三个容器的配置客户端。
但是为了正常工作,我们必须让 Docker 选择一个 Container_name 从 Docker-compose.yml 中删除,我们必须更改暴露的端口配置,以避免冲突。
之后,我们可以扩容我们的服务,扩容为拥有3台 Client 的集群:
结论
正如我们所看到的,我们现在可以构建定制的 Docker 映像,用 Docker 容器运行 Spring Boot 应用程序,并使用 Docker-compose 创建依赖容器。
有关构建文件的进一步阅读,请参考官方 Dockerfile Reference 和 Docker-compose.yml reference。
作者:靳日阳,JFrog 研发工程师
具有多年软件开发经验;对Java主流技术、前沿框架都具有丰富的开发经验;擅长Linux服务器,对优化,部署等有深入研究,熟悉Jenkins,持续集成及交付,DevOps等。
欢迎转载,但转载请注明作者与出处。谢谢!