Setup K3s Single Node Cluster, GitOps dengan Flux
Latar Belakang #
Seperti yang saya ceritakan sebelum-nya disini dan disini, saya sedang mengembangkan Homelab pribadi untuk men-support workflow development sehari-hari. Salah satu komponen penting dari setup ini adalah bagaimana saya mengelola deployment aplikasi ke cluster Kubernetes dengan cara yang lebih deklaratif menggunakan konsep GitOps.
GitOps adalah cara mengelola infrastruktur dan aplikasi melalui Git, di mana state yang diinginkan didefinisikan di repository dan tool seperti Flux akan memastikan cluster sesuai dengan definisi tersebut.
Ada beberapa tool GitOps yang populer saat ini seperti ArgoCD dan Flux. Saya memilih Flux karena integrasi native-nya dengan SOPS untuk enkripsi secret, serta kemampuan image automation yang memungkinkan deployment otomatis ketika ada image Docker baru di push ke registry.
Namun saya tidak bisa share 1:1 bagaimana setup saya karena repo github nya saya buat private. Untuk itu saya coba replicate bagaimana saya setup awal, barangkali temen2 disini ada yang tertarik untuk bikin serupa wqwq.
Persyaratan #
Install:
- lima-vm
- kubectl
- sops
- flux
- age
Semua tools diatas bisa di-install manual atau pakai arkade, saya sendiri nyaman pakai arkade. Contohnya:
arkade get kubectl flux sopsterkait lima-vm, bisa cek tulisan saya sebelum-nya membangun-local-kubernetes-development-dengan-lima-vm.
Setup K3s Single Node #
Buat dulu lima-vm instance-nya
limactl create template:k3s \
--name=k3s-demo \
--cpus=2 \
--memory=2 \
--disk=20 \
--yessetelah instance dibuat, output dibagian akhir akan menunjukan file kubeconfig yang akan digunakan untuk mengatur cluster lewat kubectl
export KUBECONFIG="/home/jimbo/.lima/k3s-demo/copied-from-guest/kubeconfig.yaml"
kubectl...Manajemen Environment Variable #
pastikan untuk export variable diatas sebelum execute perintah terkait kubectl, buat kenyamanan saya saran pakai direnv
buat file .direnv seperti ini
export KUBECONFIG="/home/jimbo/.lima/k3s-demo/copied-from-guest/kubeconfig.yaml"semua variable yang diperlukan, nanti kita bisa tambahkan seiring dengan keperluan di file diatas.
Cek koneksi ke k3s cluster dengan kubectl #
Eksekusi perintah berikut
$ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:6443
CoreDNS is running at https://127.0.0.1:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://127.0.0.1:6443/api/v1/namespaces/kube-system/services/https:metrics-server:https/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.Installing Flux CD #
Buatlah sebuah git repo, yang nanti-nya repo ini adalah sebagai source of truth dari state k8s yang kita manage, ini repo contoh yang saya buat https://github.com/wayanjimmy/k3s-demo.
Sekarang kita perlu boostrap fluxcd dulu ke k3s cluster nya.
flux bootstrap git \
--url=ssh://[email protected]/wayanjimmy/k3s-demo.git \
--private-key-file=/home/jimbo/.ssh/id_ed25519 \
--branch=main \
--path=clusters/k3s-demo \
--components-extra image-reflector-controller,image-automation-controllerpastikan ssh key yang kita gunakan sudah terdaftar di github, sehingga proses boostrap dapat mengakses repo, setelah bootstrap berhasil, eksekusi perintah berikut memastikan flux sudah ready.
$ flux check
► checking prerequisites
✔ Kubernetes 1.34.4+k3s1 >=1.32.0-0
► checking version in cluster
✔ distribution: flux-v2.7.5
✔ bootstrapped: true
► checking controllers
✔ helm-controller: deployment ready
► ghcr.io/fluxcd/helm-controller:v1.4.5
✔ image-automation-controller: deployment ready
► ghcr.io/fluxcd/image-automation-controller:v1.0.4
✔ image-reflector-controller: deployment ready
► ghcr.io/fluxcd/image-reflector-controller:v1.0.4
✔ kustomize-controller: deployment ready
► ghcr.io/fluxcd/kustomize-controller:v1.7.3
✔ notification-controller: deployment ready
► ghcr.io/fluxcd/notification-controller:v1.7.5
✔ source-controller: deployment ready
► ghcr.io/fluxcd/source-controller:v1.7.4
► checking crds
✔ alerts.notification.toolkit.fluxcd.io/v1beta3
✔ buckets.source.toolkit.fluxcd.io/v1
✔ externalartifacts.source.toolkit.fluxcd.io/v1
✔ gitrepositories.source.toolkit.fluxcd.io/v1
✔ helmcharts.source.toolkit.fluxcd.io/v1
✔ helmreleases.helm.toolkit.fluxcd.io/v2
✔ helmrepositories.source.toolkit.fluxcd.io/v1
✔ imagepolicies.image.toolkit.fluxcd.io/v1
✔ imagerepositories.image.toolkit.fluxcd.io/v1
✔ imageupdateautomations.image.toolkit.fluxcd.io/v1
✔ kustomizations.kustomize.toolkit.fluxcd.io/v1
✔ ocirepositories.source.toolkit.fluxcd.io/v1
✔ providers.notification.toolkit.fluxcd.io/v1beta3
✔ receivers.notification.toolkit.fluxcd.io/v1
✔ all checks passedkalau mau cek workload terkait flux bisa pakai perintah berikut
$ kubectl get pods -n flux-system
NAME READY STATUS RESTARTS AGE
helm-controller-68578f8447-t6v2k 1/1 Running 0 64s
kustomize-controller-7ddfbb5875-jvlvm 1/1 Running 0 64s
notification-controller-6d766f87cf-5lrvf 1/1 Running 0 64s
source-controller-6679d8bdb-dgdgs 1/1 Running 0 64ssampai disini flux sudah terinstal, lanjut ke proses deployment apps.
Testing deploy Aplikasi Demo #
Kita akan menulis yaml menggunakan kustomization, yg merupakan semacam layer abstraksi untuk manajemen yaml k8s, namun tidak akan dibahas ditulisan ini.
Buat file berikut.
clusters/k3s-demo/demo-apps-kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: demo-apps
namespace: flux-system
spec:
interval: 1m
path: ./clusters/k3s-demo/demo-apps
prune: true
sourceRef:
kind: GitRepository
name: flux-system
# decryption:
# provider: sops
# secretRef:
# name: sops-ageclusters/k3s-demo/demo-apps/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: demo-appsclusters/k3s-demo/demo-apps/nginx-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
namespace: demo-apps
labels:
app: nginx-demo
spec:
replicas: 1
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-demo
namespace: demo-apps
spec:
type: NodePort
selector:
app: nginx-demo
ports:
- port: 80
targetPort: 80
nodePort: 30081clusters/k3s-demo/demo-apps/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: demo-apps
resources:
- namespace.yaml
- nginx-demo.yaml
# - secret.yamlDeployment nginx-demo sudah di expose melalui nodeport sehingga nanti kita bisa cek melalui port tersebut apakah aplikasi berjalan normal.
Nah bagaimana cara deployment apps ini melalui flow git ops ?
Flux akan secara periodik, berdasarkan interval clusters/k3s-demo/demo-apps-kustomization.yaml, memastikan apakah definisi git repo sudah sync dengan state yang sebenarnya.
Yang perlu kita lakukan hanyalah commit semua file yaml ini dan push ke git, berikut adalah gambaran kondisi repo setelah melakukan semua file diatas di-commit https://github.com/wayanjimmy/k3s-demo/tree/81635f0f880c53a63bf8c8c53752b5d1b873a416/clusters/k3s-demo
Tunggu beberapa saat maka flux akan rekonsiliasi state-nya dan pod akan muncul
$ kubectl get pods -n demo-apps
NAME READY STATUS RESTARTS AGE
nginx-demo-6fd7fbb8f4-4jhjj 1/1 Running 0 136mEnkripsi Secret dengan SOPS #
Flux memiliki integrasi dengan sops yang bisa kita gunakan agar secret yang di commit ke git repo tidak telanjang. Untuk referensi lebih lanjut tentang Flux dan SOPS, bisa cek tulisan Budiman JoJo.
mkdir -p secretstambahkan .gitignore di di folder secrets menghindari private key yg akan kita generate ikut ke-commit.
secrets/.gitignore
*
!.gitignoreGenerate sops private & public key pair
$ age-keygen -o secrets/flux-age-key.txtkalau kamu cek struktur file flux-age-key.txt akan seperti ini
# created: 2026-02-20T07:19:42Z
# public key: age1h44rlvy9c2ffytt78msmhjtuyuwtpyc6jje7fgjkg4x7l0hpt5dsfxlf9v
AGE-SECRET-KEY-1ECFASJKZTGVTM0K2GV4QYUXGSKL0S2DG43SWV68GWLC455LAFMYQSLGHQAada public key dan secret key, public key bisa kita tambahkan di-repo agar semua orang bisa melakukan enkripsi, sedangkan private akan kita tambahkan sebagai secret di k8s cluster, agar cluster dapat melakukan dekripsi.
$ kubectl create secret generic sops-age \
--namespace=flux-system \
--from-file=age.agekey=$(pwd)/secrets/flux-age-key.txt
secret/sops-age createdPastikan secret sops-age sudah terbuat
$ kubectl get secret sops-age -n flux-system
NAME TYPE DATA AGE
sops-age Opaque 1 2m28sOk ditahap ini kita tinggal mengenalkan ke demo-apps kustomization agar mengenali sops-age sebagai key untuk decrypt secret, hilangkan tanda komentar terkait dekripsi yg sebelum-nya dalam tag komentar.
clusters/k3s-demo/demo-apps-kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: demo-apps
namespace: flux-system
spec:
interval: 1m
path: ./clusters/k3s-demo/demo-apps
prune: true
sourceRef:
kind: GitRepository
name: flux-system
decryption:
provider: sops
secretRef:
name: sops-agecommit semua changes dan push dan tunggu hingga flux melakukan rekonsiliasi, kamu bisa watch dengan perintah flux untuk lebih yakin.
$ flux get kustomizations -n flux-system --watch
NAME REVISION SUSPENDED READY MESSAGE
demo-apps main@sha1:f33a03f7 False True Applied revision: main@sha1:f33a03f7
flux-system main@sha1:f33a03f7 False True Applied revision: main@sha1:f33a03f7Jika sudah delete pods agar konfigurasi yang baru ter-aplikasi di pod yang baru
$ kubectl delete pods -l app=demo-app -n demo-appsMemastikan Pod bisa Decrypt Secret #
Sampai pada titik ini kita tinggal memastikan apakah workload demo-apps sudah bisa melakukan decrypt terhadap secret yang sudah di encrypt menggunakan sops.
Buat file secret.
$ cat clusters/k3s-demo/demo-apps/secret.yaml
apiVersion: v1
stringData:
database: somedatabase
password: secret
username: admin
kind: Secret
metadata:
name: db-credentials
namespace: demo-appsEncrypt file secret dengan sops
$ sops --encrypt \
--age $(echo $SOPS_PUBLIC_KEY) \
--encrypted-regex '^(data|stringData)$' \
--in-place clusters/k3s-demo/demo-apps/secret.yamlsetelah itu file akan ter-enkripsi seperti berikut
daftar-kan secret
clusters/k3s-demo/demo-apps/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: demo-apps
resources:
- namespace.yaml
- nginx-demo.yaml
- secret.yamlNah bagaimana memastikan kalau pod sudah dapat mengakses secret dalam keadaan decrypted? Kita bisa tambah initContainers yang mana containers ini akan dijalankan terlebih dahulu sebelum containers nginx dalam nginx-demo dijalankan.
clusters/k3s-demo/demo-apps/nginx-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
namespace: demo-apps
labels:
app: nginx-demo
spec:
replicas: 1
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
initContainers:
- name: secret-test
image: busybox:1.36
command:
- sh
- -c
- |
echo "=== SOPS Secret Decryption Test ==="
echo "DB_HOST: $DB_HOST"
envFrom:
- secretRef:
name: db-credentials
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-demo
namespace: demo-apps
spec:
type: NodePort
selector:
app: nginx-demo
ports:
- port: 80
targetPort: 80
nodePort: 30081Commit semua changes dan push, tunggu flux rekonsiliasi, kamu bisa watch log init containers tadi dengan perintah
jimbo@ser4:~/.../k3s-demo$ kubectl logs -f -l app=nginx-demo -c secret-test -n demo-apps
=== SOPS Secret Decryption Test ===
DB_HOST: postgres.demo-apps.svc.cluster.localnah disana kamu akan lihat kalau env DB_HOST dapat diakses dalam keadaan decrypted, sedangkan secret yang di git repo juga aman dalam keadaan encrypted.
Sampai saat ini proses setup flux dengan sops sudah beres!
Kesimpulan #
Sampai disini kita sudah memiliki single-node Kubernetes cluster yang dikelola dengan GitOps menggunakan Flux. Dengan setup ini, semua deployment aplikasi dapat dilakukan hanya dengan git push, sementara secret tetap aman dengan enkripsi SOPS.
Kombinasi k3s + lima-vm sangat cocok untuk ngoprek Homelab tanpa harus punya hardware khusus, semuanya bisa berjalan di laptop yang kita gunakan sehari-hari.