学习路线:将单体式应用转换为 GKE 应用 - 将模块化应用容器化


本教程是一个学习路线中的第四个教程,将介绍如何将单体式应用模块化和容器化。

此学习路线包含以下教程:

  1. 概览
  2. 了解单体式应用
  3. 将单体式应用模块化
  4. 准备好模块化应用以实现容器化
  5. 将模块化应用容器化(本教程)
  6. 将应用部署到 GKE 集群

在上一个教程准备好模块化应用以实现容器化中,您了解了需要对 Cymbal Books 应用的模块化版本进行哪些更改,才能为容器化做好准备。在本教程中,您会将该应用容器化。

费用

您可以免费完成本教程。不过,按照本系列下一个教程中的步骤操作会导致您的Google Cloud 账号产生费用。当您启用 GKE 并将 Cymbal Books 应用部署到 GKE 集群时,费用开始产生。这些费用包括按集群收取的 GKE 费用(如价格页面中所述)以及运行 Compute Engine 虚拟机的费用。

为避免产生不必要的费用,请务必在完成本教程后停用 GKE 或删除项目。

准备工作

在开始学习本教程之前,请确保您已完成本系列中的前几个教程。如需查看整个系列教程的概览以及指向特定教程的链接,请参阅学习路线:将单体式应用转换为 GKE 应用 - 概览

设置环境

在本部分中,您将设置一个环境,用于将模块化应用容器化。具体而言,您将执行以下步骤:

  1. 选择或创建 Google Cloud 项目
  2. 启用必要的 API
  3. 将 Cloud Shell 连接到您的 Google Cloud 项目
  4. 设置默认环境变量
  5. 在 Artifact Registry 中创建代码库
  6. 为 Artifact Registry 配置 Docker
  7. 获取教程代码

选择或创建 Google Cloud 项目

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  2. Make sure that billing is enabled for your Google Cloud project.

启用必要的 API

如需在 Google Cloud 项目中使用容器映像和 Kubernetes,您需要启用以下 API:

  • Artifact Registry API:此 API 可启用 Artifact Registry,这是一种用于存储和管理容器映像的服务。
  • Kubernetes Engine API:通过此 API 可访问 GKE。

如需启用这些 API,请访问 Google Cloud 控制台以启用 API

将 Cloud Shell 连接到您的 Google Cloud 项目

现在,您已设置了您的 Google Cloud 项目,接下来需要启动 Cloud Shell 实例并将其连接到 Google Cloud项目。Cloud Shell 是一种命令行工具,可让您直接在浏览器中创建和管理项目的资源。Cloud Shell 预安装了两个重要工具:gcloud CLIkubectl CLI。在本教程中,您将使用 gcloud CLI 与 Google Cloud 进行交互;在下一个教程中,您将使用 kubectl CLI 管理在 GKE 上运行的 Cymbal Books 应用。

如需将 Cloud Shell 实例与您的 Google Cloud 项目连接,请按以下步骤操作:

  1. 前往 Google Cloud 控制台:

    Google Cloud 控制台

  2. 在控制台中,点击激活 Cloud Shell 按钮:激活 Cloud Shell

    控制台下方的框架内会打开一个 Cloud Shell 会话。

  3. 在 Google Cloud CLI 中使用以下命令设置默认项目:

    gcloud config set project PROJECT_ID
    

    PROJECT_ID 替换为您在上一部分(选择或创建 Google Cloud 项目)中创建或选择的项目的项目 ID。项目 ID 是一个唯一字符串,用于将您的项目与 Google Cloud中的所有其他项目区分开来。如需查找项目 ID,请前往项目选择器。在该页面上,您可以查看每个 Google Cloud项目的项目 ID。

设置默认环境变量

为了简化您在本教程中运行的命令,您现在需在 Cloud Shell 中设置一些环境变量。这些变量用于存储项目 ID、仓库区域和映像标记等值。定义这些变量后,您可以通过引用变量名称(例如 $REPOSITORY_NAME)在多个命令中重复使用它们,而无需每次都重新输入或替换值。这种方法可让本教程更易于理解,并降低出错风险。

如需使用 Cloud Shell 设置您的环境,请执行以下步骤:

export PROJECT_ID=$(gcloud config get project)
export REPOSITORY_REGION=REPOSITORY_REGION
export REPOSITORY_NAME=REPOSITORY_NAME
export REPOSITORY_DESCRIPTION="REPOSITORY_DESCRIPTION"
export TAG=TAG

