Portable CI with Docker
April 21, 2021
Most CI implementations (e.g. Jenkins, GitHub Actions, etc.) provide a compute environment where we can run our CI pipeline steps.
The CI implementation present a few challenges:
- Lack of tooling to build and deploy requiring custom setup beforehand
- Limited scripting capability
- Company decides to change providers
The following approach attempts to address thse challenges by requiring only a CI environment with docker
and docker-compose
(optional) installed.
Example
We will look at an NodeJS API (repository).
It has two steps with the following requirements:
- Build
yarn
node
- Deploy
yarn
node
- Environment variables
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
Define steps
Build
build.Dockerfile
build
script
Deploy
deploy.Dockerfile
deploy
script
Rather than writing docker run
commands for each step, we can use docker-compose
to manage them:
- Each
docker-compose
service (e.g.build
anddeploy
) represents a step in our pipeline - Each step is configured to run the script in its corresponding container
- The entire project directory is mounted as a volume on the
/app
path- This enables and output from build to be reflected outside the container and thus shared between steps
- Any required environment variables are passed from the CI environment into the container environment
- If the step requires Docker in Docker, e.g. to build images, then it just needs a Docker client pointing to Docker server on the CI environment
- Mount the Docker socket as a volumevolumes:- /var/run/docker.sock:/var/run/docker.sock
- Mount the Docker socket as a volume
Usage
Depending on how the CI environment requires the steps to be defined. We simply need a place to call:
docker-compose build builddocker-compose run builddocker-compose down
Some CI environments do not isolate steps, so each step could share some resources. This is an issue for docker-compose
as our projects will likely be run from similar directories and have similar service names (e.g. build
and deploy
). To avoid conflicts, we simply need to set the COMPOSE_PROJECT_NAME
variable in our CI environment, which will be prepended to all resources created by docker-compose
.