Deploying a Dockerized Routing Engine on GCP

1 Introduction

This guide provides a minimal and robust method for deploying a Dockerized OpenTripPlanner (OTP) server on Google Cloud Platform (GCP), adhering to modern best practices.

Core Principles:

  • Declarative Deployment: Use create-with-container for reliable, atomic deployments.

  • Least Privilege Security: Employ a dedicated Service Account with minimal permissions.

  • Optimized Images: Use multi-stage Docker builds for smaller, more secure images.

  • Cost Efficiency: Utilize Spot VMs for non-critical workloads.


1.1 Step 1: One-Time Setup (Local & GCP)

Prepare your local environment and GCP project. This only needs to be done once.

  1. Install Local Tools & Configure GCP:

gcloud init
- Enable required APIs:
gcloud services enable compute.googleapis.com artifactregistry.googleapis.com iam.googleapis.com
- Configure Docker to authenticate with Artifact Registry (replace `europe-west2` with your region):
gcloud auth configure-docker europe-west2-docker.pkg.dev
  1. Create the Dockerfile:

    Save the following as routing-engine/Dockerfile. This version includes Nginx and Certbot to automatically handle HTTPS.

# ---- Build Stage ----
FROM ubuntu:22.04 as builder
RUN apt-get update && apt-get install -y wget unzip && rm -rf /var/lib/apt/lists/*
WORKDIR /source
RUN wget https://github.com/ITSLeeds/TDS/releases/download/0.20.1/otp_TDS.zip && unzip otp_TDS.zip

# ---- Final Stage ----
FROM eclipse-temurin:8-jre-focal
RUN apt-get update && apt-get install -y nginx certbot python3-certbot-nginx supervisor && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /source/otp_TDS/ .
RUN mkdir -p graphs
RUN rm -f /etc/nginx/sites-enabled/default
COPY nginx.conf /etc/nginx/sites-available/otp
RUN ln -s /etc/nginx/sites-available/otp /etc/nginx/sites-enabled/
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY startup.sh /startup.sh
RUN chmod +x /startup.sh
EXPOSE 80 443 8080
CMD ["/startup.sh"]
*Note: You will also need to create `nginx.conf`, `supervisord.conf`, and `startup.sh` in your project directory.*
  1. Create GCP Resources:

    • Artifact Registry Repository:
gcloud artifacts repositories create routing-engine \
  --repository-format=docker \
  --location=europe-west2 \
  --project=shiny-server-154518
- **Service Account:**
gcloud iam service-accounts create otp-vm-sa --display-name="OTP VM Service Account" --project=shiny-server-154518
gcloud projects add-iam-policy-binding shiny-server-154518 \
  --member="serviceAccount:otp-vm-sa@shiny-server-154518.iam.gserviceaccount.com" \
  --role="roles/artifactregistry.reader"

1.2 Step 2: Build and Deploy the Routing Engine

Now, build the Docker image and deploy it to a new VM.

  1. Build and Push the Docker Image:
export IMAGE_URI="europe-west2-docker.pkg.dev/shiny-server-154518/routing-engine/routing-engine:latest"
docker build -t $IMAGE_URI ./routing-engine/
docker push $IMAGE_URI
  1. Deploy the VM and Create Firewall Rule:
# Create the VM with the container
gcloud compute instances create-with-container routing-vm \
  --zone=europe-west2-a \
  --machine-type=e2-medium \
  --provisioning-model=SPOT \
  --service-account=otp-vm-sa@shiny-server-154518.iam.gserviceaccount.com \
  --tags=allow-http-traffic \
  --container-image=$IMAGE_URI \
  --container-restart-policy=always

# Create a firewall rule to allow traffic
gcloud compute firewall-rules create allow-otp-traffic \
  --allow tcp:80,tcp:443,tcp:8080 \
  --source-ranges=0.0.0.0/0 \
  --target-tags=allow-http-traffic

1.3 Step 3: Access and Verify

After deployment, verify that the server is running correctly.

  1. Get the External IP Address:
export EXTERNAL_IP=$(gcloud compute instances describe routing-vm --zone=europe-west2-a --format='get(networkInterfaces[0].accessConfigs[0].natIP)')
echo "Server IP: $EXTERNAL_IP"
  1. Test the API:

    • Via HTTPS (recommended):
curl "https://otp.robinlovelace.net/otp/routers/west-yorkshire/plan?fromPlace=53.8003,-1.5491&toPlace=53.8008,-1.5497&mode=CAR"
- **Via direct IP and port (for teaching/debugging):**
curl "http://$EXTERNAL_IP:8080/otp/routers/west-yorkshire/plan?fromPlace=53.8003,-1.5491&toPlace=53.8008,-1.5497&mode=CAR"
- **Interactive Map:** Open `https://otp.robinlovelace.net` in your browser.
  1. View Logs:
gcloud logging read "resource.type=gce_instance AND resource.labels.instance_id=$(gcloud compute instances describe routing-vm --zone=europe-west2-a --format='get(id)')" --limit 100

1.4 Step 4: Managing the Deployment

Updating the Server:

To apply changes (e.g., an updated Dockerfile), you must rebuild the image and redeploy the VM.

  1. Delete the existing VM:
gcloud compute instances delete routing-vm --zone=europe-west2-a --quiet
  1. Follow Step 2 again to build the new image and deploy the VM.

Cleaning Up Resources:

To avoid ongoing charges, delete the resources when you are finished.

# Delete the VM instance
gcloud compute instances delete routing-vm --zone=europe-west2-a --quiet

# Delete the firewall rule
gcloud compute firewall-rules delete allow-otp-traffic --quiet

# Delete the service account
gcloud iam service-accounts delete otp-vm-sa@shiny-server-154518.iam.gserviceaccount.com --quiet

# Optional: Delete the Docker image from Artifact Registry
gcloud artifacts repositories delete routing-engine --location=europe-west2 --quiet

Reuse