TL;DR;
Using the Google Cloud SDK, we will run a container that will host the website https://www.cursodegit.com in Cloud Run. The container will use nginx to serve a static site generated using webpack. We will also configure a custom domain mapping and create the appropriate records in our DNS so we can access the website using our domain name.
Motivation
At the begining of 2020, about one month before the first and biggest lockdown due to COVID-19 began in Spain, I started to use G-Suite (called Google Workspaces now) for my domain www.cursodegit.com. I migrated the email to GMail and I found myself with a 300$ credit to try Google Cloud.
With 300 bucks to spend, I decided to host the website there. I did the following:
- Created a bucket holding the static site generated by Webpack
- Spin up a load balancer that used that bucket as a backend service
The cost of this setup, with my current traffic (which is embarrasingly low) is about 16€ / month. I found that amount of money a little too much for such a low traffic site… but I was OK with it, since I was using my credit. Unfortunately, I run out of credit last April and I decided to look for an alternative.
Since Cloud Run only charges you for the CPU you use, I have the feeling that using it instead of my current set up with the Load Balancer is going to cut the costs a lot. So, let’s try it!
Deploy the container
In a previous post, I explained how we uploaded the image contaning the website to a private bucket in the Google Container Registry. We will deploy this image in Cloud Run.
As we did in that post, we will run the gcloud
command inside a docker container.
You will see some long commands for this reason (you can read
here why I’m doing this)
> docker run --rm --volumes-from gcloud-config \
gcr.io/google.com/cloudsdktool/cloud-sdk \
gcloud run deploy web-cursodegit-com \
--image eu.gcr.io/web-cursodegit-com/web \
--region europe-west1 \
--project web-cursodegit-com \
--port=80 \
--allow-unauthenticated
Deploying container to Cloud Run service [web-cursodegit-com] in project [web-cursodegit-com] region [europe-west1]
Deploying new service...
Setting IAM Policy...............done
Creating Revision.................................done
Routing traffic.....done
Done.
Service [web-cursodegit-com] revision [web-cursodegit-com-00001-paj] has been deployed and is serving 100 percent of traffic.
Service URL: https://web-cursodegit-com-b2t44kuhpa-ew.a.run.app
We chose europe-west1
because europe-west2
, europe-west3
and europe-west6
were not available to use custom domain mappings at the time I did this.
See here.
Custom domain mapping
After the container is in place, we can see the website using the following URL: https://web-cursodegit-com-b2t44kuhpa-ew.a.run.app. We will configure a custom domain mapping so we can access it using the url https://www.cursodegit.com.
The first step is to verify the domain, so let’s do it:
> docker run --rm --volumes-from gcloud-config \
gcr.io/google.com/cloudsdktool/cloud-sdk \
gcloud domains verify www.cursodegit.com
Opening [https://www.google.com/webmasters/verification/verification?authuser=0&domain=www.cursodegit.com&pli=1]
in a new tab in your default browser.
A new browser window should pop-up with that URL. If not, we will copy and paste the URL and open it in our favorite
browser. We will select Other
as the domain name provider, since we are using CloudFlare as our DNS server.
This will display the instructions we need to follow:
We will create the requested record in CloudFlare, wait for a few seconds until we can verify with the dig command that the records are in place, and click on “Verify”. We should see a message like this one when the verification succeeds::
Once the domain is verified, we can add a custom domain mapping:
> docker run --rm --volumes-from gcloud-config \
gcr.io/google.com/cloudsdktool/cloud-sdk \
gcloud beta run domain-mappings create \
--service web-cursodegit-com \
--domain www.cursodegit.com \
--region europe-west1 \
--project web-cursodegit-com
Creating......
............................done.
Waiting for certificate provisioning. You must configure your DNS records for certificate issuance to begin.
NAME RECORD TYPE CONTENTS
web-cursodegit-com A 216.239.32.21
web-cursodegit-com A 216.239.34.21
web-cursodegit-com A 216.239.36.21
web-cursodegit-com A 216.239.38.21
web-cursodegit-com AAAA 2001:4860:4802:32::15
web-cursodegit-com AAAA 2001:4860:4802:34::15
web-cursodegit-com AAAA 2001:4860:4802:36::15
web-cursodegit-com AAAA 2001:4860:4802:38::15
Now, we go to CloudFlare and add those records. Once the records were created, we needed to wait for about 5 minutes before the certificate was correctly issued by google:
> docker run --rm --volumes-from gcloud-config \
gcr.io/google.com/cloudsdktool/cloud-sdk \
gcloud beta run domain-mappings list \
--region europe-west1 \
--project web-cursodegit-com
DOMAIN SERVICE REGION
✔ www.cursodegit.com web-cursodegit-com europe-west1
We can check that the domain is in place by issuing this command:
> docker run --rm --volumes-from gcloud-config \
gcr.io/google.com/cloudsdktool/cloud-sdk \
gcloud beta run domain-mappings describe \
--domain=www.cursodegit.com \
--region europe-west1 \
--project web-cursodegit-com
apiVersion: domains.cloudrun.com/v1
kind: DomainMapping
metadata:
annotations:
serving.knative.dev/creator: [email protected]
serving.knative.dev/lastModifier: [email protected]
creationTimestamp: '2021-06-19T07:22:26.866083Z'
generation: 1
labels:
cloud.googleapis.com/location: europe-west1
run.googleapis.com/overrideAt: '2021-06-19T07:22:29.683Z'
name: www.cursodegit.com
namespace: '499154617149'
resourceVersion: AAXFGX4UMkg
selfLink: /apis/domains.cloudrun.com/v1/namespaces/499154617149/domainmappings/www.cursodegit.com
uid: 1496316d-1610-4a1c-ba6e-d80a2e4362dc
spec:
routeName: web-cursodegit-com
status:
conditions:
- lastTransitionTime: '2021-06-19T07:36:03.660360Z'
status: 'True'
type: Ready
- lastTransitionTime: '2021-06-19T07:36:03.660360Z'
status: 'True'
type: CertificateProvisioned
- lastTransitionTime: '2021-06-19T07:22:29.952459Z'
status: 'True'
type: DomainRoutable
mappedRouteName: web-cursodegit-com
observedGeneration: 1
resourceRecords:
- rrdata: 216.239.32.21
type: A
- rrdata: 216.239.34.21
type: A
- rrdata: 216.239.36.21
type: A
- rrdata: 216.239.38.21
type: A
- rrdata: 2001:4860:4802:32::15
type: AAAA
- rrdata: 2001:4860:4802:34::15
type: AAAA
- rrdata: 2001:4860:4802:36::15
type: AAAA
- rrdata: 2001:4860:4802:38::15
type: AAAA
Look for status.conditions
in the JSON output. When the certificate is issued, we should see:
...
status:
conditions:
- lastTransitionTime: '2021-06-19T07:36:03.660360Z'
status: 'True'
type: Ready
...
Once issued, we can changed the records in CloudFlare to be proxied:
After a few seconds, we should be able to access the website https://www.cursodegit.com
, but this time being
served by Cloud Run instead of our load balancer. If we have a look at the metrics in the Console, we should start
seeing some activity:
Now, let’s wait for a few days and see what is the cost of serving the website using Cloud Run compared to the current setup.
Remove the service
In case we need to remove the service, we can issue the following command:
> docker run --rm --volumes-from gcloud-config \
gcr.io/google.com/cloudsdktool/cloud-sdk
gcloud run services delete web-cursodegit-com \
--region europe-west1 \
--project web-cursodegit-com \
--quiet