替换以下内容:

  • REPOSITORY_REGION:要在其中托管 Artifact Registry 仓库的区域。例如 us-central1(爱荷华)、us-west1(俄勒冈)或 europe-west1(比利时)。如需查看完整的区域列表,请参阅区域和可用区
  • REPOSITORY_NAME:代码库的名称。 例如 book-review-service-repo
  • REPOSITORY_DESCRIPTION:仓库用途的简要说明。例如 "Repository for storing Docker images for the book review service"
  • TAG:要应用于映像的标记。标记是可附加到容器映像特定版本的标签。您可以使用如下标记命名惯例来清晰指明映像的不同版本:
    • v1
    • v1.2.3
    • 描述性标记,例如 feature-x-dev
    • 用于指示环境的标记,例如 test

在 Artifact Registry 中创建代码库

接下来,您将在 Artifact Registry 中创建一个仓库。仓库是用于存放容器映像的存储位置。构建容器映像时,您需要某个位置来存储它,以便以后可以将它部署到 Kubernetes 集群。借助 Artifact Registry,您可以在 Google Cloud 项目中创建和管理这些仓库。

如需在 Artifact Registry 中创建仓库,请运行以下命令:

gcloud artifacts repositories create ${REPOSITORY_NAME} \
    --repository-format=docker \
    --location=${REPOSITORY_REGION} \
    --description="${REPOSITORY_DESCRIPTION}"

该命令的成功输出如下所示:

Waiting for operation [...] to complete...done.
Created repository [book-review-service-repo].

为 Artifact Registry 配置 Docker

接下来,您将配置 Docker,使其可以与Google Cloud的 Artifact Registry 安全通信。Docker 是一种工具,可用于在不同环境中以一致的方式打包和运行软件。您将在下一部分中详细了解 Docker 的运作方式。目前,您需要对其进行配置,以便其可以连接到 Artifact Registry。

如果您不以这种方式配置 Docker,则无法将容器映像推送到 Artifact Registry(这是您将在本教程的后面部分中执行的任务)。您也无法从 Artifact Registry 中拉取容器映像并将其部署到 GKE 集群(这是您将在下一个教程中执行的任务)。

如需配置 Docker 以向 Artifact Registry 进行身份验证,请运行以下命令:

gcloud auth configure-docker ${REPOSITORY_REGION}-docker.pkg.dev

获取教程代码

现在,您的 Cloud Shell 环境已配置完毕,您需要在 Cloud Shell 中下载教程代码。即使您之前已在本地机器上克隆过仓库,此时也需要在 Cloud Shell 实例上再次克隆它。

虽然您可以在本地机器上完成本教程,但必须手动安装和配置 Docker、kubectl 和 gcloud CLI 等多种工具。使用 Cloud Shell 更简单,因为它已预配置了所有这些工具。

在 Cloud Shell 实例中,运行以下命令以克隆 GitHub 仓库:

git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git

容器化基础知识:容器映像、容器和 Dockerfile

现在,您已设置了环境并下载了容器化代码,接下来可以将应用容器化。将应用容器化包括使用 Dockerfile 将 Cymbal Books 的每个模块(首页、图书详细信息、图片和图书评价)打包到容器映像中。当应用部署到 GKE 集群时,Kubernetes 会使用这些容器映像在集群中创建正在运行的容器。

以下部分将详细介绍这些概念。

什么是容器化?

容器化会将模块及其所有依赖项(例如库和配置文件)打包到一个称为容器映像的单元中。开发者可以使用此容器映像在任何环境中(从开发者的笔记本电脑到测试服务器或生产 Kubernetes 集群)创建和运行容器。

什么是容器映像?

容器映像包含运行应用所需的所有文件。这些文件包括应用代码本身、系统库、运行时环境(例如 Python 解释器)、静态数据以及任何其他依赖项。

在本教程中,您将为图书评价应用的每个模块创建一个容器映像。

什么是容器?

容器是一个隔离环境,用于运行容器映像中的代码。您可以通过两种方式创建容器:在开发期间使用 docker run 命令进行测试,或将容器映像部署到 Kubernetes 集群。

在 Cymbal Books 应用的容器化版本中,模块化应用中的每个模块都在自己的容器中运行:

  • 首页容器运行首页模块,并处理对 / 的请求。
  • 图书详细信息容器运行图书详细信息模块,并为 /book/1/book/3 等端点传送数据。
  • 图书评价容器运行图书评价模块,并管理对 /book/2/reviews 等端点的请求。
  • 图片容器运行图片模块,并为 /images/fungi_frontier.jpg 等端点传送图书封面图片。

