Connecting a Jenkins node to master via ssh

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

Pre-requisites

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
http://$HostOrIP: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/

Dockerfile contents

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

  • /usr/lib/jvm/java-8-openjdk-amd64
  • /home/jenkins/apache-maven-3.5.2

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)

Slave node configuration should look something like this.

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.

From Jenkins configuration restrict to where this job can be run by choosing the newly created label. In my case Jenkins-slave1

Setup your Git Credentials and input repo URL


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.