diff --git a/.claude/settings.json b/.claude/settings.json index afb0719..72a2b47 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -4,7 +4,11 @@ "Bash(for f:*)", "Bash(do echo:*)", "Read(//Users/jan.novak/srv/personal/home-kubernetes/**)", - "Bash(done)" + "Bash(done)", + "Bash(ssh docker-30 *)", + "Bash(git add *)", + "WebSearch", + "WebFetch(domain:cert-manager.io)" ] } } diff --git a/.gitignore b/.gitignore index 690cc52..abfe0d8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ tmp/ vms/utility-101-shadow/docker/monitoring/smtp_password docker-30/zot/sync-credentials.json +vms-home/docker-30/zot/sync-credentials.json kubernetes-kvm-terraform/gke_gcloud_auth_plugin_cache diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..bcd164d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,105 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Repository Purpose + +Home Kubernetes lab infrastructure-as-code. Manages VM provisioning (Terraform/KVM), Kubernetes cluster configuration, and application deployments via Flux GitOps — all pointing at a self-hosted Gitea instance. + +## Common Commands + +### Terraform (kubernetes-kvm-terraform/) +```bash +cd kubernetes-kvm-terraform +tofu init +tofu plan +tofu apply +tofu destroy +``` + +### Flux GitOps +```bash +# Check reconciliation status +kubectl get kustomizations -A +kubectl get helmreleases -A + +# Force reconcile +flux reconcile kustomization flux-system --with-source +flux reconcile helmrelease -n + +# Watch logs +flux logs --follow + +# Check source sync +flux get sources git +``` + +### kubectl — common ops +```bash +export KUBECONFIG=kubernetes-kvm-terraform/kubeconfig +kubectl get nodes +kubectl get pods -A +kubectl get secrets -A +``` + +### Docker Compose (docker-30/ services) +```bash +# These run on 192.168.0.30 (docker-30), accessed via SSH +ssh novakj@192.168.0.30 +cd /path/to/service && docker compose up -d +docker compose logs -f +``` + +## Architecture + +### Infrastructure Layer +- **Hypervisors**: homer (192.168.0.7) and beelink (192.168.0.6) run KVM/libvirt +- **Terraform provider**: primary `qemu+ssh://novakj@192.168.0.7/system`, secondary alias `kvm-beelink` for 192.168.0.6 +- **Kubernetes master**: kube-master-31 @ 192.168.0.31:6443, bootstrapped via kubeadm +- **OS**: Ubuntu 24.04 Noble cloud images, network bridge `br0`, subnet 192.168.0.0/24 + +### GitOps Layer (`gitops/home-kubernetes/`) +Flux syncs from Gitea (`https://gitea.home.hrajfrisbee.cz`, main branch, every 10 minutes). The reconciliation order is enforced via `dependsOn` in `flux-system/extra-kustomizations.yaml`: + +``` +00-crds → 00-rbac → cilium → cert-manager → external-secrets → everything else +``` + +Each application lives in its own subdirectory under `gitops/home-kubernetes/` and is referenced as a Flux `Kustomization` resource. Prune is enabled — removing a manifest from git removes it from the cluster. + +### Secrets Flow +Vault (docker-30) → External-Secrets controller (in-cluster) → Kubernetes `Secret` objects. Applications reference `ExternalSecret` CRs that pull from Vault paths. Do not put real secrets in git. + +### Networking +- **CNI**: Cilium 1.19.x with Gateway API and Hubble UI enabled +- **L2 LB**: Cilium `CiliumL2AnnouncementPolicy` + `CiliumLoadBalancerIPPool` for bare-metal load-balancer IPs (defined in `gitops/home-kubernetes/cilium/`) +- **Ingress**: ingress-nginx for HTTP(S) workloads; Gateway API for newer apps +- **TLS**: cert-manager issues wildcard cert (`*.home.hrajfrisbee.cz`) referenced by apps + +### Storage +- **democratic-CSI**: iSCSI volumes backed by FreeNAS at 192.168.0.40 +- **Longhorn**: configured but disabled in Flux (directory present, not in `extra-kustomizations.yaml`) + +### Supporting Services on docker-30 (192.168.0.30) +All managed with Docker Compose: +- **Gitea** — Git server + act_runner (GitHub Actions-compatible CI) +- **Vault** — secrets backend for External Secrets +- **Zot** — private OCI/container registry +- **Kanidm** — identity management / OIDC provider +- **nginx** — reverse proxy for docker-30 services + +## Key Conventions + +- **New application**: create a directory under `gitops/home-kubernetes//`, add a `Kustomization` entry in `flux-system/extra-kustomizations.yaml` with appropriate `dependsOn`. +- **Helm apps**: use a `HelmRepository` + `HelmRelease` pair; pin chart versions explicitly. +- **Secrets**: add an `ExternalSecret` CR pointing to the Vault path; never commit actual secret values. +- **Terraform state**: `kubernetes-kvm-terraform/terraform.tfstate*` is gitignored/sensitive; treat it carefully. +- **kubeconfig**: `kubernetes-kvm-terraform/kubeconfig` is gitignored; obtain it from the master node after provisioning. + + +## Plans + +When Claude Code's plan mode is used, save the plan file inside the repo at +`docs/plans/YYYY-MM-DD-HHMM-.md` instead of the default `~/.claude/plans/` +location. Get the timestamp with `date "+%Y-%m-%d-%H%M"` (matches the changelog +convention). The `` should be a short kebab-case summary of the plan's topic. diff --git a/docs/kubernetes-extras.md b/docs/kubernetes-extras.md new file mode 100644 index 0000000..29a090b --- /dev/null +++ b/docs/kubernetes-extras.md @@ -0,0 +1,14 @@ +## kubectl + +```bash +# condensed -o wide +kubectl get pods -A -o wide --watch | awk ' +/NOMINATED NODE/ { sub(/[[:space:]]+NOMINATED NODE[[:space:]]+READINESS GATES[[:space:]]*$/,""); print; fflush(); next } +{ sub(/[[:space:]]+[^[:space:]]+[[:space:]]+[^[:space:]]+[[:space:]]*$/,""); print; fflush() } +' + +# condensed -o wide +kubectl get pods -A --watch -o custom-columns=\ +'NAMESPACE:.metadata.namespace,NAME:.metadata.name,STATUS:.status.phase,RESTARTS:.status.containerStatuses[0].restartCount,IP:.status.podIP,NODE:.spec.nodeName' + +``` \ No newline at end of file