mirror of
https://github.com/Luzifer/hetzner-alpine-k8s.git
synced 2024-12-20 18:01:19 +00:00
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:
parent
e4cd2dd84f
commit
7ba01f01a1
14 changed files with 276 additions and 482 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
config.json
|
||||
envrun_*
|
||||
packer_*
|
||||
yq_*
|
||||
|
|
55
Makefile
55
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
manifests/
|
||||
cache/
|
3
alpine-on-hetzner/.gitignore
vendored
3
alpine-on-hetzner/.gitignore
vendored
|
@ -1,3 +0,0 @@
|
|||
manifests/
|
||||
configs/
|
||||
cache/
|
|
@ -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"]
|
|
@ -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.
|
|
@ -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.
|
|
@ -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": []
|
||||
}
|
|
@ -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" \
|
||||
.
|
|
@ -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 }}"
|
|
@ -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"
|
||||
}
|
||||
}
|
49
config.yaml
49
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'
|
||||
|
||||
...
|
||||
|
|
194
playbook.yml
Normal file
194
playbook.yml
Normal 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 }}"
|
Loading…
Reference in a new issue