改善
Kaizen  · Today I Learned by Ville Säävuori

Sharp Edges When Building APIs On Google Cloud Run

I deployed my first Google Cloud Run project this week as my weekend project. It was a very simple Python (FastAPI) backend and a Vue (Vite/TS) frontend.

This was also my first multi-layer Docker image as it was so simple to figure out what data to copy on the final layer:

FROM node:latest AS frontbuild
COPY ./front /front
WORKDIR /front
RUN corepack enable
RUN corepack prepare pnpm@latest-8 --activate
RUN pnpm config set store-dir .pnpm-store --global
RUN pnpm install && pnpm build

FROM python:3.11

WORKDIR /code

COPY ./requirements.txt /code/requirements.txt

RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt

COPY ./backend /code/backend
COPY --from=frontbuild /front/dist /code/front/dist

CMD exec uvicorn backend.api:app --host 0.0.0.0 --port ${PORT} --workers 1

First Cut: Mind the Regions!

Deploying to Cloud Run is very simple. After activating all the necessary services and APIs, you just build the image you want to deploy and then .. deploy:

gcloud builds submit --tag gcr.io/myproject/myimage

gcloud run deploy aidocs --image gcr.io/myproject/myimage --platform managed --region europe-west3 --allow-unauthenticated

And here came the first sharp edge: for some reason you cannot connect domain names for a service that runs in the europe-west3 region! (I didn’t even think about the region too much, I just copied it from some documentation.) I couldn’t figure out how to change the region afterwards, and the answer ChatGPT told me was so complex that I didn’t want to try yet. (Apparently you need to copy the image to a new project in another region and then delete the old one or something like that.)

Second Cut: Off By One Error in the API

For some reason after deployment Firefox and Chrome (but weirdly not Safari) kept giving JS errors about mixed content when I tried to use the UI which called the API. I spent at least a coule of hours trying to google and figure it out until I facepalmed when I re-read the FastAPI function for the umpteenth time:

@app.post("/api/myendpoint")
async def myendpoint(data: MyDataclass):

..it was missing a slash at the end. The frontend code was calling an URL with the slash, so the backend was doing a redirect before serving the actual code which ended up failing in production. Nothing to do with Cloud Run I guess, in the end.

First Impressions

As all cloud services under these huge umbrellas Cloud Run works great as soon as you figure out how to put the initial lego bricks in a correct order for the first time. After the initial deployment it took me no time to figure out how to enable full CI/CD with the GitHub repo, which was nice addition as well.

The only negative thing for me was the long build times for the image. With the default settings the build took around 20 minutes. With some help from the UI suggestion bubbles I was able to tweak that down to about 14 minutes by configuring a beefier machine for the build, but still, for a super simple app like this waiting 15 minutes for the deployment is a looong time. That said, the service also scales automatically to zero so at least the cost is nice!