Windows Docker container as Jenkins slaves

Introduction: Why we need this document

Docker support is now known to be extended to Windows 2016 server and we can use these windows containers as slaves to a Jenkins master. For this, I did a small PoC and thought of sharing my experience in this document as there is very less documentation available online at the moment. Please note that this was done on a purely experimental basis and there could be other alternatives to achieve this.     

Prerequisites

Windows Docker should be installed in the ‘docker host machine’.
(NOTE: Works only with Windows Server 2016 at the moment)

  1. To check the Docker version:
    1. Open PowerShell as Administrator and type Docker version:

  1. Install Jenkins in the Docker host machine. Additional plugins to be installed in Jenkins:
    1. Docker build step plugin
      https://wiki.jenkins.io/display/JENKINS/Docker+build+step+plugin
  2. Configuration to be done in Jenkins:

    Open Jenkins URL, go to Manage Jenkins and click on Configure System. Look for the Docker Builder section and provide the Docker URL:
    tcp://<Docker Server host>:<docker server port>
    Ex: tcp://daeiglobal25978.eur.ad.sag:2375
    Click on Test Connection and validate if the connection is successful.

Next, click on Configure Global Security under Manage Jenkins and check for the Agents section. Enable the TCP Port for JNLP Agents by selecting fixed and give a port number.
Eg: 49187

  1. Pull Docker Windows image to your host machine by running the command from your PowerShell

    ‘docker pull microsoft/windowsservercore’ 

    Once pulled, run the command ‘docker images’ to check for the pulled image as seen below.

Create a container of the base Windows image

Create a container from the base windows image (pulled earlier) using the following command in the power shell window
‘docker run -it --name <anyname> -h <any hostname you give> microsoft/windowsservercore:latest’

Example: docker run –it –-name mywin –h mywin microsoft/windowsservercore:latest
This will start a container in the interactive mode.

How to view the new container from Windows Explorer?

  • Open ‘Computer Management’ under ‘Windows Management Tool’
  • Click on ‘Disk Management’ under Storage.
  • Once the container is successfully created and started, you will see a new volume added called Disk1 and the icon of the drive is shown in blue color.

  • Now right click on the volume and click on ‘Change Drive Letter and Paths’ and click on ‘Add’ button to assign a drive letter to the container. 
    For example: add ‘D’ as the drive for the container and click on OK (as shown below) 

  • Now open ‘File Explorer’ and click on ‘This PC’. You will see a new D drive which is actually the container.

[Please note that you can see the drive only till the container is running. If the container is stopped, it will be removed and you have to again go to Computer Management to add it]

Install Java into the Container

Download the java setup files. Copy the java setup files to the container. You can do a normal right click copy and paste from the file explorer or use docker command to copy the files.
Once copied, go to PowerShell where you have connected to the container in interactive mode.
Run ‘dir’ command and you will see the copied java setup files in the C drive.

Install java silently using the command 
‘jdk-8u40-windows-x64.exe /s’

This will install Java under the usual folder ‘C:\Program Files\Java’

[Never install java using the GUI by running the setup.exe as this will install java in the C drive of the docker host machine and not inside the docker container.]

Once the installation is done, add it to the path permanently using the following command inside the container:

setx -m PATH “C:\Program Files\Java\jdk1.8.0_40\bin”;%PATH%

Add Jenkins agent into the container

Open the Jenkins web console and click on Manage Jenkins from the left menu. Now click on the Manage Nodes. Click on New Node and give a Node Name (eg: mywin). Provide the remote root directory (eg: C:\jenkins). Select the launch method as “Launch agent via Java Web Start”. Click on Save. 

The newly added node will be now visible under managed nodes.

  • Click on the slave name. It will be shown as offline.
  • Click on agent.jar link seen as ‘java -jar agent.jar -jnlpUrl http://localhost:8080/computer/mywin/slave-agent.jnlp’ which downloads the jar file locally.
  • Copy this jar file to the slave container and put it under C: drive of the container.

Run a dir command inside the container to verify the copied ‘agent.jar’ file.

Create the base Jenkins slave image from the slave container

Stop the slave container. Create the image ‘slaveimage’ using the ID or name of the container that you stopped by running the following docker command on PowerShell of the docker host machine:Stop the slave container. Create the image ‘slaveimage’ using the ID or name of the container that you stopped by running the following docker command on PowerShell of the docker host machine:
docker commit <name of the stopped container> <name of the new image>
Eg: docker commit mywin slaveimage

Once the new image (slaveimage) is created, you can remove the slave container. 

Create the new slave container from the slave image

From PowerShell, run the following command to start a new container:
docker run -d –t --workDir "C:\jenkins" --name <container name> -h <host name> <imagename>:latest CMD /S /C java -jar "C:\agent.jar" -jnlpUrl "http://<jenkins server host>:8080/computer/<nodename>/slave-agent.jnlp" 

Example:
docker run -d –t --workdir "C:\jenkins" --name mywin -h mywin slaveimage:latest CMD /S /C java -jar "C:\agent.jar" -jnlpUrl "http://daeiglobal25978:8080/computer/mywin/slave-agent.jnlp" 

[Please note that the node name should be the same one as provided in the Jenkins node creation earlier.]

Once the container is run with the above command, you can see the node in Jenkins to be online.

Now, you can use the container as a normal jenkins slave machine and run any jobs you like.

To create more slave containers, you can use the same slaveimage and provide different node names to create new slave containers.