Add initial Ansible configuration and playbooks for homelab setup
- Created ansible.cfg for configuration settings - Added inventory.yml for host definitions - Implemented bootstrap playbook for Minisforum setup - Developed setup playbook for K3s installation - Defined common role with user and package management tasks - Established K3s server role with configuration and installation tasks - Included Traefik Helm values for ingress management
This commit is contained in:
parent
891588c202
commit
f33fdc4044
4
ansible.cfg
Normal file
4
ansible.cfg
Normal file
@ -0,0 +1,4 @@
|
||||
[defaults]
|
||||
inventory = ansible/inventory.yml
|
||||
roles_path = ansible/roles
|
||||
host_key_checking = False
|
||||
17
ansible/inventory.yml
Normal file
17
ansible/inventory.yml
Normal file
@ -0,0 +1,17 @@
|
||||
all:
|
||||
vars:
|
||||
ansible_user: nik
|
||||
ansible_ssh_private_key_file: ~/.ssh/id_ed25519-nik-macbookair
|
||||
|
||||
children:
|
||||
k3s_server:
|
||||
hosts:
|
||||
minisforum:
|
||||
ansible_host: 192.168.7.77
|
||||
ansible_port: 430
|
||||
|
||||
k3s_agents:
|
||||
hosts:
|
||||
# debian will be added here in Phase 2
|
||||
# debian:
|
||||
# ansible_host: 192.168.7.X
|
||||
18
ansible/playbooks/bootstrap-minisforum.yml
Normal file
18
ansible/playbooks/bootstrap-minisforum.yml
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
# Run: ansible-playbook -i ansible/inventory.yml ansible/playbooks/bootstrap-minisforum.yml
|
||||
# Requires: SSH access to 192.168.7.7 as root (or a user with NOPASSWD sudo)
|
||||
#
|
||||
# What this does:
|
||||
# - Creates the 'nik' user with sudo access
|
||||
# - Hardens SSH (no password auth, no root login)
|
||||
# - Installs base packages
|
||||
# - Configures UFW firewall
|
||||
# - Creates /data/* directories for persistent volumes
|
||||
|
||||
- name: Bootstrap Minisforum
|
||||
hosts: minisforum
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
roles:
|
||||
- common
|
||||
27
ansible/playbooks/setup-k3s.yml
Normal file
27
ansible/playbooks/setup-k3s.yml
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
# Run: ansible-playbook -i ansible/inventory.yml ansible/playbooks/setup-k3s.yml
|
||||
#
|
||||
# What this does:
|
||||
# - Installs K3s in server mode (with Traefik disabled)
|
||||
# - Installs Helm
|
||||
# - Fetches kubeconfig to /tmp/k3s-minisforum.yaml on your workstation
|
||||
# - Labels the node as node-role=primary
|
||||
#
|
||||
# After this playbook:
|
||||
# export KUBECONFIG=/tmp/k3s-minisforum.yaml
|
||||
# kubectl get nodes # should show minisforum as Ready
|
||||
#
|
||||
# Then deploy Traefik:
|
||||
# helm repo add traefik https://helm.traefik.io/traefik
|
||||
# helm repo update
|
||||
# helm upgrade --install traefik traefik/traefik \
|
||||
# --namespace traefik --create-namespace \
|
||||
# -f values/traefik.yml
|
||||
|
||||
- name: Install K3s server
|
||||
hosts: minisforum
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
roles:
|
||||
- k3s-server
|
||||
31
ansible/roles/common/defaults/main.yml
Normal file
31
ansible/roles/common/defaults/main.yml
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
username: nik
|
||||
timezone: Asia/Tokyo
|
||||
|
||||
base_packages:
|
||||
- curl
|
||||
- git
|
||||
- htop
|
||||
- vim
|
||||
- wget
|
||||
- unzip
|
||||
- ca-certificates
|
||||
- gnupg
|
||||
- lsb-release
|
||||
- nfs-common # needed in Phase 4 for Jellyfin NFS mount from Debian
|
||||
|
||||
ufw_allowed_ports:
|
||||
- { port: 430, proto: tcp, comment: SSH }
|
||||
- { port: 80, proto: tcp, comment: HTTP }
|
||||
- { port: 443, proto: tcp, comment: HTTPS }
|
||||
- { port: 6443, proto: tcp, comment: K3s API server }
|
||||
- { port: 10250, proto: tcp, comment: Kubelet }
|
||||
- { port: 8472, proto: udp, comment: Flannel VXLAN }
|
||||
|
||||
data_dirs:
|
||||
- /data/gitea
|
||||
- /data/jellyfin
|
||||
- /data/pihole
|
||||
- /data/dashy
|
||||
- /data/glances
|
||||
- /data/traefik
|
||||
5
ansible/roles/common/handlers/main.yml
Normal file
5
ansible/roles/common/handlers/main.yml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
- name: Restart sshd
|
||||
ansible.builtin.service:
|
||||
name: sshd
|
||||
state: restarted
|
||||
73
ansible/roles/common/tasks/main.yml
Normal file
73
ansible/roles/common/tasks/main.yml
Normal file
@ -0,0 +1,73 @@
|
||||
---
|
||||
- name: Set timezone
|
||||
community.general.timezone:
|
||||
name: "{{ timezone }}"
|
||||
|
||||
- name: Install base packages
|
||||
ansible.builtin.apt:
|
||||
name: "{{ base_packages }}"
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Create primary user
|
||||
ansible.builtin.user:
|
||||
name: "{{ username }}"
|
||||
groups: sudo
|
||||
shell: /bin/bash
|
||||
create_home: true
|
||||
state: present
|
||||
|
||||
- name: Set up authorized SSH key for user
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ username }}"
|
||||
state: present
|
||||
key: "{{ lookup('file', '~/.ssh/id_ed25519-nik-macbookair.pub') }}"
|
||||
|
||||
- name: Harden SSH — disable password auth
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: "{{ item.regexp }}"
|
||||
line: "{{ item.line }}"
|
||||
state: present
|
||||
loop:
|
||||
- { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
|
||||
- { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
|
||||
- { regexp: '^#?PubkeyAuthentication', line: 'PubkeyAuthentication yes' }
|
||||
- { regexp: '^#?Port ', line: 'Port 430' }
|
||||
notify: Restart sshd
|
||||
|
||||
- name: Install UFW
|
||||
ansible.builtin.apt:
|
||||
name: ufw
|
||||
state: present
|
||||
|
||||
- name: Set UFW default deny incoming
|
||||
community.general.ufw:
|
||||
default: deny
|
||||
direction: incoming
|
||||
|
||||
- name: Set UFW default allow outgoing
|
||||
community.general.ufw:
|
||||
default: allow
|
||||
direction: outgoing
|
||||
|
||||
- name: Allow required ports
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ item.port }}"
|
||||
proto: "{{ item.proto }}"
|
||||
comment: "{{ item.comment }}"
|
||||
loop: "{{ ufw_allowed_ports }}"
|
||||
|
||||
- name: Enable UFW
|
||||
community.general.ufw:
|
||||
state: enabled
|
||||
|
||||
- name: Create persistent data directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ username }}"
|
||||
group: "{{ username }}"
|
||||
mode: "0755"
|
||||
loop: "{{ data_dirs }}"
|
||||
14
ansible/roles/k3s-server/defaults/main.yml
Normal file
14
ansible/roles/k3s-server/defaults/main.yml
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
k3s_version: v1.32.2+k3s1 # pin to a specific version; update deliberately
|
||||
k3s_server_ip: 192.168.7.77
|
||||
|
||||
# Written to /etc/rancher/k3s/config.yaml on the server
|
||||
k3s_server_config:
|
||||
disable:
|
||||
- traefik # we deploy Traefik ourselves via Helm
|
||||
flannel-backend: vxlan
|
||||
node-ip: "{{ k3s_server_ip }}"
|
||||
tls-san:
|
||||
- "{{ k3s_server_ip }}"
|
||||
- minisforum
|
||||
- minisforum.local
|
||||
68
ansible/roles/k3s-server/tasks/main.yml
Normal file
68
ansible/roles/k3s-server/tasks/main.yml
Normal file
@ -0,0 +1,68 @@
|
||||
---
|
||||
- name: Create K3s config directory
|
||||
ansible.builtin.file:
|
||||
path: /etc/rancher/k3s
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Write K3s server config
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/rancher/k3s/config.yaml
|
||||
content: "{{ k3s_server_config | to_nice_yaml }}"
|
||||
mode: "0644"
|
||||
|
||||
- name: Download and install K3s
|
||||
ansible.builtin.shell:
|
||||
cmd: >
|
||||
curl -sfL https://get.k3s.io |
|
||||
INSTALL_K3S_VERSION={{ k3s_version }}
|
||||
sh -
|
||||
creates: /usr/local/bin/k3s # skip if already installed
|
||||
|
||||
- name: Wait for K3s to be ready
|
||||
ansible.builtin.wait_for:
|
||||
path: /etc/rancher/k3s/k3s.yaml
|
||||
timeout: 60
|
||||
|
||||
- name: Ensure K3s service is running
|
||||
ansible.builtin.service:
|
||||
name: k3s
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: Read node token
|
||||
ansible.builtin.slurp:
|
||||
src: /var/lib/rancher/k3s/server/node-token
|
||||
register: k3s_token_raw
|
||||
|
||||
- name: Save node token as fact
|
||||
ansible.builtin.set_fact:
|
||||
k3s_node_token: "{{ k3s_token_raw['content'] | b64decode | trim }}"
|
||||
|
||||
- name: Print node token (needed for Phase 2 agent join)
|
||||
ansible.builtin.debug:
|
||||
msg: "K3s node token: {{ k3s_node_token }}"
|
||||
|
||||
- name: Fetch kubeconfig to workstation
|
||||
ansible.builtin.fetch:
|
||||
src: /etc/rancher/k3s/k3s.yaml
|
||||
dest: /tmp/k3s-minisforum.yaml
|
||||
flat: true
|
||||
|
||||
- name: Fix kubeconfig server address
|
||||
ansible.builtin.replace:
|
||||
path: /tmp/k3s-minisforum.yaml
|
||||
regexp: 'https://127\.0\.0\.1:6443'
|
||||
replace: "https://{{ k3s_server_ip }}:6443"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
|
||||
- name: Install Helm
|
||||
ansible.builtin.shell:
|
||||
cmd: curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
||||
creates: /usr/local/bin/helm
|
||||
|
||||
- name: Label server node as primary
|
||||
ansible.builtin.shell:
|
||||
cmd: k3s kubectl label node minisforum node-role=primary --overwrite
|
||||
changed_when: false # label is idempotent but shell module always reports changed
|
||||
88
values/traefik.yml
Normal file
88
values/traefik.yml
Normal file
@ -0,0 +1,88 @@
|
||||
# Traefik Helm values — Phase 1
|
||||
# Chart: traefik/traefik
|
||||
# Deploy:
|
||||
# helm repo add traefik https://helm.traefik.io/traefik
|
||||
# helm repo update
|
||||
# helm upgrade --install traefik traefik/traefik \
|
||||
# --namespace traefik --create-namespace \
|
||||
# -f values/traefik.yml
|
||||
|
||||
globalArguments:
|
||||
- "--global.checknewversion=false"
|
||||
- "--global.sendanonymoususage=false"
|
||||
|
||||
additionalArguments:
|
||||
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
|
||||
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
|
||||
- "--certificatesresolvers.letsencrypt.acme.email=nik@nik4nao.xyz"
|
||||
- "--certificatesresolvers.letsencrypt.acme.storage=/data/traefik/acme.json"
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
http:
|
||||
redirections:
|
||||
entryPoint:
|
||||
to: websecure
|
||||
scheme: https
|
||||
websecure:
|
||||
address: ":443"
|
||||
|
||||
ingressClass:
|
||||
enabled: true
|
||||
isDefaultIngressClass: true
|
||||
|
||||
service:
|
||||
type: LoadBalancer
|
||||
# K3s includes ServiceLB (klipper) — it will bind this to the node's IP automatically
|
||||
|
||||
persistence:
|
||||
enabled: false
|
||||
existingClaim: ""
|
||||
storageClass: ""
|
||||
path: /data/traefik
|
||||
size: 128Mi
|
||||
accessMode: ReadWriteOnce
|
||||
|
||||
volumes:
|
||||
- name: traefik-data
|
||||
hostPath:
|
||||
path: /data/traefik
|
||||
type: DirectoryOrCreate
|
||||
|
||||
volumeMounts:
|
||||
- name: traefik-data
|
||||
mountPath: /data/traefik
|
||||
|
||||
deployment:
|
||||
replicas: 1
|
||||
# Pin to Minisforum (primary node)
|
||||
# Remove this section in Phase 2 once you have a multi-node cluster
|
||||
# and only want Traefik on the server node
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: node-role
|
||||
operator: In
|
||||
values:
|
||||
- primary
|
||||
|
||||
dashboard:
|
||||
enabled: true
|
||||
# Accessible internally at http://traefik.192.168.7.7.nip.io or via IngressRoute
|
||||
# Do NOT expose the dashboard externally
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
enabled: true
|
||||
matchRule: Host(`traefik.home.arpa`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))
|
||||
entryPoints:
|
||||
- websecure
|
||||
# Add BasicAuth middleware here if you want dashboard password protection
|
||||
|
||||
logs:
|
||||
general:
|
||||
level: INFO
|
||||
access:
|
||||
enabled: true
|
||||
Loading…
x
Reference in New Issue
Block a user