This sample migration uses the Spring Music project to show how a Cloud Foundry application can be built as an OCI-compliant application image. This sample is using the lift and shift strategy, which uses open source components from the Cloud Foundry ecosystem. After you create the application image, you need to configure the application for deployment to Cloud Run.
Before you start
- Make sure you have set up a new project for Cloud Run as described in the setup page.
- Make sure you have a
REGISTRY_URI
for storing containers. Cloud Run recommends using Artifact Registry. If you are under a domain restriction organization policy restricting unauthenticated invocations for your project, you will need to access your deployed service as described under Testing private services.
- Install Docker on your workstation. Docker is used to create intermediate images to build the project.
Permissions required to deploy
For this guide you need permissions to build, store the built container image and deploy.
You must have the following roles:
- Cloud Build Editor role
- Artifact Registry Admin role
- Storage Admin role
- Cloud Run Admin role
- Service Account User role
Project Structure
For this guide we suggest that you create a project directory e.g. cr-spring-music/
and create sub-directories as you progress through the guide.
cr-spring-music/
├── build
├── run
└── spring-music
Create the build 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.
Create a directory called
build/
andcd
into it:mkdir build && cd build
In the
build/
folder, create a new file calledDockerfile
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
Use Cloud Build to build and publish the
builder
imagegcloud 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.
Create a directory called
run/
andcd
into it:mkdir run && cd run
In the
run/
folder, create a new shell script calledentrypoint.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
In the
run/
folder, create a new file calledDockerfile
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={}
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.
Build Spring Music for Cloud Foundry
To clone the Spring Music project and run the build commands as if we were deploying the project to Cloud Foundry:
Clone the Spring Music repository:
git clone https://github.com/cloudfoundry-samples/spring-music.git
For the purposes of this guide, we will be using an older version of the Spring Music application that uses Java 8 and Spring Boot 2. To do this we will move to an older revision of the Spring Music project:
git checkout 610ba471a643a20dee7a62d88a7879f13a21d6a3
Move into the repository:
cd spring-music
Build the Spring Music binary:
./gradlew clean assemble
You now have a build/
folder with the compiled Spring Music app ready to
be pushed into a Cloud Foundry instance.
Convert Spring Music into a Cloud Run compatible application
You must take the output of the build command to prepare the Spring Music artifact for deployment into Cloud Run.
Create a staging directory
cr-app
and asrc
sub-directory inside of it:mkdir -p cr-app/src
Mimic
cf push
by extracting the contents of the compiled JAR into thesrc
directory:unzip build/libs/spring-music-1.0.jar -d cr-app/src
Change directory into
cr-app/
:cd cr-app/
Create a new file called
Dockerfile
. ThisDockerfile
will use the build image and runtime image created in the previous steps to create the runnable application image for Spring Music, using the Java buildpack.Paste the following code into the
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
Build Spring Music as an OCI compliant image
In this step, you instruct Cloud Build how to construct an OCI-compliant image using the build image, runtime image, and application Dockerfile created in the previous steps.
To create the OCI-compliant image:
Create a file called
cloudbuild.yaml
. This is a build configuration that will instruct Cloud Build how to build the application.Paste the following configuration into
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: REGISTRY_URI/builder:stable _RUN_IMAGE: REGISTRY_URI/runtime:stable _BUILDPACKS: https://github.com/cloudfoundry/java-buildpack _TAG: REGISTRY_URI/spring-music:latest
- Replace
REGISTRY_URI
with the URI of the container registry where you have published the builder and runner.
- Replace
Build the application image using Cloud Build:
gcloud builds submit .
When the build completes, make note of the resulting image URI. You will need it when deploying the application in the next steps. The resulting image will be an OCI-compliant container image for running the Spring Music app, built using open source Cloud Foundry components.
Deploy to Cloud Run
You must create a service definition file to use in Cloud Run:
Create a service account for your application:
gcloud iam service-accounts create spring-music
Create a
service.yaml
file with the following code:apiVersion: serving.knative.dev/v1 kind: Service metadata: name: "spring-music" # Set this to be the project number of the project you're deploying to. namespace: "PROJECT_NUMBER" labels: cloud.googleapis.com/location: us-central1 migrated-from: cloud-foundry annotations: run.googleapis.com/ingress: all spec: template: metadata: annotations: autoscaling.knative.dev/minScale: '1' autoscaling.knative.dev/maxScale: '1' run.googleapis.com/cpu-throttling: 'true' run.googleapis.com/startup-cpu-boost: 'true' run.googleapis.com/sessionAffinity: 'false' spec: containerConcurrency: 1000 timeoutSeconds: 900 serviceAccountName: spring-music@PROJECT_NUMBER.iam.gserviceaccount.com containers: - name: user-container # Set the following value to either: # - The image you built for your application in the last section of the guide. image: SPRING_IMAGE_URI ports: - name: http1 containerPort: 8080 env: - name: VCAP_APPLICATION value: |- { "application_id": "00000000-0000-0000-0000-000000000000", "application_name": "spring-music", "application_uris": [], "limits": { "disk": 0, "mem": 1024 }, "name": "spring-music", "process_id": "00000000-0000-0000-0000-000000000000", "process_type": "web", "space_name": "none", "uris": [] } - name: MEMORY_LIMIT value: '1024M' resources: limits: memory: 1024Mi cpu: "1" startupProbe: httpGet: path: / port: 8080 timeoutSeconds: 1 failureThreshold: 30 successThreshold: 1 periodSeconds: 2 livenessProbe: httpGet: path: / port: 8080 timeoutSeconds: 1 failureThreshold: 1 successThreshold: 1 periodSeconds: 30 traffic: - percent: 100 latestRevision: true
- Replace
PROJECT_NUMBER
with your project number. - Replace
SPRING_IMAGE_URI
with the URI of the application image created under build Spring Music as an OCI compliant image.
- Replace
Deploy the service to Cloud Run:
gcloud run services replace service.yaml
Once the deployment completes, you will be able to visit the running Spring Music application at the deployed URL.
What's Next
- For a more in-depth look at the containerization process see Migrate to OCI containers.