Customize migration plan for Tomcat servers

You should review the migration plan file that resulted from creating a migration. Customize the file before executing the migration. The details of your migration plan are used to extract the workload container artifacts from the source.

This section describes the contents of the migration and the kinds of customizations you might consider before you execute the migration and generate deployment artifacts.

Before you begin

Edit the migration plan

You can edit the migration plan by using the migctl tool or the Google Cloud console.


You must download the migration plan before you can edit it:

  1. Download the migration plan.

    migctl migration get my-migration
  2. Edit the downloaded migration plan, my-migration.yaml, in a text editor.

  3. When your edits are complete, save and upload the revised migration plan:

    migctl migration update my-migration --main-config my-migration.yaml
  4. Repeat these steps if more edits are necessary.


Edit the migration plan in the Google Cloud console by using the YAML editor.

  1. Open the Migrate to Containers page in the Google Cloud console.

    Go to the Migrate to Containers page.

  2. Click the Migrations tab to display a table containing the available migrations.

  3. In the row for your desired migration, select the migration Name to open the Details tab.

  4. Select the YAML tab.

  5. Edit the migration plan as necessary.

  6. When you are done editing, you can either:

    1. Save the migration plan. You will then have to manually execute the migration to generate the migration artifacts. Use the procedure shown in Executing a migration.

    2. Save and generate the artifacts. Execute the migration by using your edits to generate the migration artifacts. The process is the same as described in Executing a migration. You can then monitor the migration as described in Monitoring a migration.


You must download the migration plan, edit it, then apply it. The migration plan is stored inside the appXGenerateArtifactsConfig field of the AppXGenerateArtifactsFlowSpec CRD.

  1. Get the name of the AppXGenerateArtifactsFlow:

    kubectl get -n v2k-system -o jsonpath={} my-migration

    The naming pattern is returned in the form of appx-generateartifactsflow-id.

  2. Get the migration plan by name and write to a file named my-plan.yaml:

    kubectl -n v2k-system get -o jsonpath={.spec.appXGenerateArtifactsConfig} appx-generateartifactsflow-id > my-plan.yaml
  3. Edit the migration plan as necessary.

  4. Apply the file:

    kubectl patch --type merge -n v2k-system --patch '{"spec": {"appXGenerateArtifactsConfig": '"$(jq -n --rawfile plan my-plan.yaml '$plan')"'}}' appx-generateartifactsflow-id

Review your migration plan's details and guiding comments to add information as needed. Specifically, consider edits around the following sections:

Specify the Docker image

On the migration plan, we generate a Docker community image tag based on the Tomcat version, Java version, and Java vendor.

  • Tomcat version – The Tomcat version is detected and converted to a major version (minor versions are not supported). If we fail to detect a Tomcat version, then fromImage will contain an empty string.
  • Java version – The Java version is set using the information from the fit assessment collection. If CATALINA_BASE or CATALINA_HOME are entered manually, or if fit assessment failed to collect the Java version, the Java version is set to 11 by default.
  • Java vendor – The Java vendor is set to a constant: openjdk.

On the migration plan, the fromImage field represents the Docker Image tag used as the base of the container image.

The original Tomcat and Java versions detected on the source VM are contained in discovery-report.yaml which is generated by the initial discovery.

If you want to change the Docker community image, or provide your own docker image you can modify the fromImageTag in your migration plan using the following format:

    - name: latest
      . . .
        - name: tomcat-latest
          . . .
          fromImage: tomcat:9.0-jdk11-openjdk

Configure SSL

When you create a new Tomcat migration, a discovery process scans the server against the different applications that are discovered.

In order to upload the certificates to the repository you need to set the includeSensitiveData field on the migration plan to true. The secrets are uploaded in secrets.yaml

# Sensitive data which will be filtered out of the container image.
# If includeSensitiveData is set to true the sensitive data will be mounted on the container.

includeSensitiveData: false
- name: latest-62afb0c1
  catalinaBase: /opt/tomcat/latest
  catalinaHome: /opt/tomcat/latest
  - name: tomcat-latest-docs-62afb0c1
    # If set to true, sensitive data specified in sensitiveDataPaths will be uploaded to the artifacts repository.
    - /usr/local/ssl/server.pem
    - /usr/home/tomcat/keystore
    - /usr/home/tomcat/truststore

The migration will filter out the sensitive data paths defined in the sensitiveDataPaths field on the migration plan. If you remove a certificate path, the certificate will be uploaded to the image.

Webapps logging

