Simplify config, upgrade to K8s 1.30.0

- sever connection to upstream
- remove Docker setup
- use local packer binary to build
- add support for writing arbitrary files
- cleanup repo

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2024-04-21 15:52:40 +02:00
parent e4cd2dd84f
commit 7ba01f01a1
Signed by: luzifer
SSH key fingerprint: SHA256:/xtE5lCgiRDQr8SLxHMS92ZBlACmATUmF1crK16Ks4E
14 changed files with 276 additions and 482 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
config.json
envrun_*
packer_*
yq_*

View file

@ -1,42 +1,39 @@
ALPINE_VERSION:=3.18
PACKER_VERSION:=1.8.7-r3
ANSIBLE_CORE_VERSION:=2.14.5-r0
JQ_VERSION:=1.6-r3
ENVRUN_VERSION := 0.7.1
PACKER_VERSION := 1.10.2
YQ_VERSION := 4.31.2
ENVRUN_VERSION:=v0.7.1
YQ_VERSION:=v4.31.2
export DOCKER_BUILDKIT:=1
ENVRUN := ./envrun_$(ENVRUN_VERSION)
PACKER := ./packer_$(PACKER_VERSION)
YQ := ./yq_$(YQ_VERSION)
default:
config.json: yq_$(YQ_VERSION)
./yq_$(YQ_VERSION) -ojson . config.yaml | jq -S . >config.json
config.json: $(YQ)
$(YQ) -ojson . config.yaml | jq -S . >config.json
create-snapshot: docker-build config.json envrun_$(ENVRUN_VERSION)
./envrun_$(ENVRUN_VERSION) -- docker run --rm -i \
-e "HCLOUD_TOKEN" \
-v "$(CURDIR):/config:ro" \
registry.local/alpine-on-hetzner:latest \
/config/config.json
docker-build:
docker build \
--build-arg ALPINE_VERSION=$(ALPINE_VERSION) \
--build-arg PACKER_VERSION=$(PACKER_VERSION) \
--build-arg ANSIBLE_CORE_VERSION=$(ANSIBLE_CORE_VERSION) \
--build-arg JQ_VERSION=$(JQ_VERSION) \
-t registry.local/alpine-on-hetzner \
./alpine-on-hetzner
create-snapshot: config.json $(ENVRUN) $(PACKER)
$(PACKER) init alpine.pkr.hcl
$(ENVRUN) -- $(PACKER) build -var-file=config.json alpine.pkr.hcl
# --- Tools
envrun_$(ENVRUN_VERSION):
curl -sSfL "https://github.com/Luzifer/envrun/releases/download/$(ENVRUN_VERSION)/envrun_linux_amd64.tar.gz" | tar -xz
tools: $(ENVRUN) $(PACKER) $(YQ)
$(ENVRUN):
rm envrun_*
curl -sSfL "https://github.com/Luzifer/envrun/releases/download/v$(ENVRUN_VERSION)/envrun_linux_amd64.tar.gz" | tar -xz
mv envrun_linux_amd64 $@
yq_$(YQ_VERSION):
curl -sSfLo $@ "https://github.com/mikefarah/yq/releases/download/$(YQ_VERSION)/yq_linux_amd64"
$(PACKER):
rm packer_*
curl -sSfLo packer.zip "https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip"
unzip packer.zip
rm packer.zip
mv packer $@
$(YQ):
rm yq_*
curl -sSfLo $@ "https://github.com/mikefarah/yq/releases/download/v$(YQ_VERSION)/yq_linux_amd64"
chmod +x $@
.PHONY: config.json

View file

