From 7ba01f01a153a2db62d48410571ba51db2eb1528 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Sun, 21 Apr 2024 15:52:40 +0200 Subject: [PATCH] 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 --- .gitignore | 1 + Makefile | 55 +++-- README.md | 4 +- alpine-on-hetzner/.dockerignore | 2 - alpine-on-hetzner/.gitignore | 3 - alpine-on-hetzner/Dockerfile | 29 --- alpine-on-hetzner/LICENSE | 21 -- alpine-on-hetzner/README.md | 100 --------- alpine-on-hetzner/default.json | 70 ------- alpine-on-hetzner/entrypoint.sh | 17 -- alpine-on-hetzner/playbook.yml | 193 ----------------- .../alpine.pkr.hcl => alpine.pkr.hcl | 20 +- config.yaml | 49 +++-- playbook.yml | 194 ++++++++++++++++++ 14 files changed, 276 insertions(+), 482 deletions(-) delete mode 100644 alpine-on-hetzner/.dockerignore delete mode 100644 alpine-on-hetzner/.gitignore delete mode 100644 alpine-on-hetzner/Dockerfile delete mode 100644 alpine-on-hetzner/LICENSE delete mode 100644 alpine-on-hetzner/README.md delete mode 100644 alpine-on-hetzner/default.json delete mode 100644 alpine-on-hetzner/entrypoint.sh delete mode 100644 alpine-on-hetzner/playbook.yml rename alpine-on-hetzner/alpine.pkr.hcl => alpine.pkr.hcl (60%) create mode 100644 playbook.yml diff --git a/.gitignore b/.gitignore index 5a2427d..437d38f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ config.json envrun_* +packer_* yq_* diff --git a/Makefile b/Makefile index 8864964..74f2754 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index 53bbce7..012f542 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/alpine-on-hetzner/.dockerignore b/alpine-on-hetzner/.dockerignore deleted file mode 100644 index e7222f0..0000000 --- a/alpine-on-hetzner/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -manifests/ -cache/ diff --git a/alpine-on-hetzner/.gitignore b/alpine-on-hetzner/.gitignore deleted file mode 100644 index 4ebf2f0..0000000 --- a/alpine-on-hetzner/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -manifests/ -configs/ -cache/ diff --git a/alpine-on-hetzner/Dockerfile b/alpine-on-hetzner/Dockerfile deleted file mode 100644 index 7eb213f..0000000 --- a/alpine-on-hetzner/Dockerfile +++ /dev/null @@ -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"] diff --git a/alpine-on-hetzner/LICENSE b/alpine-on-hetzner/LICENSE deleted file mode 100644 index 47ad020..0000000 --- a/alpine-on-hetzner/LICENSE +++ /dev/null @@ -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. diff --git a/alpine-on-hetzner/README.md b/alpine-on-hetzner/README.md deleted file mode 100644 index 9c95ec8..0000000 --- a/alpine-on-hetzner/README.md +++ /dev/null @@ -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=" 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": "" } } -``` -`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. - -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. diff --git a/alpine-on-hetzner/default.json b/alpine-on-hetzner/default.json deleted file mode 100644 index 3e13952..0000000 --- a/alpine-on-hetzner/default.json +++ /dev/null @@ -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": [] -} \ No newline at end of file diff --git a/alpine-on-hetzner/entrypoint.sh b/alpine-on-hetzner/entrypoint.sh deleted file mode 100644 index df9ea27..0000000 --- a/alpine-on-hetzner/entrypoint.sh +++ /dev/null @@ -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" \ - . \ No newline at end of file diff --git a/alpine-on-hetzner/playbook.yml b/alpine-on-hetzner/playbook.yml deleted file mode 100644 index 0a929a4..0000000 --- a/alpine-on-hetzner/playbook.yml +++ /dev/null @@ -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 <=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" } } diff --git a/config.yaml b/config.yaml index ebdd8ad..8329357 100644 --- a/config.yaml +++ b/config.yaml @@ -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' - ... diff --git a/playbook.yml b/playbook.yml new file mode 100644 index 0000000..f103335 --- /dev/null +++ b/playbook.yml @@ -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 <