迁移到 OCI 容器

本页面介绍在生成与 OCI 兼容的应用映像时重现 Cloud Foundry 构建流程所需的构建基础架构。如果您已完成 Spring Music 迁移指南,则可以通过本文深入了解应用的具体迁移配置。

描述如何使用现代工具创建 OCI 映像的图表

前期准备

  1. 确保您已按照 Cloud Run 设置页面中的说明为 Cloud Run 设置了新项目。
  2. 确保您拥有用于存储容器的 REGISTRY_URI。Cloud Run 建议使用 Artifact Registry。Docker 用于创建中间映像以构建项目。

设置与 Cloud Foundry 兼容的构建流程

您必须创建两个基础 OCI 容器以支持此新流程:

  • 构建器映像,镜像 Cloud Foundry 的构建过程,并能够将应用源代码构建到 Cloud Foundry Droplet 中。
  • 镜像 Cloud Foundry 应用运行时的运行时映像

此流程需要由平台管理员至少完成一次。建立该流程后,所有需要迁移到 Cloud Run 的 Cloud Foundry 应用都可以共享构建和运行映像。

创建构建器映像

本部分使用 cflinux3 作为基础映像来创建构建映像。该构建映像用作创建应用映像的构建环境。

  1. 创建名为 build/ 的目录并通过 cd 命令进入该目录:

    mkdir build && cd build
    
  2. build/ 文件夹中,创建一个名为 Dockerfile 的新文件并粘贴以下代码:

    ARG CF_LINUX_FS=cloudfoundry/cflinuxfs3
    
    FROM golang:1.20-bullseye AS builder_build
    WORKDIR /build
    RUN ["git", "clone", "--depth=1", "https://github.com/cloudfoundry/buildpackapplifecycle.git"]
    WORKDIR /build/buildpackapplifecycle
    RUN ["go", "mod", "init", "code.cloudfoundry.org/buildpackapplifecycle"]
    RUN ["go", "mod", "tidy"]
    RUN CGO_ENABLD=0 go build -o /builder ./builder/
    
    FROM $CF_LINUX_FS
    # Set up container tools related to building applications
    WORKDIR /lifecycle
    COPY --from=builder_build /builder /lifecycle/builder
    
    # Set up environment to match Cloud Foundry's build.
    # https://docs.cloudfoundry.org/devguide/deploy-apps/environment-variable.html#app-system-env
    WORKDIR /staging/app
    WORKDIR /tmp
    ENV CF_INSTANCE_ADDR=127.0.0.1:8080 \
    CF_INSTANCE_IP=127.0.0.1 \
    CF_INSTANCE_INTERNAL_IP=127.0.0.1 \
    VCAP_APP_HOST=127.0.0.1 \
    CF_INSTANCE_PORT=8080 \
    LANG=en_US.UTF-8 \
    INSTANCE_GUID=00000000-0000-0000-0000-000000000000 \
    VCAP_APPLICATION={} \
    VCAP_SERVICES={} \
    CF_STACK=cflinuxfs3
    
  3. 使用 Cloud Build 构建和发布 builder 映像

    gcloud builds \
        submit --tag "REGISTRY_URI/builder:stable"
    

    REGISTRY_URI 替换为要发布构建映像的 Artifact Registry 的地址。例如:REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/builder:stable

创建运行时映像

