容器化 Java 应用

已完成

在本单元中,将容器化 Java 应用程序。

如前所述,容器作为普通系统进程直接在主机操作系统、内核和硬件上运行。 容器需要更少的系统资源,从而导致占用空间更小、开销更少、应用程序启动时间更快。 这些好处非常适合按需扩展的实例。

有 Windows 容器和 Linux 容器。 在本模块中,你将使用广泛使用的 Docker 运行时来生成 Linux 容器映像。 然后将 Linux 容器映像部署到本地计算机的主机作系统。 最后,将 Linux 容器映像部署到 Azure Kubernetes 服务。

Docker 概述

Docker 运行时用于生成、拉取、运行和推送容器映像,如下图所示:

显示 Docker 命令的图表。

下表描述了每个 Docker 命令:

Docker 命令 DESCRIPTION
docker build 生成包含 Docker 从映像构建运行容器所需指令或层的容器镜像。 此命令的结果是图像。
docker pull 容器从映像进行初始化,这些映像是从 Azure 容器注册表等注册表中提取的。 Azure Kubernetes 服务从此注册表中拉取。 此命令的结果是通过网络在 Azure 中拉取映像。 (可选)可以在本地拉取映像。 生成需要应用程序可能所需的依赖项或层(例如应用程序服务器)的映像时,此选项很常见。
docker run 映像的运行实例是容器,此命令执行运行和与正在运行的容器应用程序交互所需的所有层。 此命令的结果是在主机作系统上运行的应用程序进程。
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 是一个文本文档,其中包含用户将在命令行上执行的所有命令来组装容器映像。 每个图像都是一个层,可以缓存以提高效率。 层彼此叠加。

例如,航班预订系统需要部署到应用程序服务器并在应用程序服务器内部运行。 应用程序服务器未打包到 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 中的命令时,将创建层。 顶层是航空公司预订 Web 应用层的读/写航班预订系统。 该层基于以前的只读层构建。

显示 Docker 层的示意图。

Docker 具有多阶段生成的概念,该功能使你能够创建一个更小的容器映像,具有更好的缓存和较小的安全占用空间,从而在一段时间内提高 Dockerfile 的优化和维护。 例如,您可以将用于编译和构建应用程序的容器构建阶段与用于运行应用程序的阶段分开。 使用此功能,可以仅将生成过程中生成的项目复制到生产容器映像,从而减少占用空间。 由于容器映像已缓存,因此如果没有更改,则可以重复使用缓存的映像,从而减少从网络下载的成本和时间。

在生产环境中公开的服务必须经过精心管理,才能获得安全性。 因此,生产环境使用并运行安全容器镜像。 该示例使用 CBL-Mariner Microsoft提供的图像。

CBL-Mariner Linux 是一种轻型作系统,仅包含云环境所需的包。 可以通过自定义包和工具对其进行自定义,以满足应用程序的要求。 CBL-Mariner 接受 Azure 验证测试,并与 Azure 代理兼容。 Microsoft 构建和测试 CBL-Mariner,以支持多种用例,从 Azure 服务到 IoT 基础设施。 这是内部建议的 Linux 分发版,用于Microsoft云服务和相关产品。

注释

Microsoft提供与 OpenJDK 捆绑的容器映像,包括UbuntuCBL-Marinerdistroless映像。 该 distroless 图像具有最小的图像大小,但是在其上运行 Tomcat 是很有挑战性的。 为了实现轻型设计, distroless 映像会删除许多命令和工具,包括 shell,这意味着无法调用 catalina.sh 启动 Tomcat。 distroless 映像适合运行可执行的 JAR,例如与 Spring Boot 或 Quarkus 一起使用的 JAR。

在以下示例中,构建阶段和最终阶段都使用了同一版本的 Microsoft Build of OpenJDK。 此方法可确保使用服务部署 Tomcat 使用的 JDK 版本生成源代码,这有助于避免因版本不匹配而导致意外行为。

下图根据 Dockerfile 中指定的命令描述了多阶段生成和每个阶段中发生的情况:

Docker 多阶段构建关系图。

在阶段 0 中,Tomcat 将下载并提取到由 Ubuntu 映像上的环境变量指定的目录中。 该 TOMCAT_VERSION 变量指定要下载的 Tomcat 版本。 如果发布了 Tomcat 的新版本,则应更新版本号,因为只有在版本号更改时才会提取新映像。 否则,将使用缓存的映像。 被下载的 Tomcat 软件将被复制到最终阶段的环境中以供使用。

在阶段 1 中,Maven 安装在 Ubuntu 映像上,在生成 Maven 项目之前,将复制创建的源代码和配置文件。 每个层都被缓存,因此 OS 映像和 Maven 映像层会重用缓存。 如果更新配置文件、源代码文件或 Web 目录,则从更改开始的层将重新生成。 如果在编译期间生成成功完成且未出错,则会在目标目录下生成名为 AirlinesReservationSample-0.0.1-SNAPSHOT.war 的项目。 此工件被复制到最终阶段环境中用于使用。