Migrate to Containers supports logging with log4j v2, logback and log4j v1.x that reside in CATALINA_HOME.

Migrate to Containers will create an additional archive file with modified log configurations and convert all file type appenders to console appenders. You can use the content of this archive as a reference to enable log collection and stream to a log collection solution (such as Google Cloud Logging).

Memory allocation

During the migration process, Migrate to Containers attempts to locate memory limits for the max heap of the Tomcat Java Heap on the source VMs. If memory limits are detected on a source VM, Migrate to Containers sets requests to initial andlimits to maximum for your migrated container.

These values are based on the Java maximum heap size (-Xmx/-XX:MaxHeapSize) value which is collected from the source instance using the fit-assessment (mfit). The values will be: limit=200%Xmx, request=125%Xmx.

If you want to specify the memory limits of applications migrated to individual containers, or if no memory limits were found in your source VMs, you can edit memory limits directly in your migration plan using the following format:

    - name: latest
      . . .
        - name: tomcat-latest
          . . .
              limit: 2048M
              requests: 1280M

If memory limits have been defined in your migration plan, the Dockerfile that was generated alongside other artifacts after a successful migration will reflect your declaration:

FROM tomcat:8.5-jdk11-openjdk

# Add JVM environment variables for tomcat
ENV CATALINA_OPTS="${CATALINA_OPTS} -XX:MaxRAMPercentage=50.0 -XX:InitialRAMPercentage=50.0 -XX:+UseContainerSupport <additional variables>"

This defines the initial and maximum size to be 50% of limit value. This will allow the Tomcat Java heap allocation size to change according to any change with the pod memory limit.

Set Tomcat environment variables

If you would like to set CATALINA_OPTS in the Dockerfile that was generated alongside other artifacts after a successful migration, you can first add to the catalinaOpts field in your migration plan. The following example shows an updated catalinaOpts field:

    - name: latest
      . . .
        - name: tomcat-latest
          . . .
            . . .
          catalinaOpts: "-Xss10M"

Migrate to Containers will parse your catalinaOpts data to your Dockerfile. The following example shows the output of the parsing:

FROM 8.5-jdk11-openjdk-slim

## script detected.
## Modify env variables on the script and add definitions to the migrated
## tomcat server, if needed (less recommended than adding env variables directly
## to CATALINA_OPTS) by uncomment the line below
#ADD --chown=root:root /usr/local/tomcat/bin/

# Add JVM environment variables for the tomcat server
ENV CATALINA_OPTS="${CATALINA_OPTS} -XX:MaxRAMPercentage=50.0 -XX:InitialRAMPercentage=50.0 -Xss10M"

You may also set Tomcat environment variables using the script, which is located in the /bin folder on your Tomcat server. For more information about Tomcat environment variables, see the Tomcat documentation.

If you choose to use for setting your Tomcat environment variables, you will need to edit the Dockerfile.

Set Tomcat health probes

You can monitor the downtime and ready status of your managed containers by specifying probes in your Tomcat web server's migration plan. Health probe monitoring can help reduce the downtime of migrated containers and provide better monitoring.

Unknown health states can create availability degradation, false-positive availability monitoring, and potential data loss. Without a health probe, kubelet can only assume the health of a container and may send traffic to a container instance that is not ready. This can cause traffic loss. Kubelet may also not detect containers that are in a frozen state and will not restart them.

A health probe functions by running a small scripted statement when the container starts. The script checks for successful conditions, which are defined by the type of probe used, every period. The period is defined in the migration plan by a periodSeconds field. You can run or define these scripts manually.

To learn more about kubelet probes, see Configure Liveness, Readiness and Startup Probes in the Kubernetes documentation.

There are two types of probes available to configure, both probes are probe-v1-core defined in probe-v1-core reference and share the same function as the corresponding fields of container-v1-core

  • Liveness probe - Liveness probes are used to know when to restart a container.

  • Readiness probe - Readiness probes are used to know when a container is ready to start accepting traffic. To start sending traffic to a Pod only when a probe succeeds, specify a readiness probe. A readiness probe may act similarly to a liveness probe, but a readiness probe in the specifications indicates that a Pod will start without receiving any traffic and only start receiving traffic after the probe succeeds.

After discovery, the probe configuration is added to the migration plan. The probes can be used in their default configuration as shown below. To disable probes, remove the probes section from the yaml.

- name: latest
  - name: tomcat-latest
    - 8080
          port: 8080
          port: 8080

You can change this migration plan to use an existing Tomcat HTTP endpoint.

