GitHub Actions Day 20: Container Services
This is day 20 of my GitHub Actions Advent Calendar. If you want to see the whole list of tips as they're published, see the index.
It's hard to underestimate the importance of containers in DevOps practices. Often, you'll deploy a container to production -- so it's natural to start using containers to do your local development. And to manage dependencies. We looked at how we could leverage this to do our builds inside containers. But we can also leverage running containers as part of our build and test workflow, using container services.
You'll often want to run some integration tests that talk to other
services, often a database. And you could script this, by writing
a docker run
command to pull the container down, start it, and
map the necessary ports... but that's annoying in the best case.
And if you're building inside a
container
then running docker yourself becomes a lot trickier.
Using a container service lets the GitHub Actions infrastructure take care of the execution for you. You can just specify the container and any ports to map and it will start the service containers at the beginning of your job and make it available to the steps within the job.
services:
redis:
image: 'redis:latest'
ports:
- 6379/tcp
Will start the redis:latest
container and map port 6379 in the
container to a port on the virtual machine runner. This is the
moral equivalent of running docker run redis:latest -p 6379/tcp
,
and just like if you'd run that, the port that is mapped on the
local runner is not deterministic. GitHub Actions makes this
information available in the job.services
context.
You can look at ${{ job.services.redis.ports[6379] }}
to identify the local port number. (Just like running docker run
,
you can also specify the container port and the local port,
eg 6379:6379
to map the container port 6379 to port 6379 locally.)
Putting this into a workflow, if I have a
Node script
that talks to Redis, connecting to the Redis host specified by the
REDIS_HOST
environment variable on REDIS_PORT
, then I can create
a workflow that starts my Redis container and then runs my Node script.
You can use service containers to start services like Redis, PostgreSQL or MySQL -- or even Selenium. The service container execution makes executing and interacting with these containers in your workflow much easier.