Migrate to OCI containers

This page describes the build infrastructure required to reproduce the Cloud Foundry build process while generating OCI-compatible application images. If you have already completed the Spring Music migration guide you can use this a deep dive into specific migration configurations for your application.

Diagram describing how to create OCI images using modern
tooling

Before you start

  1. Make sure you have set up a new project for Cloud Run as described in the Cloud Run setup page.
  2. Make sure you have a REGISTRY_URI for storing containers. Cloud Run recommends using Artifact Registry. Docker is used to create intermediate images to build the project.

Setting up a Cloud Foundry compatible build process

You must create two base OCI containers to support this new process:

  • A builder imagethat mirrors Cloud Foundry's build process and is capable of building application source code into Cloud Foundry droplets.
  • A runtime imagethat mirrors the Cloud Foundry application runtime.

This process needs to be done at least once by platform administrators. Once the process is established, the build and run images can can be shared by all Cloud Foundry applications that need to migrate to Cloud Run.

Create the builder image

This section creates a build image using cflinux3 as the base image. The build image is used as the build environment for creating the application image.

  1. Create a directory called build/ and cd into it:

     mkdir  
    build && 
     cd 
      
    build 
    
  2. In the build/ folder, create a new file called Dockerfile and paste in the following code:

      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. Use Cloud Build to build and publish the builder image

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

    Replace REGISTRY_URI with the address of the Artifact Registry where you want to publish the build image. For example: REGION -docker.pkg.dev/ PROJECT_ID / REPOSITORY /builder:stable .

Create the runtime image

This section creates a run image using cflinux3 as the base image. The run image is used as the base image when you create the final application image.

  1. Create a directory called run/ and cd into it:

     mkdir  
    run && 
     cd 
      
    run 
    
  2. In the run/ folder, create a new shell script called entrypoint.bash with the following code:

      #!/usr/bin/env bash 
     set 
      
    -e if 
      
     [[ 
      
     " 
     $@ 
     " 
      
     == 
      
     "" 
      
     ]] 
     ; 
      
     then 
     exec 
      
    /lifecycle/launcher  
     "/home/vcap/app" 
      
     "" 
      
     "" 
     else 
     exec 
      
    /lifecycle/launcher  
     "/home/vcap/app" 
      
     " 
     $@ 
     " 
      
     "" 
     fi 
     
    
  3. In the run/ folder, create a new file called Dockerfile and paste in the following code:

      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. Use Cloud Build to build and publish the runtime image:

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

    Replace REGISTRY_URI with the address to the Artifact Registry where you want to publish the build image. For example: REGION -docker.pkg.dev/ PROJECT_ID / REPOSITORY /runtime:stable.

Building Cloud Foundry applications as OCI images

Each application migrated into Cloud Run needs its own Dockerfile that matches how Cloud Foundry runs applications. The Dockerfile does the following:

  • Loads the builder image.
  • Runs the v2 buildpack lifecycle to create a droplet.
  • Extracts the contents of the droplet.
  • Loads the contents of the droplet on the run image to create the runnable application image.

The final application image is compatible with both Cloud Foundry and Cloud Run so you can A/B test your migration to help debug any unexpected behavior.

This process must be done by the application team for each application that needs to be migrated.

Gather build information from a deployed Cloud Foundry application

  1. Look at the application stack. The stack is provided via the -s flag in cf push or the stack field of the application manifest.

    1. If the stack is Windows, the application is likely incompatible with Cloud Run. You must port the application to Linux before continuing.
    2. If the stack is blank, cflinuxfs3 , or cflinuxfs4 the application can be migrated to Cloud Run.
  2. Gather the list of the application buildpacks. Buildpacks are provided via the -b flag in cf push , the buildpack field in the application manifest, or the buildpacks field of the application manifest.

    1. If no buildpacks are specified, it means they're being auto-detected. Look at the list of detected buildpack in your most recent application deployment in Cloud Foundry, or specify them explicitly if you know the paths.
    2. If the buildpacks are URLs, make note of the URLs and proceed to the next step.
    3. For any buildpack that uses a short name, use the following table to map them to URLs:

      Short Name URL
      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

      The source for less common buildpacks can be found in the Cloud Foundry GitHub organization .

  3. Gather the source code location for the image. Source is provided via the path attribute of the application manifest or the -p flag of the cf push command. If the source is undefined, it refers to the current directory.

  4. Determine whether there is a .cfignore file in the source code directory. If it is there, move it to a file named .gcloudignore.

Build the Cloud Foundry application

In this step, you organize the build elements into the following folder structure:

 .
├── cloudbuild.yaml
├── Dockerfile
├── .gcloudignore
└── src
    ├── go.mod
    └── main.go 
  • cloudbuild.yaml provides Cloud Build with specific build instructions
  • Dockerfile will be using the build and run images from the previous steps to create the application image
  • src/ contains your application source code
  1. Create a file named Dockerfile in the directory with the following contents:

      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. Create a file named cloudbuild.yaml in the directory with the following contents:

      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 
    
    • Replace BUILD_IMAGE_URI with the URI of the build image created in previous steps.
    • Replace RUN_IMAGE_URI with the URI of the run image created in previous steps.
    • Replace BUILDPACK_URL with the URLs of the buildpacks used by your application. This can be a comma separated list with multiple buildpacks.
  3. If you have a .cfignore file, copy it to the directory with the name .gcloudignore .

  4. Create a directory called src in the directory.

  5. Copy the contents of your application into src:

    1. If the source is a zip file (including .jar files), unzip the contents into src .
    2. If the source code is a directory, copy the contents into src .
  6. Run gcloud builds submit . to build your application.

Known incompatibilities

  • Buildpacks that rely on Cloud Foundry injected environment variables like VCAP_SERVICES won't work. You should instead explicitly declare a dependency on what they inject using your language's management system.
  • To patch the images produced in this manner, you must rebuild the image using a newer version of the build and run image. Application images won't be automatically patched by updating BOSH stemcells if you run them on Cloud Foundry.
  • Builds will happen in a different network environment than your Cloud Foundry cluster, you may have to set up custom Cloud Build pools with access to your internal package mirrors.

What's Next

Create a Mobile Website
View Site in Mobile | Classic
Share by: