Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# GitHub Actions Workflows

This directory contains GitHub Actions workflows for building Linux images.

## build-linux-images.yml

This workflow builds images for three different Linux distributions:
- Alpine Linux
- Arch Linux
- Ubuntu 20.04 with kernel 5.15

### Schedule
- Runs every Monday at 2 AM UTC (weekly)
- Can also be triggered manually via GitHub UI

### What it does
1. Checks out the repository
2. Sets up Packer and QEMU
3. Builds all three Linux images in sequence
4. Uploads the resulting qcow2 images as artifacts

### Artifacts
Each build produces a qcow2 image that is retained for 7 days:
- `alpine-image`
- `archlinux-image`
- `ubuntu-image`
61 changes: 61 additions & 0 deletions .github/workflows/build-linux-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Build Linux Images

on:
schedule:
# Run every Monday at 2 AM UTC (weekly)
- cron: '0 2 * * 1'
workflow_dispatch:

jobs:
build-all-images:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Packer
uses: hashicorp/setup-packer@v3

- name: Setup QEMU
run: |
sudo apt-get update
sudo apt-get install -y qemu-system-x86 qemu-utils

- name: Build Alpine Linux Image
run: |
cd example_build_alpine
packer init .
packer build -force alpine.pkr.hcl

- name: Build Arch Linux Image
run: |
cd example_build_archlinux
packer init .
packer build -force archlinux.pkr.hcl

- name: Build Ubuntu Image
run: |
cd example_build_ubuntu-20.04-kernel-5.15
packer init .
packer build -force ubuntu-20.04-kernel-5.15.pkr.hcl

- name: Upload Alpine Artifacts
uses: actions/upload-artifact@v4
with:
name: alpine-image
path: example_build_alpine/output/*.qcow2
retention-days: 7

- name: Upload Arch Linux Artifacts
uses: actions/upload-artifact@v4
with:
name: archlinux-image
path: example_build_archlinux/output/*.qcow2
retention-days: 7

- name: Upload Ubuntu Artifacts
uses: actions/upload-artifact@v4
with:
name: ubuntu-image
path: example_build_ubuntu-20.04-kernel-5.15/output/*.qcow2
retention-days: 7
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,8 @@ See the [`make_image_bootable.sh`](example_build/files/make_image_bootable.sh) e
## Related links

* [Report bugs](https://github.com/ovh/bringyourownlinux/issues)

## Automated Builds

All example images are built automatically on a weekly basis using GitHub Actions.
See the [.github/workflows](.github/workflows) directory for details.
35 changes: 35 additions & 0 deletions example_build_alpine/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Alpine Linux Build Example

This directory contains a Packer configuration for building an Alpine Linux image suitable for OVHcloud baremetal servers.

## Files

- `alpine.pkr.hcl` - Main Packer configuration file in HCL format
- `provision.sh` - Script to prepare the Alpine Linux system for baremetal deployment
- `make_image_bootable.sh` - Script to make the image bootable on OVHcloud baremetal servers

## Building the Image

To build the image, run:

```bash
packer build alpine.pkr.hcl
```

The resulting image will be located in the `output/` directory.

## Configuration Details

This build uses:
- Alpine Linux v3.20.1 (latest stable release)
- QEMU builder for virtualization
- Cloud-init for initial configuration
- GRUB bootloader configured for baremetal boot
- Support for RAID, LVM, and various filesystems
- Intel and AMD microcode support

## Requirements

- Packer 1.7+
- QEMU/KVM support
- At least 4GB RAM for building
65 changes: 65 additions & 0 deletions example_build_alpine/alpine.pkr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
packer {
required_plugins {
qemu = {
source = "github.com/hashicorp/qemu"
version = "~> 1"
}
}
}

source "qemu" "builder" {
# cloud-init will use the CD as datasource, see https://cloudinit.readthedocs.io/en/latest/reference/datasources/nocloud.html#source-2-drive-with-labeled-filesystem
cd_label = "cidata"
cd_content = {
"/meta-data" = ""
"/user-data" = <<-EOF
#cloud-config
ssh_pwauth: true
users:
- name: packer
plain_text_passwd: packer
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: false
EOF
}
# Enabling this makes the build longer but reduces the image size
disk_compression = true
# The source is a disk image, not an ISO file
disk_image = true
# Same size as the source image
disk_size = "2G"
format = "qcow2"
# Do not launch QEMU's GUI
headless = true
iso_checksum = "file:https://alpinelinux.org/downloads/alpine-v3.20.1-x86_64.iso.sha256"
iso_url = "https://alpinelinux.org/downloads/alpine-v3.20.1-x86_64.iso"
# Allows us to see the VM's console when PACKER_LOG=1 is set
qemuargs = [["-serial", "stdio"]]
# Before shutting down, truncate logs and remove everything linked to the provisioning user
shutdown_command = "sudo sh -c 'find /var/log/ -type f -exec truncate --size 0 {} + && rm -f /etc/sudoers.d/90-cloud-init-users && userdel -fr packer && poweroff'"
communicator = "ssh"
ssh_clear_authorized_keys = true
ssh_username = "packer"
ssh_password = "packer"
# The resulting image will be written to output/alpine.qcow2
output_directory = "output"
vm_name = "alpine.qcow2"
}

build {
sources = ["source.qemu.builder"]

provisioner "shell" {
execute_command = "chmod +x {{ .Path }} && sudo {{ .Path }}"
script = "provision.sh"
}

provisioner "file" {
destination = "/tmp/make_image_bootable.sh"
source = "make_image_bootable.sh"
}

provisioner "shell" {
inline = ["sudo sh -c 'mkdir /root/.ovh/ && mv /tmp/make_image_bootable.sh /root/.ovh/ && chmod -R +x /root/.ovh/'"]
}
}
76 changes: 76 additions & 0 deletions example_build_alpine/make_image_bootable.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/bin/sh

# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software distributed under
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific language
# governing permissions and limitations under the License.

# This script makes an Alpine Linux image bootable on OVHcloud baremetal servers

set -eo pipefail

configure_console() {
echo "Getting console parameters from the cmdline"
# Get the right console parameters (including SOL if available) from the
# rescue's cmdline - PUBM-16534
console_parameters="$(grep -Po '\bconsole=\S+' /proc/cmdline | paste -s -d" ")"
if ! grep -qF "$console_parameters" /etc/default/grub; then
sed -i "s/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"$console_parameters\"/" /etc/default/grub
fi

# Also pass these parameters to GRUB
parameters=$(sed -nE "s/.*\bconsole=ttyS([0-9]),([0-9]+)([noe])([0-9]+)\b.*/\1 \2 \3 \4/p" /proc/cmdline)
if [ -z "$parameters" ]; then
# No SOL, nothing to do
return
fi
read -r unit speed parity bits <<< "$parameters"
declare -A parities=([o]=odd [e]=even [n]=no)
parity="${parities["$parity"]}"
serial_command="serial --unit=$unit --speed=$speed --parity=$parity --word=$bits"

