GitHub service containers are essentially Docker containers created for the lifetime of a workflow job. You can host services to test or operate applications in a workflow using service containers.
Service containers are created for a job and destroyed once the job is done. Each job step can communicate with the services available in service containers within a job.
Let’s explore service containers to better understand their usage and features.
Service Containers and Job Communication
It is important to understand a service container’s communication mechanism when executing a GitHub Actions workflow. Two types of communication happen, depending on whether the job is running as a container job or running directly on a runner machine.
Job Running as a Container
If you are running a job as a container on a runner machine, the network accessibility to service containers is simple because communication can happen via the label for the service container in the workflow. This is because all the containers running in the same network expose all ports.
Jobs Running Directly on a Runner Machine
When a job runs directly on a runner machine, the service container ports should be mapped to the Docker host (the runner machine) in the workflow to enable the job to gain access to the service containers. Once the service container port is mapped to the host/runner machine, you can use localhost:port or 127.0.0.1:port to access the service container from the job steps.
The next section looks at practical examples that highlight the implementation differences in action workflows when a job is executing as a container and when the job is running directly on a runner machine.
Using a Redis Service Container
You can create a Redis service and utilize it in a GitHub Actions workflow. A Redis service container executes data-related tests in workflows. Let’s implement a simple JavaScript-based test using Redis and execute it with a GitHub Actions workflow to learn how to use service containers.
You need to create a new GitHub repo called RedisServiceClientDemo, and then clone it to a local machine. Add the following code to a JavaScript file named redisclient.js, and commit and push it to the repo.
const redis = require("redis");
// Creates a new Redis client
// In the workflow we are going ot set If REDIS_HOST and REDIS_PORT
else console.log(" " + i + " German word for: " + germankey + " is: " + value)
});
});
redisClient.quit();
});
This JavaScript code uses a Redis service, adds few values, then reads and prints them. The next step is to set up a GitHub Actions workflow. You need to set up a Redis service container and execute the JavaScript pushed to the repo.
To allow the script to work, you must have the required package dependencies set in package.json and package-lock.json. First, execute the npm init -y command in the repo folder to get package.json added to the repo (see Figure 8-1).
Figure 8-1
Initialize npm
Then add a dependency for the Redis node by executing npm install redis (see Figure 8-2).
Figure 8-2
Install Redis node
Let’s see how to get it to work with the workflow job running as a container and running the workflow job directly in the runner machine.
Run a Workflow Job as a Container in the Runner
The following shows how a container job is set up in GitHub Actions.
jobs:
# Name for the container job
container-job:
# Runner for the container job. Containers have to run on Linux
runs-on: ubuntu-latest
# We are using a node container image from doker hub to run the JavaScript
container: node:10.18-jessie
When running a workflow job as a container, you do not need to use port mapping to the host (runner) from a Redis service container. To set up the Redis service container, you can use the following code. Note that there is no port mapping to the host.
# Service containers to run with `container-job`
services:
# Name for the service container
redis:
# Docker Hub image for redis
image: redis
# Setting health checks to wait until redis has started
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
Next, execute the JavaScript using the following steps. The service client’s label is used in the code as the host name to allow JavaScript to create a Redis client.
steps:
# checkout the repo
- name: Check out repository code
uses: actions/checkout@v2
# Install dependencies
- name: Install dependencies
run: npm ci
- name: Connect to Redis
# Runs JavaScript to create a Redis client, populate data and read data
run: node redisclient.js
# Environment variable are passed to JavaScript to create Redis client
env:
# As the host name service container name(label) is passed
REDIS_HOST: redis
# The default Redis port is passed to create the redis client
REDIS_PORT: 6379
The following is the full workflow.
on: [workflow_dispatch]
jobs:
# Name for the container job
container-job:
# Runner for the container job. Containers have to run on Linux
runs-on: ubuntu-latest
# We are using a node container image from doker hub to run the JavaScript
container: node:10.18-jessie
# Service containers to run with `container-job`
services:
# Name for the service container
redis:
# Docker Hub image for redis
image: redis
# Setting health checks to wait until redis has started
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
# checkout the repo
- name: Check out repository code
uses: actions/checkout@v2
# Install dependencies
- name: Install dependencies
run: npm ci
- name: Connect to Redis
# Runs JavaScript to create a Redis client, populate data and read data
run: node redisclient.js
# Environment variable are passed to JavaScript to create Redis client
env:
# As the host name service container name(label) is passed
REDIS_HOST: redis
# The default Redis port is passed to create the redis client
REDIS_PORT: 6379
Once executed as a container, the workflow utilizes Redis in the service container to add and read values. The job container and Redis service container are created, and then the job container successfully communicates with the Redis service container (see Figure 8-3).
Figure 8-3
Workflow run as container and using Redis service container
Next, let’s look at running JavaScript in a workflow directly running in a runner machine.
Run a Workflow Job Directly in the Runner
You need to ensure that the service container is created and the ports are mapped to the host (the runner machine) to allow the workflow to directly communicate with a Redis service container.
jobs:
# Name of the job running in the runner directly
runner-job:
# Must use a Linux environment to use service containers
runs-on: ubuntu-latest
# Service containers running in the `runner-job`
services:
# service container name
redis:
# Docker Hub Redis docker image
image: redis
# health checks to wait until redis is ready
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Mapping port 6379 on service container to the host (runner machine)
# to enable the job to access the Redis service container
- 6379:6379
Next, instead of using the Redis container service label (name), you must use a localhost mapped port to communicate with the Redis service container while running the JavaScript directly in the runner machine. Therefore, connection information to the Redis service container must be set up, as shown next.
- name: Connect to Redis
# Runs JavaScript to create a Redis client, populate data and read data
run: node redisclient.js
# Environment variable are passed to JavaScript to create Redis client
env:
# now need to access Redis service container via localhost as port is mapped to runner machine
# and the job and Redis service container communication is no longer container to container
REDIS_HOST: localhost
# The default Redis port is passed to create the Redis client
REDIS_PORT: 6379
The following is the full workflow of using a Redis service container while running a job directly on a runner machine.
on: [workflow_dispatch]
jobs:
# Name of the job running in the runner directly
runner-job:
# Must use a Linux environment to use service containers
runs-on: ubuntu-latest
# Service containers running in the `runner-job`
services:
# service container name
redis:
# Docker Hub Redis docker image
image: redis
# health checks to wait until redis is ready
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Mapping port 6379 on service container to the host (runner machine)
# to enable the job to access the Redis service container
- 6379:6379
steps:
# checkout the repo
- name: Check out repository code
uses: actions/checkout@v2
# Install dependencies
- name: Install dependencies
run: npm ci
- name: Connect to Redis
# Runs JavaScript to create a Redis client, populate data and read data
run: node redisclient.js
# Environment variable are passed to JavaScript to create Redis client
env:
# now need to access Redis service container via localhost as port is mapped to runner machine
# and the job and Redis service container communication is no longer container to container
REDIS_HOST: localhost
# The default Redis port is passed to create the Redis client
REDIS_PORT: 6379
The workflow now executes the job on the runner machine and successfully connects to the Redis service container to get data (see Figure 8-4).
Figure 8-4
Using Redis service container while running job on runner machine
This section looked at the practical implementation of a Redis service container and two communication modes in GitHub workflows: a job running as a container and a job running directly on the runner machine.
Summary
This chapter explored service containers and communication mechanisms to show how you can use service containers in a GitHub Actions workflow.
The next chapter discusses implementing custom actions to enhance your GitHub Actions workflows’ capabilities.