In previous articles, I have talked about using Docker Containers in smart testing strategies by creating a container image that contains the baseline of the application and the required test setup (test data for example). For each test instead of doing complex setup actions and finishing of with elaborate tear down steps, simply spinning up a container at the beginning and tossing it away at the end.
I have shown how that can be done through the command line – but that of course is not a workable procedure. In this article I will provide a brief introduction of programmatic manipulation of containers. By providing access to the Docker Daemon API from remote clients (step 1) and by leveraging the npm package Dockerode (step 2) it becomes quite simple from a straightforward Node application to create, start and stop containers – as well as build, configure, inspect, pause them and manipulate in other ways. This opens up the way for build jobs to programmatically run tests by starting the container, running the tests against that container and killing and removing the container after the test. Combinations of containers that work together can be managed just as easily.
As I said, this article is just a very lightweight introduction.
Expose Docker Daemon API to remote HTTP clients
The step that to me longest was exposing the Docker Daemon API. Subsequent versions of Docker used different configurations for this and apparently different Linux distributions also have different approaches. I was happy to find this article: https://www.ivankrizsan.se/2016/05/18/enabling-docker-remote-api-on-ubuntu-16-04 that describes for Ubuntu 16.x as Docker Host how to enable access to the API.
Edit file /lib/systemd/system/docker.service – add -H tcp://0.0.0.0:4243 to the entry that describes how to start the Docker Daemon in order to have it listen to incoming requests at port 4243 (note: other ports can be used just as well).
Reload (systemctl daemon-reload) to apply the changed file configuration
Restart the Docker Service: service docker restart
A simple check to see if HTTP requests on port 4243 are indeed received and handled: execute this command on the Docker host itself:
curl http://localhost:4243/version
The next step is the actual remote access. From a browser running on a machine that can ping successfully to the Docker Host – in my case that is the Virtual Box VM spun up by Vagrant, at IP 192.168.188.108 as defined in the Vagrantfile – open this URL: http://192.168.188.108:4243/version. The result should be similar to this:
Get going with Dockerode
To get started with npm package Dockerode is not any different really from any other npm package. So the steps to create a simple Node application that can list, start, inspect and stop containers in the remote Docker host are as simple as:
Use npm init to create the skeleton for a new Node application
Use
npm install dockerode –save
to retrieve Dockerode and create the dependency in package.json.
Create file index.js. Define the Docker Host IP address (192.168.188.108 in my case) and the Docker Daemon Port (4243 in my case) and write the code to interact with the Docker Host. This code will list all containers. Then it will inspect, start and stop a specific container (with identifier starting with db8). This container happens to run an Oracle Database – although that is not relevant in the scope of this article.
var Docker = require('dockerode'); var dockerHostIP = "192.168.188.108" var dockerHostPort = 4243 var docker = new Docker({ host: dockerHostIP, port: dockerHostPort }); docker.listContainers({ all: true }, function (err, containers) { console.log('Total number of containers: ' + containers.length); containers.forEach(function (container) { console.log(`Container ${container.Names} - current status ${container.Status} - based on image ${container.Image}`) }) }); // create a container entity. does not query API async function startStop(containerId) { var container = await docker.getContainer(containerId) try { var data = await container.inspect() console.log("Inspected container " + JSON.stringify(data)) var started = await container.start(); console.log("Started "+started) var stopped = await container.stop(); console.log("Stopped "+stopped) } catch (err) { console.log(err); }; } //invoke function startStop('db8')
The output in Visual Studio Code looks like this:
And the action can be tracked on the Docker host like this (to prove it is real…)
Resources
Article by Ivan Krizsan on configuring the Docker Daemon on Ubuntu 16.x – my life safer: https://www.ivankrizsan.se/2016/05/18/enabling-docker-remote-api-on-ubuntu-16-04
GitHub Repo for Dockerode – with examples and more: https://github.com/apocas/dockerode
Presentation at DockerCon 2016 that gave me the inspiration to use Dockerode: https://www.youtube.com/watch?v=1lCiWaLHwxo
Docker docs on Configuring the Daemon – https://docs.docker.com/install/linux/linux-postinstall/#configure-where-the-docker-daemon-listens-for-connections
Awesome, thank you 🙂