Configuring Jenkins Slaves (Linux+Windows) on AWS EC2

Jenkins is a self-contained Java-based program, ready to run out-of-the-box, with packages for Windows, Mac OS X and other Unix-like operating systems. As an extensible automation server, Jenkins can be used as a simple CI server or turned into the continuous delivery hub for any project.

Prerequisites :

  1. Jenkins Master running. Follow this article for Installing Jenkins On Ubuntu On AWS.
  2. EC2 Slave Instance – for Slave Node with Internet Access.

When you are working on a number of projects which get built on a regular basis or want to run multiple jobs or might need several different environments to test your builds, then a single Jenkins server cannot simply handle the entire load.

Jenkins supports the master-slave architecture, i.e. many slaves work for a master. It is also known as Jenkins Distributed Builds. It also allows you to run jobs on different environments like Linux, Windows, MacOS, etc. We can also run the same test case on different environments in parallel using Jenkins Distributed Builds, which in turn helps you to achieve the desired results quickly using this distributed approach. All of the job results are collected and combined on the master node for monitoring.

Master and Slave architecture

For simplicity of this post we will setup only Linux (Ubuntu) and Windows slaves.

Jenkins Master :

Your main Jenkins server is the master machine. The tasks performed by the master are :

  • Scheduling build jobs.
  • Dispatching builds to the slaves for the execution.
  • Monitor the slaves.
  • Recording and presenting the build results.
  • Can also execute build jobs directly.

Jenkins Slave :

A slave is a Java executable that runs on a remote machine. The characteristics of the slave are :

  • It hears requests from the Jenkins Master instance.
  • Slaves can run on a variety of operating systems.
  • The job of a Slave is to do as they are told to, which involves executing build jobs dispatched by the Master.
  • We can configure a project to always run on a particular Slave machine or a particular type of Slave machine, or simply let Jenkins pick the next available Slave.

Setting Up A Jenkins Slave :

For this example, I have used one AWS EC2 instance as the master and will be setting up both Linux and Windows slaves.

  • To get started, open your Jenkins master ( http://public-ip-address-or-public-dns:8080 ) and go to Jenkins dashboard -> Manage Jenkins ->Manage Nodes.
  • Click on New Node from the options on the left and give a name to the node and then click OK. For the first time user, only one option of “Permanent Agent” will be present (check it). After that, we get another option, “Copy Existing Node” which is self-explanatory.
New Linux Node
  • Click on OK and you will see various options as following image :

The various sections that came up are explained as below :

  1. Name: Name of the Slave which should be unique.
  2. Description: Description of this slave. It is optional but giving a description of it would be really helpful for other team members.
  3. # of executors: The maximum number of concurrent builds that Jenkins may perform on this agent. I have used 1 executor for testing purpose, but a good practice would be the number of CPU cores on the machine.
  4. Remote root directory: An agent needs to have a directory dedicated to Jenkins. Specify the path to this directory on the agent.
  5. Labels: Labels are used to group multiple agents into one logical group. Multiple labels must be separated by a space.
  6. Usage: Controls how Jenkins schedules builds on this node.
  7. Launch method: It controls how Jenkins starts this agent.
    • Launch agent via execution of command on the master: Starts an agent by having Jenkins execute a command from the master. Use this when the master is capable of remotely executing a process on another machine, e.g. via SSH or RSH.
    • Launch slave agents via SSH: Starts a slave by sending commands over a secure SSH connection. The slave needs to be reachable from the master, and you will have to supply an account that can log in to the target machine. No root privileges are required. If we select this, it will further need following credentials :
      • Host: The IP address of the slave machine (private IP address if inside VPC).
      • Credentials: Select the credentials to be used for logging in to the remote host.
      • Host Key Verification Strategy: Controls how Jenkins verifies the SSH key presented by the remote host whilst connecting. These are of 4 types :
        • Known Host file verification strategy: Checks the known_hosts file (~/.ssh/known_hosts) for the user Jenkins is executing under, to see if an entry exists that matches the current connection.
        • Manually provided key verification strategy: Checks the key provided by the remote host matches the key set by the user who configured this connection.
        • Manually trusted key verification strategy: Require a user with Computer. CONFIGURE permission to authorize the key presented during the first connection to this host before the connection will be allowed to be established.
        • Non-Verifying verification strategy: Does not perform any verification of the SSH key presented by the remote host, allowing all connections regardless of the key they present. It’s not advisable to select as it may open the path for attackers.
  8. Availability: Controls when Jenkins starts and stops this agent. It has two options available.
    • Keep this slave on-line as much as possible: This one is the default and normal setting. In this mode, Jenkins tries to keep the slave on-line as much as possible. If Jenkins can start the slave without user assistance, it will periodically attempt to restart the slave if it is unavailable. Jenkins will not take the slave off-line.
    • Take this slave on-line when in demand and off-line when idle: In this mode, if Jenkins can launch the slave without user assistance, it will periodically attempt to launch the slave while there are waiting jobs else the slave will be taken off-line by Jenkins.

Ubuntu Slave :

Changes on slave node :

For next steps, we will login into our Ubuntu slave machine and perform the below tasks to configure the SSH with the Jenkins master.

  • Update the APT repository and install Java on slave :
ubuntu@ip-10-0-1-75:~$ sudo apt-get update
.
.
ubuntu@ip-10-0-1-75:~$ sudo apt-get install openjdk-8-jdk -y
  • Check if java is installed :
ubuntu@ip-10-0-1-75:~$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-2ubuntu0.18.04.1-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)
  • Create user along with the home directory and bash as default shell :
