Learning Path: Transform a monolith into a GKE app - Containerize the modular app

This is the fourth tutorial in a learning path that teaches you how tomodularize and containerize a monolithic app.

The learning path consists of the following tutorials:

  1. Overview
  2. Understand the monolith
  3. Modularize the monolith
  4. Prepare the modular app for containerization
  5. Containerize the modular app (this tutorial)
  6. Deploy the app to a GKE cluster

In the previous tutorial,Prepare the modular app for containerization,you saw what changes needed to be made to the modular version of the CymbalBooks app to prepare it for containerization. In this tutorial, you containerizethe app.

Costs

You can complete this tutorial without incurring any charges. However, followingthe steps in thenext tutorial of this series incurs charges on yourGoogle Cloud account. Costs begin when you enable GKE and deploythe Cymbal Books app to a GKE cluster. These costs includeper-cluster charges for GKE, as outlined on thePricingpage, and charges for running Compute Engine VMs.

To avoid unnecessary charges, ensure that you disable GKE ordelete the project once you've completed this tutorial.

Before you begin

Before you begin this tutorial, make sure you completed the earlier tutorialsin the series. For an overview of the whole series, and links to particulartutorials, seeLearning Path: Transform a monolith into a GKE app - Overview.

Set up your environment

In this section, you set up an environment in which you containerize the modularapp. Specifically, you perform the following steps:

  1. Select or create a Google Cloud project
  2. Enable the necessary APIs
  3. Connect Cloud Shell to your Google Cloud project
  4. Set the default environment variables
  5. Create a repository in Artifact Registry
  6. Configure Docker for Artifact Registry
  7. Get the tutorial code

Select or create a Google Cloud project

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

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.create permission.Learn how to grant roles.
    Note: If you don't plan to keep the resources that you create in this procedure, create a project instead of selecting an existing project. After you finish these steps, you can delete the project, removing all resources associated with the project.

    Go to project selector

  2. Verify that billing is enabled for your Google Cloud project.

Enable the necessary APIs

To work with container images and Kubernetes in your Google Cloud project, youneed to enable the following APIs:

  • Artifact Registry API: this API enables Artifact Registry, which is a servicefor storing and managing your container images.
  • Kubernetes Engine API: this API provides access to GKE.

To enable these APIs, visitenable the APIs in Google Cloud console.

Connect Cloud Shell to your Google Cloud project

Now that you've set up your Google Cloud project, you need to launch aCloud Shell instance and connect it to your Google Cloudproject. Cloud Shell is a command-line tool that lets you create and manage aproject's resources directly from your browser. Cloud Shell comespreinstalled with two important tools: thegcloud CLIand thekubectl CLI. In thistutorial, you use the gcloud CLI to interact with Google Cloud and inthe next tutorial, you use thekubectl CLI to manage the Cymbal Books appthat runs on GKE.

To connect a Cloud Shell instance with your Google Cloud project, followthese steps:

  1. Go to the Google Cloud console:

    Google Cloud console

  2. In the console, click theActivate Cloud Shell button:Activate Cloud Shell

    A Cloud Shell session opens inside a frame lower on the console.

  3. Set your default project in the Google Cloud CLI using the following command:

    gcloudconfigsetprojectPROJECT_ID

    ReplacePROJECT_ID with the project ID of theproject that you created or selected in the previous section,Select or create Google Cloud project.A project ID is a unique string that differentiates your project from allother projects in Google Cloud. To locate the project ID, go to theproject selector.On that page, you can see the project IDs for each of your Google Cloudprojects.

Set the default environment variables

To simplify the commands that you run throughout this tutorial, you now set someenvironment variables in Cloud Shell. These variables store values such asyour project ID, repository region, and image tag. After you define thesevariables, you can reuse them in multiple commands by referencing the variablename (for example,$REPOSITORY_NAME) instead of retyping or replacing valueseach time. This approach makes the tutorial easier to follow and reduces therisk of errors.

To set up your environment with Cloud Shell, perform the following steps:

