Summary
Great features...
CloudRun, which is built on K-Native, a framework designed to run serverless containers on a managed Kubernetes cluster and allows users to deploy an application without worrying about the framework or the specs of the deployment.
There is no need to dive into Kubernetes’ hard to grasp features: load-balancing, autoscaling, permissions, etc. All this can be easily configured with a few clicks on the GCP console.
On paper, that sounds pretty great but if you choose to go the CloudRun way, beware of its limitations.
...With some caveats
No easy way to manage environment variables
To specify environment variables to a CloudRun deployment, you have to list them one by one in the command line, which is, to say the least, not ideal:
If you were to deploy your application on Kubernetes directly, you would have a configmap manifest (a template for a helm chart) committed to git. That would allow you to deploy a revision of your application with a single command that doesn’t change depending on how many environment variables you want to use in your deployment.
Secret Manager access has to be configured through Berglas
CloudRun doesn’t offer an out-of-the-box integration to secrets stored in Secret Manager (i.e., a GCP service whose purpose is to keep secret information like passwords).
The solution for a container to retrieve a password from Secret Manager is through Berglas and must be configured through the Dockerfile itself.
Here is an example of berglas used of the entrypoint of the docker container:
Berglas is a utility software designed for this very purpose; it works fine and is pretty simple to use. However I would still have appreciated a less hacky way of doing such a simple integration between GCP services.
Webserver only service
One important thing to have in mind when talking about CloudRun is that it is entirely designed to run webserver containers.
Containers aren’t even given CPU time when no request is being handled. It is an excellent feature as it lowers the bill without affecting performance: you only pay for handling requests and not for having an idle container.
Nonetheless, you have to make sure your web application lifecycle is built around the web request, and no shady operation needs to run behind the curtain. Those operations could be cronjobs or forked processes triggered after a web request, for example, because those processes won’t have CPU time given to run their task.
And some major deficiencies
One way or another, we have found solutions (sometimes very hacky ones) to all the issues mentioned above.
Nonetheless, there was one major problem we couldn’t solve: the lack of readiness and liveness probes.
If you’re not familiar with Kubernetes you might not recognize the term. Still, a readiness probe is nothing more than a monitor that will check whether your container is ready to handle the traffic. The liveness probe is pretty much the same, only once the application has already served traffic. Usually, in a web server that monitor will call on an HTTP route: if the code is within the 200 - 300 range, the system considers that the container is ready to serve.
Cloudrun, while is built on top of kubernetes, doesn’t allow users to specify their readiness and liveness probes and chooses to decide whether or not a service is ready to handle requests in a much simpler manner: it listens to a port and if the application binds to it, it is considered ready.
Even if it is not as useful as the readiness/liveness probes, this solution should be enough in most cases. That’s why we didn’t see it as a real problem.
However, we later discovered that the gunicorn/flask application we were trying to deploy in CloudRun binds to the port before being able to handle requests.
The consequences of that is Cloudrun directing traffic to a container not able to answer which leads to increase in request handling time and even to timeouts.
We discovered that issue while running performance tests with JMeter. Here is a graph plotting the Hits per Second our CloudRun deployed web server was able to handle.
As you can see, there are multiple clear drops of performance, and we’ve determined that those occurred exclusively on new container creations. As strange as it sounds, more containers meant less performance.
How did we solve that problem, you may ask.
Well, we didn’t.
At least not with CloudRun.
We decided that this new service’s cool features didn’t outweigh this huge performance issue and migrated the application to Kubernetes, a software we were more familiar with and which we knew worked.
CloudRun is still a relatively new service and, I’m sure GCP will address many of those issues shorly. That being said, I don’t think the service is ready for production use or at least not ready for migrating an application that wasn’t built specifically to run on this platform with serverless design patterns in place..
If you still think GCP’s CloudRun is right for you, learn how to deploy a NodeJS backend with Cloud Build and Cloud Run.