ubuntu@ip-10-0-1-75:~$ sudo useradd linux-slave -m -s /bin/bash
  • Verify the user created :
ubuntu@ip-10-0-1-75:~$ cat /etc/passwd | grep slave
linux-slave:x:1001:1001::/home/linux-slave:/bin/bash
  • Update the password for newly created user :
ubuntu@ip-10-0-1-75:~$ sudo passwd linux-slave
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
  • Use the usermod command to add the user to the sudo group.
ubuntu@ip-10-0-1-75:~$ sudo usermod -aG sudo linux-slave
  • Login as linux-slave user :
ubuntu@ip-10-0-1-75:~$ sudo su - linux-slave
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

linux-slave@ip-10-0-1-75:~$ whoami
linux-slave
  • Generate the keys using ssh-keygen command. The private and public keys will be created at these locations `/home/linux-slave/.ssh/id_rsa` and `/home/
    linux-slave /.ssh/id_rsa.pub`
linux-slave@ip-10-0-1-75:~$ ssh-keygen -t rsa -N "" -f /home/linux-slave/.ssh/id_rsa
Generating public/private rsa key pair.
Created directory '/home/linux-slave/.ssh'.
Your identification has been saved in /home/linux-slave/.ssh/id_rsa.
Your public key has been saved in /home/linux-slave/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:2bcDaORcBltvgN39OiODi2VxWlg/JxvCWx17/hHpH+Y linux-slave@ip-10-0-1-75
The key's randomart image is:
+---[RSA 2048]----+
|        .oo. .   |
|        .+.oo .. |
|        o o+o. o+|
|       + *o.= *+=|
|        S o*.+.Oo|
|       .  =o+.==.|
|         + .oooo=|
|        . .  . Eo|
|                 |
+----[SHA256]-----+
  • Change directory to .ssh and copy the public key (.pub) to authorized_keys file :
linux-slave@ip-10-0-1-75:~$ cd .ssh

linux-slave@ip-10-0-1-75:~/.ssh$ ls
authorized_keys  id_rsa  id_rsa.pub

linux-slave@ip-10-0-1-75:~/.ssh$ cat id_rsa.pub > authorized_keys
  • Provide permissions only to owner (linux-slave) of the authorized_keys file :
linux-slave@ip-10-0-1-75:~/.ssh$ chmod 700 authorized_keys

CHANGES ON Master NODE :

We are done with the configurations on the Ubuntu slave machine, now we will configure our Jenkins master by remoting into it :

  • Make sure jenkins user is there on the master machine and its part of the sudo group :
ubuntu@ip-10-0-1-171:~$ cat /etc/passwd | grep jenkins
jenkins:x:111:115:Jenkins,,,:/var/lib/jenkins:/bin/bash

ubuntu@ip-10-0-1-171:~$ sudo usermod -aG sudo jenkins