exportPROJECT_ID=$(gcloudconfiggetproject)exportREPOSITORY_REGION=REPOSITORY_REGIONexportREPOSITORY_NAME=REPOSITORY_NAMEexportREPOSITORY_DESCRIPTION="REPOSITORY_DESCRIPTION"exportTAG=TAG

Replace the following:

  • REPOSITORY_REGION: theregion where you want yourArtifact Registry repository to be hosted. For example,us-central1(Iowa),us-west1 (Oregon), oreurope-west1 (Belgium). For a completelist of regions, seeRegions and Zones.
  • REPOSITORY_NAME: the name of your repository.For example,book-review-service-repo.
  • REPOSITORY_DESCRIPTION: a brief description ofthe repository's purpose. For example,"Repository for storing Dockerimages for the book review service".
  • TAG: the tag that you want to apply to an image.A tag is a label that you can attach to a specific version of a containerimage. You can use tag naming conventions like these to clearly indicatedifferent versions of an image:
    • v1
    • v1.2.3
    • A descriptive tag, such asfeature-x-dev
    • A tag that indicates the environment, such astest

Create a repository in Artifact Registry

Next, you create a repository in Artifact Registry. A repository is astorage location where you keep container images. When you build a containerimage, you need somewhere to store it so that it can later be deployed to aKubernetes cluster. Artifact Registry lets you create and manage these repositorieswithin your Google Cloud project.

To create a repository in Artifact Registry, run the following command:

gcloudartifactsrepositoriescreate${REPOSITORY_NAME}\--repository-format=docker\--location=${REPOSITORY_REGION}\--description="${REPOSITORY_DESCRIPTION}"

Successful output from the command looks like the following:

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

Configure Docker for Artifact Registry

Next, you configure Docker so that it can securely communicate withGoogle Cloud's Artifact Registry. Docker is a tool that you can use topackage and run software in a consistent way across different environments. Youlearn more about how Docker works in the next section. For now, you need toconfigure it so that it can connect to Artifact Registry.

If you don't configure Docker in this way, you can't push the container imagesto Artifact Registry (a task you perform later in this tutorial). You also can't pullthe container images from Artifact Registry and deploy them to a GKEcluster (a task you perform in the next tutorial).

To configure Docker to authenticate with Artifact Registry, run this command:

gcloudauthconfigure-docker${REPOSITORY_REGION}-docker.pkg.dev

Get the tutorial code

Now that your Cloud Shell environment is configured, you need to download thetutorial code within Cloud Shell. Even if you previously cloned therepository on your local machine, you need to clone it again here on yourCloud Shell instance.

Although it's possible to complete this tutorial on your local machine, you wouldhave to manually install and configure several tools such as Docker,kubectl,and gcloud CLI. Using Cloud Shell is easier because it comespreconfigured with all of these tools.

In your Cloud Shell instance, run the following command to clone the GitHubrepository:

gitclonehttps://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git

Containerization basics: container images, containers, and Dockerfiles

Now that you have set up your environment and downloaded the containerized code,you're ready to containerize the app. Containerizing the app consists of using aDockerfile to package each module of Cymbal Books (homepage, book details,images, and book reviews) into a container image. When the application isdeployed to the GKE cluster, Kubernetes uses these containerimages to create running containers in the cluster.

The following sections explain these concepts in detail.

What is containerization?

Containerization packages a module and all its dependencies, such as librariesand configuration files, into a unit called a container image. Developers usethis container image to create and run containers across anyenvironment—from a developer's laptop to a testing server or a productionKubernetes cluster.

What are container images?

A container image contains all the files that are needed to run an application.These files include the application code itself, system libraries, the runtimeenvironment (for example, the Python interpreter), static data, and any otherdependencies.

In this tutorial, you create a container image for each module of the bookreviews app.

What is a container?

A container is an isolated environment where code from a container image runs.You can create containers in two ways: by using thedocker run command fortesting during development, or by deploying container images to a Kubernetescluster.