- name: latest
  - name: tomcat-latest
    - 8080
          path: /healthz
          port: 8080
          - name: Custom-Header
            value: Awesome
        initialDelaySeconds: 3
        periodSeconds: 3
          port: 8080

There are four predefined ways to check a container using a probe. Each probe must define exactly one of these four mechanisms:

  • exec - Executes a specified command inside the container. Execution is considered successful if the exit status code is 0.
  • grpc - Performs a remote procedure call using `gRPC`. `gRPC` probes are an alpha feature.
  • httpGet - Performs an HTTP GET request against the Pod's IP address on a specified port and path. The request is considered successful if the status code is greater than or equal to 200 and less than 400.
  • tcpSocket - Performs a TCP check against the Pod's IP address on a specified port. The diagnostic is considered successful if the port is open.

By default, a migration plan enables the tcpSocket probing method. Use the manual configuration of your migration plan to use a different probing methods. You can also define custom commands and scripts through the migration plan.

To add external dependencies for the readiness probe, while using the default liveness probe, define an exec readiness probe and a script that implements the logic.

Verify Tomcat clustering configuration

Tomcat clustering is used to replicate session information across all Tomcat nodes, which enables you to call any of the backend application servers and not lose client session information. To learn more about clustering configuration, see Clustering/Session Replication How-To in the Tomcat documentation.

Tomcat clustering class is called SimpleTcpListener, which uses multicast heartbeat messages for peer discovery. Cloud environments do not support multicast, so you must change the clustering configuration or remove it, when possible.

When a load balancer is configured to run and maintain a sticky session to the backend Tomcat server, it can use the jvmRoute property configured in the server.xml Engine configuration.

If your source environment is using an unsupported clustering configuration, modify the server.xml file to either disable the configuration, or use a supported configuration.

  • Tomcat v8.5 or above - Clustering must be disabled on Tomcat version 8.5. To disable clustering, you need to comment out the <Cluster … /> section in server.xml.
  • Tomcat v9 or above - In Tomcat version 9 or above, you can enable the Cluster class using KubernetesMembershipService. KubernetesMembershipService is a Kubernetes specific class, which uses the Kubernetes APIs for peer discovery.

    • Kubernetes provider - The following is a sample configuration for a Kubernetes provider:
    <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
    <Channel className="">
    <Membership className="" membershipProviderClassName=""/>
    • DNS provider - Use the DNSMembershipProvider to use the DNS APIs for peer discovery. The following is a sample configuration for a DNS provider:
    <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
    <Channel className="">
    <Membership className="" membershipProviderClassName=""/>
    • jvmRoute - When your load balancer relies on a jvmRoute value, the value should be changed from static to using the POD name. This will configure Tomcat to add a suffix to the session cookie with the POD name. This can be used by the front end load balancer to direct traffic to the correct Tomcat POD. Change the value in the server.xml file to use the following:
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="${HOSTNAME}">

Verify Tomcat proxy configuration

If Tomcat is configured to run behind a reverse proxy, or using several proxy configuration settings in the Connector section of server.xml, you must verify that the same proxy configurations are still applicable once migrated to run in Kubernetes.

To run a functional containerized Tomcat application, choose one of the below configuration changes to the reverse proxy configuration:

  • Disable proxy configuration — If the migrated application no longer runs behind a reverse proxy, you can disable proxy configuration by removing proxyName and proxyPort from the connector configuration.
  • Migrate the proxy — Migrate the proxy VM and retain all the existing Tomcat configurations.
  • Configure Ingress to replace the reverse proxy — If no custom or sophisticated logic has been implemented for your reverse proxy, you can configure an Ingress resource to expose your migrated Tomcat application. This process uses the same FQDN that was used prior to the migration. To configure an Ingress, you must verify that your Tomcat Kubernetes Service is of type: Nodeport. The following is a sample configuration of an Ingress:
kind: Ingress
  name: my-tomcat-ingress
  - host: APP_DOMAIN
      - pathType: ImplementationSpecific
            name: my-tomcat-service
              name: my-tomcat-port

Verify Java proxy configuration

When migrating to containers, you must verify the availability of your proxy servers in your new environment. When the proxy server is not available, choose one of the below configuration changes to the proxy:

  • Disable proxying - If the original proxy is no longer in use, then disable the proxy configuration. Remove all the proxy settings from either the script, or from the configuration file where they are maintained on your Tomcat server.
  • Change proxy settings - If your Kubernetes environment is using a different egress proxy, you can change your proxy settings in, or other file, to point at the new proxy.

What's next