Skip to content
Draft
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
27 changes: 27 additions & 0 deletions docs/toolkit/creating-derivatives/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

---
title: "Creating derivatives"
sidebar_label: "Creating derivatives"
weight: 4
date: 2023-05-11
description: >
Documents various methods for creating Elemental derivatives
---

A derivative is a standard container image which can be booted by Elemental.

We can identify a build phase where we build the derivative, and a "runtime phase" where we consume it.

The image is described by a Dockerfile, composed of a base OS of choice (e.g. openSUSE, Ubuntu, etc. ) and the Elemental toolkit itself in order to be consumed by Elemental and allow to be upgraded from by other derivatives.

elemental-toolkit then converts the OCI artifact into a bootable medium (ISO, packer, ova, etc) and the image itself then can be used to bootstrap other derivatives, which can in turn upgrade to any derivative built with Elemental.

A derivative can also be later re-used again as input as base-image for downstream derivatives.

All the documentation below imply that the container image generated will be the booting one, there are however several configuration entrypoint that you should keep in mind while building the image which are general across all the implementation:

- Custom persistent runtime configuration has to be provided in `/system/oem` for derivatives, [see also the documentation section](../customizing/configuration_persistency). Everything under `/system/oem` will be loaded during the various stages (boot, network, initramfs).
- `/etc/cos/bootargs.cfg` contains the booting options required to boot the image with GRUB, [see grub customization](../customizing/configure_grub)

Derivatives inherits `Elemental` defaults, which you can override during the build process, however there are some defaults which are relevant and listed below:

61 changes: 61 additions & 0 deletions docs/toolkit/creating-derivatives/build_disk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: "Build disk images with Elemental"
sidebar_label: "Build disk images with Elemental"
---

Requirements:

* `qemu-img` utility
* `elemental` binary
* elemental runtime dependencies

The suggested approach is based on using the Elemental installer (`elemental install` command) to run the installation
from a Linux to a loop device. The loop device can be a raw image created with `qemu-img create` that can easily be
converted to other formats after the installation by using `qemu-img convert`.

## Prepare the loop device

Preparing the a loop device for the installation is simple and straight forward.

```bash
# Create a raw image of 32G
> qemu-img create -f raw disk.img 32G

# Set the disk image as a loop device
> sudo losetup -f --show disk.img
<device>
```

## Run elemental installation

Execute the elemental installation as described in [installing](../getting-started/install):

```bash
> sudo elemental install --firmware efi --system.uri oci:<image=ref> <device>
```

Where `<image-ref>` is the Elemental derivative container image we want to use for the disk creation and `<device>` is the
loop device previously created with `losetup` (e.g. `/dev/loop0`).


## Convert the RAW image to desired format

Once the installation is done just unsetting the loop device and converting the image to the desired format is missing:

```bash
# Unset the loop device
> sudo losetup -d <device>

# Convert the RAW image to qcow2
> qemu-img convert -f raw -O qcow2 disk.img disk.qcow2
```

QEMU supports a wide range of formats including common ones such as `vdi`, `vmdk` or `vhdx`.

The result can be easily tested on QEMU with:

```bash
> qemu -m 4096 -hda disk.qcow2 -bios /usr/share/qemu/ovmf-x86_64.bin
```

Note the firmware image path varies depending on the host distro, the path provided in this example is based on openSUSE Leap.
184 changes: 184 additions & 0 deletions docs/toolkit/creating-derivatives/build_iso.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
---
title: "Build ISOs"
sidebar_label: "Build ISOs"
---

In order to build an ISO we rely on `elemental build-iso` command. It accepts a YAML file denoting the sources to bundle in an ISO. In addition it can also overlay custom files or use container images from a registry as packages.

To build an ISO, just run:

```bash
docker run --rm -ti -v $(pwd):/build ghcr.io/rancher/elemental-toolkit/elemental-cli:latest --debug build-iso -o /build $SOURCE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Porting of rancher/elemental-toolkit#2026

Suggested change
docker run --rm -ti -v $(pwd):/build ghcr.io/rancher/elemental-toolkit/elemental-cli:latest --debug build-iso -o /build $SOURCE
docker run --rm -ti -v $(pwd):/build ghcr.io/rancher/elemental-toolkit/elemental-cli:latest --debug build-iso --bootloader-in-rootfs -o /build $SOURCE

```

Where `$SOURCE` might be the container image you want to build the ISO for, you might want to check on [how to build bootable images](creating_bootable_images). Argument `$SOURCE` might be the reference to the directory, file, container image or channel we are building the ISO for, it should be provided as uri in following format `<sourceType>:<sourceName>`, where:
* `<sourceType>` - might be ["dir", "file", "oci", "docker"], as default is taken "docker"
* `<sourceName>` - is path to file or directory, channel or image name with tag version (if tag was not provided then "latest" is used)

