Continuous Deployment¶
This page documents how we use GitLab CI to deploy new versions of our applications automatically.
Overview¶
Products generated from the Google Cloud deployment
boilerplate are deployed
entirely by terraform. Everything about a deployment, including which version of each container
image is running, is described in the infrastructure project. Deploying a change means merging a
Merge Request into that project.
GitLab CI never changes Cloud Run services directly. Do not deploy by running pipelines with
variables set by hand. Older deployments did this using the DEPLOY_ENABLED and
WEBAPP_DOCKER_IMAGE variables, and that is no longer supported.
The terraform pipeline¶
The infrastructure project includes the
terraform-pipeline
CI template, which adds one stage per workspace: development, staging and production. Each
stage has a terraform-plan-{workspace} job and a terraform-apply-{workspace} job.
The apply job applies the plan produced by the plan job in the same pipeline, so what is applied is exactly what was shown in the plan.
| Pipeline | Plan jobs | development apply |
staging apply |
production apply |
|---|---|---|---|---|
| Merge Request | Run for every workspace | Manual | Not created | Not created |
| Default branch or tag | Run for every workspace | Manual | Automatic | Manual, after staging |
This means that:
- Reviewers can see the effect of a Merge Request on every environment by reading the output of the plan jobs.
- An unmerged change can be tried out in
developmentby triggering its apply job from the Merge Request pipeline. stagingalways matches the default branch.- Production is only changed when someone triggers the apply job by hand, and only once
staginghas been deployed in the same pipeline.
The template also includes linting and security jobs, such as trivy. See Secure Continuous
Deployment for why these are required.
Disabling jobs¶
Setting any of the following CI variables disables the corresponding job:
TERRAFORM_PLAN_DEVELOPMENT_DISABLEDTERRAFORM_APPLY_DEVELOPMENT_DISABLEDTERRAFORM_PLAN_STAGING_DISABLEDTERRAFORM_APPLY_STAGING_DISABLEDTERRAFORM_PLAN_PRODUCTION_DISABLEDTERRAFORM_APPLY_PRODUCTION_DISABLED
Additional workspaces¶
Products with workspaces beyond development, staging and production can copy an existing
pair of terraform-plan-... and terraform-apply-... jobs into their .gitlab-ci.yml and change
the workspace name.
Which container image is deployed¶
The container images for each workspace are set in the container_images local in locals.tf:
locals {
container_images = merge(
local.default_container_images,
lookup({
production = {
webapp_tag = "1.2.3"
}
staging = {
webapp_tag = "1.2.3"
}
}, terraform.workspace, {})
)
default_container_images = {
webapp_branch = ""
webapp_tag = "latest"
}
}
Workspaces without an explicit entry fall back to default_container_images, which deploys the
image tagged latest. Pin staging and production to a released version so that every
deployment is a reviewed change to the infrastructure project.
Releasing a new version¶
With release automation enabled in
the webapp project and renovatebot configured to track
releases in the infrastructure
project, a release flows through to production as follows:
- Merging the release Merge Request in the
webappproject creates a tag. CI builds a container image tagged with the version number and pushes it to the product's Artifact Registry. - Renovatebot opens a Merge Request in the
infrastructureproject updatingwebapp_tag. - The plan jobs in that Merge Request show the change to each environment.
- Merging the Merge Request deploys the new version to
staging. - Triggering the
terraform-apply-productionjob deploys it to production.