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.
Install Local Tools & Configure GCP:
Install the Google Cloud SDK and Docker.
Log in to GCP and initialize your project:
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
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.*
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.
- 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
- 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.
- 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"
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.
- 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.
- Delete the existing VM:
gcloud compute instances delete routing-vm --zone=europe-west2-a --quiet
- 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