本部分使用 cflinux3 作为基础映像来创建运行映像。当您创建最终应用映像时,运行映像被用作基础映像。

  1. 创建名为 run/ 的目录并通过 cd 命令进入该目录:

    mkdir run && cd run
    
  2. run/ 文件夹中,使用以下代码创建名为 entrypoint.bash 的新 Shell 脚本:

    #!/usr/bin/env bash
    set -e
    
    if [[ "$@" == "" ]]; then
    exec /lifecycle/launcher "/home/vcap/app" "" ""
    else
    exec /lifecycle/launcher "/home/vcap/app" "$@" ""
    fi
    
  3. run/ 文件夹中,创建一个名为 Dockerfile 的新文件并粘贴以下代码:

    ARG CF_LINUX_FS=cloudfoundry/cflinuxfs3
    
    FROM golang:1.20-bullseye AS launcher_build
    WORKDIR /build
    RUN ["git", "clone", "--depth=1", "https://github.com/cloudfoundry/buildpackapplifecycle.git"]
    WORKDIR /build/buildpackapplifecycle
    RUN ["go", "mod", "init", "code.cloudfoundry.org/buildpackapplifecycle"]
    RUN ["go", "mod", "tidy"]
    RUN CGO_ENABLD=0 go build -o /launcher ./launcher/
    
    FROM $CF_LINUX_FS
    # Set up container tools related to launching the application
    WORKDIR /lifecycle
    COPY entrypoint.bash /lifecycle/entrypoint.bash
    RUN ["chmod", "+rx", "/lifecycle/entrypoint.bash"]
    COPY --from=launcher_build /launcher /lifecycle/launcher
    
    # Set up environment to match Cloud Foundry
    WORKDIR /home/vcap
    USER vcap:vcap
    ENTRYPOINT ["/lifecycle/entrypoint.bash"]
    
    # Expose 8080 to allow app to be run on Cloud Foundry,
    # and PORT so the container can be run locally.
    # These do nothing on Cloud Run.
    EXPOSE 8080/tcp
    # Set up environment variables similar to Cloud Foundry.
    ENV CF_INSTANCE_ADDR=127.0.0.1:8080 \
    CF_INSTANCE_IP=127.0.0.1 \
    INSTANCE_IP=127.0.0.1 \
    CF_INSTANCE_INTERNAL_IP=127.0.0.1 \
    VCAP_APP_HOST=127.0.0.1 \
    CF_INSTANCE_PORT=80 \
    LANG=en_US.UTF-8 \
    CF_INSTANCE_GUID=00000000-0000-0000-0000-000000000000 \
    INSTANCE_GUID=00000000-0000-0000-0000-000000000000 \
    CF_INSTANCE_INDEX=0 \
    INSTANCE_INDEX=0 \
    PORT=8080 \
    VCAP_APP_PORT=8080 \
    VCAP_APPLICATION={} \
    VCAP_SERVICES={}
    
  4. 使用 Cloud Build 构建和发布 runtime 映像:

    gcloud builds submit \
        --tag "REGISTRY_URI/runtime:stable"
    

    REGISTRY_URI 替换为要发布构建映像的 Artifact Registry 的地址例如:REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/runtime:stable.

将 Cloud Foundry 应用构建为 OCI 映像

迁移到 Cloud Run 的每个应用都需要有与 Cloud Foundry 运行应用的方式相匹配的 Dockerfile。Dockerfile 有以下作用:

  • 加载构建器映像。
  • 运行 v2 Buildpack 生命周期以创建 Droplet。
  • 提取 Droplet 的内容。
  • 在运行作业映像上加载 Droplet 的内容以创建可运行的应用映像。

最终应用映像与 Cloud Foundry 和 Cloud Run 都兼容,因此您可以对迁移进行 A/B 测试,以帮助调试任何意外行为。

应用团队必须为需要迁移的每个应用完成此流程。

从已部署的 Cloud Foundry 应用收集构建信息

  1. 查看应用堆栈。堆栈通过 cf push 中的 -s 标志或应用清单的 stack 字段提供。

    1. 如果堆栈为 Windows,则应用可能与 Cloud Run 不兼容。您必须将应用移植到 Linux,然后才能继续。
    2. 如果堆栈为空、cflinuxfs3cflinuxfs4,则应用可以迁移到 Cloud Run。
  2. 收集应用 Buildpack 的列表。Buildpack 通过 cf push 中的 -b 标志、应用清单中的 buildpack 字段或应用清单的 buildpacks 字段提供。

    1. 如果未指定 Buildpack,则表示正在自动检测它们。查看 Cloud Foundry 中最新应用部署中的检测到的 Buildpack 列表,或者如果您知道路径,请明确指定它们。
    2. 如果 Buildpack 是网址,请记下网址并继续执行下一步。
    3. 对于任何使用简称的 Buildpack,请使用下表将其映射到网址:

      简称 网址
      staticfile_buildpack https://github.com/cloudfoundry/staticfile-buildpack
      java_buildpack https://github.com/cloudfoundry/java-buildpack
      ruby_buildpack https://github.com/cloudfoundry/ruby-buildpack
      dotnet_core_buildpack https://github.com/cloudfoundry/dotnet-core-buildpack
      nodejs_buildpack https://github.com/cloudfoundry/nodejs-buildpack
      go_buildpack https://github.com/cloudfoundry/go-buildpack
      python_buildpack https://github.com/cloudfoundry/python-buildpack
      php_buildpack https://github.com/cloudfoundry/php-buildpack
      binary_buildpack https://github.com/cloudfoundry/binary-buildpack
      nginx_buildpack https://github.com/cloudfoundry/nginx-buildpack

      如需查找不常见 Buildpack 的源代码,请参阅 Cloud Foundry GitHub 组织

  3. 收集映像的源代码位置。源代码通过应用清单的 path 属性或 cf push 命令的 -p 标志提供。如果未定义源代码,则引用当前目录。

  4. 确定源代码目录中是否有 .cfignore 文件。如果存在,请将其移至名为 .gcloudignore. 的文件中