@ -1,6 +1,8 @@
# Luzifer / hetzner-alpine-k8s
This repo contains my configuration for the [MathiasPius/alpine-on-hetzner](https://github.com/MathiasPius/alpine-on-hetzner) build tooling.
This repo is derived from the [MathiasPius/alpine-on-hetzner](https://github.com/MathiasPius/alpine-on-hetzner) build tooling.
It contains a modified version of the packer / ansible setup to create a snapshot containing a Kubernetes-ready setup.
## Usage

View file

@ -1,2 +0,0 @@
manifests/
cache/

View file

@ -1,3 +0,0 @@
manifests/
configs/
cache/

View file

@ -1,29 +0,0 @@
ARG ALPINE_VERSION=3.17
ARG UID=1000
ARG GID=1000
FROM alpine:$ALPINE_VERSION
ARG UID
ARG GID
SHELL ["/bin/sh", "-exc"]
RUN apk add --no-cache \
ansible-core \
jq \
packer \
&& adduser ansible -u "$UID" -D -h /home/ansible "$GID" \
&& mkdir -p /configs /manifests /cache \
&& chown ansible /manifests /configs /cache
USER ansible
WORKDIR /home/ansible
COPY default.json default.json
COPY alpine.pkr.hcl alpine.pkr.hcl
COPY playbook.yml playbook.yml
COPY --chmod=u=rx,og= entrypoint.sh entrypoint.sh
VOLUME /cache
ENTRYPOINT ["/bin/sh", "entrypoint.sh"]
CMD ["default.json"]

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 Mathias Pius
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,100 +0,0 @@
This folder contains a modified copy of [MathiasPius/alpine-on-hetzner](https://github.com/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](/entrypoint.sh) for hints on how), but this latter method is not officially supported.
# Examples
### Create an alpine image with the [default](/default.json) 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](#launching-a-server) section for how to test it!
```shell
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](#custom-configuration) for technical details on how the values are merged.
```shell
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](#custom-configuration) 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:
```json
{ "packages": { "nginx": "" } }
```
<sup><sub>`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](https://wiki.alpinelinux.org/wiki/Package_management#Holding_a_specific_package_back) for version-pinning syntax.</sub></sup>
When the container is then run like so:
```shell
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:
```json
{
(...)
"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](/default.json) config for a list of packages that will be installed into the snapshot if run without any arguments.
[playbook.yml](/playbook.yml) contains the entire ansible playbook used for generating the snapshot.
[alpine.pkr.hcl](/alpine.pkr.hcl) contains the packer build configuration which uses the playbook above via the [Ansible Provisioner](https://www.packer.io/plugins/provisioners/ansible/ansible)
# How it works
The docker image comes with packer, ansible and jq pre-installed (check labels for versions), and builds the [alpine.pkr.hcl](/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:
```yaml
#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](https://github.com/MathiasPius/hetzner-zfs-host)
- [ ] 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.
- [x] Pipelining alpine-on-hetzner docker image builds and perhaps more importantly..
- [ ] .. Testing that they work.
- [x] 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.

View file

@ -1,70 +0,0 @@
{
"apk_tools_version": "v2.12.9",
"apk_tools_arch": "x86_64",
"apk_tools_url": "https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic//{{ apk_tools_version }}/{{ apk_tools_arch }}/apk.static",
"apk_tools_checksum": "sha256:5176da3d4c41f12a08b82809aca8e7e2e383b7930979651b8958eca219815af5",
"alpine_repositories": [
{
"tag": "",
"url": "http://dl-cdn.alpinelinux.org/alpine/edge/main"
},
{
"tag": "",
"url": "http://dl-cdn.alpinelinux.org/alpine/edge/community"
}
],
"alpine_repository_keys": [],
"boot_size": "+100m",
"root_size": "0",
"hostname": "alpine",
"dhcp_interfaces": [
"eth0"
],
"packages": {
"openssh": "=8.8_p1-r1",
"syslinux": "=6.04_pre1-r9",
"linux-virt": "=5.15.16-r0",
"cloud-init": "@community=21.4-r0"
},
"services": {
"devfs": "sysinit",
"dmesg": "sysinit",
"mdev": "sysinit",
"hwdrivers": "sysinit",
"hwclock": "boot",
"modules": "boot",
"sysctl": "boot",
"hostname": "boot",
"bootmisc": "boot",
"syslog": "boot",
"networking": "boot",
"mount-ro": "shutdown",
"killprocs": "shutdown",
"savecache": "shutdown",
"sshd": "default"
},
"nameservers": [
"185.12.64.1",
"185.12.64.2",
"2a01:4ff:ff00::add:1",
"2a01:4ff:ff00::add:2"
],
"sysctl": {},
"extlinux_modules": [
"ext4"
],
"mkinitfs_features": [
"base",
"ext4",
"keymap",
"virtio"
],
"kernel_modules": [
"ipv6",
"af_packet"
],
"default_kernel_opts": [
"quiet"
],
"chroot_commands": []
}

View file

@ -1,17 +0,0 @@
#!/bin/sh
export PACKER_CACHE_DIR=/cache/.cache
export PACKER_CONFIG_DIR=/cache/.config
export PACKER_ANSIBLE_DIR=/cache/.ansible
# Combine all the configuration paths passed as arguments.
jq -s 'reduce .[] as $item ({}; . * $item)' "$@" > config.json
echo "Combined configuration:"
cat config.json
echo "Starting Packer Build"
/usr/bin/packer build \
-var-file="config.json" \
.

View file

@ -1,193 +0,0 @@
---
- name: prepare environment
hosts: localhost
tasks:
- name: cache apk tools
get_url:
url: "{{ apk_tools_url }}"
dest: /cache/apk.static
checksum: "{{ apk_tools_checksum }}"
- name: configure alpine
hosts: all
gather_facts: false
vars:
chroot_directory: /mnt
root_device_path: "/dev/sda"
tasks:
- name: deploy apk-tools to rescue system
copy:
src: /cache/apk.static
dest: apk
mode: ug=rwx,o=r
- name: "zap all partitions on {{ root_device_path }} and create GPT table"
shell: "sgdisk --zap-all {{ root_device_path }}"
- name: "create boot partition {{ root_device_path }}-boot"
shell: "sgdisk -g -n1:0:{{ boot_size }} -t1:8300 -c1:boot -A1:set:2 {{ root_device_path }}"
- name: "create root partition {{ root_device_path }}-root"
shell: "sgdisk -g -n2:0:{{ root_size }} -t2:8300 -c2:root {{ root_device_path }}"
- name: mount-drives
shell: |
umount -R /mnt
mkfs.ext4 -q -L root /dev/disk/by-partlabel/root
mkfs.ext4 -m 0 -q -L boot /dev/disk/by-partlabel/boot
mount /dev/disk/by-partlabel/root {{ chroot_directory }}
mkdir -p {{ chroot_directory }}/boot
mount /dev/disk/by-partlabel/boot {{ chroot_directory }}/boot
- name: register uuid of boot disk
shell: |
blkid /dev/disk/by-partlabel/boot | grep -o '\bUUID="[^"]*"' | sed 's/"//g'
register: boot_uuid
- name: register uuid of root disk
shell: |
blkid /dev/disk/by-partlabel/root | grep -o '\bUUID="[^"]*"' | sed 's/"//g'
register: root_uuid
- name: initialize alpine-base in directory
shell: >-
./apk -X {{ alpine_repositories[0].url }}
-u
--allow-untrusted
--root /{{ chroot_directory }}
--initdb
add alpine-base
- name: prepare chroot
shell: |
mount --bind /dev {{ chroot_directory }}/dev
mount --bind /proc {{ chroot_directory }}/proc
mount --bind /sys {{ chroot_directory }}/sys
- name: copy resolv conf from the rescue system to the server
copy:
content: |
{% for nameserver in nameservers %}
nameserver {{ nameserver }}
{% endfor %}
dest: "{{ chroot_directory }}/etc/resolv.conf"
- name: setup networking
copy:
content: |
auto lo
iface lo inet loopback
{% for dif in dhcp_interfaces %}
auto {{ dif }}
iface {{ dif }} inet dhcp
iface {{ dif }} inet6 auto
{% endfor %}
dest: "{{ chroot_directory }}/etc/network/interfaces"
- name: write out hostname file
copy:
dest: "{{ chroot_directory }}/etc/hostname"
content: "{{ hostname }}"
- name: overwrite hosts file
copy:
dest: "{{ chroot_directory }}/etc/hosts"
content: |
127.0.0.1 {{ hostname }} localhost localhost.localdomain
::1 {{ hostname }} localhost localhost.localdomain
::1 {{ hostname }} localhost ipv6-localhost ipv6-loopback
fe00::0 ipv6-localnet
ff00::0 ipv6-mcastprefix
ff02::1 ipv6-allnodes
ff02::2 ipv6-allrouters
ff02::3 ipv6-allhosts
- name: install custom repository keys
copy:
dest: "{{ chroot_directory }}/etc/apk/keys/{{ item.name }}"
content: "{{ item.public_key }}"
loop: "{{ alpine_repository_keys }}"
- name: define alpine repositories
copy:
dest: "{{ chroot_directory }}/etc/apk/repositories"
content: |
{% for repository in alpine_repositories %}
{% if repository.tag | d(false) %}@{{ repository.tag }} {% endif %}{{ repository.url }}
{% endfor %}
- name: install requisite packages
shell: |
chroot {{ chroot_directory }} apk add --no-cache {{ item.key }}{{ item.value }}
loop: "{{ packages | dict2items }}"
- name: configure services
shell: |
chroot {{ chroot_directory }} rc-update add {{ item.key }} {{ item.value }}
loop: "{{ services | dict2items }}"
- name: enable cloud-init
shell: |
chroot {{ chroot_directory }} setup-cloud-init
when: packages["cloud-init"] is defined
- name: configure fstab
copy:
dest: "{{ chroot_directory }}/etc/fstab"
content: |
{{ root_uuid.stdout | trim }} / ext4 defaults,noatime 0 0
{{ boot_uuid.stdout | trim }} /boot ext4 defaults 0 2
- name: configure sysctl
copy:
dest: "{{ chroot_directory }}/etc/sysctl.conf"
content: |
{% for setting in sysctl | dict2items %}
{{ setting.key }} = {{ setting.value }}
{% endfor %}
- name: configure kernel modules
copy:
dest: "{{ chroot_directory }}/etc/modules"
content: |
{% for module in kernel_modules %}
{{ module }}
{% endfor %}
- name: configure extlinux
copy:
dest: "{{ chroot_directory }}/etc/update-extlinux.conf"
content: |
overwrite=1
vesa_menu=0
default_kernel_opts="{{ default_kernel_opts | join(" ") }}"
modules={{ extlinux_modules | join(",") }}
root={{ root_uuid.stdout | trim }}
verbose=0
hidden=1
timeout=1
default=lts
serial_port=
serial_baud=115200
xen_opts=dom0_mem=384M
password=''
- name: configure mkinitfs
copy:
dest: "{{ chroot_directory }}/etc/mkinitfs/mkinitfs.conf"
content: |
features="{{ mkinitfs_features | join(" ") }}"
- name: install boot
shell: |
chroot {{ chroot_directory }} update-extlinux
chroot {{ chroot_directory }} extlinux -i /boot
chroot {{ chroot_directory }} dd bs=440 conv=notrunc count=1 if=/usr/share/syslinux/gptmbr.bin of={{ root_device_path }}
- name: execute arbitrary commands
shell: |
chroot {{ chroot_directory }} sh <<CHROOT_COMMAND_HD
{{ item }}
CHROOT_COMMAND_HD
loop: "{{ chroot_commands }}"

View file

@ -1,6 +1,20 @@
# Please see default.json for default values for these
variable "hostname" {}
packer {
required_plugins {
ansible = {
version = "~> 1"
source = "github.com/hashicorp/ansible"
}
hcloud = {
source = "github.com/hetznercloud/hcloud"
version = "~> 1"
}
}
}
locals {
timestamp = formatdate("YYYYMMDD-hhmmss", timestamp())
snapshot_id = sha1(uuidv4())
@ -26,7 +40,11 @@ build {
}
provisioner "ansible" {
extra_arguments = [
"--scp-extra-args", "'-O'", # Required for OpenSSH >=9 (https://github.com/hashicorp/packer-plugin-ansible/issues/110)
"--extra-vars", "@config.json",
]
playbook_file = "playbook.yml"
extra_arguments = ["--extra-vars", "@config.json"]
user = "root"
}
}

View file

@ -1,9 +1,9 @@
---
apk_tools_version: v2.14.1
apk_tools_version: v2.14.4
apk_tools_arch: x86_64
apk_tools_url: https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic//{{ apk_tools_version }}/{{ apk_tools_arch }}/apk.static
apk_tools_checksum: sha256:273227b3fdece78c8a32ba65cb14a9590988d5290f6d5ba53f2f37cbc5a7e3f5
apk_tools_checksum: sha256:42cea2a41dc09b263f04bb0ade8a2ba251256b91f89095ecc8a19903b2b3e39e
alpine_mirror: https://dl-cdn.alpinelinux.org/alpine
alpine_repositories:
@ -62,7 +62,7 @@ packages:
open-iscsi-openrc: ''
udev: ''
cni-plugin-calico: '@luzifer=3.27.2-r0'
cni-plugin-calico: '@luzifer'
cni-plugins: '@community'
cri-tools: '@community'
containerd: '@community'
@ -70,9 +70,9 @@ packages:
nfs-utils: ''
uuidgen: ''
kubelet: '@luzifer=1.29.3-r0'
kubeadm: '@luzifer=1.29.3-r0'
kubectl: '@luzifer=1.29.3-r0'
kubelet: '@luzifer=1.30.0-r0'
kubeadm: '@luzifer=1.30.0-r0'
kubectl: '@luzifer=1.30.0-r0'
services:
devfs: sysinit
@ -135,22 +135,39 @@ kernel_modules:
default_kernel_opts:
- quiet
chroot_commands:
# kernel stuff
- 'echo "br_netfilter" >/etc/modules-load.d/k8s.conf'
write_files:
# Configure chrony
- dest: /etc/chrony/chrony.conf
content: |
server ptbtime1.ptb.de iburst
server ptbtime2.ptb.de iburst
initstepslew 10 ptbtime1.ptb.de
driftfile /var/lib/chrony/chrony.drift
rtcsync
cmdport 0
# Disable overwriting network config
- 'echo "network: {config: disabled}" >/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg'
- dest: /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
content: |
network: {config: disabled}
# Fix prometheus errors
- 'echo -e "#!/bin/sh\nmount --make-rshared /" >/etc/local.d/sharemetrics.start'
- 'chmod +x /etc/local.d/sharemetrics.start'
# Fix prometheus errors: Write service
- dest: /etc/local.d/sharemetrics.start
content: |
#!/bin/sh
mount --make-rshared /
mode: '0755'
# kernel stuff
- dest: /etc/modules-load.d/k8s.conf
content: |
br_netfilter
chroot_commands:
# Fix prometheus errors: Enable sharemetrics service
- 'rc-update add local'
# Force --cloud-provider=external
- "sed -i 's/command_args=\"/command_args=\"--cloud-provider=external /' /etc/init.d/kubelet"
# Configure chrony
- 'echo -e "server ptbtime1.ptb.de iburst\nserver ptbtime2.ptb.de iburst\ninitstepslew 10 ptbtime1.ptb.de\ndriftfile /var/lib/chrony/chrony.drift\nrtcsync\ncmdport 0" >/etc/chrony/chrony.conf'
...

194
playbook.yml Normal file
View file

@ -0,0 +1,194 @@
---
- name: configure alpine
hosts: all
gather_facts: false
vars:
chroot_directory: /mnt
root_device_path: "/dev/sda"
tasks:
- name: deploy apk-tools to rescue system
get_url:
checksum: "{{ apk_tools_checksum }}"
dest: /tmp/apk
mode: ug=rwx,o=r
url: "{{ apk_tools_url }}"
- name: "zap all partitions on {{ root_device_path }} and create GPT table"
shell: "sgdisk --zap-all {{ root_device_path }}"
- name: "create boot partition {{ root_device_path }}-boot"
shell: "sgdisk -g -n1:0:{{ boot_size }} -t1:8300 -c1:boot -A1:set:2 {{ root_device_path }}"
- name: "create root partition {{ root_device_path }}-root"
shell: "sgdisk -g -n2:0:{{ root_size }} -t2:8300 -c2:root {{ root_device_path }}"
- name: mount-drives
shell: |
umount -R /mnt
mkfs.ext4 -q -L root /dev/disk/by-partlabel/root
mkfs.ext4 -m 0 -q -L boot /dev/disk/by-partlabel/boot
mount /dev/disk/by-partlabel/root {{ chroot_directory }}
mkdir -p {{ chroot_directory }}/boot
mount /dev/disk/by-partlabel/boot {{ chroot_directory }}/boot
- name: register uuid of boot disk
shell: |
blkid /dev/disk/by-partlabel/boot | grep -o '\bUUID="[^"]*"' | sed 's/"//g'
register: boot_uuid
- name: register uuid of root disk
shell: |
blkid /dev/disk/by-partlabel/root | grep -o '\bUUID="[^"]*"' | sed 's/"//g'
register: root_uuid
- name: initialize alpine-base in directory
shell: >-
/tmp/apk -X {{ alpine_repositories[0].url }}
-u
--allow-untrusted
--root /{{ chroot_directory }}
--initdb
add alpine-base
- name: prepare chroot
shell: |
mount --bind /dev {{ chroot_directory }}/dev
mount --bind /proc {{ chroot_directory }}/proc
mount --bind /sys {{ chroot_directory }}/sys
- name: copy resolv conf from the rescue system to the server
copy:
content: |
{% for nameserver in nameservers %}
nameserver {{ nameserver }}
{% endfor %}
dest: "{{ chroot_directory }}/etc/resolv.conf"
- name: setup networking
copy:
content: |
auto lo
iface lo inet loopback
{% for dif in dhcp_interfaces %}
auto {{ dif }}
iface {{ dif }} inet dhcp
iface {{ dif }} inet6 auto
{% endfor %}
dest: "{{ chroot_directory }}/etc/network/interfaces"
- name: write out hostname file
copy:
dest: "{{ chroot_directory }}/etc/hostname"
content: "{{ hostname }}"
- name: overwrite hosts file
copy:
dest: "{{ chroot_directory }}/etc/hosts"
content: |
127.0.0.1 {{ hostname }} localhost localhost.localdomain
::1 {{ hostname }} localhost localhost.localdomain
::1 {{ hostname }} localhost ipv6-localhost ipv6-loopback
fe00::0 ipv6-localnet
ff00::0 ipv6-mcastprefix
ff02::1 ipv6-allnodes
ff02::2 ipv6-allrouters
ff02::3 ipv6-allhosts
- name: install custom repository keys
copy:
dest: "{{ chroot_directory }}/etc/apk/keys/{{ item.name }}"
content: "{{ item.public_key }}"
loop: "{{ alpine_repository_keys }}"
- name: define alpine repositories
copy:
dest: "{{ chroot_directory }}/etc/apk/repositories"
content: |
{% for repository in alpine_repositories %}
{% if repository.tag | d(false) %}@{{ repository.tag }} {% endif %}{{ repository.url }}
{% endfor %}
- name: install requisite packages
shell: |
chroot {{ chroot_directory }} apk add --no-cache {{ item.key }}{{ item.value }}
loop: "{{ packages | dict2items }}"
- name: configure services
shell: |
chroot {{ chroot_directory }} rc-update add {{ item.key }} {{ item.value }}
loop: "{{ services | dict2items }}"
- name: enable cloud-init
shell: |
chroot {{ chroot_directory }} setup-cloud-init
when: packages["cloud-init"] is defined
- name: configure fstab
copy:
dest: "{{ chroot_directory }}/etc/fstab"
content: |
{{ root_uuid.stdout | trim }} / ext4 defaults,noatime 0 0
{{ boot_uuid.stdout | trim }} /boot ext4 defaults 0 2
- name: configure sysctl
copy:
dest: "{{ chroot_directory }}/etc/sysctl.conf"
content: |
{% for setting in sysctl | dict2items %}
{{ setting.key }} = {{ setting.value }}
{% endfor %}
- name: configure kernel modules
copy:
dest: "{{ chroot_directory }}/etc/modules"
content: |
{% for module in kernel_modules %}
{{ module }}
{% endfor %}
- name: configure extlinux
copy:
dest: "{{ chroot_directory }}/etc/update-extlinux.conf"
content: |
overwrite=1
vesa_menu=0
default_kernel_opts="{{ default_kernel_opts | join(" ") }}"
modules={{ extlinux_modules | join(",") }}
root={{ root_uuid.stdout | trim }}
verbose=0
hidden=1
timeout=1
default=lts
serial_port=
serial_baud=115200
xen_opts=dom0_mem=384M
password=''
- name: configure mkinitfs
copy:
dest: "{{ chroot_directory }}/etc/mkinitfs/mkinitfs.conf"
content: |
features="{{ mkinitfs_features | join(" ") }}"
- name: install boot
shell: |
chroot {{ chroot_directory }} update-extlinux
chroot {{ chroot_directory }} extlinux -i /boot
chroot {{ chroot_directory }} dd bs=440 conv=notrunc count=1 if=/usr/share/syslinux/gptmbr.bin of={{ root_device_path }}
- name: write arbitrary files
copy:
dest: "{{ chroot_directory }}{{ item.dest }}"
content: "{{ item.content }}"
mode: "{{ item.mode | default(omit) }}"
owner: "{{ item.owner | default('root') }}"
loop: "{{ write_files | default([]) }}"
- name: execute arbitrary commands
shell: |
chroot {{ chroot_directory }} sh <<CHROOT_COMMAND_HD
{{ item }}
CHROOT_COMMAND_HD
loop: "{{ chroot_commands }}"