`elemental build-iso` command also supports reading a configuration `manifest.yaml` file. It is loaded form the directory specified by `--config-dir` elemental's flag.

An example of a yaml file using the bootloader from the contained image:

```yaml
iso:
bootloader-in-rootfs: true
grub-entry-name: "Installer"

name: "Elemental-0"
date: true
```

## What's next?

- Check out on how to [build an image](build_disk) from the ISO we have just created

## Syntax

Below you can find a full reference about the yaml file format.

```yaml
iso:
# Sources to be installed in the rootfs
rootfs:
- ..
# Sources to be installed in the uefi image
uefi:
- ..
# Sources to be installed in the iso image
image:
- ..
label: "COS_LIVE"
```

Sources can be an image reference (then an explicit tag is required) or a local path. Sources are stacked in the given order, so one can easily overwrite or append data by simply adding a local path as the last source.

### Command flags

- **name**: Name of the ISO image. It will be used to generate the `*.iso` file name
- **output**: Path of the destination folder of created images
- **date**: If present it includes the date in the generated file name
- **overlay-rootfs**: Sets the path of a tree to overlay on top of the system root-tree
- **overlay-uefi**: Sets the path of a tree to overaly on top of the EFI image root-tree
- **overlay-iso**: Sets the path of a tree to overlay on top of the ISO filesystem root-tree
- **label**: Sets the volume label of the ISO filesystem

## Configuration reference

### `iso.rootfs`

A list of sources in uri format (container image or local path) [ "docker", "oci", "dir", "file" ] to install in the rootfs. The rootfs will be squashed to a `rootfs.squashfs` file

### `iso.uefi`

A list of sources in uri format (container image or local path) [ "docker", "oci", "dir", "file" ] to install in the efi FAT image or partition.

### `iso.image`

A list of sources in uri format (container image or local path) [ "docker", "oci", "dir", "file" ] to install in ISO filesystem.

### `iso.label`

The label of the ISO filesystem. Defaults to `COS_LIVE`. Note this value is tied with the bootloader and kernel parameters to identify the root device.

### `name`

A string representing the ISO final image name without including the `.iso`

### `date`

Boolean indicating if the output image name has to contain the date

### `output`

Folder destination of the built artifacts. It attempts to create if it doesn't exist.

## Customize bootloader with GRUB

Boot menu and other bootloader parameters can then be easily customized by using the overlay parameters within the ISO config yaml manifest.

Assuming the ISO being built includes:

```yaml
iso:
rootfs:
- ...
uefi:
- oci:example-grub2-efi-image:latest
image:
- oci:example-grub2:latest
- oci:example-grub2-efi-image:latest
```

We can customize either the `image` packages (in the referrence image `live/grub2` package
includes bootloader configuration) or make use of the overlay concept to include or
overwrite addition files for `image` section.

Consider the following example:

```yaml
iso:
rootfs:
- ...
uefi:
- oci:example-grub2-efi-image:latest
image:
- oci:example-grub2:latest
- oci:example-grub2-efi-image:latest
- dir:/my/path/to/overlay/iso
```

With the above the ISO will also include the files under `/my/path/to/overlay/iso` path. To customize the boot
menu parameters consider copy and modify relevant files from `example-grub2:latest` image. In this example the
`overlay` folder files list could be:

```bash
# image files for grub2 boot
boot/grub2/grub.cfg
```

Being `boot/grub2/grub.cfg` a custom grub2 configuration including custom boot menu entries. Consider the following `grub.cfg` example:

```
search --file --set=root /boot/kernel.xz
set default=0
set timeout=10
set timeout_style=menu
set linux=linux
set initrd=initrd
if [ "${grub_cpu}" = "x86_64" -o "${grub_cpu}" = "i386" ];then
if [ "${grub_platform}" = "efi" ]; then
set linux=linuxefi
set initrd=initrdefi
fi
fi

set font=($root)/boot/x86_64/loader/grub2/fonts/unicode.pf2
if [ -f ${font} ];then
loadfont ${font}
fi

menuentry "Custom grub2 menu entry" --class os --unrestricted {
echo Loading kernel...
$linux ($root)/boot/kernel.xz cdroot root=live:CDLABEL=COS_LIVE rd.live.dir=/ rd.live.squashimg=rootfs.squashfs console=tty1 console=ttyS0 rd.cos.disable
echo Loading initrd...
$initrd ($root)/boot/rootfs.xz
}
```

## Separate recovery

To make an ISO with a separate recovery image as squashfs, you can either use the default from `Elemental`, by adding it in the iso yaml file:

```yaml
iso:
rootfs:
..
uefi:
..
image:
...
- oci:example-recovery:latest
```

The installer will detect the squashfs file in the iso, and will use it when installing the system. You can customize the recovery image as well by providing your own.

56 changes: 56 additions & 0 deletions docs/toolkit/creating-derivatives/creating_bootable_images.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
title: "Creating bootable images"
sidebar_label: "Creating bootable images"
---

