Packer Workflows with Jenkins - Setup
Tags: packer, jenkins
Published: 2024-02-22 15:19:07

# Introduction

Probably most folks know both of these products by now, but I've ripped a short intro for both.

And also for a bit of a disclosure. I am by no means an expert in any of this. I run though these out of general curiosity, training, and experience. This should not be followed as a production or development environment guide. This is primarily persona lab setup. 

This is going to most likely be a two-parter. This first part will be setting up the require infrastructure. Next will be setting up a Packer template and running a couple of builds. 

What I liked about this setup is only Multipass needs setup to launch the instances, but nothing else will need installed or configured on your local system. 

## HashiCorp Packer

> Packer lets you create identical machine images for multiple platforms from a single source configuration. A common use case is creating golden images for organizations to use in cloud infrastructure.

[Packer Documentation](https://developer.hashicorp.com/packer/docs?ajs_aid=0e177459-032a-482f-8cd5-4793f1a18a31&product_intent=packer)

## Jenkins

>Jenkins is a self-contained, open source automation server which can be used to automate all sorts of tasks related to building, testing, and delivering or deploying software.
>
Jenkins can be installed through native system packages, Docker, or even run standalone by any machine with a Java Runtime Environment (JRE) installed.

[What is Jenkins?](https://www.jenkins.io/doc/#what-is-jenkins)

# Objectives

I have honestly not used Jenkins that much, but it is a heavily used CI/CD solution. There are many more options out there - some much more modern solutions - Jenkins still is very prevalent and will most likely be here to stay for some time. 

For this the objects are: 

1. Use Multipass to create two instances.
	1. Jenkins
	2. Packer
2. Set up Jenkins. 
3. Set up Packer instance as a worker node. Agent will only run for Packer jobs. 

# Prerequisites

- Knowledge of Jenkins and Packer. 
- [Multipass](https://multipass.run/)  
	- I love Multipass for quick Ubuntu instances spun up for testing or as a playground. Wish I would have known and used of it sooner.

# Instance Setup

Following sections will walk through the necessary setup. 

Using Multipass create two virtual machines. 

```
╰─ multipass version                               
multipass   1.13.1+mac
multipassd  1.13.1+mac
```

Launch Jenkins-Server

`multipass launch -n jenkins-server`

Launch Jenkins-Packer Server

`multipass launch -n jenkins-packer`

Run multipass list to see instances.
```
multipass list                                     
Name                    State             IPv4
jenkins-packer          Running      192.168.64.9
jenkins-server          Running      192.168.64.8
```
And there we go, we have two instances set up for testing. Multipass is great for this since it can easily spin up and down instances for testing. 

These were set up with the default Ubuntu image and configuration settings, there is much more which can be done with Multipass, so check out their [documentation](https://multipass.run/docs). 

## Jenkins

First step will be to connect to the instance you will run Jenkins on. 

`multipass shell jenkins-server`

![Ubuntu version](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2h5hyqdwuze40o7ge44m.png)

Once on the server, I will run through the setup necessary for Jenkins. 

*Note: I will be using root permissions to simplify steps.*

### Install Jenkins

For full install instructions, please see the following documentation:  

[Installing Jenkins - Ubuntu](https://www.jenkins.io/doc/book/installing/linux/#debianubuntu)

```bash
sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \
  https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
  https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
  /etc/apt/sources.list.d/jenkins.list > /dev/null
sudo apt-get update
sudo apt-get install jenkins
```

Error due to not having Java on the instance, this will get corrected: 

```
Job for jenkins.service failed because the control process exited with error code.
```

Run the following to setup openjdk: 

`snap install openjdk`

```
openjdk 21.0.3+3 from John Neffenger (jgneff) installed
```

Run the following three commands to start up Jenkins : 

```bash
sudo systemctl enable jenkins
```

```bash
sudo systemctl start jenkins
```

```bash
sudo systemctl status jenkins
```

Should get a confirmation when running status command. 

```
root@jenkins-server:~# sudo systemctl status jenkins
● jenkins.service - Jenkins Continuous Integration Server
     Loaded: loaded (/lib/systemd/system/jenkins.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2024-02-21 12:31:36 EST; 1s ago
   Main PID: 7306 (java)
      Tasks: 40 (limit: 1069)
     Memory: 367.2M
        CPU: 9.747s
     CGroup: /system.slice/jenkins.service
             └─7306 /usr/bin/java -Djava.awt.headless=true -jar /usr/share/java/jenkins.war --webroot=/var/cache/jenkins/war --httpPort=8080
```

### Jenkins - Firewall

Needing to open up the firewall on this host to ensure communication from my personal laptop and the packer instance. 

Installing 

```bash
apt install firewalld
```

Once installed, run the following commands: 

_Note: For testing I'm going to have port 50000 open for the agent to connect back to the Jenkins controller_ 

```bash
YOURPORT=8080
PERM="--permanent"
SERV="$PERM --service=jenkins"

firewall-cmd $PERM --new-service=jenkins
firewall-cmd $SERV --set-short="Jenkins ports"
firewall-cmd $SERV --set-description="Jenkins port exceptions"
firewall-cmd $SERV --add-port=$YOURPORT/tcp
firewall-cmd $SERV --add-port=50000/tcp
firewall-cmd $PERM --add-service=jenkins
firewall-cmd --zone=public --add-service=http --permanent
firewall-cmd --reload
```

Most likely will get some warnings, but to verify, I will connect from my laptop to the host. If done correctly, you should see the following:

![Jenkins Initial Setup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cmqgqmpqsdxl9wufn7ow.png)

For Jenkins configuration, I will run through the defaults, without any major changes. 

Once set up is complete, you have a clean slate to work with. 

![Jenkins dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wxce0dmh13fat65ykmqv.png)
___

## Packer Node

Before jumping onto the instance we set up for Packer, I we need to stay on the Jenkins console for a little bit more. 

### Node Setup

For more detailed instructions and terminology on distributed nodes, see the [documentation](https://www.jenkins.io/doc/book/managing/nodes/).

I'll keep this relatively light for prep.  

1. Install java. 
2. Create the following directory /home/packer/agent. 

### Agent Setup - Jenkins Side

Back in the Jenkins UI, go to manage Jenkins and select nodes: 

![manage jenkins](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47ckfhr2bpckmct9ip7p.png)

Create a new node: 

![Packer Node](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j22n0n69qne5mtvqqmgh.png)

Configure node: 

![Configuration-1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b9m9j862qxq8hbvvvd9w.png)

continued...

![Configuration continued](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cjbxiootkqexlencqrp8.png)

Once configured, select status for the node.

![Node Status](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0abhep6x5n040sx6xgn7.png)

Grab the commands and we will use them when we are back on the node. 

![CLI command](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ehgj7xisn7sw40yx5c3s.png)

One last stop, go back to manage Jenkins and go to security. 

In security, make the following updates to enable agent connectivity. Using port `50000`, as it is the static port I opened up on the firewall. 

![Agent networking](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o9hifel06bz0b83ivwav.png)

### Agent Setup - Packer Instance 

Make sure you still have the command you copied earlier, we will use it here. 

Change directory to - `/home/packer/agent`

Run the first command to download the agent. Port 8080 should be open to connect to the Jenkins instance. 

```
root@jenkins-packer:/home/packer/agent# curl -sO http://192.168.64.8:8080/jnlpJars/agent.jar
root@jenkins-packer:/home/packer/agent# ls
agent.jar  remoting
```

Run the second command to start the Jenkins agent. In the info, you should see that port 50000 is in use for the connection. I cleared out most of the message and focusing on the connection information. 

```
root@jenkins-packer:/home/packer/agent# java -jar agent.jar -url http://192.168.64.8:8080/ -secret 322747aa59050708e4af27206812443a9556c25ca45b0456ea70799e20f7db23 -name packer -workDir "/home/packer/agent"
...
INFO: Agent discovery successful
  Agent address: 192.168.64.8
  Agent port:    50000
  Identity:      1f:dc:34:23:1b:e1:84:6f:9c:34:72:c7:d1:5f:c2:74
...
```

If going back to the UI, you can see the Packer agent is now connected. This agent will drop out when no communication is occurring and will come back up when a job is started. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ew7bjbqzdfng18co6suo.png)

_Note: Agent can be run as a service or in a Docker container. For this setup I'm keeping it simple, but both of those are options._

---

## First Job

Now that we have both a Jenkins node and a Packer node with an agent, we will set up our first job. 

![Dashboard-create job](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gbfuhulsd55toz8uhwqv.png)

Select create a job and select pipeline as the type. 

![selecting job type](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5g9passtihr27xryj8o.png)

After hitting OK, I am going to do a quick hello world to confirm connectivity with the node. Here is a sample that Jenkins provides. I do update to use the packer agent and node: 

```groovy
pipeline {
    agent {
  label 'packer'
}
    stages {
        stage('Hello') {
            steps {
                echo 'Hello World'
            }
        }
    }
}
```

From the console output : 

```log
Started by user [lykins](http://192.168.64.8:8080/user/lykins)
[Pipeline] Start of Pipeline[](http://192.168.64.8:8080/job/packer/2/console#)
[Pipeline] node[](http://192.168.64.8:8080/job/packer/2/console#)
Running on [packer](http://192.168.64.8:8080/computer/packer/) in /home/packer/agent/workspace/packer
[Pipeline] {[](http://192.168.64.8:8080/job/packer/2/console#)
[Pipeline] stage[](http://192.168.64.8:8080/job/packer/2/console#)
[Pipeline] { (Hello)[](http://192.168.64.8:8080/job/packer/2/console#)
[Pipeline] echo[](http://192.168.64.8:8080/job/packer/2/console#)
Hello World
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
```

At this point, we are done with setting up the worker node with the agent. Other options would be running it in docker or as a service, but doing a simple setup for now.