Dev Log - Deploying a NestJs App to Cloud Run Pt. 2
My Angular App for Social Sprinkler renders the HTML, but it's pretty useless for the purpose of being a landing page. Drawing interest from potential customers needs a call to action which collects an email address.
I've separated my Angular Universal Application and email services because as I move along in this project the email service requests may take longer to process I'm domaining out several other key services in which this email service will fall under my marketing domain.
Goals
1. Create a Docker Image for the Email Service
2. Stand up Service in Cloud Run
3. Configure a Custom Domain
Create a Docker Image for Email Service
The email API exposes an endpoint accepting an email address and a listId. The service runs on Node, but unlike my implementation with the Angular Universal App, the node modules need to be added to the image.
The time consuming process of creating the image involves installing all the modules. I shortened the amount of time by creating an image with the required dependencies. I plan to deploy the base image anytime the package.json changes.
Another Docker file for deployment of the application inherits the base Docker image created in the previous step. The Docker file then copies the built application to an /app
directory, uses the same build arguments as used in Part 1, and starts the service.
Up Email Service in Cloud Run
The steps for building and pushing the image created in the prior step do not differ from Deploying an Angular Universal Application to Cloud Run. I ran the commands below to build my-backend-image-name:0.0.1
.
Artifact Registry is listed below for reference for configuration and pushing the image.
The following commands tag and push the Docker Image to Artifact Registry.
Creating the Email API requires the secret to be made available to the container for Sendgrid.
Secret Manager
Navigating to the Secret Manager, I generated a text secret from the Sendgrid API key.
The secret doesn't require more than the name of the secret and it's value.
I returned to Cloud Run to create a new service. In the Container
tab, select Reference a Secret
. I want to expose this as an environment variable to the container. With everything configured, I clicked Create
.
In about a minute, the container spun up. I tested the default URL from Postman checking if a POST request accepted my email address for subscription to a list. YAHTZEE!
Configuring a Custom Domain
How many domains do you own that aren't in use? This time I actually have intentions of using SocialSprinkler.com. Until I deploy to production, I'm going to map my domain to a development subdomain. Once completed, certain routes will navigate to my services on the subdomain https://dev.socialsprinkler.com.
As I create separate services per my domain, the split of services respond to defined routes and what's exposed to the internet. Configuring routes to specific services in Cloud Run requires a load balancer.
I misconfigured domain mapping per my requirements the first time around. I mapped the custom domain to the front end using the ghs.google.com
site by clicking the Map Custom Domain
button. If you have separate services routed to different services, you'll need to remove this in DNS.
Configuring the Load Balancer
I navigated to the create load balancer choosing an Https Load Balancer.
Then I named the load balancer.
The load balancer configuration requires a certificate, an external IP address. It can't be seen in the screen shot, but I also enabled the Http -> Https redirect
.
I created a certificate example-certificate
and an IP Address ss-fe-ip
.
In the backend configuration, I mapped the two services to a Network Endpoint Group for Serverless.
I made sure to select the Backend Type
to Serverless network Endpoint Group
. You'll notice it calls out Cloud Run
, App Engine
, and others in the description. On selection, the New Backend section will list services available to map your service. I left the rest as defaults and clicked create.
Each service listens on a different route. The routes configuration determines which service handles the request based on a Host and a Path.
I saved the configuration for the load balancer. In the final step, I mapped my custom domain to the IP address earlier.
After a few minutes, the Email Service and Angular Universal Application were running, and I was able to add an email address to my Sendgrid email list.
Conclusion
I created a base.docker file that required a deploy of a specific Docker image for my Email Service to Artifact Registry.
The Email Service in Cloud Run consumed secrets specific to a third party service that required an Api Key to make requests. The Api Key is confidential, so I used Secret Manager to store the key. Configuring the Cloud Run Service for email, I mapped the secret to an environment variable.
Multiple services exposed to the internet with a custom domain for Cloud Run required a Load Balancer to route requests to different services.