Packer Workflows with Jenkins - Building

lykins

lykins

Posted on February 29, 2024

Packer Workflows with Jenkins - Building

This is the second part of a two-parter. Just realized I was about to write a book if I kept going. Now that we have the necessary instances and setup in part 1, we will continue and start building out our images.

Note:
I am using openSUSE Leap, which will soon be discontinued and only maintained for a few years after 15.6. Alternatives would be either Tumbleweed, MicroOS, and Slowroll.

Objectives

Primary:

  • Create a repository for Packer Templates.
  • Use Jenkins to build the images.
    • I'm going to use a Jenkinsfile as well.

Secondary:

  • Parallel Build in AWS and Azure.

Prerequisites

Assumption is a basic knowledge of Jenkins, Packer, and Azure. I'm not going to go into much details on how to set up Azure accounts, service accounts, and some other items in this. There are basic examples out the which can be referenced.

  1. Version Control System (GitHub, GitLab, etc.)
  2. Azure Subscription
  3. AWS Account

Repository Setup

*I'm going to keep the repository simple. In most cases the following are needed. *

  • Folder/Files necessary for CI/CD pipelines
    • In this case, I will end up using Jenkinsfile after all. Didn't initially plan to.
    • If this was GitHub actions, I'd have a .github with workflows folder. For Jenkins, I'll drop them under a Jenkins directory.
  • Packer Folder
    • In this folder I also have:
      • Builds: for build templates
      • Files: used by the templatefile function or file provisioner.
      • Scripts: for longer scripts which will be more frequently updated. I try to stay away from inline scripts with a provisioner.
      • Var-Files: directory to store var-files which can get pulled in by the pipeline.

*For a tree view: *



.
├── jenkins
│   └── suse-linux-base
└── packer
    ├── builds
    │   ├── builds_suse.pkr.hcl
    │   └── variables.pkr.hcl
    ├── files
    │   └── README.md
    ├── scripts
    │   └── README.md
    └── var-files
        └── README.md

7 directories, 6 files



Enter fullscreen mode Exit fullscreen mode

The repository was made public for anyone that wants to fork it as a demo.

https://github.com/benjamin-lykins/demo-jenkins-packer.git

Jenkins Setup

Create a folder for packer builds.

Folder

Jenkins Setup - Credentials

The next item you will want to set up is credentials which will be used by jobs in this folder.

*On the left hand side, select Credentials. *

Credentials

*Select stores scoped to packer builds or the name of the folder you created. Click on the global domain. *

Scope

*Add credentials. *

Add Credentials

*For credentials, we will do secret text for all Azure related credentials. Create a secret for each: *
1. Client ID (Service Account ID)
- azure_client_id
2. Client Secret (Service Account Secret)
- azure_client_secret
3. Subscription ID
- azure_subscription_id
4. Tenant ID
- azure_tenant_id

Credential Setup

*When all the credentials have been added, it should look like this: *

Credentials

Jenkins Setup - First Workflow

This first workflow will be simple. Get the latest openSuse image and build and image.

Go into the packer builds folder and create a new pipeline. I called mine suse-linux-base.

*Nothing major setup wise, for Git: *

Pipeline Setup

For Jenkinsfile:

Jenkinsfile

Jenkins - First Build

If forking the repository I provided, the first run should build and run properly.

This is coming from a fresh Azure Account and Subscription.

Azure Welcome Screen

No resource groups exist.

Resource Groups

At this point we can kick off the first build.

In suse-linux-base, select Build Now.

In my case I had a small hiccup, took me a couple of tries to get it working:

Pipeline Runs

Ended up changing the Create Azure Resource step a couple of times, it was running commands out of order, once I got it running, it was doing its thing properly.

  • packer-rg was created.
  • packer-rg-temp was created as a temporary resource group to build images in.

Resource Groups

Build Completion

You can see the build was completed in the both Jenkins and in the Azure console.

Managed Image

Create VM from Image

From the image, select create VM. Once ready, ssh to connect.



openSUSE Leap 15.5 x86_64 (64-bit)

If you are using extensions consider to enable the auto-update feature
of the extension agent and restarting the service. As root execute:
  - sed -i s/AutoUpdate.Enabled=n/AutoUpdate.Enabled=y/ /etc/waagent.conf
  - rcwaagent restart

As "root" use the:
- zypper command for package management
- yast command for configuration management

Have a lot of fun...


Enter fullscreen mode Exit fullscreen mode

Now that is it. Pretty straight-forward setup. If this is all I wanted, then this would be the stopping point. Very basic usage of Packer, but looking to expand upon what is possible.

