Cheap and minimal container orchestration with Docker Context using GitHub Actions
Workflow settings and setup steps
GitHub Actions is the CI/CD platform of choice for this example . For the sake of example, the two triggers for this workflow are workflow_call
and workflow_dispatch
.
name: Deploy
on:
workflow_call:
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
Checking out the code is an easy first step because without it, we won't be able to build our image in the runner. And ecause I'm using DigitalOcean's container registry, we'll install DigitalOcean’s CLI, doctl
, and authenticate with a personal access token. Best practice for using tokens and secrets in a workflow is to store the secrets in the repository so that we can conveniently reference secrets in the workflow. Once that is done, we can authenticate Docker with the container registry.
- name: Checkout code
uses: actions/checkout@v3
- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: Authenticate Docker with registry
run: doctl registry login
Build, cache, and push the image
To build and push the image, we'll set up and use docker/build-push-action to build and push Docker image with Buildx with full support of the features provided by Moby BuildKit builder toolkit. What's great about this action is that it can be configured to cache the build to and from our repository so that we don't have to build our image from scratch every time this workflow runs. The only tradeoff with caching is that the runner needs to download and upload the cache to the repository, but the time saved from using the cache during the build is worth it more times than not.
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: registry.digitalocean.com/<registry-name>/<image-name>:latest
cache-from: type=gha
cache-to: type=gha,mode=max
Pull and run the image remotely
The image has been built and pushed to our container registry, and now it’s time to update the image running on the remote server. There’s an infinite number of ways this can be done, but we’ll be using Docker Context to help make this as simple as possible. Definitely take the time to read the small amount of documentation about Docker Context, but the TL;DR is that the docker context
command allows the local Docker CLI to interface with Docker running on a remote machine.
Docker Context is helpful because even though your local Docker CLI is controlling Docker on a remote machine, local docker
related commands will still use the local file system. So if we tell Docker to use a remote context and run a docker compose
command with a local compose file, Docker will use the local compose file on the remote machine. This behavior will allow us to update and run the image running on our remote server from within the workflow.
In order to create and use the context in the runner, we’ll use a shortcut from a community action and use SSH to control Docker running on the production server. To get the public key needed for the ssh_cert
input, run ssh-keyscan -H <ip-address>
and store the output as a repository secret. For the ssh_key
input, copy whatever private key you use to connect as the local user and store is as a repository secret.
- name: Create and use production server Docker Context
uses: arwynfr/actions-docker-context@v2
with:
docker_host: 'ssh://<user>@<ip-address:port>'
context_name: 'production'
ssh_cert: ${{ secrets.SSH_CERT_SECRET }}
ssh_key: ${{ secrets.SSH_KEY_SECRET }}
use_context: true
Now that we have access to Docker and docker-compose
on the server, we can run the same docker-compose
commands we would use if we wanted to run the application locally in our dev environment. And after a quick image prune
step to remove any unused layers from the previous image, we have successfully updated the application 😎
- name: Pull
run: docker-compose pull
- name: Up
run: docker-compose up -d
- name: Remove previous image
run: docker image prune -af