容器的一个主要优势是,Kubernetes 可以在需要时自动创建更多容器。例如,如果大量用户在阅读图书评价,Kubernetes 可以启动更多图书评价容器来处理负载。

如需在不使用容器的模块化应用中实现扩缩,您需要编写自定义代码以启动模块的新实例并在这些实例之间分配流量。使用 Kubernetes 时,此扩缩功能是内置功能:您无需编写任何自定义扩缩代码。

什么是 Dockerfile?

Dockerfile 是一种脚本,用于定义如何将模块打包到容器映像中。在本教程中,您无需创建任何 Dockerfile,因为您之前克隆的 GitHub 仓库中已为您提供这些文件。kubernetes-engine-samples/quickstarts/monolith-to-microservices/containerized/ 的本地副本中的每个模块目录都包含各自的 Dockerfile。

例如,您可以通过 kubernetes-engine-samples/quickstarts/monolith-to-microservices/containerized/home_app/Dockerfile,在 Cloud Shell 实例中找到 home_app 模块的 Dockerfile。此 Dockerfile 如下所示:

# Dockerfile for home_app
FROM python:3.9-slim #line 1
WORKDIR /app #line 2
COPY requirements.txt . #line 3
RUN pip install --no-cache-dir -r requirements.txt #line 4
COPY home_app.py . #line 5
COPY templates/ ./templates/ #line 6
COPY static/ ./static/ #line 7
CMD ["python", "home_app.py"] #line 8

此 Dockerfile 会执行以下步骤来创建 home_app 模块的容器映像:

  • 第 1 行:FROM python:3.9-slim 将 Python 3.9 解释器及其所需的文件下载到容器映像中。这些文件可让模块运行。
  • 第 2 行:WORKDIR /app 在容器内创建一个名为 /app 的目录,并将此目录设置为当前工作目录。在容器内运行的所有命令都将在此目录内运行。
  • 第 3 行和第 4 行:COPY requirements.txt .requirements.txt 文件从本地机器复制到容器映像的 /app 目录中。requirements.txt 文件列出了 home_app.py 需要的所有 Python 库。RUN pip install 行将这些库安装到容器映像中。
  • 第 5 至 7 行:这些行中显示的 COPY 命令将模块的代码 (home_app.py) 及其支持文件(模板和静态资源)复制到容器映像中的 /app 目录。
  • 第 8 行:CMD 指定 Docker 在容器启动时所运行的默认命令。在此 Dockerfile 中,CMD ["python", "home_app.py"] 指示 Docker 在容器启动时使用 Python 解释器自动执行 home_app.py 模块。

容器化如何实施更严格的数据隔离

Dockerfile 的第 5 至 7 行(如上一部分中所述)展示了容器化如何实施比应用的模块化版本更严格的数据隔离。在之前的教程中,您在仅向每个模块授予对所需数据的访问权限部分中了解到,应用的模块化版本会将数据整理到单独的目录中,但模块仍然共享相同的文件系统,并且可能会访问彼此的数据。

而在此处,在应用的容器化版本中,每个模块的容器仅包含所需文件。例如,如果 home_app 模块不需要访问图书评价数据,那么 home_app 容器中根本不会存在该数据。默认情况下,一个容器无法访问其他容器中的文件,除非明确配置为可以访问。这有助于确保每个模块完全隔离,还有助于防止意外或未经授权的数据访问。

在下一部分中,您将了解 docker build 命令如何采用 Dockerfile 作为输入,并按照 Dockerfile 中的说明创建容器映像。

使用 Docker 构建容器映像

在本部分中,您将为每个图书评价模块构建 Docker 容器映像,并将它们推送到 Artifact Registry 仓库。您将在后续教程中使用这些容器映像在 Kubernetes 中部署和运行 Cymbal Books 示例应用。

  1. 进入容器化应用的根目录:

    cd kubernetes-engine-samples/quickstarts/monolith-to-microservices/containerized/
    
  2. 使用 docker build 命令创建容器映像:

    docker build -t ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/home-app:${TAG} ./home_app
    docker build -t ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-details-app:${TAG} ./book_details_app
    docker build -t ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-reviews-app:${TAG} ./book_reviews_app
    docker build -t ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/images-app:${TAG} ./images_app
    
  3. 查看在 Cloud Shell 实例中构建的容器映像:

    docker images
    

    检查列表中是否显示以下映像:

    • home-app
    • book-details-app
    • book-reviews-app
    • images-app

    如果列出了所有四个映像,则说明您已成功创建容器映像。