ubuntu@ip-10-0-1-171:~$ groups jenkins
jenkins : jenkins sudo
  • Here, we can see that the home directory of jenkins user is /var/lib/jenkins and default shell is bash.
  • Login as jenkins user in your master machine :
ubuntu@ip-10-0-1-171:~$ sudo su - jenkins
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

jenkins@ip-10-0-1-171:~$
  • Create the .ssh directory if it doesn’t exist and change the current directory to .ssh.
jenkins@ip-10-0-1-171:~$ mkdir -p ~/.ssh
jenkins@ip-10-0-1-171:~$ cd ~/.ssh
  • Tell Jenkins master to grab the public key of slave machine (using its private IP address) and add it to its known_hosts file for connection to take place every time seamlessly.
jenkins@ip-10-0-1-171:~/.ssh$ ssh-keyscan -H 10.0.1.75 >>/var/lib/jenkins/.ssh/known_hosts
# 10.0.1.75:22 SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.2
# 10.0.1.75:22 SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.2
# 10.0.1.75:22 SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.2
  • Finally, make jenkins user as owner of it and remove other user’s access to it.
jenkins@ip-10-0-1-171:~/.ssh$ chown jenkins:jenkins known_hosts

jenkins@ip-10-0-1-171:~/.ssh$ chmod 700 known_hosts

jenkins@ip-10-0-1-171:~/.ssh$ ls -l
total 4
-rwx------ 1 jenkins jenkins 2418 Mar 14 10:26 known_hosts

Changes on Jenkins Master :

We are done with the changes in master node as well. Now, lets open the Jenkins master (UI) and complete the setup :

  • Open the new node configurations again and fill the sections as below :
  • To add credentials, click on Add drop down and then on Jenkins Credential Provider icon. It will show a pop up to create the credential that will be used to SSH on slave node, hence we will select our pre-configured SSH username and private key of slave node.
  • While setting up our slave node earlier, we gave the username as linux-slave and private key was generated at ~/.ssh folder. So, to fetch the private key we will run below commands on slave node :
linux-slave@ip-10-0-1-75:~$ cd .ssh

linux-slave@ip-10-0-1-75:~/.ssh$ ls -l
total 12
-rwx------ 1 linux-slave linux-slave  406 Mar 14 09:55 authorized_keys
-rw------- 1 linux-slave linux-slave 1675 Mar 14 09:53 id_rsa
-rw-r--r-- 1 linux-slave linux-slave  406 Mar 14 09:53 id_rsa.pub

linux-slave@ip-10-0-1-75:~/.ssh$ cat id_rsa
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA4aj4TH/W2/SkqrK26OuWddnzGPaiD3dcL/bRPP5Z2cN5he8e
hfAVpx22ImnvZbtWLaAtbZzPBtULCRSVHyeQwyYzzymV4dRLu986PUMbIJk4lTI8
.
.
.
RBvteF/8QJDp/gqJG0j0OG1PKj1XiOeVrJfsbLYakKFCX6l5Qqfr0Cx5gLB3lB7X
clrc3H5gd6Rvs93NlOEWvjhxKBrN6zkn39zeGWOKjGpoimjITDsn
-----END RSA PRIVATE KEY-----
  • Copy the private key from slave node and paste it on the Private Key (Enter directly) section of the pop up as below :
Add credentials
  • Finally click on Add and our credentials will be added. Then select our added credential in the Credentials section of the Launch method and click on Save.
  • Once the Save button is clicked we will be directed back to Manage Nodes page and we can see our newly added linux-slave, but it is offline :
  • Don’t loose patience and simply press Refresh status button :
  • We can now see our linux-slave is online. Click on the linux-slave to get more info about our Agent:
  • We are done successfully setting up our Linux Slave, let’s create a job and give it a test.

Test linux slave :

  • To test our linux-slave we will create a freestyle job :
  • In the General section of the configuration, we will setup Restrict where this project can be run to linux label :
  • Scroll down to Build section and add an Execute shell build step. Provide any linux specific command to test our linux-slave :
  • Finally click on Apply and Save. Then go ahead and trigger the job with Build Now option, after few seconds we should get the following Console Output :

Yayy! We have successfully added and tested our Linux Slave on Jenkins.

Windows Slave :