![](https://docs.google.com/drawings/d/e/2PACX-1vSmIZ5FTInGjtkGonUOgwhti6DZnSoeexGmWL9CAmbdiIGtBGnzDuGNj80Lj_206hP0MOxQGpEdYFvK/pub?w=1223&h=691)

A derivative is a simple container image which can be processed by the Elemental toolkit in order to be bootable and installable. This section describes the requirements to create a container image that can be run by `Elemental`.

## Requirements
{{<image_right image="https://docs.google.com/drawings/d/e/2PACX-1vQBfT10W88mD1bbReDmAJIOPF3tWdVHP7QE9w7W7ByOIzoKGOdh2z5YWsKf7wn8csFF_QGrDXgGsPWg/pub?w=478&h=178">}}

Bootable images are standard container images, that means the usual `build` and `push` workflow applies, and building images is also a way to persist [oem customizations](../customizing/oem_configuration).

The base image can be any Linux distribution that is compatible with our flavors.

The image needs to ship:
- parts of the elemental-toolkit (required, see below)
- kernel (required)
- initrd (required)
- grub2 (required)
- dracut (required)
- microcode (optional, not required in order to boot, but recomended)

## Example

An illustrative example can be:


<!--{{<githubembed repo="rancher/elemental-toolkit" file="examples/green/Dockerfile" lang="Dockerfile">}}-->

In the example above, the elemental-toolkit parts that are **required** are pulled in by `COPY --from=TOOLKIT /install-root /`.

## Initrd
The image should provide at least `grub`, `systemd`, `dracut`, a kernel and an initrd. Those are the common set of packages between derivatives. See also [package stack](package_stack).
By default the initrd is expected to be symlinked to `/boot/initrd` and the kernel to `/boot/vmlinuz`, otherwise you can specify a custom path while [building an iso](build_iso) and [by customizing grub](../customizing/configure_grub).

## Building

![](https://docs.google.com/drawings/d/e/2PACX-1vS6eRyjnjdQI7OBO0laYD6vJ2rftosmh5eAog6vk_BVj8QYGGvnZoB0K8C6Qdu7SDz7p2VTxejcZsF6/pub?w=956&h=339)

The workflow would be then:

1) `docker build` the image
2) `docker push` the image to some registry
3) `elemental upgrade --docker-image $IMAGE` from a Elemental machine or (`elemental reset` if bootstrapping a cloud image)

The following can be incorporated in any standard gitops workflow.

You can explore more examples in the [example section](../examples/creating_bootable_images) on how to create bootable images.

## What's next?

Now that we have created our derivative container, we can either:

- [Build an iso](build_iso)
11 changes: 11 additions & 0 deletions docs/toolkit/creating-derivatives/package_stack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
title: "Package stack"
sidebar_label: "Package stack"
---


When building a `elemental-toolkit` derivative, a common set of packages are required with a common default configuration. Some of the most notably are:

- systemd as init system
- grub for boot loader
- dracut for initramfs
9 changes: 9 additions & 0 deletions docs/toolkit/customizing/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

---
title: "Customizing"
sidebar_label: "Customizing"
weight: 3
date: 2017-01-05
description: >
This section contains various articles relative on how to customize Elemental, branding and behavior.
---
45 changes: 45 additions & 0 deletions docs/toolkit/customizing/configuration_persistency.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
title: "Configuration persistency"
sidebar_label: "Configuration persistency"
---


By default Elemental and derivatives are reading and executing cloud-init files in (lexicopgrahic) sequence inside:

- `/system/oem`
- `/usr/local/cloud-config`
- `/oem`

It is also possible to run cloud-init file in a different location (URLs included, too) from boot cmdline by using the `cos.setup=..` option.

{{% alert title="Note" %}}
It is possible to install a custom [cloud-init style file](../reference/cloud_init/) during install with `--cloud-init` flag on `elemental install` command or, it's possible to add one or more files manually inside the `/oem` directory after installation.
{{% /alert %}}

While `/system/oem` is reserved for system configurations to be included directly in the derivative container image, the `/oem` folder instead is reserved for persistent cloud-init files that can be extended in runtime.

For example, if you want to change `/etc/issue` of the system persistently, you can create `/usr/local/cloud-config/90_after_install.yaml` or alternatively in `/oem/90_after_install.yaml` with the following content:

```yaml
# The following is executed before fs is setted up:
stages:
fs:
- name: "After install"
files:
- path: /etc/issue
content: |
Welcome, have fun!
permissions: 0644
owner: 0
group: 0
- name: "After install (second step)"
files:
- path: /etc/motd
content: |
Welcome, have more fun!
permissions: 0644
owner: 0
group: 0
```

For more examples you can find `/system/oem` inside Elemental vanilla images containing files used to configure on boot a pristine `Elemental`.
Loading