在 Cloud Shell 中测试容器

如需验证容器映像是否已正确构建,您可以在 Cloud Shell 中将它们作为容器运行并测试其端点。

book_details_appbook_reviews_appimages_app 容器可以单独测试,因为它们不需要相互通信。不过,使用 Docker 测试 home_app 容器很困难,因为 home_app 配置为查找使用服务名称(例如 http://book-details-service:8081)的其他容器。

虽然可以通过查找每个容器的 IP 地址并将 home_app 配置为使用这些地址而不是服务名称来测试 home_app 容器,但这种方法需要执行大量工作。相反,最好在将应用部署到 Kubernetes 集群后,再测试 home_app 容器。将应用部署到集群后,您可以确定首页模块是否正常运行。

请按照以下步骤测试容器:

  1. 启动 book_details_appbook_reviews_appimages_app 容器:

    docker run -d -p 8081:8080 ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-details-app:${TAG}
    docker run -d -p 8082:8080 ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-reviews-app:${TAG}
    docker run -d -p 8083:8080 ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/images-app:${TAG}
    
  2. 通过列出所有活跃容器来检查容器是否正在运行:

    docker ps
    

    此命令的输出应显示三个正在运行且状态为 Up 的容器:

    CONTAINER ID   IMAGE                PORTS                        STATUS
    a1b2c3d4e5f6   REGION/.../details   0.0.0.0:8081->8080/tcp       Up
    g7h8i9j0k1l2   REGION/.../reviews   0.0.0.0:8082->8080/tcp       Up
    m3n4o5p6q7r8   REGION/.../images    0.0.0.0:8083->8080/tcp       Up
    
  3. 如需测试 book_details_app 容器的端点,请使用以下 curl 命令:

    curl http://localhost:8081/books
    curl http://localhost:8081/book/1
    curl http://localhost:8081/book/2
    curl http://localhost:8081/book/3
    

    所有这些命令都会以 JSON 格式返回数据。例如,curl http://localhost:8081/book/1 命令的输出如下所示:

    {"author":"Aria Clockwork","description":"In a world where time is a tangible substance, a young clockmaker discovers she can manipulate the fabric of time itself, leading to unforeseen consequences in her steampunk-inspired city.","id":1,"image_url":"zephyrs_timepiece.jpg","title":"Zephyr's Timepiece","year":2023}
    
  4. 使用以下 curl 命令从 book_reviews_app 容器中检索图书评价:

    curl http://localhost:8082/book/1/reviews
    

    此命令会以 JSON 格式返回图书 1 的 20 条评价的列表。以下是该列表中的一条评价示例:

    {
    "content": "The concept of time as a tangible substance is brilliantly explored in 'Zephyr's Timepiece'.",
    "rating": 5
    }
    
  5. 测试 images_app 容器:

    1. 点击 **Web Preview** 按钮 网页预览按钮

    2. 选择更改端口,然后输入 8083。此时会打开一个浏览器窗口,其中包含类似于以下内容的网址:

      https://8083-your-instance-id.cs-your-region.cloudshell.dev/?authuser=0
      
    3. 移除网址末尾的 ?authuser=0,并添加图片文件的路径,例如 /images/fungi_frontier.jpg。下面给出了一个示例:

      https://8083-your-instance-id.cs-your-region.cloudshell.dev/images/fungi_frontier.jpg
      

      您应该会看到浏览器中显示的是 Fungi Frontier 的图书封面图片。

  6. 测试完成后,停止容器以释放资源:

    1. 列出正在运行的容器并找到其容器 ID:

      docker ps
      
    2. 停止每个容器:

      docker stop CONTAINER_ID
      

      CONTAINER_ID 替换为要停止的容器的 ID。

将容器映像推送到 Artifact Registry

需要先将容器映像存储在 Kubernetes 集群可以访问的位置,然后才能将应用部署到集群。在此步骤中,您会将映像推送到之前创建的 Artifact Registry 仓库。在下一个教程中,您将从 Artifact Registry 仓库将这些映像部署到 GKE 集群:

  1. 如需将容器映像推送到 Artifact Registry,请运行以下命令:

    docker push ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/home-app:${TAG}
    docker push ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-details-app:${TAG}
    docker push ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-reviews-app:${TAG}
    docker push ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/images-app:${TAG}
    
  2. 推送映像后,通过列出映像来验证它们是否已成功上传:

    gcloud artifacts docker images list ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}
    

    您将看到如下所示的输出:

    Listing items under project ${PROJECT_ID}, location ${REPOSITORY_REGION}, repository ${REPOSITORY_NAME}.
    
    IMAGE: ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-details-app
    DIGEST: sha256:f7b78f44d70f2eedf7f7d4dc72c36070e7c0dd05daa5f473e1ebcfd1d44b95b1
    CREATE_TIME: 2024-11-14T00:38:53
    UPDATE_TIME: 2024-11-14T00:38:53
    SIZE: 52260143
    
    IMAGE: ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-reviews-app
    DIGEST: sha256:875ac8d94ef54db2ff637e49ad2d1c50291087623718b854a34ad657748fac86
    CREATE_TIME: 2024-11-14T00:39:04
    UPDATE_TIME: 2024-11-14T00:39:04
    SIZE: 52262041
    
    IMAGE: ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/home-app
    DIGEST: sha256:70ddc54ffd683e2525d87ee0451804d273868c7143d0c2a75ce423502c10638a
    CREATE_TIME: 2024-11-14T00:33:56
    UPDATE_TIME: 2024-11-14T00:33:56
    SIZE: 52262412
    
    IMAGE: ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/images-app
    DIGEST: sha256:790f0d8c2f83b09dc3b431c4c04d7dc68254fecc76c48f00a83babc2a5dc0484
    CREATE_TIME: 2024-11-14T00:39:15
    UPDATE_TIME: 2024-11-14T00:39:15
    SIZE: 53020815
    

    输出内容包含每个映像的以下详细信息:

    • IMAGE:仓库路径和映像名称。
    • DIGEST:映像的唯一标识符。
    • CREATE_TIME 或 UPDATE_TIME:映像的创建时间或上次修改时间。
    • SIZE:映像的大小(以字节为单位)。

