mirror of
https://github.com/Luzifer/hetzner-alpine-k8s.git
synced 2024-12-21 02:11: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
|
config.json
|
||||||
envrun_*
|
envrun_*
|
||||||
|
packer_*
|
||||||
yq_*
|
yq_*
|
||||||
|
|
55
Makefile
55
Makefile
|
@ -1,42 +1,39 @@
|
||||||
ALPINE_VERSION:=3.18
|
ENVRUN_VERSION := 0.7.1
|
||||||
PACKER_VERSION:=1.8.7-r3
|
PACKER_VERSION := 1.10.2
|
||||||
ANSIBLE_CORE_VERSION:=2.14.5-r0
|
YQ_VERSION := 4.31.2
|
||||||
JQ_VERSION:=1.6-r3
|
|
||||||
|
|
||||||
ENVRUN_VERSION:=v0.7.1
|
ENVRUN := ./envrun_$(ENVRUN_VERSION)
|
||||||
YQ_VERSION:=v4.31.2
|
PACKER := ./packer_$(PACKER_VERSION)
|
||||||
|
YQ := ./yq_$(YQ_VERSION)
|
||||||
export DOCKER_BUILDKIT:=1
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
config.json: yq_$(YQ_VERSION)
|
config.json: $(YQ)
|
||||||
./yq_$(YQ_VERSION) -ojson . config.yaml | jq -S . >config.json
|
$(YQ) -ojson . config.yaml | jq -S . >config.json
|
||||||
|
|
||||||
create-snapshot: docker-build config.json envrun_$(ENVRUN_VERSION)
|
create-snapshot: config.json $(ENVRUN) $(PACKER)
|
||||||
./envrun_$(ENVRUN_VERSION) -- docker run --rm -i \
|
$(PACKER) init alpine.pkr.hcl
|
||||||
-e "HCLOUD_TOKEN" \
|
$(ENVRUN) -- $(PACKER) build -var-file=config.json alpine.pkr.hcl
|
||||||
-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
|
|
||||||
|
|
||||||
# --- Tools
|
# --- Tools
|
||||||
|
|
||||||
envrun_$(ENVRUN_VERSION):
|
tools: $(ENVRUN) $(PACKER) $(YQ)
|
||||||
curl -sSfL "https://github.com/Luzifer/envrun/releases/download/$(ENVRUN_VERSION)/envrun_linux_amd64.tar.gz" | tar -xz
|
|
||||||
|
$(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 $@
|
mv envrun_linux_amd64 $@
|
||||||
|
|
||||||
yq_$(YQ_VERSION):
|
$(PACKER):
|
||||||
curl -sSfLo $@ "https://github.com/mikefarah/yq/releases/download/$(YQ_VERSION)/yq_linux_amd64"
|
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 $@
|
chmod +x $@
|
||||||
|
|
||||||
.PHONY: config.json
|
.PHONY: config.json
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# Luzifer / hetzner-alpine-k8s
|
# 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
|
## 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
|
# Please see default.json for default values for these
|
||||||
variable "hostname" {}
|
variable "hostname" {}
|
||||||
|
|
||||||
|
packer {
|
||||||
|
required_plugins {
|
||||||
|
ansible = {
|
||||||
|
version = "~> 1"
|
||||||
|
source = "github.com/hashicorp/ansible"
|
||||||
|
}
|
||||||
|
|
||||||
|
hcloud = {
|
||||||
|
source = "github.com/hetznercloud/hcloud"
|
||||||
|
version = "~> 1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
timestamp = formatdate("YYYYMMDD-hhmmss", timestamp())
|
timestamp = formatdate("YYYYMMDD-hhmmss", timestamp())
|
||||||
snapshot_id = sha1(uuidv4())
|
snapshot_id = sha1(uuidv4())
|
||||||
|
@ -26,7 +40,11 @@ build {
|
||||||
}
|
}
|
||||||
|
|
||||||
provisioner "ansible" {
|
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"
|
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_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_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_mirror: https://dl-cdn.alpinelinux.org/alpine
|
||||||
alpine_repositories:
|
alpine_repositories:
|
||||||
|
@ -62,7 +62,7 @@ packages:
|
||||||
open-iscsi-openrc: ''
|
open-iscsi-openrc: ''
|
||||||
udev: ''
|
udev: ''
|
||||||
|
|
||||||
cni-plugin-calico: '@luzifer=3.27.2-r0'
|
cni-plugin-calico: '@luzifer'
|
||||||
cni-plugins: '@community'
|
cni-plugins: '@community'
|
||||||
cri-tools: '@community'
|
cri-tools: '@community'
|
||||||
containerd: '@community'
|
containerd: '@community'
|
||||||
|
@ -70,9 +70,9 @@ packages:
|
||||||
nfs-utils: ''
|
nfs-utils: ''
|
||||||
uuidgen: ''
|
uuidgen: ''
|
||||||
|
|
||||||
kubelet: '@luzifer=1.29.3-r0'
|
kubelet: '@luzifer=1.30.0-r0'
|
||||||
kubeadm: '@luzifer=1.29.3-r0'
|
kubeadm: '@luzifer=1.30.0-r0'
|
||||||
kubectl: '@luzifer=1.29.3-r0'
|
kubectl: '@luzifer=1.30.0-r0'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
devfs: sysinit
|
devfs: sysinit
|
||||||
|
@ -135,22 +135,39 @@ kernel_modules:
|
||||||
default_kernel_opts:
|
default_kernel_opts:
|
||||||
- quiet
|
- quiet
|
||||||
|
|
||||||
chroot_commands:
|
write_files:
|
||||||
# kernel stuff
|
# Configure chrony
|
||||||
- 'echo "br_netfilter" >/etc/modules-load.d/k8s.conf'
|
- 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
|
# 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
|
# Fix prometheus errors: Write service
|
||||||
- 'echo -e "#!/bin/sh\nmount --make-rshared /" >/etc/local.d/sharemetrics.start'
|
- dest: /etc/local.d/sharemetrics.start
|
||||||
- 'chmod +x /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'
|
- 'rc-update add local'
|
||||||
|
|
||||||
# Force --cloud-provider=external
|
# Force --cloud-provider=external
|
||||||
- "sed -i 's/command_args=\"/command_args=\"--cloud-provider=external /' /etc/init.d/kubelet"
|
- "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