In the containerized version of the Cymbal Books app, each module from themodular app runs in its own container:

  • The homepage container runs the homepage module and handles requests to/.
  • The book details container runs the book details module and serves data forendpoints such as/book/1 or/book/3.
  • The book reviews container runs the book reviews module and manages requeststo endpoints such as/book/2/reviews.
  • The images container runs the images module and serves book cover images forendpoints such as/images/fungi_frontier.jpg.

A key advantage of containers is that Kubernetes can automatically create morecontainers when needed. For example, if numerous users are reading book reviews,Kubernetes can start additional book reviews containers to handle the load.

To implement scaling in a modular app that doesn't use containers, you'd need towrite custom code to launch new instances of a module and distribute trafficbetween them. With Kubernetes, this scaling capability is built-in: you don'tneed to write any custom scaling code.

What are Dockerfiles?

A Dockerfile is a script that defines how to package a module into a containerimage. In this tutorial, you don't need to create any Dockerfiles—they'realready provided for you in the GitHub repository you cloned earlier. Eachmodule's directory in your local copy ofkubernetes-engine-samples/quickstarts/monolith-to-microservices/containerized/contains its own Dockerfile.

For example, you can find thehome_app module's Dockerfile in yourCloud Shell instance atkubernetes-engine-samples/quickstarts/monolith-to-microservices/containerized/home_app/Dockerfile.This Dockerfile looks like the following:

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

This Dockerfile performs the following steps to create the container image forthehome_app module:

  • Line 1:FROM python:3.9-slim downloads a Python 3.9 interpreter andits required files into the container image. These files enable the moduleto run.
  • Line 2:WORKDIR /app creates a directory called/app inside thecontainer and sets this directory as the current working directory. Allcommands that are run inside the container will run from within thisdirectory.
  • Lines 3 and 4:COPY requirements.txt . copies therequirements.txtfile from your local machine into the container image's/app directory.Therequirements.txt file lists all the Python libraries thathome_app.py needs. The lineRUN pip install installs those librariesinto the container image.
  • Lines 5 to 7: TheCOPY commands that appear in these lines copy themodule's code (home_app.py) and its supporting files (templates and staticassets) to the/app directory within the container image.
  • Line 8:CMD specifies the default command that Docker runs when thecontainer starts. In this Dockerfile,CMD ["python", "home_app.py"] tellsDocker to use the Python interpreter to execute thehome_app.py moduleautomatically when the container is launched.

How containerization can enforce stricter data isolation

Lines 5 to 7 of the Dockerfile, which were described in the previous section,show how containerization can enforce stricter data isolation than themodularized version of the app. In a previous tutorial, in the sectionGiveeach module access to only the data it needs, youlearned that the modular version of the app organized data into separatedirectories, but modules still shared the same file system and could potentiallyaccess each other's data.

Here, in the containerized version of the app, each module's container includesonly its necessary files. For example, if thehome_app module doesn't needaccess to book reviews data, that data simply doesn't exist inside thehome_app container. By default, a container can't access files from anothercontainer unless explicitly configured to do so. This helps ensure that eachmodule is fully isolated, and also helps prevent accidental or unauthorized dataaccess.

In the next section, you see how thedocker build command takes a Dockerfileas input, and follows the instructions in the Dockerfile to create a containerimage.

Build container images using Docker

