Compare commits

..

58 Commits

Author SHA1 Message Date
JamesAllenby 0c1c1a24ee fix: use import instead of include 2026-08-14 23:15:32 +01:00
JamesAllenby 9a5c8288b0 feat: add init target to justfile 2026-08-14 23:10:25 +01:00
JamesAllenby f946f0f2bf fix: ensure kolibri user is system user 2026-08-14 22:21:16 +01:00
JamesAllenby 80d48e6f46 feat: add power and performance playbook 2026-08-14 22:21:03 +01:00
JamesAllenby 8dc6ac2939 feat: add ansible galaxy path 2026-08-14 22:19:18 +01:00
JamesAllenby 7f80884082 feat: add target for fetching ansible dependencies 2026-08-14 22:18:48 +01:00
JamesAllenby 9abb0b010e feat: add variable master and worker counts 2026-08-14 22:08:45 +01:00
JamesAllenby aa0e9f37bf fix: add cluster name to incus cluster 2026-08-09 20:50:02 +01:00
JamesAllenby 5613699540 fix: add roles path to ansible config 2026-08-09 20:49:40 +01:00
JamesAllenby 03dc10c0e5 chore: set minimum tofu version to 1.11.5 2026-07-28 16:47:14 +01:00
JamesAllenby 573afc50a4 doc: readme update 2026-07-28 16:37:33 +01:00
JamesAllenby 6dab1dccd9 chore: rename bare metal to hive 2026-07-28 16:37:20 +01:00
JamesAllenby ce16338203 doc: add quick start guide 2026-07-05 23:02:30 +01:00
JamesAllenby 591541215b fix: down recipe destroys entire tofu instance 2026-07-05 23:00:12 +01:00
JamesAllenby adccf7af22 style: format opentofu code 2026-07-05 22:54:07 +01:00
JamesAllenby 87e90a2327 ci: add yamlfmt config 2026-07-05 22:53:35 +01:00
JamesAllenby 9e0f9697a8 refactor: remove unused load balancer ranges 2026-07-05 22:38:01 +01:00
JamesAllenby 819add7725 refactor: remove longhorn 2026-07-05 22:32:12 +01:00
JamesAllenby 8c665eeff4 refactor: remove metrics-server 2026-07-05 22:31:56 +01:00
JamesAllenby b3279dd23c refactor: remove cilium 2026-07-05 22:31:32 +01:00
JamesAllenby 0a4c621c57 refactor: remove cert-manager 2026-07-05 22:24:18 +01:00
JamesAllenby 3ecf328d2a refactor: remove authentik 2026-07-05 22:23:50 +01:00
JamesAllenby 390e2b29f5 refactor: remove keepalived 2026-07-05 22:23:36 +01:00
JamesAllenby 259abad851 refactor: remove prometheus 2026-07-05 22:21:00 +01:00
JamesAllenby b7a7ed5632 refactor: remove traefik 2026-07-05 22:20:41 +01:00
JamesAllenby 6f62bf2a1b refactor: disable kube-vip svc load balancer 2026-07-05 22:19:34 +01:00
JamesAllenby 1efa0f9426 feat: add prometheus role 2026-07-05 22:19:07 +01:00
JamesAllenby 81bf536ac3 feat: add cert-manager role 2026-07-05 22:18:55 +01:00
JamesAllenby 02af027e43 feat: add authentik role 2026-07-05 22:18:43 +01:00
JamesAllenby dd8e1e3fc9 refactor: remove longhorn and traefik 2026-07-05 22:18:12 +01:00
JamesAllenby 5e11ed8fdc feat: add traefik role 2026-07-01 00:51:20 +01:00
JamesAllenby bcc7922f46 refactor: add l2 announcements and lb ipam to cilium 2026-07-01 00:50:52 +01:00
JamesAllenby 89f28c2738 feat: add longhorn role to site.yaml 2026-07-01 00:23:22 +01:00
JamesAllenby c27709a0ef fix: enable insecure tls for metrics server 2026-07-01 00:23:11 +01:00
JamesAllenby 9fc67c61c0 feat: add metrics server install to site.yaml 2026-07-01 00:10:27 +01:00
JamesAllenby 5f7848a198 feat: enable hubble for cilium 2026-07-01 00:10:17 +01:00
JamesAllenby 4a7030f7ac refactor: rename to kubernetes_metrics_server 2026-07-01 00:10:07 +01:00
JamesAllenby d733c45323 feat: add cluster pod subnet and service subnet for incus 2026-06-30 23:58:07 +01:00
JamesAllenby 20f8648d86 feat: add untaint control plane nodes task 2026-06-30 23:57:52 +01:00
JamesAllenby 74bfab7bd8 fix: clear cni config in reset playbook 2026-06-30 23:41:50 +01:00
JamesAllenby 71edc8d0b7 feat: update site playbook and add cluster network vars 2026-06-30 23:41:27 +01:00
JamesAllenby 3699aac3ae feat: add helm and kubectl to kubernetes_common 2026-06-30 23:40:52 +01:00
JamesAllenby 8ae456bda7 feat: add longhorn role 2026-06-30 23:40:18 +01:00
JamesAllenby 363e218033 feat: add cilium cni role 2026-06-30 23:39:55 +01:00
JamesAllenby 068659e174 refactor: split control plane role into init/join/copy tasks 2026-06-30 23:39:34 +01:00
JamesAllenby 90686995d6 feat: add recipe for launching headlamp 2026-06-30 21:54:40 +01:00
JamesAllenby b18af66f03 fix: update just recipe 2026-06-30 00:28:28 +01:00
JamesAllenby 010f0657b5 feat: update control plane role 2026-06-30 00:25:44 +01:00
JamesAllenby 6a127588f0 fix: use correct permission for static pod manifest 2026-06-30 00:25:14 +01:00
JamesAllenby 248ccb48fe feat: add task for copying admin configurations 2026-06-30 00:25:01 +01:00
JamesAllenby 1f13980b53 feat: install open-iscsi for longhorn 2026-06-30 00:24:43 +01:00
JamesAllenby ff35802ba5 feat: add containerd service task 2026-06-30 00:24:28 +01:00
JamesAllenby b82155193e feat: add reset playbook 2026-06-30 00:23:56 +01:00
JamesAllenby 474a296b60 refactor: remove unused playbooks 2026-06-30 00:23:05 +01:00
JamesAllenby 34812b901c refactor: rename provisioning playbook 2026-06-30 00:22:50 +01:00
JamesAllenby e6d48c6802 feat: set ansible output format to yaml 2026-06-29 22:07:55 +01:00
JamesAllenby 1592c8290d fix: allow metrics-server to work on insecure tls 2026-06-29 01:43:28 +01:00
JamesAllenby 2c0e7245b6 fix: remove taint from control-plane nodes 2026-06-29 01:43:01 +01:00
38 changed files with 269 additions and 422 deletions
+2
View File
@@ -3,8 +3,10 @@
terraform.tfstate
terraform.tfstate.backup
ansible/lookup_plugins
ansible/galaxy_roles
!.gitignore
!.editorconfig
!.vscode
!.pre-commit-config.yaml
!.yamlfmt.yaml
+5
View File
@@ -0,0 +1,5 @@
formatter:
indent: 2
retain_line_breaks_single: true
scan_folded_as_literal: true
indentless_arrays: false
+12 -7
View File
@@ -4,31 +4,36 @@ opentofu_dir := "infra/"
ansible_proton_pass_plugin_ref := "4bd0741c347646060ec59c73f8adf7ed8e706cf3"
ansible_proton_pass_plugin_url := "https://raw.githubusercontent.com/protonpass/proton-pass-ansible-integration/" + ansible_proton_pass_plugin_ref + "/lookup_plugins/proton_pass.py"
init: tofu-init fetch-ansible-deps fetch-ansible-plugins
up: tofu-apply ansible-run-playbook
down: tofu-destroy-instances
down: tofu-destroy
tofu-init:
tofu -chdir={{ opentofu_dir }} init -upgrade
tofu-apply: tofu-init
tofu-apply:
tofu -chdir={{ opentofu_dir }} apply -auto-approve
tofu-destroy: tofu-init
tofu-destroy:
tofu -chdir={{ opentofu_dir }} apply -auto-approve -destroy
tofu-destroy-instances:
tofu -chdir={{ opentofu_dir }} apply -auto-approve -destroy -target=module.master -target=module.worker
ansible-run-playbook:
ansible-playbook -i ansible/inventories/incus/incus.yaml -c community.general.incus ansible/playbooks/provision.yaml -e ansible_user=root
ansible-playbook -i ansible/inventories/incus/incus.yaml -c community.general.incus ansible/playbooks/provisioning.yaml -e ansible_user=root
ansible-playbook -i ansible/inventories/incus/incus.yaml -c community.general.incus ansible/site.yaml
get-admin-conf:
incus exec master-0 -- cat /etc/kubernetes/admin.conf > k8s-admin.conf
headlamp:
incus exec master-0 -- cat /etc/kubernetes/admin.conf > .incus-admin.conf
flatpak run io.kinvolk.Headlamp --kubeconfig "$(pwd)/.incus-admin.conf"
clean: tofu-destroy
git clean -fdx
fetch-plugins:
fetch-ansible-deps:
ansible-galaxy install --role-file ansible/requirements.yml --roles-path ansible/galaxy_roles
fetch-ansible-plugins:
mkdir -p ansible/lookup_plugins
curl -fsSL -o ansible/lookup_plugins/proton_pass.py {{ ansible_proton_pass_plugin_url }}
+50
View File
@@ -1,5 +1,39 @@
# Home Infrastructure
An Ansible + OpenTofu project for installing a minimal Kubernetes installation
to bare-metal and virtual machines.
## Quick Start
Get started with a local Kubernetes cluster which can be used to design and test
new configuration before pushing them to real machines.
1. Login with Proton Pass and start the SSH daemon.
```shell
pass-cli login
pass-cli ssh-agent daemon start
export SSH_AUTH_SOCK="$HOME/.ssh/proton-pass-agent.sock"
```
2. Install the Proton Pass plugin for Ansible
```shell
just fetch-plugins
```
3. Launch Incus virtual machines
```shell
just up
```
4. Tear down the Incus virtual machines
```shell
just down
```
## Setup
```shell
@@ -15,3 +49,19 @@ The Kolibri automation user is used for all other playbooks and has permissions
```shell
ansible-playbook -i ansible/inventories/bare_metal/hosts.yaml ansible/playbooks/provision.yaml -K -e ansible_user=<your_interactive_user>
```
## Local Testing with Incus
```shell
just up
```
```shell
just headlamp
```
> The above command runs the Flatpak version of Headlamp which may not have permissions
> to access the Kubernetes config file copied from the instance.
>
> You should run `flatpak override --user --filesystem=$(pwd) io.kinvolk.Headlamp`
> to ensure that Headlamp can access the directory contents.
+2
View File
@@ -1,2 +1,4 @@
[defaults]
roles_path = ansible/roles:ansible/galaxy_roles
lookup_plugins = ansible/lookup_plugins
callback_result_format = yaml
@@ -1,8 +0,0 @@
masters:
hosts:
vineta:
ansible_host: 10.0.0.201
rotfront:
ansible_host: 10.0.0.202
leng:
ansible_host: 10.0.0.203
@@ -3,3 +3,6 @@ ansible_become_exe: sudo.ws
ansible_user: kolibri
vip_interface: eno1
vip_address: 10.0.0.200
cluster_name: hive
cluster_pod_subnet: 172.16.0.0/16
cluster_service_subnet: 10.96.0.0/16
+8
View File
@@ -0,0 +1,8 @@
masters:
hosts:
vineta:
ansible_host: vineta.local
rotfront:
ansible_host: rotfront.local
leng:
ansible_host: leng.local
@@ -2,3 +2,6 @@ ansible_python_interpreter: /usr/bin/python3
ansible_user: kolibri
vip_interface: enp5s0
vip_address: 10.150.0.100
cluster_name: hive
cluster_pod_subnet: 172.16.0.0/16
cluster_service_subnet: 10.96.0.0/16
-137
View File
@@ -1,137 +0,0 @@
- name: Primary Master Initialisation
hosts: masters[0]
tasks:
- name: Initialise high-availability Kubernetes cluster
ansible.builtin.command: >-
kubeadm init --pod-network-cidr="172.16.0.0/16" --control-plane-endpoint="{{ ansible_default_ipv4.address }}:6443" --upload-certs
args:
creates: /etc/kubernetes/admin.conf
- name: Copy Kubernetes Admin Config
ansible.builtin.command: "{{ item }}"
loop:
- mkdir -p $HOME/.kube
- cp /etc/kubernetes/admin.conf $HOME/.kube/config
- chown $(id -u):$(id -g) $HOME/.kube/config
args:
creates: $HOME/.kube/config
- name: Install Pod Network (Calico)
ansible.builtin.shell: kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.0/manifests/calico.yaml > pod_network_setup.txt
args:
chdir: $HOME
creates: pod_network_setup.txt
- name: Add Helm repositories
kubernetes.core.helm_repository:
name: "{{ item.name }}"
repo_url: "{{ item.url }}"
loop:
- name: metrics-server
url: https://kubernetes-sigs.github.io/metrics-server/
- name: prometheus-community
url: https://prometheus-community.github.io/helm-charts
- name: longhorn
url: https://charts.longhorn.io
- name: traefik
url: https://traefik.github.io/charts
- name: minecraft-server-charts
url: https://itzg.github.io/minecraft-server-charts/
- name: Install Metrics Server
kubernetes.core.helm:
release_name: metrics-server
release_namespace: kube-system
chart_ref: metrics-server/metrics-server
chart_version: 3.13.0
values:
args:
- --kubelet-insecure-tls
- name: Install Longhorn
kubernetes.core.helm:
release_name: longhorn
release_namespace: longhorn-system
create_namespace: true
chart_ref: longhorn/longhorn
chart_version: 1.12.0
- name: Install Prometheus
kubernetes.core.helm:
release_name: prometheus
release_namespace: monitoring
create_namespace: true
chart_ref: prometheus-community/prometheus
chart_version: 29.10.0
state: absent
- name: Install Traefik
kubernetes.core.helm:
release_name: traefik
release_namespace: default
create_namespace: true
chart_ref: traefik/traefik
chart_version: 40.2.0
values:
deployment:
kind: DaemonSet
securityContext:
capabilities:
drop: [ALL]
add: [NET_BIND_SERVICE]
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
ports:
web:
port: 80
containerPort: 80
hostPort: 80
websecure:
port: 443
containerPort: 443
hostPort: 443
- name: Install Minecraft
kubernetes.core.helm:
release_name: minecraft
release_namespace: default
chart_ref: minecraft-server-charts/minecraft
chart_version: 5.1.3
values:
minecraftServer:
eula: true
persistence:
dataDir:
enabled: true
- name: Extract Control Plane Decryption Key & Join Token
block:
- name: Generate Fresh Join Command
ansible.builtin.command: kubeadm token create --print-join-command
register: join_command_raw
- name: Upload Certs and Capture Certificate Key
shell: kubeadm init phase upload-certs --upload-certs | tail -n 1
register: cert_key_raw
- name: Set facts across playbooks
ansible.builtin.set_fact:
k8s_join_base: "{{ join_command_raw.stdout }}"
k8s_cert_key: "{{ cert_key_raw.stdout }}"
delegate_to: localhost
delegate_facts: true
- name: Join Secondary Control Planes
hosts: masters:!masters[0] # <--- Targets masters 1 and 2
tasks:
- name: Join Cluster as Control Plane Node
ansible.builtin.shell: "{{ hostvars['localhost']['k8s_join_base'] }} --control-plane --certificate-key {{ hostvars['localhost']['k8s_cert_key'] }}"
args:
creates: /etc/kubernetes/admin.conf
- name: Copy Kubernetes Admin Config
ansible.builtin.shell: "{{ item }}"
loop:
- mkdir -p $HOME/.kube
- cp /etc/kubernetes/admin.conf $HOME/.kube/config
- chown $(id -u):$(id -g) $HOME/.kube/config
args:
creates: $HOME/.kube/config
- name: Remove control plane taint
hosts: masters[0]
tasks:
- name: Remove control plane NoSchedule taint from all nodes
kubernetes.core.k8s_taint:
state: absent
name: "{{ item }}"
taints:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
loop: "{{ groups['masters'] }}"
+7
View File
@@ -0,0 +1,7 @@
---
- name: Configure performance tuning
hosts: all
become: true
roles:
- giovtorres.tuned
- irqbalance
@@ -1,4 +1,4 @@
- name: Provision kolibri automation user
- name: Create Kolibri automation user
hosts: all
become: true
tasks:
@@ -10,6 +10,7 @@
groups:
- sudo
shell: /bin/bash
system: true
- name: Ensure authorised keys for Kolibri
ansible.posix.authorized_key:
+13
View File
@@ -0,0 +1,13 @@
- name: Reset nodes
hosts: all
become: true
tasks:
- name: Reset kubeadm
ansible.builtin.command: kubeadm reset -f
args:
removes: /etc/kubernetes/*.conf
- name: Clear CNI configuration
ansible.builtin.file:
path: /etc/cni/net.d
state: absent
-78
View File
@@ -1,78 +0,0 @@
- name: Initialise Master and Worker Nodes
hosts: all
tasks:
- name: Enable IPv4 forwarding
ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: 1
sysctl_set: true
- name: Enable Kernel Modules
community.general.modprobe:
name: "{{ item }}"
persistent: present
loop:
- br_netfilter
- dm-crypt
- name: Ensure /sys mount is set to shared for container runtimes
block:
- name: Remount /sys as shared immediately
ansible.builtin.command: mount --make-rshared {{ item }}
loop:
- /
- /sys
- /run
changed_when: false
- name: Create Alpine local.d script for permanent shared mount
ansible.builtin.copy:
dest: /etc/local.d/mount-shared.start
content: |
#!/bin/sh
mount --make-rshared /
mount --make-rshared /sys
mount --make-rshared /run
owner: root
group: root
mode: '0755'
- name: Ensure Alpine local service is enabled on boot
ansible.builtin.service:
name: local
enabled: true
runlevel: default
- name: Install tools
ansible.builtin.package:
name: "{{ item }}"
state: present
loop:
- htop
- name: Install kubectl, kubelet, kubeadm and containerd
ansible.builtin.package:
name: "{{ item }}"
state: present
loop:
- kubectl
- kubeadm
- kubelet
- containerd
- cni-plugins
- helm
- open-iscsi
- name: Start kubelet and containerd
ansible.builtin.service:
name: "{{ item }}"
enabled: true
state: started
loop:
- kubelet
- containerd
- iscsid
- name: Add /opt/cni/bin to containerd CNI binary directories
ansible.builtin.replace:
path: /etc/containerd/config.toml
regexp: "bin_dirs = \\['/usr/libexec/cni'\\]"
replace: "bin_dirs = ['/opt/cni/bin', '/usr/libexec/cni']"
notify: Restart containerd
handlers:
- name: Restart containerd
ansible.builtin.service:
name: containerd
state: restarted
-22
View File
@@ -1,22 +0,0 @@
- name: Extract Worker Join Token from Primary Master
hosts: masters[0] # <--- Targets only the first master safely
gather_facts: false
tasks:
- name: Generate worker join command
ansible.builtin.command: kubeadm token create --print-join-command
register: join_command_raw
changed_when: false # Reading/generating a token text string changes no host state
- name: Save join command globally
ansible.builtin.set_fact:
k8s_worker_join: "{{ join_command_raw.stdout }}"
delegate_to: localhost
delegate_facts: true
- name: Join Worker Nodes to Cluster
hosts: workers
gather_facts: false
tasks:
- name: Join cluster
ansible.builtin.shell: "{{ hostvars['localhost']['k8s_worker_join'] }} >> node_joined.txt"
args:
chdir: $HOME
creates: node_joined.txt
+6
View File
@@ -0,0 +1,6 @@
---
collections:
- ansible.posix
roles:
- name: giovtorres.tuned
version: 2.0.2
+11
View File
@@ -0,0 +1,11 @@
---
- name: Install irqbalance
ansible.builtin.package:
name: irqbalance
state: present
- name: Ensure irqbalance service
ansible.builtin.service:
name: irqbalance
state: started
enabled: true
@@ -1,4 +0,0 @@
- name: Restart keepalived
ansible.builtin.service:
name: keepalived
state: restarted
-12
View File
@@ -1,12 +0,0 @@
- name: Install keepalived
ansible.builtin.apt:
pkg:
- keepalived
state: present
- name: Install keepalived config
ansible.builtin.template:
src: keepalived.conf.j2
dest: /etc/keepalived/keepalived.conf
mode: "0644"
notify: Restart keepalived
@@ -1,19 +0,0 @@
vrrp_instance VI_1 {
state MASTER
interface enp5s0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.150.0.100
10.150.0.101
10.150.0.102
}
# Allow packets addressed to the VIPs above to be received
accept
}
@@ -2,3 +2,5 @@
ansible.builtin.import_tasks: repo.yaml
- name: Install Kubernetes packages
ansible.builtin.import_tasks: packages.yaml
- name: Ensure services running
ansible.builtin.import_tasks: services.yaml
@@ -4,4 +4,6 @@
- kubeadm
- kubelet
- containerd
- kubectl
- helm
update_cache: true
@@ -6,3 +6,13 @@
suites: /
signed_by: https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key
state: present
- name: Add Helm APT repository
ansible.builtin.deb822_repository:
name: helm
types: deb
uris: https://packages.buildkite.com/helm-linux/helm-debian/any/
suites: any
components: main
signed_by: https://packages.buildkite.com/helm-linux/helm-debian/gpgkey
state: present
@@ -0,0 +1,5 @@
- name: Ensure containerd is running
ansible.builtin.service:
name: containerd
state: started
enabled: true
@@ -1,12 +0,0 @@
- name: Ensure Cilium repository exists
kubernetes.core.helm_repository:
name: cilium
url: https://helm.cilium.io/
- name: Ensure Cilium is installed
kubernetes.core.helm:
release_name: cilium
release_namespace: kube-system
chart_ref: cilium/cilium
chart_version: 1.19.5
run_once: true
@@ -0,0 +1,18 @@
- name: Setup Kubernetes admin config for root user
become: true
block:
- name: Ensure .kube directory
ansible.builtin.file:
path: "{{ ansible_facts['env']['HOME'] }}/.kube"
state: directory
owner: "{{ ansible_facts['user_id'] }}"
group: "{{ ansible_facts['user_id'] }}"
mode: "0755"
- name: Copy admin configuration
ansible.builtin.copy:
src: /etc/kubernetes/admin.conf
dest: "{{ ansible_facts['env']['HOME'] }}/.kube/config"
remote_src: true
owner: "{{ ansible_facts['user_id'] }}"
group: "{{ ansible_facts['user_id'] }}"
mode: "0600"
@@ -1,15 +0,0 @@
- name: Add Helm APT repository
ansible.builtin.deb822_repository:
name: helm
types: deb
uris: https://packages.buildkite.com/helm-linux/helm-debian/any/
suites: any
components: main
signed_by: https://packages.buildkite.com/helm-linux/helm-debian/gpgkey
state: present
- name: Install Helm
ansible.builtin.apt:
name: helm
state: present
update_cache: true
@@ -0,0 +1,21 @@
- name: Check if cluster is initialised
ansible.builtin.stat:
path: /etc/kubernetes/admin.conf
register: kubernetes_control_plane_admin_conf
run_once: true
- name: Initialise Kubernetes cluster
when: not kubernetes_control_plane_admin_conf.stat.exists
block:
- name: Copy kubeadm config
ansible.builtin.template:
src: kubeadm-config.yaml.j2
dest: /tmp/kubeadm-config.yaml
mode: "0600"
run_once: true
- name: Initialise Kubernetes with kubeadm
ansible.builtin.command: kubeadm init --config /tmp/kubeadm-config.yaml
args:
creates: /etc/kubernetes/admin.conf
run_once: true
@@ -0,0 +1,20 @@
- name: Generate join command
ansible.builtin.command: kubeadm token create --print-join-command --kubeconfig /etc/kubernetes/admin.conf
register: kubernetes_control_plane_join_command
run_once: true
changed_when: false
- name: Get certificate key
ansible.builtin.command: kubeadm init phase upload-certs --upload-certs --kubeconfig /etc/kubernetes/admin.conf
register: kubernetes_control_plane_certificate_key
run_once: true
changed_when: false
- name: Join additional control planes
ansible.builtin.command: >-
{{ kubernetes_control_plane_join_command.stdout }}
--control-plane
--certificate-key
{{ kubernetes_control_plane_certificate_key.stdout_lines[-1] }}
args:
creates: /etc/kubernetes/admin.conf
@@ -1,5 +1,5 @@
- name: Ensure kube-vip static pod
- name: Ensure kube-vip static pod manifest exists
ansible.builtin.template:
src: kube-vip.yaml.j2
dest: /etc/kubernetes/manifests/kube-vip.yaml
mode: "0644"
mode: "0600"
@@ -1,62 +1,14 @@
- name: Install kube-vip
- name: Configure kube-vip static pod manifest
ansible.builtin.import_tasks: kube-vip.yaml
- name: Initialise Kubernetes cluster
ansible.builtin.command:
argv:
- kubeadm
- init
- --pod-network-cidr=172.16.0.0/16
- --control-plane-endpoint={{ vip_address }}:6443
- --upload-certs
args:
creates: /etc/kubernetes/admin.conf
run_once: true
ansible.builtin.import_tasks: init-cluster.yaml
- name: Join nodes as control planes
ansible.builtin.import_tasks: join-cluster.yaml
- name: Generate Join Command
ansible.builtin.command:
argv:
- kubeadm
- token
- create
- --print-join-command
register: kubernetes_control_plane_join_command
run_once: true
changed_when: true
- name: Copy Kubernetes admin configuration on all nodes
ansible.builtin.import_tasks: copy-config.yaml
- name: Get Certificate Key
ansible.builtin.shell:
cmd: set -o pipefail && kubeadm init phase upload-certs --upload-certs | tail -n 1
register: kubernetes_control_plane_certificate_key
run_once: true
changed_when: true
- name: Join Additional Control Planes
ansible.builtin.command:
cmd: "{{ kubernetes_control_plane_join_command.stdout }} --control-plane --certificate-key {{ kubernetes_control_plane_certificate_key.stdout }}"
args:
creates: /etc/kubernetes/admin.conf
- name: Copy Kubernetes Admin Config
ansible.builtin.command: "{{ item }}"
loop:
- mkdir -p $HOME/.kube
- cp /etc/kubernetes/admin.conf $HOME/.kube/config
- chown $(id -u):$(id -g) $HOME/.kube/config
args:
creates: $HOME/.kube/config
- name: Install Kubernetes CLI
ansible.builtin.apt:
pkg:
- kubectl
state: present
- name: Install Helm
ansible.builtin.import_tasks: helm.yaml
- name: Install Cilium CNI
ansible.builtin.import_tasks: cilium.yaml
- name: Remove control plane scheduling restrictions
ansible.builtin.import_tasks: untaint.yaml
@@ -0,0 +1,7 @@
- name: Remove control plane NoSchedule taint
kubernetes.core.k8s_taint:
state: absent
name: "{{ ansible_facts['hostname'] }}"
taints:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
@@ -29,9 +29,7 @@ spec:
- name: cp_namespace
value: kube-system
- name: svc_enable
value: "true"
- name: svc_leasename
value: plndr-svcs-lock
value: "false"
- name: vip_leaderelection
value: "true"
- name: vip_leasename
@@ -0,0 +1,18 @@
# Configuration reference can be found at https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta4/
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
clusterName: "{{ cluster_name }}"
controlPlaneEndpoint: "{{ vip_address }}"
networking:
podSubnet: "{{ cluster_pod_subnet }}"
serviceSubnet: "{{ cluster_service_subnet }}"
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
failSwapOn: false
memorySwap:
swapBehavior: LimitedSwap
@@ -1,12 +0,0 @@
- name: Ensure Metrics Server repository exists
kubernetes.core.helm_repository:
name: metrics-server
url: https://kubernetes-sigs.github.io/metrics-server/
- name: Ensure Metrics Server is installed
kubernetes.core.helm:
release_name: metrics-server
release_namespace: kube-system
chart_ref: metrics-server/metrics-server
chart_version: 3.13.1
run_once: true
+3 -16
View File
@@ -1,3 +1,6 @@
- name: Apply power and performance settings
ansible.builtin.import_playbook: playbooks/performance.yaml
- name: Baseline configuration
hosts: all
become: true
@@ -5,24 +8,8 @@
- common
- kubernetes_common
# ========================
# Setup Kubernetes Cluster
# ========================
- name: Initialise Kubernetes Cluster
hosts: masters
become: true
roles:
- kubernetes_control_plane
- kubernetes_metric_server
# # =======================
# # JOIN MASTERS TO CLUSTER
# # =======================
# - name: Adkfk
# hosts: masters[0]
# tasks:
# - name: Generate Kubernetes 'join' command
# ansible.builtin.command: echo hi
# changed_when: true
+14 -18
View File
@@ -3,14 +3,10 @@
# ------
locals {
image = "ubuntu/26.04"
root_disk_size = "16GiB"
ext_disk_size = "16GiB"
network_cidr = "10.150.0.1/24"
master_count = 3
worker_count = 0
image = "ubuntu/26.04"
}
# ---------
@@ -46,13 +42,13 @@ resource "incus_network" "this" {
module "master" {
source = "./modules/incus_vm"
count = local.master_count
count = var.master_count
name = "master-${count.index}"
project = incus_project.this.name
image = incus_image.this.fingerprint
cpu = 2
memory = "2GiB"
name = "master-${count.index}"
project = incus_project.this.name
image = incus_image.this.fingerprint
cpu = 2
memory = "2GiB"
network = incus_network.this.name
ipv4_address = cidrhost(local.network_cidr, 2 + (2 * count.index))
@@ -60,13 +56,13 @@ module "master" {
module "worker" {
source = "./modules/incus_vm"
count = local.worker_count
count = var.worker_count
name = "worker-${count.index}"
project = incus_project.this.name
image = incus_image.this.fingerprint
cpu = 2
memory = "2GiB"
name = "worker-${count.index}"
project = incus_project.this.name
image = incus_image.this.fingerprint
cpu = 2
memory = "2GiB"
network = incus_network.this.name
ipv4_address = cidrhost(local.network_cidr, 3 + (2 * count.index))
@@ -77,7 +73,7 @@ module "worker" {
# ---------
terraform {
required_version = "~> 1.12"
required_version = ">= 1.11.5"
required_providers {
incus = {
+14
View File
@@ -1,3 +1,17 @@
variable "master_count" {
description = "The number of master nodes to provision."
type = number
default = 1
nullable = false
}
variable "worker_count" {
description = "The number of worker nodes to provision."
type = number
default = 0
nullable = false
}
variable "cpus" {
description = "The number of CPU cores allocated to each virtual machine."
type = number