Parallel Builds

One of my old jobs I was a vSphere administrator. Every month the virtual machine templates would need to be patched and updated in some way. It was a very manual process and we did not use a tool like Packer to automate or store the configuration in code. It was document and click-ops heavy.

Now, I will not be building images in vSphere, but I will like to build the same image in two public clouds, AWS and Azure.

AWS - Setup

Nothing major needed here, you will need two items. Packer will use default VPC and subnet.

  1. AWS Secret and Key
  2. Subscription to openSUSE Leap

openSUSE Leap Subscription

Packer - Setup

To set this up, I will need to add in an additional plugin to the configuration.

Create New Branch

I am going to create a new branch called parallel-aws-azure for this use case.



packer {
  required_plugins {
    azure = {
      source  = "github.com/hashicorp/azure"
      version = "~> 2"
    }
    amazon = {
      source  = "github.com/hashicorp/amazon"
      version = "~> 1"
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

Add a new source block for AWS.

Check for ssh_username: Default user names. In this scenario ec2-user or root would have worked.



source "amazon-ebs" "suse" {
  ami_name      = "suse-image-${local.time}"
  instance_type = "t2.micro"
  region        = "us-east-2"
  source_ami_filter {
    filters = {
      name                = "openSUSE-Leap-*-hvm-ssd-x86_64-*"
      root-device-type    = "ebs"
      virtualization-type = "hvm"
    }
    most_recent = true
    owners      = ["679593333241"]
  }
  ssh_username = "ec2-user"
}


Enter fullscreen mode Exit fullscreen mode

And lastly, update the build block with the new source.



build {
  sources = ["source.azure-arm.suse", "source.amazon-ebs.suse"]

  provisioner "shell" {
    inline = ["echo foo"]
  }
}


Enter fullscreen mode Exit fullscreen mode

Jenkins - Setup

I'll keep the same jenkinsfile for this. All I will update is adding credentials for AWS.



    environment {
        AZURE_SUBSCRIPTION_ID = credentials('azure_subscription_id')
        AZURE_TENANT_ID = credentials('azure_tenant_id')
        AZURE_CLIENT_ID = credentials('azure_client_id')
        AZURE_CLIENT_SECRET = credentials('azure_client_secret')
        AWS_ACCESS_KEY_ID= credentials('aws_access_key_id')
        AWS_SECRET_ACCESS_KEY= credentials('aws_secret_access_key')
    }


Enter fullscreen mode Exit fullscreen mode

Also, ensure thepacker build folder has these keys.

AWS secrets

Create New Pipeline

I called mine parallel-build.

Point to the new branch:

Point to new branch

Keep the same Jenkinsfile path:

Jenkinsfile path

Once ready, hit build and see how it goes.

Packer by default will run both builds concurrently.



[1;32mazure-arm.suse: output will be in this color.[0m
[1;36mamazon-ebs.suse: output will be in this color.[0m


Enter fullscreen mode Exit fullscreen mode

When completed, Packer will output the AMI id and Azure managed image:

Packer:



==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs.suse: AMIs were created:
us-east-2: ami-09de620f124c9bc00


Enter fullscreen mode Exit fullscreen mode

Azure:



--> azure-arm.suse: Azure.ResourceManagement.VMImage:

OSType: Linux
ManagedImageResourceGroupName: packer-rg
ManagedImageName: suse-image-20240226195516
ManagedImageId: /subscriptions/****/resourceGroups/packer-rg/providers/Microsoft.Compute/images/suse-image-20240226195516
ManagedImageLocation: East US


Enter fullscreen mode Exit fullscreen mode

In AWS console:

AWS AMI

In Azure console:

Azure Image

Nothing too drastically different when it comes to parallel builds in multiple public clouds when setting up in Jenkins.

Conclusion

This is a very surface level example of using Packer with Jenkins. Additional use cases would be:

  • Layering Builds
    • Reference newly build images and add components necessary for hosting an application, such as IIS or Tomcat on the host.
  • Chaining Pipelines
    • This can be done similar to layering builds. Have the base image pipeline run first to build a base image, if success, have a subsequent pipeline trigger to build on the image built in the base pipeline.
  • Automate Provisioning of Images
  • Automate Testing of Images
    • I honestly do not have many ideas for this. For me if an image is built with Packer and can be provisioned whether in AWS or Azure, and finally an application can be deployed on it, then that is enough for me. I could see security and compliance tools. Probably just do not have the exposure for this.
💖 💪 🙅 🚩
lykins
lykins

Posted on February 29, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related