Google Cloud Platform offers another service to operate containers called Cloud Run. It brings the idea of serverless computing to your containers.
Instead of being limited to the available support for languages, libraries and code restrictions in Cloud Functions you can create and run your own container in a similar, serverless fashion. Compared to Kubernetes Engine, Cloud Run abstracts the overhead of administration and enables you to deploy your application almost instantly.
Cloud Run is a managed compute platform that automatically scales your stateless containers. Cloud Run is serverless: it abstracts away all infrastructure management, so you can focus on what matters most — building great applications.
Find out more information about Cloud Run.
Taking the React app to Cloud Run
Similar to the deployment to Google Kubernetes Engine (ALC 4.0 Cloud Challenge I) you would want to deploy the existing React app to Cloud Run.
The following command allows you to use any of the available containers in Container Registry (or Docker) and launch it on Cloud Run.
> gcloud run deploy --image gcr.io/alc-4-program/alc4cloud:cloudbuild
If you do not specify a service name, platform, region, or access mode the command is going to prompt you for the missing information.
Here is the complete command with all mandatory switches for easier handling.
> gcloud run deploy alc4cloud \ --image gcr.io/alc-4-program/alc4cloud:cloudbuild \ --platform=managed \ --region=us-central1 \ --allow-unauthenticated
After a short while the command output is most probably going to respond with the following information.
... Deployment failed ERROR: (gcloud.run.deploy) Cloud Run error: Container failed to start. Failed to start and then listen on the port defined by the PORT environment variable. Logs for this revision might contain more information.
Oh, what happened here?
Your container is in perfectly good shape but still Cloud Run rejects the deployment due to the inability to listen on the port defined by the PORT environment variable.
RTFM - Read The Fine Manual
According to the documentation on Building Containers for Cloud Run container images are accepted as long as they respect the container contract.
In particular, your code must listen for HTTP requests on the port defined by the
Okay, then let's have a look.
To learn more about the contract your containers must respect to be deployed to Cloud Run, see Container Contract.
In Cloud Run container instances, the
PORTenvironment variable is always set to
8080, but for portability reasons, your code should not hardcode this value.
How is this related to your container running the React app?
Following the article Working with Docker (ALC 4.0 Cloud Challenge I) you wrote a
Dockerfile to build containers using nginx webserver. Using the default configuration and listening on port 80.
That's a violation to the container contract as per specification and you have to change the implementation of the Docker image.
Modifying the container
In order to respect the container contract for Cloud Run you need to amend the existing
Dockerfile slightly. There are two changes needed.
First, you have to change the
Listen directive in the default configuration of nginx. An elegant approach would be to use
sed - the stream editor for filtering and transforming text - in the Linux system used to run the React app.
Add the following
RUN statement to the production-stage in the
RUN sed -i 's/80/8080/g' /etc/nginx/conf.d/default.conf
Next, you have to change the exposed port of the container to grant access to it from external sources. Change the
EXPOSE line like so.
Dockerfile should have the following content.
# build stage FROM node:lts-alpine as build-stage RUN npm i -g react-scripts WORKDIR /app COPY package*.json yarn.lock ./ RUN npm ci COPY . ./ RUN npm run build # production stage FROM nginx:stable-alpine as production-stage RUN apk add --no-cache bash COPY --from=build-stage /app/build /usr/share/nginx/html COPY env.sh /usr/share/nginx/html COPY .env /usr/share/nginx/html RUN chmod +x /usr/share/nginx/html/env.sh WORKDIR /usr/share/nginx/html # Cloud Run requires port 8080. Let's change nginx... RUN sed -i 's/80/8080/g' /etc/nginx/conf.d/default.conf EXPOSE 8080 CMD ["/bin/bash", "-c", "/usr/share/nginx/html/env.sh && nginx -g \"daemon off;\""]
Now, your local source is ready for Cloud Run.
Submit and Run
Either you would go forward to create the new container on your local system according to the article Working with Docker or you might like to send the sources as described in Working with Cloud Build.
Being smart you submit to Cloud Build using this command.
> gcloud builds submit -t gcr.io/alc-4-program/alc4cloud:cloudrun .
Although it is the same React app you should give the container image a new tag, here
cloudrun to differentiate it a little bit from the previously created images.
Note: I put a file called
Dockerfile.Cloudrun into my repository on GitHub for reference. Unfortunately, you cannot specify a different file when submitting to Cloud Build. You might have to rearrange the files if you want to use it.
After successful submission to Cloud Build your Container Registry might look a little like the following.
Reflecting all tagged images built so far.
With the containerized image available in the registry it's now time to deploy to Cloud Run using the
gcloud run command.
> gcloud run deploy alc4cloud \ --image gcr.io/alc-4-program/alc4cloud:cloudrun \ --platform=managed \ --region=us-central1 \ --allow-unauthenticated \ --set-env-vars=SITE_URL="https://jochen.kirstaetter.name",SITE_STATUS="Cloud Run"
Note: Amend the tag value according to the image you built. Setting the environment variables might not be necessary depending on your implementation.
The deployment is going to take a few minutes. Maybe you like to stretch your legs, grab a glass of water, and have a cereal bar to re-energize yourself while waiting.
Cloud Run will respond with a simple
Done. and provides you with the URL to run your stateless container on the web.
Awesome, well done!
You have managed to adjust the Docker image to listen to port 8080 and deployed the container to Cloud Run.
Same steps in Cloud Console
In case that you feel more comfortable in Cloud Console than typing commands in the terminal, you can navigate to Cloud Run in the sidebar menu. There you click on
Create Service to set up a new service operating on Cloud Run.
Similar to the
gcloud command you specify the container image, the preferred platform to deploy inclusive a location, give the service a name and choose the authentication mode.
Then you hit the
Create button and wait until the service has been created.
Shortly after the endpoint will be accessible via exposed URL and the service metrics are shown in Cloud Console.
The choice is yours. Remember that the container image has to respect the container contract and listen to port 8080. Otherwise, deployment is going to fail.
Pricing of Cloud Run
At the time of writing the following price structure applies to Cloud Run.
A small project like the React app could be an interesting candidate to deploy and run on Cloud Run. Depending on the number of visitors and hence invocations per month it might be very affordable, too.
Cloud Run has HTTPS built-in
Each Cloud Run service gets an out-of-the-box stable HTTPS endpoint, with TLS termination handled for you.
Using your own domain
You might have seen that the auto-generated URL Cloud Run allocates to your service isn't the most user-friendly. Conveniently, Cloud Run offers custom domains out-of-the-box.
On the Cloud Run service overview click on the triple dot button and select
Manage Custom Domains to map your own domain for your service.
Next, click on
Add Mapping in case that you are not greeted by the Add mapping dialog already.
In case that your domain is not listed as a verified domain you are offered to do it on the spot.
Wait until the verification process has been completed successfully and then specify the domain to use.
Cloud Run is able to map your service to the base domain as well as to a subdomain record.
Finally, Cloud Run gives you details on how to update the DNS records of your custom domain. This step completes the mapping to your service.
It takes about five to six minutes until the domain mapping is done. However, give it some more time until the Cloud Run service responds to your custom domain as expected. See for yourself here: https://alc4cloud.kirstaetter.name/
And you're done, again.
Check out the documentation on Mapping custom domains for more details and background information on this matter.
Enable Cloud Run API
The Cloud Run API is not enabled by default in your GCP project. The
gcloud command will prompt you accordingly and allows you to activate it.
Confirm the question with
Y and wait as instructed to retry the submission.
Of course, you could also navigate around in APIs & Service > Library in the Cloud Console. Enable both Cloud Build and Cloud Run APIs.
Using Cloud Run as a substitute for Kubernetes Engine has its charm. It offers a faster path to develop, deploy and operate with less administrative overhead. Additionally, Cloud Run provides a richer environment for your applications based on your custom-made stateless container compared to the pre-defined language runtime provided by Cloud Functions.
Apart from adjusting the container to listen to the default port 8080 as per container contract and optionally mapping a custom domain it is an almost effortless workflow to deploy a container image to Cloud Run and make it publicly accessible.
What do you think? Do you have use-cases where Cloud Run is the right choice for you? Leave a comment below and start the conversation with me and other readers.Image credit: Fitsum Admasu