在最后阶段,Microsoft 提供的安全 CBL-Mariner 映像用于分别从阶段 0 和阶段 1 复制 Tomcat 和 Java 生成工件。 命名 app 的用户拥有项目中使用的所有文件,并且应用程序也作为 app 用户运行,而不是拥有 root 权限。 此设置可确保可以安全地运行容器映像,而无需授予不必要的权限。 最后,将公开端口号 8080,并执行 catalina.sh 脚本以启动 Tomcat。 在本地 Docker Desktop 上运行时,可以通过 URL http://localhost:8080/AirlinesReservationSample访问它。

在项目的根文件夹中, containerize-and-deploy-Java-app-to-Azure/Project/Airlines 使用以下命令创建名为 Dockerfile 的文件:

vi Dockerfile

将以下内容添加到 Dockerfile,然后保存并退出。 若要保存并退出,请按 ESC,键入 :wq!,然后按 Enter

############################################
# Tomcat Intall stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat

ENV CATALINA_HOME=/usr/local/tomcat

# Configure Tomcat Version (Be sure to use the latest version)
ENV TOMCAT_VERSION=10.1.33

# Install Tomcat and required packages
RUN apt-get update ; \
    apt-get install -y curl ; \
    curl -O https://downloads.apache.org/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz ; \
    tar xzf apache-tomcat-${TOMCAT_VERSION}.tar.gz ; \
    mv apache-tomcat-${TOMCAT_VERSION} ${CATALINA_HOME} ; \
    rm apache-tomcat-${TOMCAT_VERSION}.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-mariner

# 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_Solution 文件,其中包含所需的内容。

Dockerfile 分为三个阶段,如下表所述:

  • Tomcat 安装阶段:

    Docker 命令 DESCRIPTION
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat 将基础映像设置为在 Ubuntu 上的 Microsoft 构建的 OpenJDK 17,并将此阶段命名为 tomcat。 这是安装 Tomcat 的位置。
    ENV ENV CATALINA_HOME=/usr/local/tomcat 设置 Tomcat 安装目录的环境变量。
    ENV ENV TOMCAT_VERSION=10.1.33 设置要安装的 Tomcat 版本。 这应根据需要更新到最新版本。
    RUN RUN 命令更新包列表、安装 curl、下载指定的 Tomcat 版本、提取包、将其移动到指定目录,并清理不必要的文件和包。 这可确保映像保持轻量级。
  • 使用 Java 17 编译的生成阶段:

    Docker 命令 DESCRIPTION
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build 将基础映像设置为在 Ubuntu 上的 Microsoft OpenJDK 17 版本,并将此阶段命名为 build。 此阶段用于编译 Java 应用程序。
    WORKDIR WORKDIR /build 将容器内的工作目录设置为 /build,其中复制并编译源代码。
    RUN RUN apt-get update && apt-get install -y maven && mvn --version 安装 Maven,一个用于 Java 项目的生成自动化工具,并验证其安装。
    COPY COPY pom.xml . 将 Maven 配置文件复制到工作目录中。 此文件对于生成项目至关重要。
    COPY COPY src ./src 将源代码目录复制到容器中。 这是 Java 应用程序代码所在的位置。
    COPY COPY web ./web 将 Web 资源目录复制到容器中。 这包括生成所需的 Web 应用程序资源。
    RUN RUN mvn clean package 执行 Maven 生成过程,它将 Java 应用程序编译并打包到 WAR 文件中。
  • 软件包最后阶段:

    Docker 命令 DESCRIPTION
    FROM FROM mcr.microsoft.com/openjdk/jdk:17-mariner 将基础映像设置为 CBL-Mariner 上的 Microsoft Build of OpenJDK 17,用于应用程序的最终部署。
    ENV ENV CATALINA_HOME=/usr/local/tomcat 设置 Tomcat 安装目录的环境变量,类似于安装阶段。
    ENV ENV PATH=$CATALINA_HOME/bin:$PATH 将 Tomcat bin 目录添加到系统 PATH,允许轻松运行 Tomcat 命令。
    USER USER app 指定 Tomcat 进程在其中运行的用户,通过不作为根用户运行来提高安全性。
    COPY COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME}tomcat 阶段复制 Tomcat 安装,并将所有权设置为 app 用户。
    COPY COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf 将 Tomcat 用户配置文件复制到容器中,将所有权设置为 app 用户。
    COPY COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war 将编译的 WAR 文件从 build 阶段复制到 Tomcat webapps 目录,并将所有权设置为 app 用户。
    EXPOSE EXPOSE 8080 公开端口 8080(Tomcat 的默认端口),允许外部访问应用程序。
    CMD CMD ["catalina.sh", "run"] 指定在运行容器时启动 Tomcat 的命令。

有关 Dockerfile 构造的详细信息,请参阅 Dockerfile 参考