容器化 Java 应用
在本单元中,将容器化 Java 应用程序。
如前所述,容器作为普通系统进程直接在主机操作系统、内核和硬件上运行。 容器需要更少的系统资源,从而导致占用空间更小、开销更少、应用程序启动时间更快。 这些好处非常适合按需扩展的实例。
有 Windows 容器和 Linux 容器。 在本模块中,你将使用广泛使用的 Docker 运行时来生成 Linux 容器映像。 然后将 Linux 容器映像部署到本地计算机的主机作系统。 最后,将 Linux 容器映像部署到 Azure Kubernetes 服务。
Docker 概述
Docker 运行时用于生成、拉取、运行和推送容器映像,如下图所示:
下表描述了每个 Docker 命令:
| Docker 命令 | DESCRIPTION |
|---|---|
docker build |
生成包含 Docker 从映像构建运行容器所需指令或层的容器镜像。 此命令的结果是图像。 |
docker pull |
将注册表中的映像下载到客户端选择的 Docker 守护程序的映像存储中。 可以重复使用现有的映像层。 在此模块中,本地 Docker 环境在开发期间使用注册表中的映像;稍后,AKS 节点将从Azure 容器注册表拉取应用程序映像进行部署。 |
docker run |
从映像创建并启动一个新容器。 容器运行其配置的启动过程,但可被命令行覆盖。 映像层提供容器的文件系统;容器启动时不会再次执行 Dockerfile 生成指令。 |
docker push |
Azure 容器注册表存储映像,以便它们随时可用,并与 Azure 的部署和缩放密切相关。 |
克隆 Java 应用程序
首先,克隆用于航空公司预订的航班预订系统的存储库,并导航到航空公司网站应用程序项目文件夹。
注释
如果在 CLI 选项卡中完成 Azure Kubernetes 服务创建,请使用该选项卡。如果它仍在运行,请打开一个新选项卡,并导航到想要克隆航空公司预订航班预订系统的位置。
运行以下命令:
git clone https://github.com/Azure-Samples/containerize-and-deploy-Java-app-to-Azure.git
cd containerize-and-deploy-Java-app-to-Azure/Project/Airlines
(可选)如果已安装 Java 和 Maven,可以在终端控制台中运行以下命令,了解在没有 Docker 的情况下生成应用程序的体验。 如果没有安装 Java 和 Maven,可以安全地转到下 一部分“构造 Docker 文件”。 在此部分中,你需要使用 Docker 来拉取将代表你执行生成的 Java 和 Maven。
mvn clean package
注释
我们使用了 mvn clean package 命令来演示不使用 Docker 多阶段构建所面临的运营挑战,接下来我们将介绍这些挑战。 同样,此步骤是可选的。 无论哪种方式,都可以安全地继续,而无需执行 Maven 命令。
如果该过程成功,Maven 就成功构建了用于航空公司预订的航班预订系统 Web 应用程序归档工件 AirlinesReservationSample-0.0.1-SNAPSHOT.war,如下方历史输出所示。 生成时间戳和持续时间可能有所不同:
[INFO] Building war: $PROJECT_PATH/containerize-and-deploy-Java-app-to-Azure/Project/Airlines/target/AirlinesReservationSample-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.776 s
[INFO] Finished at: 2024-11-15T09:33:26+09:00
[INFO] ------------------------------------------------------------------------
假设你是 Java 开发人员,你刚刚构建了 AirlinesReservationSample-0.0.1-SNAPSHOT.war。 下一步可能是与运维工程师合作,将该制品部署到本地服务器或虚拟机上。 若要使应用程序成功启动和运行,服务器和虚拟机必须可用,并配置所需的依赖项。 此过程具有挑战性且非常耗时,尤其是在应用程序负载增加时,更是如此。 借助容器,这些挑战将得到缓解。
构建 Dockerfile
现在可以构造 Dockerfile 了。 Dockerfile 是一个文本文档,其中包含用户将在命令行上执行的所有命令来组装容器映像。 容器映像可以包含多个只读文件系统层。 Docker 可以重复使用缓存的构建结果用于未更改的步骤。 层彼此叠加。
例如,航班预订系统需要部署到应用程序服务器并在应用程序服务器内部运行。 应用程序服务器未打包到 AirlinesReservationSample-0.0.1-SNAPSHOT.war 中。 这是 AirlinesReservationSample-0.0.1-SNAPSHOT.war 运行、侦听和处理 HTTP 请求、管理用户会话以及促进航班预订的外部依赖项。 如果使用了传统的非容器化部署,运营工程师会在某些物理服务器或虚拟机上安装和配置应用程序服务器,然后再将 AirlinesReservationSample-0.0.1-SNAPSHOT.war 部署到它。 这些运维工程师还需要确保您计算机上使用的 JDK(这是 mvn clean package 用于编译 WAR 文件的),实际上对应于应用程序服务器上所使用的相同 JRE。 管理这些依赖项非常困难且耗时。
借助 Dockerfile,你可以编写自动完成此操作所需的指令或层,通过叠加所需的步骤,确保航空公司的航班预订系统拥有部署到 Docker 容器运行时所需的所有依赖项。 在非计划的时间间隔内进行按需扩展时时,此解决方案非常具有吸引力。 每个层都使用 Docker 缓存,其中包含每个指令里程碑的容器映像的状态,优化计算时间和重复使用。 如果层未更改,则使用缓存层。 缓存层的常见用例是适用于航空公司预订 Web 应用程序的航班预订系统 Java 运行时、应用程序服务器和其他依赖项。 如果在以前缓存的层上更改版本,则会创建一个新的缓存条目。
容器映像包含只读文件系统层。 在此示例中,Dockerfile 在生成时将 Tomcat 和航班预订系统 WAR 文件复制到映像中。 Docker 从映像创建容器时,会添加单独的可写层。 每个容器的可写层都会记录其自己的文件系统更改,而无需修改映像层。
注释
对于存储在容器可写层中的文件,添加、修改和删除会影响该容器,而不是原始映像。 对来自映像层的文件进行更改时,会使用写时复制机制。
Docker 具有多阶段生成的概念,该功能使你能够创建一个更小的容器映像,具有更好的缓存和较小的安全占用空间,从而在一段时间内提高 Dockerfile 的优化和维护。 例如,您可以将用于编译和构建应用程序的容器构建阶段与用于运行应用程序的阶段分开。 使用此功能,可以仅将生成过程中生成的项目复制到生产容器映像,从而减少占用空间。 由于容器映像已缓存,因此如果没有更改,则可以重复使用缓存的映像,从而减少从网络下载的成本和时间。
在生产环境中公开的服务必须经过精心管理,才能获得安全性。 因此,生产环境使用并运行安全容器镜像。 此示例使用Microsoft提供的 Azure Linux 映像。
最后阶段使用 Azure Linux 3.0 上的 Microsoft Build of OpenJDK 17。
17-azurelinux 标记显式命名此基类。 旧 17-mariner 标记仍然是与 Azure Linux 映像保持一致的兼容性别名;不应将其与已停用的 17-mariner-cm1 和 17-mariner-cm2 映像混淆。 有关详细信息,请参阅Microsoft Build of OpenJDK容器映像。
注释
Microsoft提供与 OpenJDK 捆绑的容器映像,包括Ubuntu、Azure Linux 和distroless映像。 该 distroless 图像具有最小的图像大小,但是在其上运行 Tomcat 是很有挑战性的。 为了实现轻型设计, distroless 映像会删除许多命令和工具,包括 shell,这意味着无法调用 catalina.sh 启动 Tomcat。
distroless 映像适合运行可执行的 JAR,例如与 Spring Boot 或 Quarkus 一起使用的 JAR。
在以下示例中,构建阶段和最终阶段都使用了同一版本的 Microsoft Build of OpenJDK。 此方法可确保使用服务部署 Tomcat 使用的 JDK 版本生成源代码,这有助于避免因版本不匹配而导致意外行为。
下图根据 Dockerfile 中指定的命令描述了多阶段生成和每个阶段中发生的情况:
在阶段 0 中,从特定于版本的 Apache 存档下载 Tomcat,使用 SHA-512 校验和进行验证,并将其提取到 Ubuntu 映像上的环境变量指定的目录中。 变量 TOMCAT_VERSION 指定发行版,而 TOMCAT_SHA512 构建参数指定其预期的校验和。 选择较新的支持版本时,请将这两个值更新在一起。 Docker 可以重复使用缓存的安装层,同时其指令和输入保持不变。 Tomcat 安装将复制到最终阶段环境以供使用。
在阶段 1 中,Maven 安装在 Ubuntu 映像上,在生成 Maven 项目之前,将复制创建的源代码和配置文件。 每个层都被缓存,因此 OS 映像和 Maven 映像层会重用缓存。 如果更新配置文件、源代码文件或 Web 目录,则从更改开始的层将重新生成。 如果在编译期间生成成功完成且未出错,则会在目标目录下生成名为 AirlinesReservationSample-0.0.1-SNAPSHOT.war 的项目。 此工件被复制到最终阶段环境中用于使用。
在最后阶段,将来自阶段 0 和阶段 1 的 Tomcat 和 Java 构建产物分别复制到 Azure Linux 3.0 上的 Microsoft Build of OpenJDK 17 映像中。 命名 app 的用户拥有项目中使用的所有文件,并且应用程序也作为 app 用户运行,而不是拥有 root 权限。 此设置可确保可以安全地运行容器映像,而无需授予不必要的权限。 最后,将公开端口号 8080,并执行 catalina.sh 脚本以启动 Tomcat。 在本地 Docker Desktop 上运行时,可以通过 URL http://localhost:8080/AirlinesReservationSample访问它。
在 containerize-and-deploy-Java-app-to-Azure/Project/Airlines中,使用已安装的文本编辑器创建名为 Dockerfile 的文件。 添加以下内容并保存文件:
Important
在生成要公开的映像之前,请检查 Apache Tomcat 下载页上当前支持的 Tomcat 10.1.x 版本,并查看 安全通知。 使用 Apache 发布的发行信息同时更新 TOMCAT_SHA512 和 TOMCAT_VERSION。
Apache 存档中仍可用的工件并不意味着其版本是最新版本或推荐版本。
############################################
# Tomcat Intall stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat
ENV CATALINA_HOME=/usr/local/tomcat
# Configure the reviewed Tomcat version and checksum
ENV TOMCAT_VERSION=10.1.61
ARG TOMCAT_SHA512=35120a2b1182bac3d55438fbec0dc581d03b0f1c16ce692a930f3bb6a1097cf896b94b389152257071a87cb2928ac8c15f1bad08387c14acc7c17dbc1bb598fb
# Install Tomcat and required packages
RUN apt-get update && \
apt-get install -y --no-install-recommends curl ca-certificates && \
curl -fSL \
"https://archive.apache.org/dist/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz" \
-o /tmp/tomcat.tar.gz && \
echo "${TOMCAT_SHA512} /tmp/tomcat.tar.gz" | sha512sum -c - && \
mkdir -p "${CATALINA_HOME}" && \
tar -xzf /tmp/tomcat.tar.gz --strip-components=1 -C "${CATALINA_HOME}" && \
rm /tmp/tomcat.tar.gz && \
apt-get remove --purge -y curl && \
apt-get autoremove -y && \
apt-get clean
############################################
# Build stage (Compiles with Java 17)
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build
WORKDIR /build
# Install Maven
RUN apt-get update && apt-get install -y maven && mvn --version
# Copy source code
COPY pom.xml .
COPY src ./src
COPY web ./web
# Build the project
RUN mvn clean package
############################################
# Package final stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-azurelinux
# Configure the location of the Tomcat installation
ENV CATALINA_HOME=/usr/local/tomcat
# Configure the path to the Tomcat binaries
ENV PATH=$CATALINA_HOME/bin:$PATH
# This is the user that runs the Tomcat process
USER app
# Copy the Tomcat installation from the Tomcat stage
COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME}
# Copy the Tomcat configuration files
COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf
# Copy the compiled WAR file from the build stage
COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war
# Expose the default Tomcat port
EXPOSE 8080
# Start Tomcat
CMD ["catalina.sh", "run"]
注释
在本练习中,请使用本单元中显示的 Dockerfile。 必须将应用程序复制到 webapps/AirlinesReservationSample.war,以便以下单元中的 URL 使用 /AirlinesReservationSample/ 上下文。 不同的 WAR 文件名会更改默认的 Tomcat 应用程序上下文。
Dockerfile 分为三个阶段,如下表所述:
Tomcat 安装阶段:
Docker 命令 DESCRIPTION FROMFROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat将基础映像设置为在 Ubuntu 上的 Microsoft 构建的 OpenJDK 17,并将此阶段命名为tomcat。 这是安装 Tomcat 的位置。ENVENV CATALINA_HOME=/usr/local/tomcat设置 Tomcat 安装目录的环境变量。ENVENV TOMCAT_VERSION=10.1.61设置了此示例中固定使用的 Tomcat 版本。 查看支持的版本,并根据需要与TOMCAT_SHA512一起更新。ARGARG TOMCAT_SHA512为所选存档提供 Apache 已发布的 SHA-512 校验和作为生成参数。RUN安装 curl和 CA 证书,然后使用curl -fSL下载特定版本的 Apache 存档。-f遇到 HTTP 错误时失败;-L遵循重定向。 在使用sha512sum -c -将其提取到--strip-components=1之前,会先用CATALINA_HOME验证存档。 链&&在失败时停止,而不是在清理期间屏蔽错误。 最后,它会删除存档和不需要的包,并清理包缓存。使用 Java 17 编译的生成阶段:
Docker 命令 DESCRIPTION FROMFROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build将基础映像设置为在 Ubuntu 上的 Microsoft 构建的 OpenJDK 17,并将此阶段命名为build。 此阶段用于编译 Java 应用程序。WORKDIRWORKDIR /build将容器内的工作目录设置为/build,其中复制并编译源代码。RUNRUN apt-get update && apt-get install -y maven && mvn --version安装 Maven,一个用于 Java 项目的生成自动化工具,并验证其安装。COPYCOPY pom.xml .将 Maven 配置文件复制到工作目录中。 此文件对于生成项目至关重要。COPYCOPY src ./src将源代码目录复制到容器中。 这是 Java 应用程序代码所在的位置。COPYCOPY web ./web将 Web 资源目录复制到容器中。 这包括生成所需的 Web 应用程序资源。RUNRUN mvn clean package执行 Maven 生成过程,它将 Java 应用程序编译并打包到 WAR 文件中。软件包最后阶段:
Docker 命令 DESCRIPTION FROMFROM mcr.microsoft.com/openjdk/jdk:17-azurelinux将基础映像设置为 Azure Linux 3.0 上的 Microsoft Build of OpenJDK 17,该映像用于应用程序的最终部署。ENVENV CATALINA_HOME=/usr/local/tomcat设置 Tomcat 安装目录的环境变量,类似于安装阶段。ENVENV PATH=$CATALINA_HOME/bin:$PATH将 Tomcat bin 目录添加到系统PATH,允许轻松运行 Tomcat 命令。USERUSER app指定 Tomcat 进程在其中运行的用户,通过不作为根用户运行来提高安全性。COPYCOPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME}从tomcat阶段复制 Tomcat 安装,并将所有权设置为app用户。COPYCOPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf将 Tomcat 用户配置文件复制到容器中,将所有权设置为app用户。COPYCOPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war将编译的 WAR 文件从build阶段复制到 Tomcat webapps 目录,并将所有权设置为app用户。EXPOSEEXPOSE 8080文档说明应用程序应侦听 TCP 端口 8080。 它不暴露主机端口。 下一个单元在-p 8080:8080上使用docker run选项发布该端口。CMDCMD ["catalina.sh", "run"]指定在运行容器时启动 Tomcat 的命令。
有关 Dockerfile 构造的详细信息,请参阅 Dockerfile 参考。