2021-11-07 20:37:56 +00:00
|
|
|
## _**NOTE: This is a Work-in-Progress**_
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
_It is intended that this will eventually replace
|
|
|
|
https://gitlab.alpinelinux.org/alpine/cloud/alpine-ec2-ami
|
|
|
|
as the offical multi-cloud image builder for Alpine Linux._
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
----
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
# Alpine Linux Cloud Image Builder
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
This repository contains the code and and configs for the build system used to
|
|
|
|
create official Alpine Linux images for various cloud providers, in various
|
|
|
|
configurations. This build system is flexible, enabling others to build their
|
|
|
|
own customized images.
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
----
|
|
|
|
## Pre-Built Offical Cloud Images
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
To get started with offical pre-built Alpine Linux cloud images, visit
|
|
|
|
https://alpinelinux.org/cloud. Currently, we build official images for the
|
|
|
|
following providers:
|
|
|
|
* AWS
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
You should also be able to find the most recently published Alpine Linux
|
|
|
|
images via your cloud provider's web console, or programatically query their
|
|
|
|
API with a CLI tool or library.
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
_(TODO: examples)_
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
----
|
|
|
|
## Build System
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
The build system consists of a number of components:
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
* the primary `build` script and related cloud-specific helpers
|
|
|
|
* a directory of `configs/` defining the set of images to be built
|
|
|
|
* a Packer `alpine.pkr.hcl` orchestrating the images' local build, as well as
|
|
|
|
importing them to cloud providers and publishing them to desitnation regions
|
|
|
|
* a directory of `scripts/` which set up the images' contents during
|
|
|
|
provisioning
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
### Build Requirements
|
|
|
|
* [Python](https://python.org) (3.9.7 is known to work)
|
|
|
|
* [Packer](https://packer.io) (1.7.6 is known to work)
|
|
|
|
* [QEMU](https://www.qemu.org) (6.1.0 is known to work)
|
|
|
|
* cloud provider account(s)
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
### Cloud Credentials
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
This build system relies on the cloud providers' Python API libraries to find
|
|
|
|
and use the necessary credentials -- via configuration in the user's home
|
|
|
|
directory (i.e. `~/.aws/...`, `~/.oci/...`, etc.) or with special environment
|
|
|
|
variables (i.e. `AWS_...`, `OCI_...`, etc.)
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
It is expected that each cloud provider's user/role will have been set up with
|
|
|
|
sufficient permission in order to accomplish the operations necessary to query,
|
|
|
|
import, and publish images; _it is highly recommended that no permissions are
|
|
|
|
granted beyond what is absolutely necessary_.
|
2019-05-28 05:27:55 +00:00
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
### The `build` Script
|
2019-05-28 05:27:55 +00:00
|
|
|
|
|
|
|
```
|
2021-11-07 20:37:56 +00:00
|
|
|
usage: build [-h] [--debug] [--clean] [--revise] {configs,local,import,publish}
|
|
|
|
[--custom DIR [DIR ...]] [--skip KEY [KEY ...]] [--only KEY [KEY ...]]
|
|
|
|
[--no-color] [--parallel N] [--vars FILE [FILE ...]]
|
|
|
|
|
|
|
|
build steps:
|
|
|
|
configs resolve build configuration
|
|
|
|
local build local images
|
|
|
|
import import to cloud providers
|
|
|
|
publish set permissions and publish to cloud regions
|
|
|
|
|
|
|
|
optional arguments:
|
|
|
|
-h, --help show this help message and exit
|
|
|
|
--debug enable debug output (False)
|
|
|
|
--clean start with a clean work environment (False)
|
|
|
|
--revise bump revisions if images already published (False)
|
|
|
|
--custom DIR [DIR ...] overlay custom directory in work environment
|
|
|
|
--skip KEY [KEY ...] skip variants with dimension key(s)
|
|
|
|
--only KEY [KEY ...] only variants with dimension key(s)
|
|
|
|
--no-color turn off Packer color output (False)
|
|
|
|
--parallel N build N images in parallel (1)
|
|
|
|
--vars FILE [FILE ...] supply Packer with additional -vars-file(s)
|
2019-05-28 05:27:55 +00:00
|
|
|
```
|
|
|
|
|
2021-11-07 20:37:56 +00:00
|
|
|
A `work/` directory will be created for its Python virtual environment, any
|
|
|
|
necessary Python libraries will be `pip install`ed, and `build` will execute
|
|
|
|
itself to ensure that it's running in the work environment.
|
|
|
|
|
|
|
|
This directory also contains `configs/` and `scripts/` subdirs (with custom
|
|
|
|
overlays), UEFI firmware for QEMU, Packer cache, the generated `configs.yaml`
|
|
|
|
and `actions.yaml` configs, and the `images/` tree for local image builds.
|
|
|
|
|
|
|
|
Use `--clean` if you want to re-overlay, re-download, re-generate, or rebuild
|
|
|
|
anything in the `work/` directory. To redo the Python virtual environment,
|
|
|
|
simply remove the `work/` directory and its contents, and it will be recreated
|
|
|
|
the next time `build` is run.
|
|
|
|
|
|
|
|
### Build Steps
|
|
|
|
|
|
|
|
When executing `build` you also provide the target step you wish to reach. For
|
|
|
|
example, if you only want to build local images, use `build local`. Any
|
|
|
|
predecessor steps which haven't been done will also be executed -- that is,
|
|
|
|
`build local` also implies `build configs` if that step hasn't completed yet.
|
|
|
|
|
|
|
|
The **configs** step determines the latest stable Alpine Linux release, and
|
|
|
|
ensures that the `configs/` and `scripts/` overlays, UEFI firmware, and
|
|
|
|
`configs.yaml` exist. This allows you to validate the generated build variant
|
|
|
|
configuration before attempting to build any images locally.
|
|
|
|
|
|
|
|
If `build` is moving on past **configs** to other steps, it will determine which
|
|
|
|
image variants to work on (based on `--skip` and `--only` values) and what
|
|
|
|
actions will be taken, based on existence of local/imported/published images, and
|
|
|
|
generate the `actions.yaml` file. Providing the `--revise` flag allows you to
|
|
|
|
rebuild local images that were previously built, reimport unpublished images to
|
|
|
|
cloud providers, and bump the "revision" value of previously published images --
|
|
|
|
this is useful if published images require fixes but the Alpine release itself
|
|
|
|
isn't changing; published images are not removed (though they may be pruned once
|
|
|
|
their "end-of-life" date has passed).
|
|
|
|
|
|
|
|
At this point, `build` executes Packer, which is responsible for the remaining
|
|
|
|
**local**, **import**, and **publish** steps -- and also for parallelization, if
|
|
|
|
the `--parallel` argument is given. Because build hardware varies, it is also
|
|
|
|
possible to tune a number of QEMU timeouts and memory requirements by providing
|
|
|
|
an HCL2 Packer Vars file and specifying `--vars <filename>` to override the
|
|
|
|
defaults in `alpine.pkr.hcl`.
|
|
|
|
|
|
|
|
### Packer and `alpine.pkr.hcl`
|
|
|
|
|
|
|
|
Packer loads and merges `actions.yaml` and `configs.yaml`, and iterates the
|
|
|
|
resulting object in order to determine what it should do with each image
|
|
|
|
variant configuration.
|
|
|
|
|
|
|
|
`alpine.pkr.hcl` defines two base `source` blocks -- `null` is used when an
|
|
|
|
image variant is already built locally and/or already imported to the
|
|
|
|
destination cloud provider; otherwise, the `qemu` source is used.
|
|
|
|
|
|
|
|
The `qemu` builder spins up a QEMU virtual machine with a blank virtual disk
|
|
|
|
attached, using the latest stable Alpine Linux Virtual ISO, brings up the VM's
|
|
|
|
network, enables the SSH daemon, and sets a random password for root.
|
|
|
|
|
|
|
|
If an image variant is to be **built locally**, the two dynamic provisioners copy
|
|
|
|
the required data for the setup scripts to the VM's `/tmp/` directory, and then
|
|
|
|
run those setup scripts. It's these scripts that are ultimately responsible for
|
|
|
|
installing and configuring the desired image on the attached virtual disk.
|
|
|
|
When the setup scripts are complete, the virtual machine is shut down, and the
|
|
|
|
resulting local disk image can be found at
|
|
|
|
`work/images/<cloud>/<build-name>/image.qcow2`.
|
|
|
|
|
|
|
|
The dynamic post-processor uses the `cloud_helper.py` script to **import** a
|
|
|
|
local image to the cloud provider, and/or **publish** an imported image to the
|
|
|
|
cloud provider's destination regions, based on what actions are applicable for
|
|
|
|
that image variant. When the **publish** step is reapplied to an
|
|
|
|
already-published image, the script ensures that images have been copied to all
|
|
|
|
destination regions (for example, if the cloud provider recently added a new
|
|
|
|
region), and that all launch permissions are set as expected.
|
|
|
|
|
|
|
|
### The `cloud_helper.py` Script
|
|
|
|
|
|
|
|
This script is only meant to be imported by `build` and called from Packer, and
|
|
|
|
provides a normalized cloud-agnostic way of doing common cloud operations --
|
|
|
|
getting details about a variant's latest imported image, importing new local
|
|
|
|
image to the cloud, removing a previouly imported (but unpublished) image so it
|
|
|
|
can be replaced, or publishing an imported image to destination regions.
|
|
|
|
|
|
|
|
----
|
|
|
|
## Build Configuration
|
|
|
|
|
|
|
|
The `build` script generates `work/configs.yaml` based on the contents of the
|
|
|
|
top-level config file, `work/configs/configs.conf`; normally this is a symlink to
|
|
|
|
`alpine.conf`, but can be overridden for custom builds. All configs are
|
|
|
|
declared in [HOCON](https://github.com/lightbend/config/blob/master/HOCON.md)
|
|
|
|
format, which allows importing from other files, simple variable interpolation,
|
|
|
|
and easy merging of objects. This flexibility helps keep configuration
|
|
|
|
[DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).
|
|
|
|
|
|
|
|
The top-level `build.conf` has three main blocks, `Default` (default/starting
|
|
|
|
values), `Dimensions` (with configs that apply in different circumstances), and
|
|
|
|
`Mandatory` (required/final values). The configuration for these blocks are
|
|
|
|
merged in this exact order.
|
|
|
|
|
|
|
|
### Dimensions and Build Variants
|
|
|
|
|
|
|
|
Build variants _(I was watching Loki™ at the time...)_ are the sets of
|
|
|
|
dimensional "features" and related configuration details produced from a
|
|
|
|
Cartesian product across each of the dimensional keys. Dimensional configs are
|
|
|
|
merged together in the order they appear in `build.conf`.
|
|
|
|
|
|
|
|
If two dimensional keys are incompatible (for example, **version/3.11** did not
|
|
|
|
yet support **arch/aarch64**), an `EXCLUDE` directive indicates that such a
|
|
|
|
variant is non-viable, and will be skipped.
|
|
|
|
|
|
|
|
Likewise, if one dimension's configuration depends on the value of a different
|
|
|
|
dimensional key, the `WHEN` directive will supply the conditional config
|
|
|
|
details when that other dimensional key is part of the variant.
|
|
|
|
|
|
|
|
Currently the base set of dimensions (and dimension keys) are...
|
|
|
|
|
|
|
|
**version** - current "release" value for each is autodetected, and always a
|
|
|
|
component of an image's name
|
|
|
|
* **edge** ("release" value is the current UTC date)
|
|
|
|
* all *non-EOL* Alpine Linux versions
|
|
|
|
|
|
|
|
**arch** - machine architecture
|
|
|
|
* **x86_64** (aka "amd64")
|
|
|
|
* **aarch64** (aka "arm64")
|
|
|
|
|
|
|
|
**firmware** - machine boot firmware
|
|
|
|
* **bios** (legacy BIOS)
|
|
|
|
* **uefi**
|
|
|
|
|
|
|
|
**bootstrap** - image instantiation bootstrap is provided by...
|
|
|
|
* **tiny** (tiny-cloud-boostrap)
|
|
|
|
* **cloudinit** (cloud-init)
|
|
|
|
|
|
|
|
**cloud** - cloud provider or platform
|
|
|
|
* **aws** - Amazone Web Services / EC2
|
|
|
|
* **oci** - Oracle Cloud Infrastructure _(WiP)_
|
|
|
|
* **gcp** - Google Cloud Platform _(WiP)_
|
|
|
|
* **azure** - Microsoft Azure _(WiP)_
|
|
|
|
|
|
|
|
...each dimension may (or may not) contribute to the image name or description,
|
|
|
|
if the dimensional key's config contributes to the `name` or `description`
|
|
|
|
array values.
|
|
|
|
|
|
|
|
### Customized Builds
|
|
|
|
|
|
|
|
_(TODO)_
|