Introducing the Dockerfile: The Most Under-documented Google Cloud Feature

Google Cloud is great for its versatility. As with other big cloud platforms, though, flexibility means that seemingly simple tasks can get complicated in a hurry. Venturing outside the Quickstart documentation means you’re likely to encounter additional services and capabilities that you may not want or need right away. I thought I would share a specific experience deploying a recent prototype in the hopes that it saves others time and frustration.

Cloud Run: Perfect for Prototypes and Beyond

Google Cloud Run is a great platform for both prototyping and scaling cloud applications. Google abstracts almost all of the complexities of hosting while providing enough flexibility to accommodate a variety of use cases. When testing and sharing new applications, it’s great because you can spin up a new app with just a couple shell commands. Eventually, your app can scale right where you deployed your prototype–just a few commands enable auto-scaling and a wide variety of other services to keep up with demand.

There are two features that make Cloud Run great for prototypes: deploying from source code and Google Cloud buildpacks. Deploying from source code means that when you’re ready to bring your app to the cloud, you can simply execute a simple CLI command to upload, build, and deploy within minutes right within your source code directory. Google Cloud’s buildpacks enable this capability by providing a build and runtime environment for most of the popular languages and frameworks. Google will figure out from your source code which buildpack to use, so you don’t even have to think about it!

The aforementioned Quickstart gives a pretty good overview of how to do this. However, in case you landed here and are looking for a quick solution, here’s what you need to initialize and deploy from source code for a standard language/framework combo.

I will assume you have downloaded and installed the Google CLI and logged in using:

gcloud init

I would suggest reviewing the language-specific requirements for the buildpacks. Depending on the buildpack, you may need to make some simple tweaks before deploying the first time. From there, it’s as simple as:

gcloud run deploy SERVICE --source .

That’s it! Wait a few minutes, and you’ll get a URL for your freshly-deployed service. Well done!

When Things Go Awry: Enter the Dockerfile

For most people using common languages and frameworks, that’s probably all you’ll need until you can hire that fancy DevOps engineer. However, if you encounter a problem on deployment related to some requirement that isn’t satisfied by the buildpack, don’t fret. Google provides a helpful (if under-documented) feature: the Dockerfile.

You may already be very familiar with Docker, containers, and the like. Essentially, containers allow you to nearly completely customize the runtime environment for your application and host it on a variety of platforms. You can even test the container locally before pushing to the cloud and expect that things should work exactly the same in both locations.

While containers are great, you can imagine the layers of complexity required to build, maintain, and deploy them at commercial scale. That’s why Google has all sorts of other products relating to building and managing containers that are perfect for just about any organization’s needs.

Anyway, on the Cloud Run documentation, Google mentions that if their buildpacks can’t address your use case, you can deploy any container to Cloud Run. So you can build a container locally or at Google, store it in a registry, then deploy from the registry to Cloud Run.

However, when you’re just getting started, these may be a few layers of complexity you just don’t need. This is the situation I found myself in–I needed to rapidly iterate over a small application in the cloud but I didn’t want to mess with all the containers and related services.

Google mentions the Dockerfile in the Cloud Run documentation as a requirement for Cloud Build or for building locally. These are the steps I was trying to avoid in my prototype. However, in my experience, simply including a Dockerfile and deploying from source as mentioned above is sufficient to deploy your app with custom build or runtime requirements to Cloud Run (as long as those requirements can be satisfied with a Dockerfile).

In my case, I needed to add “git” to the runtime environment as an external dependency–the standard Cloud Run environment doesn’t include this. I found a sample Dockerfile for a Python application through Google, added a command to install “git”, and saved it to the root of my project:

FROM python:3.10-slim

ENV PYTHONUNBUFFERED True

ENV APP_HOME /app

ENV PORT 5000

WORKDIR $APP_HOME

COPY . ./

RUN apt-get update -y && apt-get install -y \
  git \
  && apt-get clean

RUN pip install --no-cache-dir -r requirements.txt

CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 app:app

From there, I executed the same Cloud Run deployment command:

gcloud run deploy

Much to my amazement, I could see in the logs that the Google Cloud Build service had picked up my Dockerfile and run the commands to install “git”. Most importantly, the application now worked!

Someday I will likely want to have the flexibility of versioned containers, multi-cloud deployments, etc., but for getting started nothing beats simplicity. I hope this will help someone else save a few hours.