Compare commits
89 Commits
c00faad987
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c1c1a24ee | |||
| 9a5c8288b0 | |||
| f946f0f2bf | |||
| 80d48e6f46 | |||
| 8dc6ac2939 | |||
| 7f80884082 | |||
| 9abb0b010e | |||
| aa0e9f37bf | |||
| 5613699540 | |||
| 03dc10c0e5 | |||
| 573afc50a4 | |||
| 6dab1dccd9 | |||
| ce16338203 | |||
| 591541215b | |||
| adccf7af22 | |||
| 87e90a2327 | |||
| 9e0f9697a8 | |||
| 819add7725 | |||
| 8c665eeff4 | |||
| b3279dd23c | |||
| 0a4c621c57 | |||
| 3ecf328d2a | |||
| 390e2b29f5 | |||
| 259abad851 | |||
| b7a7ed5632 | |||
| 6f62bf2a1b | |||
| 1efa0f9426 | |||
| 81bf536ac3 | |||
| 02af027e43 | |||
| dd8e1e3fc9 | |||
| 5e11ed8fdc | |||
| bcc7922f46 | |||
| 89f28c2738 | |||
| c27709a0ef | |||
| 9fc67c61c0 | |||
| 5f7848a198 | |||
| 4a7030f7ac | |||
| d733c45323 | |||
| 20f8648d86 | |||
| 74bfab7bd8 | |||
| 71edc8d0b7 | |||
| 3699aac3ae | |||
| 8ae456bda7 | |||
| 363e218033 | |||
| 068659e174 | |||
| 90686995d6 | |||
| b18af66f03 | |||
| 010f0657b5 | |||
| 6a127588f0 | |||
| 248ccb48fe | |||
| 1f13980b53 | |||
| ff35802ba5 | |||
| b82155193e | |||
| 474a296b60 | |||
| 34812b901c | |||
| e6d48c6802 | |||
| 1592c8290d | |||
| 2c0e7245b6 | |||
| d4e7d2ca0e | |||
| 293df1cfbe | |||
| 8d821d643c | |||
| 480bdf3fe2 | |||
| 0d079ee672 | |||
| 82b3e5f9dd | |||
| 0d5e4f8949 | |||
| f7865c59cf | |||
| fbdb8346b6 | |||
| 23a7020549 | |||
| ccaa1d180c | |||
| b17ab6c8bb | |||
| 9cb2cb5bd2 | |||
| 966a0d1e09 | |||
| e509fd8225 | |||
| 470fd15b56 | |||
| a1d9ea1a67 | |||
| c7f2bda277 | |||
| fb471b78a1 | |||
| 732cbf5d4d | |||
| efa85016cf | |||
| 872161a16a | |||
| 4e0b073a39 | |||
| 83abf1f978 | |||
| fba3827961 | |||
| 687e061b93 | |||
| 71214883fb | |||
| 41852cc2d3 | |||
| 628dd64ca2 | |||
| a4f3cf95c5 | |||
| 0cc8ea528a |
@@ -12,3 +12,7 @@ indent_style = space
|
|||||||
[*.json]
|
[*.json]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
|||||||
@@ -2,8 +2,11 @@
|
|||||||
.*
|
.*
|
||||||
terraform.tfstate
|
terraform.tfstate
|
||||||
terraform.tfstate.backup
|
terraform.tfstate.backup
|
||||||
|
ansible/lookup_plugins
|
||||||
|
ansible/galaxy_roles
|
||||||
|
|
||||||
!.gitignore
|
!.gitignore
|
||||||
!.editorconfig
|
!.editorconfig
|
||||||
!.vscode
|
!.vscode
|
||||||
!.pre-commit-config.yaml
|
!.pre-commit-config.yaml
|
||||||
|
!.yamlfmt.yaml
|
||||||
|
|||||||
@@ -9,8 +9,16 @@ repos:
|
|||||||
args:
|
args:
|
||||||
- --autofix
|
- --autofix
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
|
- repo: https://github.com/google/yamlfmt
|
||||||
|
rev: v0.21.0
|
||||||
|
hooks:
|
||||||
|
- id: yamlfmt
|
||||||
- repo: https://github.com/tofuutils/pre-commit-opentofu
|
- repo: https://github.com/tofuutils/pre-commit-opentofu
|
||||||
rev: v2.4.2
|
rev: v2.4.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: tofu_validate
|
- id: tofu_validate
|
||||||
- id: tofu_fmt
|
- id: tofu_fmt
|
||||||
|
- repo: https://github.com/ansible/ansible-lint
|
||||||
|
rev: v26.4.0
|
||||||
|
hooks:
|
||||||
|
- id: ansible-lint
|
||||||
|
|||||||
Vendored
+2
-1
@@ -2,6 +2,7 @@
|
|||||||
"recommendations": [
|
"recommendations": [
|
||||||
"EditorConfig.EditorConfig",
|
"EditorConfig.EditorConfig",
|
||||||
"opentofu.vscode-opentofu",
|
"opentofu.vscode-opentofu",
|
||||||
"skellock.just"
|
"skellock.just",
|
||||||
|
"redhat.ansible"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
formatter:
|
||||||
|
indent: 2
|
||||||
|
retain_line_breaks_single: true
|
||||||
|
scan_folded_as_literal: true
|
||||||
|
indentless_arrays: false
|
||||||
@@ -1,26 +1,39 @@
|
|||||||
#!/usr/bin/env just
|
#!/usr/bin/env just
|
||||||
|
|
||||||
opentofu_dir := "infra/"
|
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
|
up: tofu-apply ansible-run-playbook
|
||||||
down: tofu-destroy
|
down: tofu-destroy
|
||||||
|
|
||||||
tofu-init:
|
tofu-init:
|
||||||
tofu -chdir={{ opentofu_dir }} init -upgrade
|
tofu -chdir={{ opentofu_dir }} init -upgrade
|
||||||
|
|
||||||
tofu-apply: tofu-init
|
tofu-apply:
|
||||||
tofu -chdir={{ opentofu_dir }} apply -auto-approve
|
tofu -chdir={{ opentofu_dir }} apply -auto-approve
|
||||||
|
|
||||||
tofu-destroy: tofu-init
|
tofu-destroy:
|
||||||
tofu -chdir={{ opentofu_dir }} apply -auto-approve -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-run-playbook:
|
||||||
ansible-playbook -i ansible/inventory/incus.yaml -c community.general.incus ansible/playbook.yaml
|
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
|
||||||
|
|
||||||
clean:
|
headlamp:
|
||||||
rm ansible/hosts.ini || true
|
incus exec master-0 -- cat /etc/kubernetes/admin.conf > .incus-admin.conf
|
||||||
rm -r ansible/.ssh/ || true
|
flatpak run io.kinvolk.Headlamp --kubeconfig "$(pwd)/.incus-admin.conf"
|
||||||
rm admin.conf || true
|
|
||||||
|
|
||||||
get-admin-conf:
|
clean: tofu-destroy
|
||||||
incus exec master-0 -- cat /etc/kubernetes/admin.conf > k8s-admin.conf
|
git clean -fdx
|
||||||
|
|
||||||
|
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 }}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# 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
|
||||||
|
just fetch-plugins
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bare-metal provisioning
|
||||||
|
|
||||||
|
When creating a set of fresh machines, you must run the provisioning step to create the "Kolibri" automation user.
|
||||||
|
|
||||||
|
The Kolibri automation user is used for all other playbooks and has permissions to execute passwordless sudo.
|
||||||
|
|
||||||
|
```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.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[defaults]
|
||||||
|
roles_path = ansible/roles:ansible/galaxy_roles
|
||||||
|
lookup_plugins = ansible/lookup_plugins
|
||||||
|
callback_result_format = yaml
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
ansible_python_interpreter: /usr/bin/python3
|
||||||
|
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
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
masters:
|
||||||
|
hosts:
|
||||||
|
vineta:
|
||||||
|
ansible_host: vineta.local
|
||||||
|
rotfront:
|
||||||
|
ansible_host: rotfront.local
|
||||||
|
leng:
|
||||||
|
ansible_host: leng.local
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
plugin: community.general.incus
|
||||||
|
strict: true
|
||||||
|
remotes:
|
||||||
|
- local:homelab
|
||||||
|
groups:
|
||||||
|
masters: "'master' in inventory_hostname"
|
||||||
|
workers: "'worker' in inventory_hostname"
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
plugin: community.general.incus
|
|
||||||
strict: true
|
|
||||||
remotes:
|
|
||||||
- local:deskpi-cluster
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Provision with Incus
|
|
||||||
ansible.builtin.import_playbook: playbooks/common.yaml
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Bootstrap - Stage 1
|
|
||||||
hosts: all
|
|
||||||
gather_facts: false
|
|
||||||
tasks:
|
|
||||||
- name: Install Python 3
|
|
||||||
ansible.builtin.raw: |
|
|
||||||
if ! command -v python3 >/dev/null 2>&1; then
|
|
||||||
apk add --no-cache python3
|
|
||||||
fi
|
|
||||||
register: apk_result
|
|
||||||
changed_when: "'OK' in apk_result.stdout or 'Installing' in apk_result.stdout"
|
|
||||||
|
|
||||||
- name: Bootstrap - Stage 2
|
|
||||||
hosts: all
|
|
||||||
tasks:
|
|
||||||
- name: Install Python 3 Libraries
|
|
||||||
ansible.builtin.package:
|
|
||||||
name: "{{ item }}"
|
|
||||||
loop:
|
|
||||||
- py3-yaml
|
|
||||||
- py3-kubernetes
|
|
||||||
@@ -1,154 +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'] }}"
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
- name: Configure performance tuning
|
||||||
|
hosts: all
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- giovtorres.tuned
|
||||||
|
- irqbalance
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
- name: Create Kolibri automation user
|
||||||
|
hosts: all
|
||||||
|
become: true
|
||||||
|
tasks:
|
||||||
|
- name: Ensure Kolibri user exists
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: kolibri
|
||||||
|
comment: Hummingbird
|
||||||
|
create_home: true
|
||||||
|
groups:
|
||||||
|
- sudo
|
||||||
|
shell: /bin/bash
|
||||||
|
system: true
|
||||||
|
|
||||||
|
- name: Ensure authorised keys for Kolibri
|
||||||
|
ansible.posix.authorized_key:
|
||||||
|
user: kolibri
|
||||||
|
key: "{{ lookup('proton_pass', vault_name='Home Lab', item_title='Kolibri Automation', field='Public key') }}"
|
||||||
|
|
||||||
|
- name: Allow passwordless sudo for Kolibri
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: "kolibri ALL=(ALL) NOPASSWD: ALL\n"
|
||||||
|
dest: /etc/sudoers.d/kolibri
|
||||||
|
mode: "0440"
|
||||||
|
validate: visudo -cf %s
|
||||||
@@ -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
|
||||||
@@ -1,87 +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
|
|
||||||
@@ -1,24 +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
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
collections:
|
||||||
|
- ansible.posix
|
||||||
|
roles:
|
||||||
|
- name: giovtorres.tuned
|
||||||
|
version: 2.0.2
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<service-group>
|
||||||
|
<name replace-wildcards="yes">%h</name>
|
||||||
|
<service>
|
||||||
|
<type>_ssh._tcp</type>
|
||||||
|
<port>22</port>
|
||||||
|
</service>
|
||||||
|
</service-group>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
- name: Restart Avahi Daemon
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: avahi-daemon
|
||||||
|
state: restarted
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
- name: Install Avahi Daemon
|
||||||
|
ansible.builtin.apt:
|
||||||
|
pkg:
|
||||||
|
- avahi-daemon
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Enable Avahi Daemon Service
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: avahi-daemon
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
- name: Copy Avahi SSH service configuration
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: ssh.service
|
||||||
|
dest: /etc/avahi/services/ssh.service
|
||||||
|
mode: "0644"
|
||||||
|
notify:
|
||||||
|
- Restart Avahi Daemon
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
- name: Install required packages
|
||||||
|
ansible.builtin.import_tasks: packages.yaml
|
||||||
|
|
||||||
|
- name: Setup networking configuration
|
||||||
|
ansible.builtin.import_tasks: network.yaml
|
||||||
|
|
||||||
|
- name: Setup Avahi Daemon
|
||||||
|
ansible.builtin.import_tasks: avahi.yaml
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- name: Enable IPv4 Forwarding
|
||||||
|
ansible.posix.sysctl:
|
||||||
|
name: net.ipv4.ip_forward
|
||||||
|
value: "1"
|
||||||
|
sysctl_set: true
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- name: Install Ansible Dependencies
|
||||||
|
ansible.builtin.apt:
|
||||||
|
pkg:
|
||||||
|
- python3-debian
|
||||||
|
- python3-kubernetes
|
||||||
|
state: present
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- name: Install Kubernetes APT repository
|
||||||
|
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
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
- name: Install Kubernetes packages
|
||||||
|
ansible.builtin.apt:
|
||||||
|
pkg:
|
||||||
|
- kubeadm
|
||||||
|
- kubelet
|
||||||
|
- containerd
|
||||||
|
- kubectl
|
||||||
|
- helm
|
||||||
|
update_cache: true
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
- name: Add Kubernetes APT repository
|
||||||
|
ansible.builtin.deb822_repository:
|
||||||
|
name: kubernetes
|
||||||
|
types: deb
|
||||||
|
uris: https://pkgs.k8s.io/core:/stable:/v1.36/deb/
|
||||||
|
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
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- name: Ensure kube-vip static pod manifest exists
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: kube-vip.yaml.j2
|
||||||
|
dest: /etc/kubernetes/manifests/kube-vip.yaml
|
||||||
|
mode: "0600"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
- name: Configure kube-vip static pod manifest
|
||||||
|
ansible.builtin.import_tasks: kube-vip.yaml
|
||||||
|
|
||||||
|
- name: Initialise Kubernetes cluster
|
||||||
|
ansible.builtin.import_tasks: init-cluster.yaml
|
||||||
|
|
||||||
|
- name: Join nodes as control planes
|
||||||
|
ansible.builtin.import_tasks: join-cluster.yaml
|
||||||
|
|
||||||
|
- name: Copy Kubernetes admin configuration on all nodes
|
||||||
|
ansible.builtin.import_tasks: copy-config.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
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: kube-vip
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- manager
|
||||||
|
env:
|
||||||
|
- name: vip_arp
|
||||||
|
value: "true"
|
||||||
|
- name: port
|
||||||
|
value: "6443"
|
||||||
|
- name: vip_nodename
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
- name: vip_interface
|
||||||
|
value: {{ vip_interface }}
|
||||||
|
- name: vip_subnet
|
||||||
|
value: "32"
|
||||||
|
- name: dns_mode
|
||||||
|
value: first
|
||||||
|
- name: dhcp_mode
|
||||||
|
value: ipv4
|
||||||
|
- name: cp_enable
|
||||||
|
value: "true"
|
||||||
|
- name: cp_namespace
|
||||||
|
value: kube-system
|
||||||
|
- name: svc_enable
|
||||||
|
value: "false"
|
||||||
|
- name: vip_leaderelection
|
||||||
|
value: "true"
|
||||||
|
- name: vip_leasename
|
||||||
|
value: plndr-cp-lock
|
||||||
|
- name: vip_leaseduration
|
||||||
|
value: "15"
|
||||||
|
- name: vip_renewdeadline
|
||||||
|
value: "10"
|
||||||
|
- name: vip_retryperiod
|
||||||
|
value: "2"
|
||||||
|
- name: address
|
||||||
|
value: {{ vip_address }}
|
||||||
|
- name: prometheus_server
|
||||||
|
value: :2112
|
||||||
|
image: ghcr.io/kube-vip/kube-vip:v1.2.1
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
name: kube-vip
|
||||||
|
resources: {}
|
||||||
|
securityContext:
|
||||||
|
capabilities:
|
||||||
|
add:
|
||||||
|
- NET_ADMIN
|
||||||
|
- NET_RAW
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /etc/kubernetes/admin.conf
|
||||||
|
name: kubeconfig
|
||||||
|
hostAliases:
|
||||||
|
- hostnames:
|
||||||
|
- kubernetes
|
||||||
|
ip: 127.0.0.1
|
||||||
|
hostNetwork: true
|
||||||
|
volumes:
|
||||||
|
- hostPath:
|
||||||
|
path: /etc/kubernetes/admin.conf
|
||||||
|
name: kubeconfig
|
||||||
|
status: {}
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
- name: Apply power and performance settings
|
||||||
|
ansible.builtin.import_playbook: playbooks/performance.yaml
|
||||||
|
|
||||||
|
- name: Baseline configuration
|
||||||
|
hosts: all
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- common
|
||||||
|
- kubernetes_common
|
||||||
|
|
||||||
|
- name: Initialise Kubernetes Cluster
|
||||||
|
hosts: masters
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- kubernetes_control_plane
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
err() { echo "$@" >&2; exit 1; }
|
|
||||||
log() { echo "$@" >&2; }
|
|
||||||
|
|
||||||
[ "$(id -u)" -ne 0 ] && err "You must run this script as root"
|
|
||||||
|
|
||||||
# Install minimal packages for provisioning
|
|
||||||
PACKAGES="dropbear openssh-sftp-server python3 py3-yaml py3-kubernetes cloud-utils-growpart e2fsprogs-extra lsblk"
|
|
||||||
apk update && apk upgrade
|
|
||||||
apk add ${PACKAGES}
|
|
||||||
|
|
||||||
# Enable and start services
|
|
||||||
enable_and_start() {
|
|
||||||
service="$1"
|
|
||||||
rc-update add "${service}" default
|
|
||||||
service "${service}" start
|
|
||||||
}
|
|
||||||
|
|
||||||
enable_and_start dropbear
|
|
||||||
|
|
||||||
# increase root partition size
|
|
||||||
root_dev=$(readlink -f /dev/root)
|
|
||||||
disk=$(lsblk -no pkname "$root_dev")
|
|
||||||
part_num=$(echo "$root_dev" | sed 's|.*[^0-9]||')
|
|
||||||
|
|
||||||
echo "Growing /dev/${disk}${part_num}..."
|
|
||||||
|
|
||||||
# Grow partition and resize
|
|
||||||
growpart "/dev/$disk" "$part_num" || err "Partition growth failed"
|
|
||||||
resize2fs "$root_dev" || err "Filesystem resize failed"
|
|
||||||
+29
-118
@@ -3,12 +3,10 @@
|
|||||||
# ------
|
# ------
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
|
image = "ubuntu/26.04"
|
||||||
root_disk_size = "16GiB"
|
root_disk_size = "16GiB"
|
||||||
ext_disk_size = "16GiB"
|
ext_disk_size = "16GiB"
|
||||||
network_cidr = "10.150.0.1/24"
|
network_cidr = "10.150.0.1/24"
|
||||||
|
|
||||||
master_count = 2
|
|
||||||
worker_count = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---------
|
# ---------
|
||||||
@@ -16,49 +14,23 @@ locals {
|
|||||||
# ---------
|
# ---------
|
||||||
|
|
||||||
resource "incus_project" "this" {
|
resource "incus_project" "this" {
|
||||||
name = "deskpi-cluster"
|
name = "homelab"
|
||||||
description = "The emulated DeskPi Super6C stack."
|
description = "An emulated home infrastructure stack."
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "incus_image" "alpine" {
|
resource "incus_image" "this" {
|
||||||
project = incus_project.this.name
|
project = incus_project.this.name
|
||||||
|
|
||||||
source_image = {
|
source_image = {
|
||||||
remote = "images"
|
remote = "images"
|
||||||
name = "alpine/edge"
|
name = local.image
|
||||||
type = "virtual-machine"
|
type = "virtual-machine"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "incus_profile" "this" {
|
|
||||||
name = "Default"
|
|
||||||
description = "The default profile applied to both masters and workers."
|
|
||||||
project = incus_project.this.name
|
|
||||||
|
|
||||||
depends_on = [incus_network.this]
|
|
||||||
|
|
||||||
config = {
|
|
||||||
"limits.cpu" = var.cpus
|
|
||||||
"limits.memory" = var.memory
|
|
||||||
"boot.autostart" = false
|
|
||||||
"security.secureboot" = false
|
|
||||||
}
|
|
||||||
|
|
||||||
device {
|
|
||||||
name = "root"
|
|
||||||
type = "disk"
|
|
||||||
|
|
||||||
properties = {
|
|
||||||
"pool" = "default"
|
|
||||||
"path" = "/"
|
|
||||||
"size" = local.root_disk_size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "incus_network" "this" {
|
resource "incus_network" "this" {
|
||||||
name = "deskpi-cluster"
|
name = "homelab"
|
||||||
description = "An emulated network for the DeskPi Super6C cluster project."
|
description = "An emulated home infrastructure stack."
|
||||||
project = incus_project.this.name
|
project = incus_project.this.name
|
||||||
type = "bridge"
|
type = "bridge"
|
||||||
|
|
||||||
@@ -68,93 +40,32 @@ resource "incus_network" "this" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "incus_instance" "master" {
|
module "master" {
|
||||||
count = local.master_count
|
source = "./modules/incus_vm"
|
||||||
|
count = var.master_count
|
||||||
|
|
||||||
name = "master-${count.index}"
|
name = "master-${count.index}"
|
||||||
description = "Kubernetes Master ${count.index}"
|
project = incus_project.this.name
|
||||||
project = incus_project.this.name
|
image = incus_image.this.fingerprint
|
||||||
|
cpu = 2
|
||||||
|
memory = "2GiB"
|
||||||
|
|
||||||
type = "virtual-machine"
|
network = incus_network.this.name
|
||||||
image = incus_image.alpine.fingerprint
|
ipv4_address = cidrhost(local.network_cidr, 2 + (2 * count.index))
|
||||||
profiles = [incus_profile.this.name]
|
|
||||||
|
|
||||||
wait_for {
|
|
||||||
type = "agent"
|
|
||||||
}
|
|
||||||
|
|
||||||
device {
|
|
||||||
name = "eth0"
|
|
||||||
type = "nic"
|
|
||||||
|
|
||||||
properties = {
|
|
||||||
"network" = incus_network.this.name
|
|
||||||
"ipv4.address" = cidrhost(local.network_cidr, 2 + (2 * count.index))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
device {
|
|
||||||
name = "ext"
|
|
||||||
type = "disk"
|
|
||||||
|
|
||||||
properties = {
|
|
||||||
"pool" = "default"
|
|
||||||
"source" = incus_storage_volume.master[count.index].name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "incus_storage_volume" "master" {
|
module "worker" {
|
||||||
count = local.master_count
|
source = "./modules/incus_vm"
|
||||||
|
count = var.worker_count
|
||||||
|
|
||||||
name = "master-ext-${count.index}"
|
name = "worker-${count.index}"
|
||||||
description = "External drive for master ${count.index}"
|
project = incus_project.this.name
|
||||||
pool = "default"
|
image = incus_image.this.fingerprint
|
||||||
project = incus_project.this.name
|
cpu = 2
|
||||||
|
memory = "2GiB"
|
||||||
|
|
||||||
content_type = "block"
|
network = incus_network.this.name
|
||||||
|
ipv4_address = cidrhost(local.network_cidr, 3 + (2 * count.index))
|
||||||
config = {
|
|
||||||
"size" = local.ext_disk_size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "incus_instance" "worker" {
|
|
||||||
count = local.worker_count
|
|
||||||
|
|
||||||
name = "worker-${count.index}"
|
|
||||||
description = "Kubernetes Worker ${count.index}"
|
|
||||||
project = incus_project.this.name
|
|
||||||
|
|
||||||
type = "virtual-machine"
|
|
||||||
image = incus_image.alpine.fingerprint
|
|
||||||
profiles = [incus_profile.this.name]
|
|
||||||
|
|
||||||
wait_for {
|
|
||||||
type = "agent"
|
|
||||||
}
|
|
||||||
|
|
||||||
device {
|
|
||||||
name = "eth0"
|
|
||||||
type = "nic"
|
|
||||||
|
|
||||||
properties = {
|
|
||||||
"network" = incus_network.this.name
|
|
||||||
"ipv4.address" = cidrhost(local.network_cidr, 3 + (2 * count.index))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# -------
|
|
||||||
# Outputs
|
|
||||||
# -------
|
|
||||||
|
|
||||||
output "master_addresses" {
|
|
||||||
value = incus_instance.master[*].ipv4_address
|
|
||||||
}
|
|
||||||
|
|
||||||
output "worker_addresses" {
|
|
||||||
value = incus_instance.worker[*].ipv4_address
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---------
|
# ---------
|
||||||
@@ -162,12 +73,12 @@ output "worker_addresses" {
|
|||||||
# ---------
|
# ---------
|
||||||
|
|
||||||
terraform {
|
terraform {
|
||||||
required_version = "~> 1"
|
required_version = ">= 1.11.5"
|
||||||
|
|
||||||
required_providers {
|
required_providers {
|
||||||
incus = {
|
incus = {
|
||||||
source = "lxc/incus"
|
source = "lxc/incus"
|
||||||
version = "~> 1"
|
version = "~> 1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
incus = {
|
||||||
|
source = "lxc/incus"
|
||||||
|
version = ">= 1.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "incus_instance" "this" {
|
||||||
|
name = var.name
|
||||||
|
description = var.description
|
||||||
|
project = var.project
|
||||||
|
|
||||||
|
type = "virtual-machine"
|
||||||
|
image = var.image
|
||||||
|
profiles = concat([incus_profile.this.name], var.profiles)
|
||||||
|
|
||||||
|
wait_for {
|
||||||
|
type = "agent"
|
||||||
|
}
|
||||||
|
|
||||||
|
device {
|
||||||
|
name = "eth0"
|
||||||
|
type = "nic"
|
||||||
|
|
||||||
|
properties = {
|
||||||
|
"network" = var.network
|
||||||
|
"ipv4.address" = var.ipv4_address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device {
|
||||||
|
name = "ext"
|
||||||
|
type = "disk"
|
||||||
|
|
||||||
|
properties = {
|
||||||
|
"pool" = "default"
|
||||||
|
"source" = incus_storage_volume.this.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "incus_storage_volume" "this" {
|
||||||
|
name = "${var.name}-ext"
|
||||||
|
description = "External disk for ${var.name}"
|
||||||
|
pool = "default"
|
||||||
|
project = var.project
|
||||||
|
|
||||||
|
content_type = "block"
|
||||||
|
|
||||||
|
config = {
|
||||||
|
"size" = "16GiB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "incus_profile" "this" {
|
||||||
|
name = var.name
|
||||||
|
description = "Default profile for ${var.name}"
|
||||||
|
project = var.project
|
||||||
|
|
||||||
|
config = {
|
||||||
|
"limits.cpu" = var.cpu
|
||||||
|
"limits.memory" = var.memory
|
||||||
|
"boot.autostart" = false
|
||||||
|
"security.secureboot" = true
|
||||||
|
}
|
||||||
|
|
||||||
|
device {
|
||||||
|
name = "root"
|
||||||
|
type = "disk"
|
||||||
|
|
||||||
|
properties = {
|
||||||
|
"pool" = "default"
|
||||||
|
"path" = "/"
|
||||||
|
"size" = "16GiB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
variable "name" {
|
||||||
|
type = string
|
||||||
|
description = "The name for this instance."
|
||||||
|
nullable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "description" {
|
||||||
|
type = string
|
||||||
|
description = "The description for this instance."
|
||||||
|
nullable = false
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "project" {
|
||||||
|
type = string
|
||||||
|
description = "The project this instance belongs to."
|
||||||
|
nullable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "image" {
|
||||||
|
type = string
|
||||||
|
description = "The image for this instance."
|
||||||
|
nullable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "profiles" {
|
||||||
|
type = list(string)
|
||||||
|
description = "The profiles for this instance."
|
||||||
|
nullable = false
|
||||||
|
default = []
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "network" {
|
||||||
|
type = string
|
||||||
|
description = "The network for this instance."
|
||||||
|
nullable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ipv4_address" {
|
||||||
|
type = string
|
||||||
|
description = "The IPv4 address for this instance."
|
||||||
|
nullable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cpu" {
|
||||||
|
type = number
|
||||||
|
description = "The number of CPUs for this instance."
|
||||||
|
nullable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "memory" {
|
||||||
|
type = string
|
||||||
|
description = "The amount of memory allocated for this instance."
|
||||||
|
nullable = false
|
||||||
|
}
|
||||||
@@ -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" {
|
variable "cpus" {
|
||||||
description = "The number of CPU cores allocated to each virtual machine."
|
description = "The number of CPU cores allocated to each virtual machine."
|
||||||
type = number
|
type = number
|
||||||
|
|||||||
Reference in New Issue
Block a user