We will now add another slave having Windows OS which can be used to build .NET applications and some other windows specific purposes :

  • To create a new Slave – Click on New Node, name the node and click OK .
  • Fill all the required details, if needed check the above sections for information on the various options.
  • Choose the appropriate Launch method – Launch agent via Java Web Start.
  • If you are missing this option, you might not be able to start/configure the Jenkins slave because, by default Jenkins disabled the TCP Port for JNLP agents. We can set this up in Configure global security option in Manage Jenkins .
  • And then change the TCP port for JNLP agents to Fixed (8888) from Disabled.

  • Since we are using port 8888 for incoming agent connections through JNLP, we need to expose the same in our AWS Security group for Jenkins Master instance.
  • Once the new node is created, it will be in Offline mode with instructions on some ways to connect this slave to master.

Changes on slave :

  • Install JRE on the windows slave machine, a comprehensive tutorial on installing JRE on Windows 10 can be found here.
  • Open the Jenkins master URL from Windows slave machine and navigate to windows-slave Configuration page.
  • Once done, just click on Launch Button – You will see a new popup which confirms the Jenkins Master-Slave Connection.
  • Refresh the Agent windows-slave page to see the node online.
  • Lets now create and install this as a Windows service which will avoid the hassle to go to url and perform the same operation every time.
  • Once you create the service it will always be up & running and then you can manage it through Windows Services.
  • Don’t close the pop-up, instead just click on File Menu – If its not visible just maximise the popup window – and click on Install as a service option.
  • It will prompt you for the confirmation, click on OK.
  • Go to Windows Settings and then to check the Windows Services.
  • You can see the Jenkins Windows Slave is successfully configure and running (If not then just Start the Service)
  • Go back to your Jenkins Slave Page and now you will see that Agent is Successfully Running and ready to build your project.

That’s it. You have successfully configured the Jenkins Slave on Windows Machine and Installed it as a Windows Service.

Test windows slave :

  • To test our windows-slave we will create a freestyle job :
  • In the General section of the configuration, we will setup Restrict where this project can be run to windows label :
  • Scroll down to Build section and add an Execute windows batch command build step. Provide any windows specific command to test our windows-slave:
  • Finally click on Apply and Save. Then go ahead and trigger the job with Build Now option, after few seconds we should get the following Console Output :

Yayy! We have successfully added and tested our Windows Slave on Jenkins as well.

To sum up, we have successfully spun up two AWS instances (Ubuntu and Windows), configured them as Jenkins Slaves and finally tested them separately by running a test job.

Thanks for reading the article, let me know what you think of this and feel free to contact in case anything bothers.

CHEERS !!

6 thoughts on “Configuring Jenkins Slaves (Linux+Windows) on AWS EC2

  1. hello
    I have a question.
    The private IP of EC2 will change after reboot.
    Will the “known_hosts” you set will work after reboot?

    Like

    1. Once an EC2 instance is launched, it’s assigned a private IP address at boot time. An instance’s private IP address will never change during the lifetime of that instance. Private IP addresses do not change in EC2 with a simple restart. As per AWS, when an instance is launched in EC2-Classic, it is automatically assigned a public IP address to the instance from the EC2-Classic public IPv4 address pool. This behaviour cannot be modified. When an instance is launched in a VPC, you control whether it receives a public IP or not. The public IP address can change under certain circumstances.

      Like

  2. Hi
    I followed the steps described to create the slave node.
    But when trying to connect to the slave node amd getting below shown error.
    Can you help me on this.

    [SSH] The SSH key presented by the remote host does not match the key saved in the Known Hosts file against this host. Connections to this host will be denied until the two keys match.

    Like

    1. Hello ANANDASOKANVIT,

      I have verified the steps above and it works fine, please make sure the key pairs are rightly set and within proper linux users.

      Thanks for the comment and let me know if the problem persists.

      Like

  3. Hi All,

    Might be a dumb question, but want to know, can I connect AWS machine as client to jenkins which is hosted on my local. I’ll keep AWS VPN connected.

    Thanks in Anticipation

    Like

    1. Honestly I haven’t tried it, but it should work if your are connected via AWS VPN, all it takes it ssh connection to internal hosts. Give it a try and let us know as well 🙂

      Like

Leave a Reply to Vishnu Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s