The previous posts in this series on Docker have looked at using containers to run services, specifically a web server and database server. However, Docker allows containers to be created, run, stopped and destroyed so cheaply that they can be used to run a single job. This job could be a script or even a single command. Unlike a service, a job will stop running when it’s complete. A container running a short lived job can be set to automatically stop and remove itself once the job is complete. If the job needs to be run again, it is reasonably efficient for Docker to start up a brand new container as required.
To recap, this series covers:
- Running Containers: Installing Docker and starting containers;
- Building Images: How to create a new container image, customized to your requirements;
- Disposable Containers (this post);
- Composing an Environment Stack: Creating an environment composed of multiple linked containers.
Maven build job
This example will look at a simple job: to build and deploy the Spanners demo app from source. The application is built in Maven and deployed to a Tomcat webserver.
We can build and deploy the application using a single Maven command:
mvn clean install tomcat7:redeploy
The clean and install goals make a fresh build and install the built artifacts to the local repository. The tomcat7:redeploy goal takes a built war and deploys it into a running Tomcat instance, as described in a previous article on Deploying to Tomcat 7 with Maven.
The Docker Container
All we need to have Docker run the Maven build is to run the build command in a container that has Maven installed. Once again, we can use an official vendor image on Docker Hub as a start. The official Maven image defines a container that has Maven (and Java) installed. We can extend this image so that the build job is started when the container starts. The Dockerfile for this image is fairly simple:
FROM maven:3.3.3-jdk-8 MAINTAINER Stuart 'Stevie' Leitch <[email protected]> # Copy settings.xml ADD settings.xml $MAVEN_HOME/conf/settings.xml # Build and deploy to the spanners-webserver container CMD ["mvn", "clean", "install", "tomcat7:redeploy", "-Pdockerbuild"]
The settings.xml file copied into the container (using the ADD command) just contains connection details for the target webserver:
<?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <servers> <server> <id>docker-webserver</id> <username>admin</username> <password>admin</password> </server> </servers> </settings>
CMD is used to execute the Maven command when the container starts. Notice that the -P flag is used to specify a Maven profile to activate. The dockerbuild profile is defined in the project’s pom and specifies the target webserver for the tomcat7 plugin:
<profile> <id>dockerbuild</id> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <server>docker-webserver</server> <url>http://docker-webserver:8080/manager/text</url> </configuration> </plugin> </plugins> </build> </profile>
These settings tell the tomcat7 plugin to deploy to a machine called docker-webserver (port 8080) using the username and password specified in the settings.xml under server id docker-webserver.
Running the build from a container
This container is designed to build source code from the host machine and then deploy it to a machine called docker-webserver. The container assumes that the source code already exists and that the webserver (and corresponding database server) are already running.
The source code of the Spanners demo app can be downloaded from GitHub with the following command:
git clone https://github.com/hotblac/spanners.git $HOME/spanners-latest
The Dockerized database and webservers can be started as described in the previous article:
sudo docker run --name spanners-database -e MYSQL_ROOT_PASSWORD=my-secret-pw -d hotblac/spanners-database:3.2 sudo docker run --name spanners-webserver --link spanners-database:spanners-database -p 8080:8080 -d hotblac/spanners-webserver:3.2
The container image for this example has been configured as an automated build at Docker Hub with the name hotblac/spanners-builder. This means it can be downloaded and run from the docker command line without having to manually build it on the target machine. It can be run from any internet connected machine with Docker installed with the following command:
sudo docker run --name spanners-builder --link spanners-webserver:docker-webserver -v $HOME/spanners-latest:$HOME/container-build-dir -w $HOME/container-build-dir -it --rm hotblac/spanners-builder
This command contains a number of flags to set up the container correctly:
- –name spanners-builder : Assigns a memorable name to this container
- –link spanners-webserver:docker-webserver: Creates a virtual network connection between this container and the spanners-webserver container. The link is aliased as docker-webserver meaning that this container can access the webserver if it were a physical machine called docker-webserver. This link is required so that Maven has a running Tomcat webserver in which to deploy the built application.
- -v $HOME/spanners-latest:$HOME/container-build-dir : Sets up a volume at $HOME/container-build-dir on the container, pointing to $HOME/spanners-latest on the host machine. $HOME/spanners-latest is where we told git to clone our source code into. This allows the container to access the downloaded source code on the host machine.
- -w $HOME/container-build-dir : Sets $HOME/container-build-dir as the current working directory of the container. This is the directory containing the source code to be built. When Maven starts, it will run from this directory and build the code.
- -it : This is a shorthand for specifying the two single letter switches-iand -t . These flags together allow a container to run interactively (rather than detached in the background) and connect the container’s input and output to the host shell.
- –rm : Automatically removes the container when it exits. This makes our container truly disposable. When it completes, it will remove itself from Docker.
When this rather lengthy command is run, Docker will start the hotblac/spanners-builder container, downloading it from Docker Hub first if necessary. The container will run a single Maven command to build the source code on the host machine and then deploy the resulting war into the spanners-webserver container. On completion of the Maven build, the container will stop and remove itself.
If we want to build the code again later, we can run the same Docker command to start a brand new instance of the container.
This example demonstrates how Docker could be used to efficiently and consistently run any job. The advantage of running a job in a fresh container is that the container starts in a consistent state. In this example of a Maven build job, the fresh container ensures that the build runs correctly from any machine and that it’s not dependent on any artifacts or settings on the developer’s workstation. This meets a requirement of a Continuous Integration (CI) build server. In addition, any number of container instances can run simultaneously, enabling consistent parallel builds.
This principle can be expanded to any finite job: backup processing, application deployment, reporting and so on. Running a job within a Docker container is advantageous because:
- It’s not necessary to have a physical server waiting idle for a job to start. The job can be started with its container only when it’s needed.
- Jobs run in disposable containers are isolated from each other and across multiple runs of the same job.
- Jobs can very easily be moved from one physical server to another. Specifically, the same container can be used to run the job in a development environment, on a test server and in production.
In the next and final article in this series we’ll look in more detail at how individual Docker containers can be composed into a complete environment stack.