This entry will document connecting and configuring a Jenkins slave node to Jenkins master instance via ssh. The servers will run as docker containers. Ideally the docker jenkins plugin would be utilized to connect the 2 together, in the future I intend to document that approach but for now I am using the older technique of allowing the master to control slave nodes via ssh.
The following items will be covered
- Installing and running Jenkins master docker container
- Creating and obtaining the Jenkins public ssh key from Jenkins master container
- Creating and running a customized docker container to be used as the Jenkins slave; setup with Java8, Git, Maven, Jenkins user and open ssh server
- Connecting the node to the master in Jenkins and setup of tools.
- Creating a Jenkins job that executes on the slave
A Docker daemon install and some working knowledge of docker
A recommendation (not a requirement) is add your Linux user account to the docker group. Without this step many of the commands in the entry will require you to sudo to execute properly.
#verify docker group exists cat /etc/group | grep docker sudo adduser dave docker #log in and out and verify docker works by executing this command docker ps
Install Master Jenkins
In preparation of running the Jenkins master container you will want to create a directory to utilize as the home volume on the container. This folder must be accessible by the Jenkins user in the container (jenkins user – uid 1000). You may need to change ownership of the directory to user 1000.
mkdir jenkins_home && sudo chown 1000:1000 jenkins_home
Some alternatives to this approach
- use the -u flag on the docker run command to set the user associated with the volume folder
- set privileges of the folder to 777 (unsecure)
Run this command to pull the latest Jenkins master from docker hub and instruct the docker daemon to run it
docker run -d --restart=always --name jenkins -p 8082:8080 -p 50000:50000 -v /home/dave/data/jenkins_home:/var/jenkins_home jenkins/jenkins:lts
For easy reference I am naming the container “Jenkins”, altering the default port of 8080 to port 8082 on my local machine and setting the data volume.
Once this is running you will need the “initialAdminPassword” in order to unlock Jenkins. Navigate to the secrets folder on the jenkins home volume and read the initialAdminPassword file (in my case /home/dave/data/jenkins_home/secrets/initialAdminPassword). An alternative method for obtaining the “initialAdminPassword” would be to grep the docker log
docker logs -f jenkins
In your browser you should be able to access you Jenkins master instance at port 8082
Go through the Jenkins setup process to select plugins and create a user account
Create the public key in Jenkins Master and save it for later use.
To do this access the Jenkins container and run ssh-keygen.
docker exec -it --user 1000 jenkins bash ssh-keygen -t rsa ( accept defaults)
In my case the new Public key will be created outside of the contain on the host machine at /home/dave/data/jenkins_home/.ssh/id_rsa.pub
You can copy that file from there and place it somewhere to be accessible during the Jenkins slave build process
Building the Jenkins Slave
I am building a customized container that will support jdk8, Maven, Git and OpenSsh. To build custom containers in docker create a Dockerfile and run the docker build command on it. In this case 3 files where needed in order to generate the Jenkins slave image.
- Apache Maven install tarball (apache-maven-3.5.2-bin.tar.gz), which can be downloaded here https://maven.apache.org/download.cgi
- A copy of the Jenkins Master public key (the id_rsa.pub file mentioned above)
- And a file named Dockerfile that will contain the commands to build the image
I placed these files in the same directory and ran docker build command to generate the image. More information can be found here regarding using Dockerfile. https://docs.docker.com/engine/reference/builder/
FROM java:8-jdk RUN apt-get update -y RUN apt-get install -y openssh-server initscripts passwd git RUN groupadd -g 1000 jenkins && useradd -d "/home/jenkins" -u "1000" -g "1000" -m -s /bin/bash "jenkins" RUN echo "jenkins:jenkins" | chpasswd RUN mkdir /var/run/sshd RUN echo "session required pam_mkhomedir.so skel=/etc/skel/ umask=0022" >> /etc/pam.d/sshd WORKDIR "/home/jenkins" COPY id_rsa.pub.jenkins-master ./ RUN mkdir .ssh && cat id_rsa.pub.jenkins-master >> .ssh/authorized_keys && rm id_rsa.pub.jenkins-master COPY apache-maven-3.5.2-bin.tar.gz ./ RUN tar xvf /home/jenkins/apache-maven-3.5.2-bin.tar.gz && rm apache-maven-3.5.2-bin.tar.gz ENV M2_HOME=/home/jenkins/apache-maven-3.5.2 ENV PATH="$M2_HOME/bin:$PATH" EXPOSE 22 CMD /usr/sbin/sshd -D
Details regarding the Dockerfile
- uses open jdk 8 image as a base
- installs Git and openssh server from Debian’s apt tool
- creates the “jenkins” user account
- configures ssh
- adds Jenkins master public key to authorized users which enables Jenkins node configuration process to handle the ssh key
- adds Maven tarball to the container, unpacks it and adds it to path
- exposes port 22 and starts openssh server on container start
Note: using apt to install maven resulted in jdk7 being installed and overwriting java on the $PATH so I elected to install it manually.
To generate the image execute docker build from the same directory where Dockerfile exists. Use -t flag to “tag” the image with a recognizable name
docker build -t slave1 .
On successful build of the container run it
docker run -d --restart=always --name jenkins-slave1 -p 8375:22 jenkins-slave1:latest
Note: I am the exposing ssh on port 8375
In order to configure this properly from Jenkins master you will need to know the location of Maven and Java. Access the slave machine and grab the details by executing these commands
docker exec -it jenkins-slave1 bash echo $JAVA_HOME mvn -version
In my case the needed values are
Sanity test to ensure ssh access from Jenkins master to Jenkins slave
I accessed Jenkins master and ensured I was able to ssh to jenkins-slave1 with no password requirement
docker exec -it --user 1000 jenkins bash ssh jenkins@$HostNameOrIP -p 8375
Complete Jenkins configuration
At this point configuring Jenkins should be easy. Access Jenkins master via the browser at http://$HostNameOrIP:8082/manage
and go “Manage Jenkins” and select Manage Nodes
From the Nodes screen click “New Node”
Give the new node a name and mark it as a “permanent agent”
On the configuration screen make the necessary Changes
- Remote root directory=/home/Jenkins
- Give it a unique label
- Launch Method= Launch Slave agents via SSH
- The proper hostname
- Click advanced and ensure the port is correct
- Full path to java executable (in my case /usr/lib/jvm/java-8-openjdk-amd64/bin/java)
Edit Jenkins to support the Jenkins Slave maven install
Under manage Jenkins choose Global Tool Configuration
Under the Maven section edit the configuration to point to the $M2_HOME declared on the slave. In my Dockerfile it was declared as /home/jenkins/apache-maven-3.5.2
Create a Jenkins Project
To test this, create a project to proof everything is working as expected. I will be executing mvn install on personal project located at https://github.com/davetii/java-samples.git
Click new item from top of Jenkins screen and create a new Freestyle project.
Configure the build to utilize the maven details associated with the slave
I am selecting to persist the newly created jar files by editing a post build action
If everything is successful it should result in a screen like this.
I am finding docker a really interesting technology. Its a great enabler for any team or organization I hope to post many more entries regarding the different ways I am using docker.
Hoping you found this useful… good day.