Cloud Run custom domain mappings
- 3 minutes read - 510 wordsI have several Cloud Run services that I want to map to a domain.
During development, I create a Google Cloud Platform (GCP) project each day into which everything is deployed. This means that, every day, the Cloud Run services have newly non-inferable (to me) URLs. I thought this would be tedious to manage because:
- My DNS service isn’t programmable (I know!)
- Cloud Run services have non-inferable (by me) URLs
i.e. I thought I’d have to manually update the DNS entries each day.
Then I tried Mapping custom domains and I realized that this is actually trivial because Google (currently) requires a CNAME
record with ghs.googlehosted.com.
as its data. ghs.googlehosted.com
is a Google service that resolves custom(er) domains to customer resources hosted by Google. The service is not only used by Cloud Run but App Engine, Google Workspace etc. etc.
Effectively, this means that I can litter my DNS records with CNAME
entries with data ghs.googlehosted.com.
and these become available as soon as, in my case, a Cloud Run service custom domain mapping is created that references the host.
Then, each day, I simply create and delete domain mappings against the Cloud Run services as they’re deployed to the day’s project:
Assuming I own example.com
, I could have DNS records that are set once and left:
Name | Type | Data |
---|---|---|
foo |
CNAME |
ghs.googlehosted.com. |
bar |
CNAME |
ghs.googlehosted.com. |
Then, each day:
gcloud beta run services replace ${CONFIG} \
--platform=managed \
--region=${REGION} \
--project=${PROJECT}
DOMAIN="foo.example.com"
gcloud beta run domain-mappings create \
--service=${SERVICE} \
--domain=${DOMAIN} \
--region=${REGION} \
--project=${PROJECT}
The process takes some time because Cloud Run needs to confirm that the DNS mapping resolves, i.e. DOMAIN
resolves correctly to the Cloud Run service’s URL, and it needs to provision a TLS x509 certificate for DOMAIN
.
Cloud Run is built atop Knative (and Kubernetes) and uses a Kubernetes vernacular of status
and status.conditions
and – I think – gcloud
is parsing these conditions to display it’s active status:
gcloud beta run domain-mappings list
✔ foo.example.com foo us-west1
Because the underlying config is:
gcloud beta run domain-mappings describe \
--domain=${DOMAIN} \
--region=${REGION} \
--project={PROJECT} \
--format="yaml(status)"
Yields:
status:
conditions:
- lastTransitionTime: '2022-05-06T00:00:00.000000Z'
status: 'True'
type: Ready
- lastTransitionTime: '2022-05-06T00:00:00.000000Z'
status: 'True'
type: CertificateProvisioned
- lastTransitionTime: '2022-05-06T00:00:00.000000Z'
status: 'True'
type: DomainRoutable
mappedRouteName: foo
observedGeneration: 1
resourceRecords:
- name: foo
rrdata: ghs.googlehosted.com.
type: CNAME
I assume type: Ready
gives the green ✔
but type: CertificateProvisioned
is an important precursor:
gcloud beta run domain-mappings describe \
--domain=${DOMAIN} \
--region=${REGION} \
--project={PROJECT} \
--format="yaml(status.conditions.filter(\"type:CertificateProvisioned\"))"
Yields:
status:
conditions:
- lastTransitionTime: '2022-05-06T16:00:14.694301Z'
status: 'True'
type: CertificateProvisioned
And:
openssl s_client -showcerts -connect ${DOMAIN}:443 2> /dev/null \
| openssl x509 -noout -subject -dates -issuer
Yields:
subject=CN = foo.example.com
notBefore=May 6 00:00:00 2022 GMT
notAfter=Aug 4 00:00:00 2022 GMT
issuer=C = US, O = Google Trust Services LLC, CN = GTS CA 1D4
NOTE A 90-day certificate
And, of course, it’s now possible to e.g. securely (TLS) browse DOMAIN
.
For completeness, I’m deleting the domain mappings when I delete projects:
gcloud beta run domain-mappings delete \
--domain=${DOMAIN} \
--project=${PROJECT} \
--region=${REGION} \
--quiet
That’s all!