In April 2019, at Google Next event, Google announced a new serverless product: Cloud Run. I’m a Cloud Run alpha tester and I experimented this awesome product on many use cases. I was also honored to be on stage in San Francisco and in Paris (for Google Cloud Summit) to present my experiences on this product.
Cloud Run comes in addition of other Google Serverless products, especially Cloud Function. Even if each product has a typical use case and recommendations, some use cases can be implemented in several ways with several products. So, What I use in my current developments? And Why do I make this choice?
Before going further, here’s a quick overview of each product
Cloud Functions are serverless, single purpose and event driven solution. They can be also triggered on HTTP request. Each request/event are processed by a new instance of the function and provide a strong isolation between each invocation.
Today in Generally Available (GA). Several langages are handled like NodeJS8, Python3.7 and Go 1.11; NodeJS10 in Beta ; In Alpha, Go 1.12 and Java
Only the code and the dependencies list are provided, the platform does all the rest: package, deploy and run the code.
Typical Use case
Single purpose workload, especially event driven (PubSub, Storage, Firestore/Firebase, …) but also HTTP triggered. Possible languages are limited to NodeJS 8/10, Go 1.11/1.12 and Python3.7 with only dependencies publicly reachable (not true with Java). Image transformation, micro-batching (duration less than 9 minutes), streaming processing,… are common use cases
Cloud Run is build on top of Knative, that allows to serve stateless containers in a serverless way and it’s natively portable. Only stateless containers with HTTP endpoints are required to be run. There is no limitation in langages, binaries and dependencies.
By this way, the container has to integrate a webserver and can handle several requests in the same time and thus, can be multi-purpose.
Container packaging has to be done before deployment. Cloud Build can be used for this, or locally with Docker or other tools (like Kaniko or Jib)
Typical Use case
All workloads working in stateless mode, with a processing time less than 15 minutes and can be triggered by one or several HTTP end-points, like CRUD REST API. Langages, libraries and binaries don’t matter, only a container is required. Website backend, micro-batching, ML inference,… are possible use cases
However, when I want to implement, in Python, Go or NodeJS, a workload triggered by an HTTP request, like a batch processing triggering by a Cloud Scheduler or a single purpose HTTP webhook, What I should use ?
It’s possible to use a Cloud Function to address this problem. Single purpose, http triggered and language in GA. But it’s also possible to build a HTTP container and to deploy it with Cloud Run.
When I have to cope with this question, I always use Cloud Run. Why ? Indeed, this choice could seem overkill for a single purpose usage. However, I have 2 main reasons
When you develop for Cloud Run, you have to build a container. This container have the ability to be deployed anywhere: on premise, on Kubernetes/GKE, on Cloud Run (serverless and GKE), on a VM,… anywhere.
On the other side, code deployed on Cloud Function implicitly assumes that there is a webserver, somewhere, able to route the request to the code. For changing the execution environment, you have to repackage the code, maybe in a container, but at least with a webserver in front of it.
Cloud Run has here this strong advantage to reduce the rework and the refactoring when you want to change the environment. And, you are less locked in to Google Cloud.
In mission critical development, the testability and the reliability are paramount. I’m not talking about unit test here, but about end-to-end tests also called “integration” tests. In a production environment in real time IoT application, we can’t be approximative or accept to lose messages because of a bad version.
With Cloud Function, the code handles the request forwarded by the webserver of the execution environment.
Thereby, for testing your function you have to simulate the webserver request and to fill correctly the request object passed in parameter of the code. Not sure that is the best way, but, even in the GCP doc, there is no example in Python; I did what could with my (low) level in Python -> I’m open to suggestions!
Here is the problem: How can you be sure that the simulated object is correct ? Do you know all the headers passed in this object ? How arebasic authentication, Bearer or API keys set ? Do you know the version of the request object used in Cloud Function environment ?
Personally, I’m not able to answer these questions. And the new developer that joins your team, is he able ? Moreover, I’m not confident in my test code, and it’s a problem not to be confident in tests !
My solution for solving this issue is to create a test file, with a webserver that imports the function code.
To launch a test, simply calls the webserver with CURL command. This way, the webserver will format correctly your request object, with all the rules and the parameters, and forward it to your code. The additional advantage is that you can change your webserver, and even your dev language, without changing your CURL test requests.
And, it’s at this moment that Cloud Run make all its sense. In this context, Cloud Run is no more than Cloud Function code + simple webserver that forwards requests. Simply add a standard Dockerfile for your language. And it’s over - you don’t have a Cloud Function with a test server, but a packaged container ready to be deployed on Cloud Run.
Cloud Run everytime!
So, Cloud Run is the good mix of simplicity of Cloud Function, end-to-end testability and portability. Many pain-points are solved, many advantages are gained, that’s why I use and I recommend Cloud Run for all your stateless HTTP container, even for events like PubSub (create a push subscription for calling your Cloud Run) or Storage (create a PubSub notification on GCS). For other events, Cloud Functions are, until now, unavoidable.
In addition, you can consider several other advantages of Cloud Run like the capacity to handle several entry-points in only one deployment. Or also, the capability to run a workload up to 15 minutes (only 9 minutes with Cloud Function). But also, the capability to be charged only once and to be impacted by the cold-start once when several requests are handled concurrently (up to 80), but with the trade-off of a loss of isolation/memory sharing between all requests (also possible with Cloud Run if you set the concurrency param to 1).
Finally, for me, Cloud Run is the best solution for a majority of use cases. The development team is very active and new features are constantly added: Metrics, Cloud SQL connectivity, Service Account Identity,… I strongly recommend you to stay tuned of this product, it will become essential for all purposes!