使用容器映像的路径更新 Kubernetes 清单

正如您在上一个教程准备好模块化应用以实现容器化中所了解到的,Kubernetes 清单是一个 YAML 文件,用于定义应用在 Kubernetes 集群中的运行方式。其中包括以下这类详细信息:

  • 应用的模块(例如,home-appbook-details-app
  • 容器映像的路径
  • 配置详细信息,例如资源限制
  • 用于在模块之间路由请求的服务定义

在本部分中,您将更新在上一个教程中查看的同一清单文件。该文件为 kubernetes-manifest.yaml,其中包含映像路径的占位值。您需要将这些占位符替换为在上一部分中推送到 Artifact Registry 仓库的容器映像的实际路径。

如需更新 kubernetes-manifest.yaml Kubernetes 清单文件,请按以下步骤操作:

  1. 在 Cloud Shell 中,进入包含 Kubernetes 清单文件 kubernetes-manifest.yamlcontainerized/ 目录:

    cd kubernetes-engine-samples/quickstarts/monolith-to-microservices/containerized/
    
  2. 在文本编辑器中打开 kubernetes-manifest.yaml 文件。

    vim kubernetes-manifest.yaml
    
  3. 找到包含如下所示占位符的 image 字段:

    image: REPOSITORY_REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME/home-app:TAG
    

    将每个占位符替换为您推送到 Artifact Registry 的容器映像的实际路径:

    进行这些替换后,路径可能如下所示:

    image:us-west1-docker.pkg.dev/your-project-id/book-review-service-repo/home-app:v1
    
  4. 更新所有容器映像的路径:

    • home-app
    • book-details-app
    • book-reviews-app
    • images-app
  5. 更新路径后,保存清单文件并关闭编辑器。例如,如果您使用的是 vim,请按 Esc 进入命令模式,输入 wq,然后按 Enter 保存并退出。

您的 Kubernetes 清单现已配置完毕,可将容器映像从 Artifact Registry 仓库部署到 Kubernetes 集群。

摘要

在本教程中,您通过执行以下任务,准备好了模块化 Cymbal Books 应用以便部署到 Kubernetes 集群:

  1. 设置 Google Cloud 项目并为您的环境配置 Cloud Shell。
  2. 查看为每个应用模块提供的 Dockerfile。
  3. 使用 Docker 为应用模块构建容器映像。
  4. 在 Cloud Shell 中测试容器以验证其功能。
  5. 将容器映像推送到 Artifact Registry 以进行存储。
  6. 更新 Kubernetes 清单,以使用来自 Artifact Registry 的正确容器映像路径。

后续步骤

在下一个教程将应用部署到 GKE 集群中,您会将容器化应用部署到 GKE 集群。