Compare commits

..

8 Commits

4 changed files with 267 additions and 61 deletions

23
.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
# Terraform
terraform/.terraform/
terraform/.terraform.lock.hcl
terraform/terraform.tfstate
terraform/terraform.tfstate.backup
terraform/*.tfstate
terraform/*.tfstate.*
terraform/*.tfvars.local
terraform/.env
terraform/k8s_terraform
terraform/k8s_terraform.pub
terraform/crash.log
terraform/crash.*.log
# Ansible
ansible/*.retry
ansible/.vault_pass
ansible/*.log
# OS/editor
.DS_Store
*.swp
CLAUDE.md

245
README.md
View File

@@ -29,36 +29,49 @@ Pool: IaC
### 1.1 Install tools ### 1.1 Install tools
**Terraform:**
```bash ```bash
# Terraform
yum install -y yum-utils yum install -y yum-utils
```
```bash
yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
```
```bash
yum install -y terraform yum install -y terraform
terraform version ```
# Ansible **Ansible:**
```bash
pip3 install ansible pip3 install ansible
# or: dnf install ansible ```
# Git **Git:**
```bash
yum install -y git yum install -y git
``` ```
### 1.2 Clone repo ### 1.2 Clone repo
```bash ```bash
git clone <your-gitea-repo-url> ~/k8s git clone https://gitea.fireflylab.cc/duynguyen/cluster-init.git
cd ~/k8s ```
```bash
cd cluster-init
``` ```
### 1.3 Set up Terraform credentials ### 1.3 Set up Terraform credentials
```bash ```bash
cp cluster-init/terraform/.env.example cluster-init/terraform/.env cp cluster-init/terraform/.env.example cluster-init/terraform/.env
# edit cluster-init/terraform/.env — fill in endpoint + token
``` ```
`cluster-init/terraform/.env` is gitignored — never commit it. Edit `cluster-init/terraform/.env` — fill in endpoint + token. This file is gitignored — never commit it.
### 1.4 Generate SSH keypair for VM access ### 1.4 Generate SSH keypair for VM access
@@ -68,7 +81,7 @@ cp cluster-init/terraform/.env.example cluster-init/terraform/.env
ssh-keygen -t ed25519 -f ~/k8s/cluster-init/terraform/k8s_terraform -N "" -C "terraform-k8s" ssh-keygen -t ed25519 -f ~/k8s/cluster-init/terraform/k8s_terraform -N "" -C "terraform-k8s"
``` ```
Private key: `cluster-init/terraform/k8s_terraform` (gitignored — do not commit) Private key: `cluster-init/terraform/k8s_terraform` (gitignored)
Public key: `cluster-init/terraform/k8s_terraform.pub` (gitignored — injected into VMs via cloud-init) Public key: `cluster-init/terraform/k8s_terraform.pub` (gitignored — injected into VMs via cloud-init)
--- ---
@@ -85,37 +98,66 @@ Enable:
### 2.2 Create Terraform user, role, token ### 2.2 Create Terraform user, role, token
SSH into any Proxmox node as root: SSH into any Proxmox node as root, then run each command:
**Create role:**
```bash ```bash
# Create role pveum role add TerraformRole --privs "VM.Allocate,VM.Clone,VM.Config.Disk,VM.Config.CPU,VM.Config.Memory,VM.Config.Network,VM.Config.Options,VM.Config.CloudInit,VM.Config.CDROM,VM.PowerMgmt,VM.Audit,Datastore.AllocateSpace,Datastore.AllocateTemplate,Datastore.Allocate,Datastore.Audit,Sys.Audit,Sys.Modify,Pool.Allocate,SDN.Use"
pveum role add TerraformRole --privs \ ```
"VM.Allocate,VM.Clone,VM.Config.Disk,VM.Config.CPU,\
VM.Config.Memory,VM.Config.Network,VM.Config.Options,\
VM.Config.CloudInit,VM.Config.CDROM,VM.PowerMgmt,VM.Audit,\
Datastore.AllocateSpace,Datastore.AllocateTemplate,\
Datastore.Allocate,Datastore.Audit,Sys.Audit,Sys.Modify,\
Pool.Allocate,SDN.Use"
# Create user + token — SAVE the secret, shown only once **Create user:**
```bash
pveum user add terraform@pve --comment "Terraform IaC" pveum user add terraform@pve --comment "Terraform IaC"
```
**Create token** — SAVE the secret, shown only once:
```bash
pveum user token add terraform@pve terraform --privsep 0 pveum user token add terraform@pve terraform --privsep 0
```
# ACLs **ACLs:**
pveum acl modify /nodes --user terraform@pve --role TerraformRole
pveum acl modify /nodes/proxmox03 --user terraform@pve --role TerraformRole ```bash
pveum acl modify /storage/local --user terraform@pve --role TerraformRole pveum acl modify /nodes --user terraform@pve --role TerraformRole
pveum acl modify /storage/xpen --user terraform@pve --role TerraformRole ```
pveum acl modify /pool/IaC --user terraform@pve --role TerraformRole
```bash
pveum acl modify /nodes/proxmox03 --user terraform@pve --role TerraformRole
```
```bash
pveum acl modify /storage/local --user terraform@pve --role TerraformRole
```
```bash
pveum acl modify /storage/xpen --user terraform@pve --role TerraformRole
```
```bash
pveum acl modify /pool/IaC --user terraform@pve --role TerraformRole
```
```bash
pveum acl modify /sdn/zones/localnetwork/vmbr0 --user terraform@pve --role TerraformRole pveum acl modify /sdn/zones/localnetwork/vmbr0 --user terraform@pve --role TerraformRole
```
# Required for proxmox_download_file — Sys.Audit + Sys.Modify must be at root **Required for proxmox_download_file — must be at root:**
```bash
pveum role add SysDownloadRole --privs "Sys.Audit,Sys.Modify" pveum role add SysDownloadRole --privs "Sys.Audit,Sys.Modify"
pveum acl modify / --user terraform@pve --role SysDownloadRole ```
# SDN — token needs explicit ACL (separate from user ACL) ```bash
pveum acl modify /sdn/zones/localnetwork/vmbr0 \ pveum acl modify / --user terraform@pve --role SysDownloadRole
--token terraform@pve!terraform --role TerraformRole ```
**SDN — token needs explicit ACL (separate from user ACL):**
```bash
pveum acl modify /sdn/zones/localnetwork/vmbr0 --token terraform@pve!terraform --role TerraformRole
``` ```
Update `cluster-init/terraform/.env` with the token secret. Update `cluster-init/terraform/.env` with the token secret.
@@ -128,7 +170,13 @@ Update `cluster-init/terraform/.env` with the token secret.
```bash ```bash
cd ~/k8s/cluster-init/terraform cd ~/k8s/cluster-init/terraform
```
```bash
source .env source .env
```
```bash
terraform init terraform init
``` ```
@@ -136,6 +184,9 @@ terraform init
```bash ```bash
source .env source .env
```
```bash
terraform plan terraform plan
``` ```
@@ -148,22 +199,23 @@ Expected: 5 resources to create
### 3.3 Apply ### 3.3 Apply
> Must use `-parallelism=1` — parallel disk clones to xpen NFS cause storage lock timeout.
> Takes ~10-15 min (cloud image download ~1 min + 4 VMs × ~3 min each).
```bash ```bash
terraform apply -parallelism=1 terraform apply -parallelism=1
``` ```
> Must use `-parallelism=1` — parallel disk clones to xpen NFS cause storage lock timeout.
Takes ~10-15 min (cloud image download ~1 min + 4 VMs × ~3 min each).
### 3.4 Post-apply state sync ### 3.4 Post-apply state sync
After first apply, run once to sync pool membership state: After first apply, run once to sync pool membership state:
```bash ```bash
terraform apply -refresh-only terraform apply -refresh-only
``` ```
Type `yes`. Then verify no further changes: Type `yes`. Then verify no further changes:
```bash ```bash
terraform plan terraform plan
``` ```
@@ -171,45 +223,80 @@ terraform plan
### 3.5 Verify VMs ### 3.5 Verify VMs
```bash ```bash
ssh -i cluster-init/terraform/k8s_terraform cloud-user@192.168.1.31 ssh -i ~/k8s/cluster-init/terraform/k8s_terraform cloud-user@192.168.1.31
hostname # master01 ```
free -h # Swap: 0 (disabled by cloud-init)
```bash
hostname
```
```bash
free -h
``` ```
--- ---
## Phase 4 — Configure k8s with Ansible ## Phase 4 — Configure k8s with Ansible
> Playbooks in `cluster-init/ansible/` ### 4.1 Run all playbooks (one command)
### 4.1 Run all playbooks
```bash ```bash
cd ~/k8s/cluster-init/ansible cd ~/k8s/cluster-init/ansible
```
# All nodes — OS prerequisites (swap, kernel modules, sysctl) ```bash
ansible-playbook -i inventory.ini cluster.yml
```
### 4.2 Or run individual playbooks
```bash
cd ~/k8s/cluster-init/ansible
```
All nodes — OS prerequisites (swap, kernel modules, sysctl):
```bash
ansible-playbook -i inventory.ini playbooks/01-common.yml ansible-playbook -i inventory.ini playbooks/01-common.yml
```
# All nodes — /etc/hosts (nodes know each other by hostname) All nodes — /etc/hosts:
```bash
ansible-playbook -i inventory.ini playbooks/02-hosts.yml ansible-playbook -i inventory.ini playbooks/02-hosts.yml
```
# All nodes — container runtime (containerd) All nodes — container runtime (containerd):
```bash
ansible-playbook -i inventory.ini playbooks/03-containerd.yml ansible-playbook -i inventory.ini playbooks/03-containerd.yml
```
# All nodes — kubeadm/kubelet/kubectl All nodes — kubeadm/kubelet/kubectl:
```bash
ansible-playbook -i inventory.ini playbooks/04-kube-tools.yml ansible-playbook -i inventory.ini playbooks/04-kube-tools.yml
```
# master01 only — init cluster + CNI master01 only — init cluster + CNI:
```bash
ansible-playbook -i inventory.ini playbooks/05-master-init.yml ansible-playbook -i inventory.ini playbooks/05-master-init.yml
```
# worker01/02/03 — join cluster worker01/02/03 — join cluster:
```bash
ansible-playbook -i inventory.ini playbooks/06-worker-join.yml ansible-playbook -i inventory.ini playbooks/06-worker-join.yml
``` ```
### 4.2 Verify cluster (from master01) ### 4.3 Verify cluster (from master01)
```bash
ssh -i ~/k8s/cluster-init/terraform/k8s_terraform cloud-user@192.168.1.31
```
```bash ```bash
ssh -i ../terraform/k8s_terraform cloud-user@192.168.1.31
kubectl get nodes kubectl get nodes
``` ```
@@ -222,10 +309,18 @@ All 4 nodes should be `Ready`.
### 5.1 Install kubectl ### 5.1 Install kubectl
```bash ```bash
# Match the cluster version (1.32)
curl -LO "https://dl.k8s.io/release/v1.32.0/bin/linux/amd64/kubectl" curl -LO "https://dl.k8s.io/release/v1.32.0/bin/linux/amd64/kubectl"
```
```bash
chmod +x kubectl chmod +x kubectl
```
```bash
sudo mv kubectl /usr/local/bin/kubectl sudo mv kubectl /usr/local/bin/kubectl
```
```bash
kubectl version --client kubectl version --client
``` ```
@@ -233,9 +328,10 @@ kubectl version --client
```bash ```bash
mkdir -p ~/.kube mkdir -p ~/.kube
scp -i ~/k8s/cluster-init/terraform/k8s_terraform \ ```
cloud-user@192.168.1.31:/home/cloud-user/.kube/config \
~/.kube/config ```bash
scp -i ~/k8s/cluster-init/terraform/k8s_terraform cloud-user@192.168.1.31:/home/cloud-user/.kube/config ~/.kube/config
``` ```
### 5.3 Add cluster nodes to client /etc/hosts ### 5.3 Add cluster nodes to client /etc/hosts
@@ -255,16 +351,35 @@ EOF
```bash ```bash
kubectl get nodes kubectl get nodes
```
```bash
kubectl get pods -A kubectl get pods -A
``` ```
--- ---
## Phase 6 — Bootstrap platform services
Cluster is now running with bare k8s only. To install ArgoCD, MetalLB, Envoy Gateway, NFS storage, and all platform services — continue to the cluster-bootstrap repo:
```
https://gitea.fireflylab.cc/duynguyen/cluster-bootstrap
```
---
## Teardown ## Teardown
```bash ```bash
cd ~/k8s/cluster-init/terraform cd ~/k8s/cluster-init/terraform
```
```bash
source .env source .env
```
```bash
terraform destroy -parallelism=1 terraform destroy -parallelism=1
``` ```
@@ -275,13 +390,22 @@ Destroys all 4 VMs + cloud image file. Re-run Phase 3 to rebuild from scratch.
## SSH access ## SSH access
```bash ```bash
ssh -i ~/k8s/cluster-init/terraform/k8s_terraform cloud-user@192.168.1.31 # master01 ssh -i ~/k8s/cluster-init/terraform/k8s_terraform cloud-user@192.168.1.31
ssh -i ~/k8s/cluster-init/terraform/k8s_terraform cloud-user@192.168.1.35 # worker01
ssh -i ~/k8s/cluster-init/terraform/k8s_terraform cloud-user@192.168.1.36 # worker02
ssh -i ~/k8s/cluster-init/terraform/k8s_terraform cloud-user@192.168.1.37 # worker03
``` ```
Or add to `~/.ssh/config`: ```bash
ssh -i ~/k8s/cluster-init/terraform/k8s_terraform cloud-user@192.168.1.35
```
```bash
ssh -i ~/k8s/cluster-init/terraform/k8s_terraform cloud-user@192.168.1.36
```
```bash
ssh -i ~/k8s/cluster-init/terraform/k8s_terraform cloud-user@192.168.1.37
```
Or add to `~/.ssh/config` for shorthand (`ssh master01`, `ssh worker01`, etc.):
``` ```
Host master01 Host master01
@@ -305,15 +429,13 @@ Host worker03
IdentityFile ~/k8s/cluster-init/terraform/k8s_terraform IdentityFile ~/k8s/cluster-init/terraform/k8s_terraform
``` ```
Then: `ssh master01`, `ssh worker01`, etc.
--- ---
## File reference ## File reference
``` ```
k8s/cluster-init/ k8s/cluster-init/
├── RUNBOOK.md ├── README.md
├── terraform/ ├── terraform/
│ ├── .env # Terraform credentials (gitignored) │ ├── .env # Terraform credentials (gitignored)
│ ├── .env.example # template — safe to commit │ ├── .env.example # template — safe to commit
@@ -327,6 +449,7 @@ k8s/cluster-init/
│ ├── k8s_terraform.pub # SSH public key (gitignored) │ ├── k8s_terraform.pub # SSH public key (gitignored)
│ └── .gitignore │ └── .gitignore
└── ansible/ └── ansible/
├── cluster.yml # run all playbooks in one command
├── inventory.ini ├── inventory.ini
├── ansible.cfg ├── ansible.cfg
└── playbooks/ └── playbooks/

7
ansible/cluster.yml Normal file
View File

@@ -0,0 +1,7 @@
---
- import_playbook: playbooks/01-common.yml
- import_playbook: playbooks/02-hosts.yml
- import_playbook: playbooks/03-containerd.yml
- import_playbook: playbooks/04-kube-tools.yml
- import_playbook: playbooks/05-master-init.yml
- import_playbook: playbooks/06-worker-join.yml

View File

@@ -3,7 +3,52 @@
hosts: k8s hosts: k8s
become: true become: true
vars:
timezone: Asia/Ho_Chi_Minh # GMT+7
tasks: tasks:
# ── Timezone + NTP ────────────────────────────────────────────────
- name: Set timezone
community.general.timezone:
name: "{{ timezone }}"
- name: Ensure chrony is installed
dnf:
name: chrony
state: present
- name: Enable and start chronyd
systemd:
name: chronyd
enabled: true
state: started
# ── SELinux ───────────────────────────────────────────────────────
- name: Set SELinux to permissive (runtime)
command: setenforce 0
ignore_errors: true # already permissive/disabled is fine
- name: Set SELinux to permissive (persistent)
replace:
path: /etc/selinux/config
regexp: '^SELINUX=enforcing'
replace: 'SELINUX=permissive'
# ── Firewalld ─────────────────────────────────────────────────────
- name: Check if firewalld is installed
command: systemctl list-unit-files firewalld.service
register: firewalld_check
changed_when: false
failed_when: false
- name: Disable and stop firewalld
systemd:
name: firewalld
enabled: false
state: stopped
when: "'firewalld.service' in firewalld_check.stdout"
# ── Swap ──────────────────────────────────────────────────────────
- name: Disable swap now - name: Disable swap now
command: swapoff -a command: swapoff -a
when: ansible_facts['swaptotal_mb'] > 0 when: ansible_facts['swaptotal_mb'] > 0
@@ -14,6 +59,7 @@
regexp: '^([^#].*\s+swap\s+.*)$' regexp: '^([^#].*\s+swap\s+.*)$'
replace: '# \1' replace: '# \1'
# ── Kernel modules ────────────────────────────────────────────────
- name: Load kernel modules at boot - name: Load kernel modules at boot
copy: copy:
dest: /etc/modules-load.d/k8s.conf dest: /etc/modules-load.d/k8s.conf
@@ -28,6 +74,7 @@
- overlay - overlay
- br_netfilter - br_netfilter
# ── sysctl ────────────────────────────────────────────────────────
- name: Set sysctl for k8s networking - name: Set sysctl for k8s networking
copy: copy:
dest: /etc/sysctl.d/k8s.conf dest: /etc/sysctl.d/k8s.conf
@@ -38,3 +85,9 @@
- name: Apply sysctl - name: Apply sysctl
command: sysctl --system command: sysctl --system
# ── NFS client ────────────────────────────────────────────────────
- name: Install nfs-utils (required for NFS PVC mounts)
dnf:
name: nfs-utils
state: present