hetzner-alpine-k8s/alpine-on-hetzner
Knut Ahlers e4cd2dd84f
Cleanup, rename kernel_features to match purpose
Signed-off-by: Knut Ahlers <knut@ahlers.me>
2024-03-26 13:28:32 +01:00
..
.dockerignore Move git module to included files 2023-05-06 12:56:29 +02:00
.gitignore Move git module to included files 2023-05-06 12:56:29 +02:00
alpine.pkr.hcl Cleanup, rename kernel_features to match purpose 2024-03-26 13:28:32 +01:00
default.json Cleanup, rename kernel_features to match purpose 2024-03-26 13:28:32 +01:00
Dockerfile Multiple improvements 2023-05-06 19:36:12 +02:00
entrypoint.sh Move git module to included files 2023-05-06 12:56:29 +02:00
LICENSE Move git module to included files 2023-05-06 12:56:29 +02:00
playbook.yml Cleanup, rename kernel_features to match purpose 2024-03-26 13:28:32 +01:00
README.md Move git module to included files 2023-05-06 12:56:29 +02:00

This folder contains a modified copy of MathiasPius/alpine-on-hetzner.


alpine-hetzner

Tool for building cloud-init ready Alpine snapshots on Hetzner Cloud.

You can either run it as a docker container or as a regular packer build (see entrypoint.sh for hints on how), but this latter method is not officially supported.

Examples

Create an alpine image with the default configuration

Running this will create an alpine snapshot within your Hetzner Cloud project, ready to use for creating new servers. See the launching a server section for how to test it!

docker run -it --rm -e "HCLOUD_TOKEN=<YourTokenHere>" ghcr.io/mathiaspius/alpine-on-hetzner:latest

Default image, with doas installed, and template.local as default hostname

Configuration values can be overwritten by creating new configuration file with just the changes you want, and supplying the path as an argument when running it. See Custom Configuration for technical details on how the values are merged.

mkdir -p configs
echo '{ 
  "packages": { "doas": "=6.8.1-r7" },
  "hostname": "template.local"
}' > configs/my-override.json


export HCLOUD_TOKEN=myHetznerCloudToken
docker run -it --rm                     \
    -e "HCLOUD_TOKEN"                   \
    -v "$(pwd)/configs:/configs"        \
    ghcr.io/mathiaspius/alpine-on-hetzner:latest default.json /configs/my-override.json

There are a number of optional docker mounts you can use:

  • /manifests contains the output manifests from the run.
  • /cache used for caching the apk-tools package locally between runs.
  • /configs used for providing custom configuration files to builds.

Custom Configuration

Any command arguments passed to the docker run invocation will be treated as paths to configuration files to merge into a single combined configuration file which is then fed into the packer build.

The merge is a "deep merge", meaning you can only add to or change the configuration file not remove from it. If you want to remove a package from the default.json configuration for example you will have to create a copy of it without the package in question and use that as the basis for your build.

Adding a custom package to your image

In order to add a custom package like nginx for example you can create the following config file configs/nginx.json in your local directory:

{ "packages": { "nginx": "" } }

packages is a map where the keys are package names and the value is the version selector. The map is passed directly to an apk add command, see this link for version-pinning syntax.

When the container is then run like so:

docker run -it --rm                     \
    -e "HCLOUD_TOKEN"                   \
    -v "$(pwd)/configs:/configs"        \
    ghcr.io/mathiaspius/alpine-on-hetzner:latest default.json /configs/nginx.json

The package will be appended to packages array, like so, immediately before the packer build runs:

{
  (...)
  "packages": {
    "openssh": "=8.8_p1-r1",
    "syslinux": "=6.04_pre1-r9",
    "linux-virt": "=5.15.16-r0",
    "cloud-init": "@community=21.4-r0",
    "nginx": ""
  }
}

What's in the finished snapshot?

See the default.json config for a list of packages that will be installed into the snapshot if run without any arguments.

playbook.yml contains the entire ansible playbook used for generating the snapshot. alpine.pkr.hcl contains the packer build configuration which uses the playbook above via the Ansible Provisioner

How it works

The docker image comes with packer, ansible and jq pre-installed (check labels for versions), and builds the alpine.pkr.hcl build against your Hetzner Cloud project using your provided API key. The Packer build will boot a server in rescue mode, then format and install Alpine Linux onto the primary drive of the server. Once done, the server will be saved as a snapshot and shut down. You can then create Alpine Linux servers using the finished snapshot.

Launching a server

Servers built from the snapshot won't be immediately accessible because the root user is locked by default, but can be configured using the Hetzner interface. Use the following cloud-init config to enable root access and select an ssh key when creating the server to allow login:

#cloud-config
disable_root: false
users:
- name: root
  lock-passwd: false

Development

I have a number of ideas I would like to explore:

  • Re-using or expanding this tool to provision Alpine Linux on dedicated servers, but maintaining the same configuration -interface. I've previously done a less refined version fo this project for dedicated servers here
  • Splitting up configuration files so you can mix-and-match a little more. Would also allow optional hardened configurations for example which you could opt into for stricter security.
  • Creating configuration files for older versions of Alpine Linux.
  • Pipelining alpine-on-hetzner docker image builds and perhaps more importantly..
  • .. Testing that they work.
  • Add more customization abilities to the configuration file. Being able to enable openrc services with a simple array for example would be simple to implement and very useful.