This site is deployed to Google Cloud Run using an nginx docker image with overlaid content and configuration.
Cloud Run was adopted since it is a technology I use for some professional projects and was therefore able to reuse some experience.
Additionally nginx is a technology which fits in to some of my other goals, and while alternatives such as publishing static files only may seem simpler they are also less transparent and more limiting in terms of some basic functionality such as content negotiation which can facilitate logical URLs which are more easily kept cool.
The build requires docker CLI (or compatible) to produce the images, and the gcloud CLI to autenticate with the artifact registry and trigger updates.
Those dependencies are defined here for possible overriding or extensibility such as more sophisticated feedback.
DOCKER := docker GCLOUD := gcloud
Generated files will be placed in a conventional "build" directory to keep things tidier and cleanup more simple.
BUILD_DIR := build $(BUILD_DIR): ; mkdir -p $(@)
Define the logical name of the service for use elsewhere.
SERVICE_NAME := mweb
The tag for the image will be generated based on the current git ref. As a side effect changes should be committed prior to building.
A pattern I often use elsewhere is to add an additional qualifier to the tag if the working directory contains any uncommitted changes which guards against inadvertently pushing an image which has local changes that deviate from what is reflected in the repository (and therefore having a misleading tag). Given the sole authorship of the content and the nature of the project itself (i.e. that it is primarily content rather than logic), I am dispensing with that additional logic for the time being.
IMAGE_TAG := $(call shell,git rev-parse --verify --short HEAD)
Define the image name based on the SERVICE_NAME and derived tag.
IMAGE = $(SERVICE_NAME):$(IMAGE_TAG)
Build the image as necessary. This makes use of the iid file to act as a marker. The prerequisites at this point are fairly loose and likely to be refined over time.
$(BUILD_DIR)/%.iid: Dockerfile $(wildcard *) | $(BUILD_DIR) $(DOCKER) build --tag $(*) --iidfile $(@) .
Provide a phony alias for building the image.
This makes use of a pattern which will defined elsewhere which invokes a submake for the sake of a dynamic precondition without requiring secondary expansion.
image: ; $(MAKE) $(BUILD_DIR)/$(IMAGE).iid site .PHONY: image
Deployment relies upon pushing the image to a repository. This was done manually rather than making use of a tool such as infrastructure-as-code, where a repository was created within Artifact Registry (which must be enabled) using the Google Cloud Console. The Google Container Registry is an older, deprecated alternative but the one I tend to stumble across first and then notice the deprecation warning.
I have also enabled immutable tags to enforce clearer traceabililty of image contents.
The region is selected as one that is geographically close to where I am located and also one of those indicated as low emission. This in turn is used to compose the host for the registry.
REGION := us-central1 PUSH_REGISTRY := $(REGION)-docker.pkg.dev GCP_PROJECT := mweb-411216 GCP_SERVICE := mweb GCP_COMPONENT := www
Through the console the service account was created, and my primary user was granted the ability to impersonate that service account. The principal of the service account was then granted some needed permissions. This can be non-obvious in that the principal of the service account is managed like others rather than being directly navigable from the service account page.
PUSH_REPO_PATH := $(PUSH_REGISTRY)/$(GCP_PROJECT)/$(GCP_COMPONENT) PUSH_SA := mweb-569@mweb-411216.iam.gserviceaccount.com GCP_AUTH := --impersonate-service-account=$(PUSH_SA) logged-in: ; #$(GCLOUD) auth login $(BUILD_DIR)/pushed-%: $(BUILD_DIR)/%.iid | $(BUILD_DIR) $(DOCKER) tag $(*) $(PUSH_REPO_PATH)/$(*) $(GCLOUD) auth print-access-token $(GCP_AUTH) \ | $(DOCKER) login -u oauth2accesstoken --password-stdin $(PUSH_REGISTRY) $(DOCKER) push $(PUSH_REPO_PATH)/$(*) > $(@) push-image: ; $(MAKE) $(BUILD_DIR)/pushed-$(IMAGE) .PHONY: push-image Configure custom domain Using beta cloud run domain mappings tucked away in the console a bit. Added Cloud Run Admin $(BUILD_DIR)/deployed-%: $(BUILD_DIR)/pushed-% $(GCLOUD) $(GCP_AUTH) --project=$(GCP_PROJECT) \ run deploy $(GCP_SERVICE) \ --image=$(PUSH_REPO_PATH)/$(*) --region=$(REGION) deploy: ; $(MAKE) $(BUILD_DIR)/deployed-$(IMAGE) .PHONY: deploy