构建 Cloud Foundry 应用

在此步骤中,您需要将构建元素整理到以下文件夹结构中:

.
├── cloudbuild.yaml
├── Dockerfile
├── .gcloudignore
└── src
    ├── go.mod
    └── main.go
  • cloudbuild.yaml 为 Cloud Build 提供特定的构建说明
  • Dockerfile 将使用前面步骤中的构建和运行映像来创建应用映像
  • src/ 包含应用的源代码
  1. 在目录中创建一个名为 Dockerfile 的文件,其中包含以下内容:

    ARG BUILD_IMAGE
    ARG RUN_IMAGE
    FROM $BUILD_IMAGE as build
    
    COPY src /staging/app
    COPY src /tmp/app
    
    ARG BUILDPACKS
    RUN /lifecycle/builder \
    -buildArtifactsCacheDir=/tmp/cache \
    -buildDir=/tmp/app \
    -buildpacksDir=/tmp/buildpacks \
    -outputBuildArtifactsCache=/tmp/output-cache \
    -outputDroplet=/tmp/droplet \
    -outputMetadata=/tmp/result.json \
    "-buildpackOrder=${BUILDPACKS}" \
    "-skipDetect=true"
    
    FROM $RUN_IMAGE
    COPY --from=build /tmp/droplet droplet
    RUN tar -xzf droplet && rm droplet
    
  2. 在目录中创建一个名为 cloudbuild.yaml 的文件,其中包含以下内容:

    steps:
    - name: gcr.io/cloud-builders/docker
      args:
      - 'build'
      - '--network'
      - 'cloudbuild'
      - '--tag'
      - '${_TAG}'
      - '--build-arg'
      - 'BUILD_IMAGE=${_BUILD_IMAGE}'
      - '--build-arg'
      - 'RUN_IMAGE=${_RUN_IMAGE}'
      - '--build-arg'
      - 'BUILDPACKS=${_BUILDPACKS}'
      - '.'
    images:
    - "${_TAG}"
    options:
      # Substitute build environment variables as an array of KEY=VALUE formatted strings here.
      env: []
    substitutions:
      _BUILD_IMAGE: BUILD_IMAGE_URI
      _RUN_IMAGE:  RUN_IMAGE_URI
      _BUILDPACKS: BUILDPACK_URL
      _TAG: APP_ARTIFACT_REGISTRY/APP_NAME:latest
    
    • BUILD_IMAGE_URI 替换为之前步骤中创建的构建映像的 URI。
    • RUN_IMAGE_URI 替换为之前步骤中创建的运行映像的 URI。
    • BUILDPACK_URL 替换为应用使用的 Buildpack 的网址。这可以是包含多个 Buildpack 的逗号分隔列表。
  3. 如果您有 .cfignore 文件,请将其复制到名为 .gcloudignore 的目录。

  4. 在该目录中创建一个名为 src 的目录。

  5. 将应用的内容复制到 src:

    1. 如果源代码是 zip 文件(包括 .jar 文件),请将内容解压缩到 src
    2. 如果源代码是目录,请将内容复制到 src 中。
  6. 运行 gcloud builds submit . 以构建应用。

已知的不兼容问题

  • 依赖于 Cloud Foundry 注入的环境变量(如 VCAP_SERVICES)的 Buildpack 不起作用。您应使用您的语言的管理系统明确声明依赖于它们注入的内容。
  • 如需修补以这种方式生成的映像,您必须使用较新版本的构建重新构建并运行映像。如果您在 Cloud Foundry 上运行 BOSH stemcell,更新它们不会自动修补应用映像。
  • 构建将在与 Cloud Foundry 集群不同的网络环境中进行,您可能需要设置自定义 Cloud Build 池并使其具有内部软件包镜像的访问权限。

后续步骤