if grep -qFx 'GRUB_TERMINAL="console serial"' /etc/default/grub; then
# Configuration already applied
return
fi

sed -i \
-e "/^# Uncomment to disable graphical terminal/d" \
-e "s/^#GRUB_TERMINAL=.*/GRUB_TERMINAL=\"console serial\"\nGRUB_SERIAL_COMMAND=\"$serial_command\"/" \
/etc/default/grub
}

# Detect boot mode and install GRUB
if [ -d /sys/firmware/efi ]; then
echo "INFO - GRUB will be configured for UEFI boot"
# Pass --no-nvram to avoid changing the boot order, the server needs to boot via PXE
grub-install --target=x86_64-efi --no-nvram
else
echo "INFO - GRUB will be configured for legacy boot"
bootDevice="$(findmnt -A -c -e -l -n -T /boot/ -o SOURCE)"
realBootDevices="$(lsblk -n -p -b -l -o TYPE,NAME $bootDevice -s | awk '$1 == "disk" && !seen[$2]++ {print $2}')"
# realBootDevices are disks at this point
for realBootDevice in $realBootDevices; do
echo "INFO - GRUB will be configured on disk ${realBootDevice}"
# realBootDevice is the boot disk device canonical path
grub-install --target=i386-pc --force "$realBootDevice"
done
fi

# Configure SOL console
configure_console

# Generate unique machine-id
systemd-machine-id-setup

# Generate GRUB config
grub-mkconfig -o /boot/grub/grub.cfg

# Regenerate initramfs to include e.g. mdadm.conf and required kernel modules
mkinitfs -b

# Cleanup
rm -fr /root/.ovh/
41 changes: 41 additions & 0 deletions example_build_alpine/provision.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/sh
# This script prepares an Alpine Linux cloud image for OVHcloud baremetal servers

set -euo pipefail

# Update the system
apk update && apk upgrade

# Install useful packages for baremetal (RAID/LVM/filesystems/microcodes)
# We need to install packages that support hardware components commonly found in OVH baremetal servers
apk add --no-cache \
mdadm \
lvm2 \
btrfs-progs \
dosfstools \
xfsprogs \
linux-firmware-intel \
intel-ucode \
amd-ucode \
openrc

# Remove Network configuration
rm -f /etc/network/interfaces

# Configure GRUB for proper boot on baremetal
# Alpine uses OpenRC instead of systemd, so we need to adjust accordingly
# Set default GRUB parameters
echo 'GRUB_CMDLINE_LINUX_DEFAULT=""' > /etc/default/grub
echo 'GRUB_CMDLINE_LINUX="nomodeset"' >> /etc/default/grub
echo 'GRUB_GFXPAYLOAD_LINUX="text"' >> /etc/default/grub

# Add mdadm, LVM and net hooks to the initramfs
# In Alpine, we need to ensure the right modules are included
echo 'MODULES="mdraid lvm2"' > /etc/mkinitfs/modules
echo 'HOOKS="mount rootfs"' > /etc/mkinitfs/hooks

# Remove machine-id, it is regenerated during OS installation
rm -f /etc/machine-id

# Clean package cache
apk cache clean