In this section, you build Docker container images for each of the book reviewmodules and push them to your Artifact Registry repository. You'll use these containerimages in a following tutorial to deploy and run the Cymbal Books sample app inKubernetes.

  1. Navigate to the root directory of the containerized application:

    cdkubernetes-engine-samples/quickstarts/monolith-to-microservices/containerized/
  2. Create the container images by using thedocker build command:

    dockerbuild-t${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/home-app:${TAG}./home_appdockerbuild-t${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-details-app:${TAG}./book_details_appdockerbuild-t${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-reviews-app:${TAG}./book_reviews_appdockerbuild-t${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/images-app:${TAG}./images_app
  3. View the container images that were built inside your Cloud Shellinstance:

    dockerimages

    Check that the following images appear in the list:

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

    If all four images are listed, you successfully created the containerimages.

Test containers in Cloud Shell

To verify that the container images are built correctly, you can run them ascontainers and test their endpoints in Cloud Shell.

Thebook_details_app,book_reviews_app, andimages_app containers can betested individually because they don't need to communicate with each other.However, testing thehome_app container by using Docker is difficult becausehome_appis configured to find the other containers that use service namessuch ashttp://book-details-service:8081.

Although it's possible to test thehome_app container by finding eachcontainer's IP address and configuringhome_app to use them instead of servicenames, this approach requires a lot of effort. Instead, it's a good idea todefer testing thehome_app container until after you deploy the application toa Kubernetes cluster. After the app is on the cluster, you can determine whetherthe home module is working correctly.

Follow these steps to test the containers:

  1. Start thebook_details_app,book_reviews_app, andimages_app containers:

    dockerrun-d-p8081:8080${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-details-app:${TAG}dockerrun-d-p8082:8080${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-reviews-app:${TAG}dockerrun-d-p8083:8080${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/images-app:${TAG}
  2. Check that the containers are running by listing all active containers:

    dockerps

    Output from this command should show three containers that are running,with the statusUp:

    CONTAINER ID   IMAGE                PORTS                        STATUSa1b2c3d4e5f6   REGION/.../details   0.0.0.0:8081->8080/tcp       Upg7h8i9j0k1l2   REGION/.../reviews   0.0.0.0:8082->8080/tcp       Upm3n4o5p6q7r8   REGION/.../images    0.0.0.0:8083->8080/tcp       Up
  3. To test the endpoints of thebook_details_app container, use the followingcurl commands:

    curlhttp://localhost:8081/bookscurlhttp://localhost:8081/book/1curlhttp://localhost:8081/book/2curlhttp://localhost:8081/book/3

    Each of these commands returns data in JSON format. For example, the outputfrom thecurl http://localhost:8081/book/1 command looks like this:

    {"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. Retrieve book reviews from thebook_reviews_app container by using thiscurl command:

    curlhttp://localhost:8082/book/1/reviews

    This command returns a list of 20 reviews of book 1 in JSON format. Here'san example of one review from the list:

    {"content": "The concept of time as a tangible substance is brilliantly explored in 'Zephyr's Timepiece'.","rating": 5}
  5. Test theimages_app container:

    1. Click the**Web Preview** buttonWeb Preview Button

    2. SelectChange port and enter 8083. A browser window opens with a URLsimilar to this:

      https://8083-your-instance-id.cs-your-region.cloudshell.dev/?authuser=0
    3. Remove?authuser=0 at the end of the URL and add the path to an imagefile, such as/images/fungi_frontier.jpg. The following is an example:

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

      You should see the book cover image forFungi Frontier displayed inyour browser.

  6. After testing, stop the containers to release resources:

    1. List the running containers and find their container IDs:

      dockerps
    2. Stop each container:

      dockerstopCONTAINER_ID

      ReplaceCONTAINER_ID with the ID of the container youwant to stop.

Push the container images to Artifact Registry

Before you can deploy your app to a Kubernetes cluster, the container imagesneed to be stored in a location that the cluster can access. In this step, youpush the images to the Artifact Registry repository you created earlier. In the nexttutorial, you deploy those images from the Artifact Registry repository to aGKE cluster:

  1. To push your container images to Artifact Registry, run these commands:

    dockerpush${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/home-app:${TAG}dockerpush${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-details-app:${TAG}dockerpush${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-reviews-app:${TAG}dockerpush${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/images-app:${TAG}
  2. After pushing the images, verify that they were successfully uploaded bylisting them:

    gcloudartifactsdockerimageslist${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}

    You should see output similar to the following:

    Listing items under project ${PROJECT_ID}, location ${REPOSITORY_REGION}, repository ${REPOSITORY_NAME}.IMAGE: ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-details-appDIGEST: sha256:f7b78f44d70f2eedf7f7d4dc72c36070e7c0dd05daa5f473e1ebcfd1d44b95b1CREATE_TIME: 2024-11-14T00:38:53UPDATE_TIME: 2024-11-14T00:38:53SIZE: 52260143IMAGE: ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/book-reviews-appDIGEST: sha256:875ac8d94ef54db2ff637e49ad2d1c50291087623718b854a34ad657748fac86CREATE_TIME: 2024-11-14T00:39:04UPDATE_TIME: 2024-11-14T00:39:04SIZE: 52262041IMAGE: ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/home-appDIGEST: sha256:70ddc54ffd683e2525d87ee0451804d273868c7143d0c2a75ce423502c10638aCREATE_TIME: 2024-11-14T00:33:56UPDATE_TIME: 2024-11-14T00:33:56SIZE: 52262412IMAGE: ${REPOSITORY_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_NAME}/images-appDIGEST: sha256:790f0d8c2f83b09dc3b431c4c04d7dc68254fecc76c48f00a83babc2a5dc0484CREATE_TIME: 2024-11-14T00:39:15UPDATE_TIME: 2024-11-14T00:39:15SIZE: 53020815

    The output includes the following details for each image:

    • IMAGE: the repository path and image name.
    • DIGEST: a unique identifier for the image.
    • CREATE_TIME or UPDATE_TIME: when the image was created or lastmodified.
    • SIZE: the size of the image in bytes.

Update the Kubernetes manifest with paths to container images

As you learned in the previous tutorial,Prepare the modular app for containerization, aKubernetes manifest is a YAML file that defines how your app runs in aKubernetes cluster. It includes details such as the following:

  • The modules of your app (for example,home-app,book-details-app)
  • Paths to the container images
  • Configuration details such as resource limits
  • Service definitions for routing requests between modules

In this section, you update the same manifest file that you reviewed in theprevious tutorial. That file iskubernetes-manifest.yaml and it containsplaceholder values for the image paths. You need to replace those placeholderswith the actual paths to the container images you pushed to your Artifact Registryrepository in the previous section.

To update thekubernetes-manifest.yaml Kubernetes manifest file, follow thesesteps:

  1. In Cloud Shell, navigate to thecontainerized/ directory, whichcontains the Kubernetes manifest filekubernetes-manifest.yaml:

    cdkubernetes-engine-samples/quickstarts/monolith-to-microservices/containerized/
  2. Open thekubernetes-manifest.yaml file in a text editor:

    vimkubernetes-manifest.yaml
  3. Locate theimage fields that contain placeholders such as this:

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

    Replace each placeholder with the actual paths to the container images thatyou pushed to Artifact Registry:

    Here's what a path might look like after making these replacements:

    image:us-west1-docker.pkg.dev/your-project-id/book-review-service-repo/home-app:v1
  4. Update the paths for all container images:

    • home-app
    • book-details-app
    • book-reviews-app
    • images-app
  5. After you update the paths, save the manifest file and close the editor. Forexample, if you're using vim, pressEsc to enter command mode,typewq, and pressEnter to save and exit.

Your Kubernetes manifest is now configured to deploy the container images fromyour Artifact Registry repository to a Kubernetes cluster.

Summary

In this tutorial, you prepared the modular Cymbal Books app for deployment to aKubernetes cluster by performing the following tasks:

  1. Set up a Google Cloud project and configured Cloud Shell for yourenvironment.
  2. Reviewed the provided Dockerfiles for each app module.
  3. Built container images for the app modules by using Docker.
  4. Tested containers in Cloud Shell to verify their functionality.
  5. Pushed the container images to Artifact Registry for storage.
  6. Updated the Kubernetes manifest to use the correct container image pathsfrom Artifact Registry.

What's next

In the next tutorial,Deploy the app to a GKE cluster,you deploy the containerized application to a GKE cluster.

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2025-12-15 UTC.