First look: Jenkins CI with Windows Containers and Docker

In 2014 Microsoft partnered with Docker and the results are in: Windows Containers combined with Docker give the same great experience we've gotten used to in the Linux world.

I've set up a proof of concept to convert the Docker voting app to Windows Containers and have got msbuild.exe working inside a container - now let's move on to the next step - continuous integration with Jenkins CI.

We'll look at creating:

  • a Jenkins master Docker image running on Windows Server Core
  • a Jenkins agent to run headless in a Windows Container

Now the following is a disclaimer - the CloudBees team can tune Jenkins CI far better than I can. Keep an eye out for an official solution on Github from their team of hackers.

Create a Java base image

The main pre-requisite is a JRE and fortunately Stefan Scherer (a fellow Docker Captain) has come up with a base image. We'll take his Java JRE base image as a template:

# escape=`
FROM microsoft/windowsservercore

RUN powershell -Command `
    wget 'http://javadl.oracle.com/webapps/download/AutoDL?BundleId=210185' -Outfile 'C:\jreinstaller.exe' ; `
    Start-Process -filepath C:\jreinstaller.exe -passthru -wait -argumentlist "/s,INSTALLDIR=c:\Java\jre1.8.0_91" ; `
    del C:\jreinstaller.exe

ENV JAVA_HOME c:\\Java\\jre1.8.0_91
RUN setx PATH %PATH%;%JAVA_HOME%\bin

CMD [ "java.exe" ]

Build this as a local image:

$ docker build -t windows-java:jre1.8.0_91 .

Before you share or push this image to the public Docker Hub etc bear in mind that Oracle products such as Java have their own EULA and licensing terms.

Create a Jenkins master image

The second pre-requisite for a Jenkins master is to download a Jenkins release as a .war file. Head over to the mirrors site and pick the release you want to work with:

http://mirrors.jenkins-ci.org/war/

Now add a layer to download the .war file:

FROM windows-java:jre1.8.0_91

ENV HOME /jenkins
ENV JENKINS_VERSION 2.0
RUN mkdir \jenkins
RUN powershell -Command "wget -Uri https://updates.jenkins-ci.org/download/war/2.0/jenkins.war -UseBasicParsing -OutFile /jenkins/jenkins.war"

EXPOSE 8080
EXPOSE 50000

CMD [ "java","-jar", "c:\jenkins\jenkins.war" ]

If you have issues with the CMD entry please try this instead:

CMD java -jar C:\\jenkins\\jenkins.war

We have used wget an alias in PowerShell for Invoke-WebRequest. This may be deprecated in the future but is concise enough for our purposes.

  • Port 50000 is used for agent/slave communication
  • Port 8080 provides the web interface

Build the image:

docker build -t jenkins-windows:2.0 .

You can now run your Jenkins master:

$ docker run --name jenkinsci -p 8080:8080 -p 50000:50000 -d jenkins-windows:2.0

Since the Jenkins 2.0 release security is enabled by default and you will need to find the initial password from the Jenkins container before you can login.

$ docker logs jenkinsci

Connecting to Windows Containers

Once you have found the initial password from the logs, you will need to connect to Jenkins in a web-browser.

Due to networking differences between the Linux and Windows Docker implementation you won't be able to access Jenkins through http://localhost:8080.

  • Type in docker inspect jenkinsci
  • Look for the (NAT) IP address of the container
  • For Windows 10 - use that IP address in place of localhost. For Windows 2016 use the NAT IP address or the IP address of one of your Ethernet adapters such as http://10.95.11.1:8080.

Here's a screenshot of my initial attempt of running the master:

Create a Jenkins agent image

A Jenkins agent image can be created and connected headless later on. This is really useful for running through a CLI or script.

For this image we'll derive from our Java JRE base image, but this time we don't have to fetch a .jar or .war file from the internet. We can fetch it from our running instance of a Jenkins master.

FROM windows-java:jre1.8.0_91

SHELL ["powershell"]
ARG BASE_URL
ARG SECRET

RUN (New-Object System.Net.WebClient).DownloadFile('{0}/jnlpJars/slave.jar' -f $env:BASE_URL, 'slave.jar') ;
ENTRYPOINT ["C:\\Java\\jre1.8.0_91\\bin\\java.exe", "-jar", ".\\slave.jar"]

For the build pass in the BASE_URL environmental variable like this:

$ docker build --build-arg BASE_URL=http://192.168.0.101:8080 -t jenkins_windows_agent:2.0 .

Now, add a new agent on the Jenkins UI and look for the "secret" needed to authenticate new agents to the master.

Copy the secret and use it when you run your agent:

$ docker run -ti jenkins_windows_agent -jnlpUrl http://192.168.0.101:8080/computer/Windows/slave-agent.jnlp -secret e9714c100fb003e2cef3609b96a255da5f488bc5f195ef6a0fafcebb2836d4e3

You now have a running agent and can run builds and jobs on it:

Wrapping up

You now have a fully-functional Jenkins CI master and agent running on Windows Containers through Docker. Please treat this as a proof of concept and let me know if you have any ideas for improving the level of automation or general experience.

The source for the Dockerfiles is available on Github:

Here are a few areas you could move onto with this basis:

  • Installing custom software such as IIS or .NET into the Jenkins agent
  • Launching agents through the Docker Swarm plugin (classical)
  • Enabling the Blue Ocean UX
  • Integrating containers into the workflow - using a custom Docker registry

Hire me for Cloud Native / Docker / Go / CI & CD or Kubernetes

Could you use some help with a difficult problem, an external view on a new idea or project? Perhaps you would like to build a technology proof of concept before investing more? Get in touch via sales@openfaas.com or book a session with me on calendly.com/alexellis.

You may also like

Alex Ellis

Read more posts by this author.

Subscribe to Alex Ellis' Blog

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!