Environments
This best practice details the required deployment environments for a product.
The term Environment and Stage are used interchangeably within CRUK. The term environment has been used for this document to avoid confusion with the Staging environment used by some teams. An environment described here is a collection of hardware and software tools used to run an instance of the product.
To understand how to deploy and release environments see the Release Process.
Description
Environments
The minimum required environments for a product are PR and Production. Below is a summary table with extra information provided below on the use case for each environment.
Summary
Environment | Short Name | Required | Static | Branch | Integrates with |
---|---|---|---|---|---|
PR | pr | ✔️ | ❌ | feature branches | Integration |
Production | prod | ✔️ | ✔️ | main/master | Production |
Integration | int | ❌ | ✔️ | main/master | Integration |
PR
PR environments are ephemeral. They are created automatically for every PR to the product repository and destroyed automatically on merge/cancellation. This includes all infrastructure and logic for a product. For products that have a front-end and back-end this includes both.
This environment is used to test the proposed PR changes in full before it is merged into the main branch.
Data storages, such as RDS and DynamoDB, are also created and destroyed with the PR. No static storage exists for PR environments.
The PR frontend environments call their respective backend PR environments for the same PR. When downstream services are required the PR environment integrates with with the integration environments of the downstream.
When creating non-prod environments it is valuable to have the configuration of the environment to be the same as production. This is done to avoid issues that may arise from differences in configuration and it makes testing more confident.
Not all existing applications have been setup this way so apply caution and check configurations between environments when doing new deployments.
Production
The production environment is static. It is used to serve public facing traffic and is built from the main branch. It is deployed automatically on changes to the main branch.
Integration
This is an optional static environment. It is required if the product has upstream services that integrate with it. This allows upstream clients to integrate with this environment for their PR environments. It is deployed automatically on changes to the main branch.
Inter Product Connections
The following diagram outlines the connections between different product environments.
Production integrates with Production always.
PR integrates with static integration environments and, where applicable, integration environments integrate with other integration environments.
Rationale
The choice of these environments and setup was derived with the following considerations:
-
Fewer static environments
- Reduces environment rot - static environments are likely to experience manual configuration changes over time which can result in inconsistency between environments and inability to reproduce issues. By spinning up ephemeral environments we avoid this as configuration must always be part of the code itself.
- Lowers cost - environments are only active for when they are required reducing long term cost when no active development is being performed. Long running static environments are removed completely.
- Removes bottlenecks - if there is an issue on a static environment it blocks other features and changes being pushed to production until it is fixed. This can also waste developer time investigating the issue.
-
Quicker releases; having fewer environments from the PR to Production results in quicker releases as every change approved is merged and pushed to the Production environment.
-
Integration; it is acknowledged that teams wish to integrate and test against downstream dependencies. The Integration environments provide a way to do this safely and against an environment that is prod-like. Whilst it is another static environment it is required whilst we investigate the possibility of using multi-tenancy and contract testing.
The rationale for the naming is as follows:
- PR - these environments are spun up from a GitHub PR.
- Production - a common term used for live environments.
- Integration - an environment that PRs use to test integration against.
The integration environment is named because it is used by other environments to integrate against. It is not the environment that is used to run integration tests on for the product itself. This should be done in the PR environments and is the most effective place for doing so. You can think of the integration environment as a way for other teams to test against your service; not as an environment you yourself should test on.
GitHub Environments
GitHub Environments provide a mechanism to manage and control deployments to different stages of your environments, such as PR, Integration, and Production. They allow you to define specific settings, secrets, and protection rules tailored to each environment.
Using GitHub Environments, you can:
Control Access: Limit who can deploy to sensitive environments like Production.
Manage Secrets: Store environment-specific secrets (e.g., API keys, database credentials) securely.
Enforce Policies: Require specific checks, reviews, or approvals before deploying to certain environments.
Track Deployments: Gain insights into when and by whom deployments were made.
By defining these environments in GitHub, you maintain a structured and secure workflow, reducing the risk of errors and ensuring a smooth deployment process.
How to Add an Environment in GitHub
Example: Using GitHub Environments in GitHub Actions
name: CI/CD Pipeline
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Run tests
run: npm test
deploy:
runs-on: ubuntu-latest
needs: test
environment: Integration # Add your environment that you want to use here
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Deploy to environment
env:
API_KEY: ${{ secrets.API_KEY }} # This is how you can use your secrets
run: |
# Add deployment commands here
Examples
This environment structure was defined whilst building the online payments product. It can be used as an example for a product that encapsulates the environments above. (NOTE: Names have changed since this product has been deployed and so may differ with this document).
References & Further Reading
Articles from DevOps experts
- KubeCon NA: Are you about to break Prod? (https://about.gitlab.com/blog/2020/01/27/kubecon-na-2019-are-you-about-to-break-prod/)
- Ephemeral Environments for DevOps: Why, What and How? (https://pipelinedriven.org/article/ephemeral-environment-why-what-how-and-where)
- THE FOUR BIGGEST ISSUES WITH HAVING STATIC ENVIRONMENTS (https://pipelinedriven.org/article/the-four-biggest-issues-with-having-static-environments)
Products using ephemeral environments
- AWS Amplify's Web previews (https://docs.aws.amazon.com/amplify/latest/userguide/pr-previews.html)
- GitLab's Review Apps (https://docs.gitlab.com/ee/ci/review_apps/)
- Azure Static Web apps's Seamless staging environments (https://azure.microsoft.com/en-au/services/app-service/static/#features)
- Jenkins X's Preview Environments (https://jenkins-x.io/v3/develop/environments/preview/)
Github environments
- GitHub Documentation on Environments: (https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment)