Build and Consume Alpine Linux vSphere Images
Published: 2023-12-25 20:39:21

---
title: Build and Consume Alpine Linux vSphere Images
published: true
date: 2023-12-24 09:00:00 UTC
tags: 
canonical_url: https://blog.engyak.co/2023/12/alpine/
---

## Deploying Linux for the impatient

If you've ever wanted to just "test something out really quick" in a live environment, Linux distributions have always been generally lightweight, but that's not the only implicit requirement for experimentation.

A Linux IaaS distribution should be:

- Reasonably secure (basic hardening applied, fewer packages == fewer vulnerabilities)
- Light on disk usage (shortening deployment times)
- Light on system resources, e.g. CPU/Memory
- Flexible (supports a package manager with a wide ecosystem of packages)

Package flexibility is usually the compromise made here - but when you're deploying programmable code, container images and virtual environments like Python's `venv` should be able to bridge _some_ gaps.

[Alpine Linux](https://www.alpinelinux.org/about/) focuses on these goals - but doesn't compromise on automation. Combined with a [dynamic inventory bootstrapping process](https://blog.engyak.co/2023/09/vsphere-dayn/), it's relatively straightforward to bring Alpine's [APK ansible module](https://docs.ansible.com/ansible/latest/collections/community/general/apk_module.html) into play to build any extra software on a new machine.

### Customizing and building the ISO

First, let's upload the ISO to a datastore:

[![Add ISO Image](https://blog.engyak.co/2023/12/alpine/1_addiso.png)](1_addiso.png)

Let's create a new Virtual Machine. We'll attach the ISO to it

[![Create VM from cluster](https://blog.engyak.co/2023/12/alpine/2_createvm.png)](2_createvm.png)
[![Set VM Name](https://blog.engyak.co/2023/12/alpine/3_names.png)](3_names.png)

Target any storage and compute as preferred. SSD datastores will be faster, of course.

vSphere 8.0 Update 2 doesn't have a preset for Alpine Linux, and the guest OS options are _important_ - it defines what paravirtualized hardware is available:

[![Guest OS Customization: Linux Other 5.x (64 Bit)](https://blog.engyak.co/2023/12/alpine/4_guest.png)](4_guest.png)

Ensure that VMXNET 3 and PVSCSI are both available. The "New Network" will become the default port-group assigned to the template.

CPU/Memory are mostly irrelevant, as the deployment pipeline can customize afterwards - and this OS doesn't need much in terms of resources:

[![Guest Hardware Customization](https://blog.engyak.co/2023/12/alpine/5_hardware.png)](5_hardware.png)

Select the Alpine "Datastore ISO" and enable "Connect and Power On" for the assigned CD/DVD drive:

[![ISO Configuration](https://blog.engyak.co/2023/12/alpine/6_iso.png)](6_iso.png)

Start the machine - it'll boot to a command prompt _very quickly_. Log in as root:

[![Start Alpine](https://blog.engyak.co/2023/12/alpine/7_alpine_start.png)](7_alpine_start.png)

The [installation guide](https://wiki.alpinelinux.org/wiki/Installation) indicates to use the `setup-alpine` script, and follow the prompts:

[![Alpine Setup](https://blog.engyak.co/2023/12/alpine/8_alpine_setup.png)](8_alpine_setup.png)

The majority of setup here is extremely simple - because it's not installing a bunch of software. GUIs are also possible after the installation is complete - but it does defeat the point.

Instead of rebooting as instructed, shut the virtual machine down and delete the disk drive:

- **Note: The shutdown process isn't installed with Alpine, and the following command _does_ execute a graceful shutdown!**

```
shutdown now
```

[![Remove CD/DVD-ROM drive](https://blog.engyak.co/2023/12/alpine/9_remove_iso.png)](9_remove_iso.png)

Start the machine up - we'll want to add some quality of life improvements to this machine like guest tools:

```
{{ insert favorite editor here }} /etc/apk/repositories
```

Remove the `#` from the line ending in `/alpine/v{{ version }}/community` and save.

Per [Alpine's guide](https://wiki.alpinelinux.org/wiki/Open-vm-tools), install and enable open-vm-tools:

```
apk add open-vm-tools open-vm-tools-guestinfo open-vm-tools-deploypkg
rc-service open-vm-tools start
rc-update add open-vm-tools boot

```

Once running, ensure that guest power actions are available:

[![Guest Power Actions](https://blog.engyak.co/2023/12/alpine/10_test_guest.png)](10_test_guest.png)

Personally, I like testing, so instead of powering off the VM, I use the guest action to ensure everything is working. Either way, shut the VM down.

Hit "Actions" on the VM, or right-click it, and select "Clone → Clone as Template to Library":

[![Clone as Template to Library](https://blog.engyak.co/2023/12/alpine/11_clone_to_library.png)](11_clone_to_library.png)

Select whatever storage backing and content libraries are preferable at this point. It won't take long to clone in. Delete the old VM whenever it makes sense, I usually do so after testing a deployment:

[![Deploy Test VM](https://blog.engyak.co/2023/12/alpine/12_deploy_test.png)](12_deploy_test.png)

- Note: Set "Power on VM after creation" - this will clone _extremely_ quickly and boot even faster.

### Modifying the deployment pipeline
<style>
     
    @import url('https://cdn.rawgit.com/lonekorean/gist-syntax-themes/d49b91b3/stylesheets/idle-fingers.css');
    
    @import url('https://fonts.googleapis.com/css?family=Open+Sans');
    body {
      font: 16px 'Open Sans', sans-serif;
    }
    body .gist .gist-file {
      border-color: #555 #555 #444
    }
    body .gist .gist-data {
      border-color: #555
    }
    body .gist .gist-meta {
      color: #ffffff;
      background: #373737; 
    }
    body .gist .gist-meta a {
      color: #ffffff
    }
</style>

The deployment pipeline code itself is available [here](https://gist.github.com/ngschmidt/88fe09a1c5733735a4232dd24c44f78e). I've made some modifications from previous versions:

- GitHub Actions now supports the `choice` type, which means we can select UUIDs. There isn't a way to build a "friendly name" mapping. We achieve this by creating a "lookup dictionary" with the friendly name as a key and a UUID as the value. This list will need to be populated via data collection (featured below).
<script type="application/javascript" src="https://gist.github.com/ngschmidt/88fe09a1c5733735a4232dd24c44f78e.js"></script>

First, we'll need to find out what the UUID of the template and cluster are. [Here's an example](https://gist.github.com/ngschmidt/0c7687cb62ba6f7bb98feb67ff936906) to collect the required information. The UUIDs of system resources (and this template) are only available via the API. Use this information to form the `parameters.yml file created in the GitHub Action workflow`, e.g. `datastore`, `cluster`, `folder`

<script type="application/javascript" src="https://gist.github.com/ngschmidt/0c7687cb62ba6f7bb98feb67ff936906.js"></script>

Adjusting and running this workflow will allow an engineer to populate the previous workflow and expose vSphere assets to further deployment automation!

For reference, this machine deployed in about 3 seconds on a shared SSD (iSCSI):

[![Deployment timeline](https://blog.engyak.co/2023/12/alpine/13_deployment_benchmark.png)](13_deployment_benchmark.png)

The GitHub workflow takes >2 minutes to complete, but the workflow it's attached to has manual wait step:

[![GitHub Actions](https://blog.engyak.co/2023/12/alpine/14_actions.png)](14_actions.png)