diff --git a/README.md b/README.md index a9dad83..8f4e5cf 100644 --- a/README.md +++ b/README.md @@ -1,116 +1,104 @@ # Kubernetes GitOps Workshop -A hands-on 2.5–4 hour workshop teaching real-world cluster operations using -ArgoCD, MetalLB, Ingress-Nginx, and Tekton — all on a local single-node k3s cluster. +Twee en een half tot vier uur hands-on cluster operations met ArgoCD, MetalLB, Ingress-Nginx en Tekton. Alles draait lokaal op een single-node k3s cluster in een VM. --- -## Quick start +## Vóór je begint -### Requirements +Je hebt drie dingen nodig op je laptop. Installeer ze de dag van tevoren — niet op de dag zelf. -Install all three **before** the workshop (ideally the day before — downloads are large): +| Tool | Download | +|----------------|-------------------------------------------------------| +| VirtualBox 7.x | https://www.virtualbox.org/wiki/Downloads — herstart je laptop erna | +| Vagrant 2.4.x | https://developer.hashicorp.com/vagrant/downloads | +| Git | https://git-scm.com/downloads | -| Tool | Install | -|--------------------|------------------------------------------------------------------| -| **VirtualBox 7.x** | https://www.virtualbox.org/wiki/Downloads — reboot after install | -| **Vagrant 2.4.x** | https://developer.hashicorp.com/vagrant/downloads | -| **Git** | https://git-scm.com/downloads | +Minimaal 12 GB vrij RAM en ~15 GB schijfruimte. Snelle check: -12 GB RAM free, ~15 GB disk. On Apple Silicon (M1/M2/M3/M4), download the **Apple Silicon build** of VirtualBox — see [docs/vm-setup.md](docs/vm-setup.md). - -Quick check — all three must print a version: ```bash VBoxManage --version && vagrant --version && git --version ``` -### 1. Start the VM - -```bash -git clone https://github.com/paulharkink/ops-demo.git -cd ops-demo -vagrant up # first run: ~10–15 min -vagrant ssh -cd /vagrant -``` - -See [docs/vm-setup.md](docs/vm-setup.md) for verification steps and troubleshooting. - -### 2. Bootstrap ArgoCD - -```bash -./scripts/bootstrap.sh -``` - -Then follow the exercises in order. +Als één van de drie niets teruggeeft: installeren en opnieuw proberen. --- -## Exercises +## Aan de slag -| # | Exercise | Guide | Type | Est. Time | -|----|---------------------------|------------------------------------------------------------|-------|-----------| -| 01 | Bootstrap ArgoCD | [docs/01-argocd-bootstrap.md](docs/01-argocd-bootstrap.md) | Core | 30 min | -| 02 | Deploy podinfo via GitOps | [docs/02-deploy-podinfo.md](docs/02-deploy-podinfo.md) | Core | 30 min | -| 03 | MetalLB + Ingress-Nginx | [docs/03-metallb-ingress.md](docs/03-metallb-ingress.md) | Core | 45 min | -| 04 | Tekton pipeline | [docs/04-tekton-pipeline.md](docs/04-tekton-pipeline.md) | Core | 45 min | -| 05 | App upgrade + reflection | [docs/05-app-upgrade.md](docs/05-app-upgrade.md) | Core | 15 min | -| 06 | Prometheus + Grafana | [docs/06-monitoring.md](docs/06-monitoring.md) | Bonus | 60 min | +**Fork eerst de repo** naar je eigen GitHub-account — ga naar https://github.com/paulharkink/ops-demo en klik Fork. Zo kun je zelf pushen zonder dat je toegang nodig hebt tot de originele repo. -**Beginners**: aim for Exercises 01–03 (~1h45m). -**Everyone else**: target 01–05 for the full core loop. +```bash +git clone https://github.com/JOUW_USERNAME/ops-demo.git +cd ops-demo +vagrant up # eerste keer ~10–15 min +vagrant ssh +cd /vagrant +./scripts/bootstrap.sh +``` + +Volg dan de oefeningen in volgorde. Zie [docs/vm-setup.md](docs/vm-setup.md) als er iets misgaat bij de VM. + +--- + +## Oefeningen + +| # | Oefening | Gids | Type | Tijd | +|----|-----------------------------|------------------------------------------------------------|-------|--------| +| 01 | ArgoCD bootstrappen | [docs/01-argocd-bootstrap.md](docs/01-argocd-bootstrap.md) | Kern | 30 min | +| 02 | podinfo deployen via GitOps | [docs/02-deploy-podinfo.md](docs/02-deploy-podinfo.md) | Kern | 30 min | +| 03 | MetalLB + Ingress-Nginx | [docs/03-metallb-ingress.md](docs/03-metallb-ingress.md) | Kern | 45 min | +| 04 | Tekton pipeline | [docs/04-tekton-pipeline.md](docs/04-tekton-pipeline.md) | Kern | 45 min | +| 05 | App upgrade + reflectie | [docs/05-app-upgrade.md](docs/05-app-upgrade.md) | Kern | 15 min | +| 06 | Prometheus + Grafana | [docs/06-monitoring.md](docs/06-monitoring.md) | Bonus | 60 min | + +Beginners: focus op 01–03 (~1u45m). De rest: probeer 01–05 te halen. --- ## Stack -| Component | Purpose | Version | +| Component | Rol | Versie | |-----------------------|-------------------------|------------------------| | k3s | Kubernetes | v1.31.4 | | ArgoCD | GitOps engine | v2.13.x (chart 7.7.11) | | MetalLB | Bare-metal LoadBalancer | v0.14.9 | -| Ingress-Nginx | HTTP routing | chart 4.12.0 | -| Tekton | CI pipeline | v0.65.1 | -| podinfo | Demo app | 6.6.2 → 6.7.0 | +| Ingress-Nginx | HTTP-routing | chart 4.12.0 | +| Tekton | CI-pipeline | v0.65.1 | +| podinfo | Demo-app | 6.6.2 → 6.7.0 | | kube-prometheus-stack | Observability (bonus) | chart 68.4.4 | --- -## Solution branches +## Vastgelopen? -Stuck on an exercise? Each solution branch is cumulative — it contains the complete -working state up to and including that exercise. +Elke solution branch is cumulatief — hij bevat alles t/m die oefening. Je kunt een PR openen van een solution branch naar jouw eigen branch om precies te zien wat er mist. ```bash -# View a specific file without checking out the branch git fetch origin git show origin/solution/03-metallb-ingress:manifests/networking/metallb/metallb-config.yaml ``` -| Branch | State | -|--------------------------------|----------------------------------| -| `solution/01-argocd-bootstrap` | ArgoCD running | -| `solution/02-deploy-podinfo` | podinfo synced via ArgoCD | -| `solution/03-metallb-ingress` | LAN access via MetalLB + Ingress | -| `solution/04-tekton-pipeline` | Full GitOps CI loop | -| `solution/05-app-upgrade` | podinfo at v6.7.0 | -| `solution/06-monitoring` | Prometheus + Grafana running | +| Branch | Bevat | +|--------------------------------|-------------------------------------| +| `solution/01-argocd-bootstrap` | ArgoCD draait | +| `solution/02-deploy-podinfo` | podinfo gesynchroniseerd via ArgoCD | +| `solution/03-metallb-ingress` | LAN-toegang via MetalLB + Ingress | +| `solution/04-tekton-pipeline` | Volledige GitOps CI-loop | +| `solution/05-app-upgrade` | podinfo op v6.7.0 | +| `solution/06-monitoring` | Prometheus + Grafana actief | --- -## Network layout +## Netwerk ``` -Your laptop - │ - │ 192.168.56.x (VirtualBox host-only) +Jouw laptop + │ 192.168.56.x (VirtualBox host-only) ▼ VM: 192.168.56.10 - │ └── MetalLB pool: 192.168.56.200–192.168.56.220 - │ └── 192.168.56.200 → Ingress-Nginx - │ ├── podinfo.192.168.56.200.nip.io ├── argocd.192.168.56.200.nip.io └── grafana.192.168.56.200.nip.io (bonus) diff --git a/apps/apps/podinfo.yaml b/apps/apps/podinfo.yaml deleted file mode 100644 index f692ae1..0000000 --- a/apps/apps/podinfo.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: podinfo - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "10" -spec: - project: workshop - source: - repoURL: https://github.com/paulharkink/ops-demo.git - targetRevision: HEAD - path: manifests/apps/podinfo - destination: - server: https://kubernetes.default.svc - namespace: podinfo - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/apps/argocd.yaml b/apps/argocd.yaml deleted file mode 100644 index 3abfe3b..0000000 --- a/apps/argocd.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: argocd - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "0" -spec: - project: workshop - source: - repoURL: https://argoproj.github.io/argo-helm - chart: argo-cd - targetRevision: "7.7.11" - helm: - valueFiles: - - $values/manifests/argocd/values.yaml - sources: - - repoURL: https://argoproj.github.io/argo-helm - chart: argo-cd - targetRevision: "7.7.11" - helm: - valueFiles: - - $values/manifests/argocd/values.yaml - - repoURL: https://github.com/paulharkink/ops-demo.git - targetRevision: HEAD - ref: values - destination: - server: https://kubernetes.default.svc - namespace: argocd - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true - - ServerSideApply=true diff --git a/apps/ci/pipeline.yaml b/apps/ci/pipeline.yaml deleted file mode 100644 index 54f92ca..0000000 --- a/apps/ci/pipeline.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: workshop-pipeline - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "6" -spec: - project: workshop - source: - repoURL: https://github.com/paulharkink/ops-demo.git - targetRevision: HEAD - path: manifests/ci/pipeline - destination: - server: https://kubernetes.default.svc - namespace: tekton-pipelines - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/apps/ci/tekton.yaml b/apps/ci/tekton.yaml deleted file mode 100644 index 8f62994..0000000 --- a/apps/ci/tekton.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: tekton - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "5" -spec: - project: workshop - source: - repoURL: https://github.com/paulharkink/ops-demo.git - targetRevision: HEAD - path: manifests/ci/tekton - kustomize: {} - destination: - server: https://kubernetes.default.svc - namespace: tekton-pipelines - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true - - ServerSideApply=true diff --git a/apps/monitoring/prometheus-grafana.yaml b/apps/monitoring/prometheus-grafana.yaml deleted file mode 100644 index 5df96c5..0000000 --- a/apps/monitoring/prometheus-grafana.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: prometheus-grafana - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "10" -spec: - project: workshop - sources: - - repoURL: https://prometheus-community.github.io/helm-charts - chart: kube-prometheus-stack - targetRevision: "68.4.4" - helm: - valueFiles: - - $values/manifests/monitoring/values.yaml - - repoURL: https://github.com/paulharkink/ops-demo.git - targetRevision: HEAD - ref: values - destination: - server: https://kubernetes.default.svc - namespace: monitoring - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true - - ServerSideApply=true diff --git a/apps/networking/ingress-nginx.yaml b/apps/networking/ingress-nginx.yaml deleted file mode 100644 index 873c369..0000000 --- a/apps/networking/ingress-nginx.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: ingress-nginx - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "3" -spec: - project: workshop - sources: - - repoURL: https://kubernetes.github.io/ingress-nginx - chart: ingress-nginx - targetRevision: "4.12.0" - helm: - valueFiles: - - $values/manifests/networking/ingress-nginx/values.yaml - - repoURL: https://github.com/paulharkink/ops-demo.git - targetRevision: HEAD - ref: values - destination: - server: https://kubernetes.default.svc - namespace: ingress-nginx - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/apps/networking/metallb-config.yaml b/apps/networking/metallb-config.yaml deleted file mode 100644 index 8ffaf84..0000000 --- a/apps/networking/metallb-config.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: metallb-config - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "2" -spec: - project: workshop - source: - repoURL: https://github.com/paulharkink/ops-demo.git - targetRevision: HEAD - path: manifests/networking/metallb - directory: - include: "metallb-config.yaml" - destination: - server: https://kubernetes.default.svc - namespace: metallb-system - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/apps/networking/metallb.yaml b/apps/networking/metallb.yaml deleted file mode 100644 index 0794416..0000000 --- a/apps/networking/metallb.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: metallb - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "1" -spec: - project: workshop - sources: - - repoURL: https://metallb.github.io/metallb - chart: metallb - targetRevision: "0.14.9" - helm: - valueFiles: - - $values/manifests/networking/metallb/values.yaml - - repoURL: https://github.com/paulharkink/ops-demo.git - targetRevision: HEAD - ref: values - destination: - server: https://kubernetes.default.svc - namespace: metallb-system - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true - - ServerSideApply=true diff --git a/apps/root.yaml b/apps/root.yaml deleted file mode 100644 index f3d0476..0000000 --- a/apps/root.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: root - namespace: argocd -spec: - project: workshop - source: - repoURL: https://github.com/paulharkink/ops-demo.git - targetRevision: HEAD - path: apps - destination: - server: https://kubernetes.default.svc - namespace: argocd - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/docs/01-argocd-bootstrap.md b/docs/01-argocd-bootstrap.md index 2522ba4..0f29035 100644 --- a/docs/01-argocd-bootstrap.md +++ b/docs/01-argocd-bootstrap.md @@ -1,117 +1,151 @@ -# Exercise 01 — Bootstrap ArgoCD +# Oefening 01 — ArgoCD bootstrappen -**Time**: ~30 min -**Goal**: Get ArgoCD running on your local k3s cluster and apply the App-of-Apps root application. +**Tijd**: ~30 minuten +**Doel**: ArgoCD aan de praat krijgen op je cluster en de App-of-Apps opzetten. --- -## What you'll learn -- How to install ArgoCD via Helm -- The App-of-Apps pattern: one ArgoCD Application that manages all others -- How ArgoCD watches a Git repository and syncs cluster state +## Wat je leert + +- ArgoCD installeren via Helm +- Het App-of-Apps patroon: één ArgoCD Application die alle andere beheert +- Hoe ArgoCD je Git-repo in de gaten houdt en de cluster-staat synchroniseert --- -## Prerequisites +## Vereisten -Make sure your VM is up and you are SSHed in: +De VM draait en je bent ingelogd: ```bash -vagrant up # first time takes ~10 min (downloads images) vagrant ssh cd /vagrant -``` - -Verify k3s is healthy: - -```bash kubectl get nodes # NAME STATUS ROLES AGE VERSION -# ops-demo Ready control-plane,master Xm v1.31.x+k3s1 +# ops-demo Ready control-plane,master ... ``` --- -## Steps +## Stappen -### 1. Run the bootstrap script +### 1. Bootstrap-script uitvoeren ```bash ./scripts/bootstrap.sh ``` -This script: -1. Creates the `argocd` namespace -2. Installs ArgoCD via Helm (chart 7.7.11 → ArgoCD v2.13.x) -3. Applies `apps/project.yaml` — a permissive `AppProject` for all workshop apps -4. Applies `apps/root.yaml` — the App-of-Apps entry point +Het script doet het volgende: +1. Detecteert de URL van jouw fork op basis van `git remote` +2. Maakt de `argocd` namespace aan +3. Installeert ArgoCD via Helm +4. Past `apps/project.yaml` toe +5. Genereert `apps/root.yaml` met jouw fork-URL en past het toe -At the end it prints the admin password. **Copy it now.** +Aan het einde zie je het admin-wachtwoord. **Kopieer het nu.** --- -### 2. Open the ArgoCD UI +### 2. ArgoCD UI openen -In a second terminal on your laptop (not the VM), run: +In een tweede terminal op je laptop: ```bash -vagrant ssh -- -L 8080:localhost:8080 & -# or, inside the VM: kubectl port-forward svc/argocd-server -n argocd 8080:443 ``` -Then open **https://localhost:8080** in your browser (accept the self-signed cert). - -Login: `admin` / `` +Open **https://localhost:8080** (accepteer het self-signed certificaat). +Login: `admin` / het wachtwoord uit de output van het script. --- -### 3. Explore the root Application +### 3. root.yaml committen en pushen -In the ArgoCD UI you should see one application: **root**. - -- Click it. Notice it is syncing the `apps/` directory from this repo. -- It found `apps/argocd.yaml` and `apps/project.yaml` and is managing them. -- ArgoCD is now **self-managing** — any change you push to `apps/` will be picked up automatically. +Het bootstrap-script heeft `apps/root.yaml` aangemaakt met jouw fork-URL. Dit bestand moet in je repo staan zodat ArgoCD het kan synchroniseren: + +```bash +git add apps/root.yaml +git commit -m "feat: add root app-of-apps" +git push +``` + +--- + +### 4. De root Application bekijken + +In de ArgoCD UI zie je nu de **root** application verschijnen. Klik erop. + +- Hij kijkt naar de `apps/` directory in jouw fork +- Alles wat je daar commit, pikt ArgoCD automatisch op + +Controleer ook via de CLI: ```bash -# Confirm from the CLI too kubectl get applications -n argocd ``` --- -### 4. Check the self-managing ArgoCD app +### 5. ArgoCD zichzelf laten beheren (optioneel maar mooi) -Click the **argocd** application in the UI. It should show **Synced / Healthy**. +Maak `apps/argocd.yaml` aan: -ArgoCD is now reconciling its own Helm release from Git. If you push a change to -`manifests/argocd/values.yaml`, ArgoCD will apply it to itself. +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: argocd + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "0" +spec: + project: workshop + sources: + - repoURL: https://argoproj.github.io/argo-helm + chart: argo-cd + targetRevision: "7.7.11" + helm: + valueFiles: + - $values/manifests/argocd/values.yaml + - repoURL: JOUW_FORK_URL + targetRevision: HEAD + ref: values + destination: + server: https://kubernetes.default.svc + namespace: argocd + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true +``` + +Vervang `JOUW_FORK_URL` door jouw fork-URL (staat ook in `apps/root.yaml`). Commit en push — ArgoCD beheert zichzelf vanaf nu via Git. --- -## Expected outcome +## Verwacht resultaat ``` NAME SYNC STATUS HEALTH STATUS -argocd Synced Healthy root Synced Healthy +argocd Synced Healthy ``` --- -## Troubleshooting +## Probleemoplossing -| Symptom | Fix | -|---------|-----| -| `kubectl get nodes` shows NotReady | Wait 30–60 s; k3s is starting | -| Helm install fails with timeout | Run `kubectl get pods -n argocd` — if image pull is slow, wait | -| UI shows "Unknown" sync status | Click **Refresh** on the application | -| Port-forward drops | Re-run the `kubectl port-forward` command | +| Symptoom | Oplossing | +|----------|-----------| +| root Application toont "Unknown" | Nog niet gepusht, of ArgoCD kan de repo nog niet bereiken — even wachten | +| Helm install time-out | `kubectl get pods -n argocd` — waarschijnlijk nog images aan het downloaden | +| UI toont "Unknown" sync status | Klik **Refresh** op de application | --- -## What's next +## Volgende stap -In Exercise 02 you will deploy your first application — **podinfo** — purely through -Git: no `kubectl apply`, just a YAML file committed to the repo. +In Oefening 02 deploy je je eerste echte applicatie via GitOps — geen `kubectl apply`, alleen een YAML-bestand in Git. diff --git a/docs/02-deploy-podinfo.md b/docs/02-deploy-podinfo.md index c4ba5b0..6de321f 100644 --- a/docs/02-deploy-podinfo.md +++ b/docs/02-deploy-podinfo.md @@ -1,158 +1,222 @@ -# Exercise 02 — Deploy podinfo via GitOps +# Oefening 02 — podinfo deployen via GitOps -**Time**: ~30 min -**Goal**: Deploy a real application to the cluster purely through Git — no `kubectl apply`. +**Tijd**: ~30 minuten +**Doel**: Een echte applicatie deployen puur via Git — geen `kubectl apply`. --- -## What you'll learn -- How adding an ArgoCD `Application` manifest to Git is the only deploy action needed -- How to read ArgoCD sync status and application health -- The GitOps feedback loop: commit → push → ArgoCD detects change → cluster updated +## Wat je leert + +- Hoe je een ArgoCD Application toevoegt door één bestand te committen +- Hoe je de sync-status en health van een applicatie leest +- De GitOps-loop in de praktijk: commit → push → ArgoCD detecteert → cluster bijgewerkt --- -## Prerequisites +## Vereisten -Exercise 01 complete: ArgoCD is running and the root app is Synced. +Oefening 01 afgerond. ArgoCD draait en de root app is Synced. --- -## Background: what is podinfo? +## Achtergrond: wat is podinfo? -`podinfo` is a tiny Go web app by Stefan Prodan (ArgoCD's author) — often used in -Kubernetes demos. It shows its own version number, has `/healthz` and `/readyz` -endpoints, and looks good in a browser. No external dependencies, no secrets needed. +podinfo is een kleine Go-webapp van Stefan Prodan (ook de maker van Flux). Hij wordt veel gebruikt in Kubernetes-demo's: toont zijn eigen versienummer, heeft `/healthz` en `/readyz` endpoints, en ziet er prima uit in een browser. Geen externe dependencies, geen secrets nodig. --- -## Steps +## Stappen -### 1. Understand what already exists +### 1. De manifests aanmaken -The repo already contains the podinfo manifests. Take a look: - -```bash -ls manifests/apps/podinfo/ -# namespace.yaml deployment.yaml service.yaml -``` - -Open `manifests/apps/podinfo/deployment.yaml` and find the image tag: +Maak de volgende bestanden aan: +**`manifests/apps/podinfo/namespace.yaml`** ```yaml -image: ghcr.io/stefanprodan/podinfo:6.6.2 +apiVersion: v1 +kind: Namespace +metadata: + name: podinfo ``` -This is version **6.6.2**. Remember it — you'll upgrade it later. +**`manifests/apps/podinfo/deployment.yaml`** +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: podinfo + namespace: podinfo +spec: + replicas: 1 + selector: + matchLabels: + app: podinfo + template: + metadata: + labels: + app: podinfo + spec: + containers: + - name: podinfo + image: ghcr.io/stefanprodan/podinfo:6.6.2 + ports: + - containerPort: 9898 + name: http + env: + - name: PODINFO_UI_COLOR + value: "#6C48C5" + readinessProbe: + httpGet: + path: /readyz + port: 9898 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + requests: + cpu: 50m + memory: 64Mi +``` + +**`manifests/apps/podinfo/service.yaml`** +```yaml +apiVersion: v1 +kind: Service +metadata: + name: podinfo + namespace: podinfo +spec: + selector: + app: podinfo + ports: + - port: 80 + targetPort: 9898 + name: http +``` --- -### 2. Create the ArgoCD Application +### 2. De ArgoCD Application aanmaken -This is the only thing you need to "deploy" the app — tell ArgoCD to watch the manifests: +**`apps/apps/podinfo.yaml`** +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: podinfo + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "10" +spec: + project: workshop + source: + repoURL: JOUW_FORK_URL + targetRevision: HEAD + path: manifests/apps/podinfo + destination: + server: https://kubernetes.default.svc + namespace: podinfo + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true +``` + +Vervang `JOUW_FORK_URL` door jouw fork-URL. + +--- + +### 3. Committen en pushen ```bash -cat apps/apps/podinfo.yaml +git add apps/apps/podinfo.yaml manifests/apps/podinfo/ +git commit -m "feat: deploy podinfo via GitOps" +git push ``` -You'll see it points ArgoCD at `manifests/apps/podinfo/` in this repo. The app -already exists in the repo, so ArgoCD's root app will pick it up automatically. +Dit is de enige actie die nodig is om de applicatie te deployen. -Check ArgoCD now — you should already see a **podinfo** application appearing. - -> **The GitOps point**: You didn't run any `kubectl apply` for podinfo. You committed -> `apps/apps/podinfo.yaml` to Git, and ArgoCD synced it. That's the entire workflow. +> **Het GitOps-punt**: je hebt geen `kubectl apply` uitgevoerd voor podinfo. Je hebt een bestand gecommit, en ArgoCD regelt de rest. --- -### 3. Watch it sync +### 4. Wachten tot het Synced is ```bash kubectl get application podinfo -n argocd -w ``` -Wait until you see `Synced` and `Healthy`. Then: +Wacht tot je `Synced` en `Healthy` ziet. Dan: ```bash kubectl get pods -n podinfo -# NAME READY STATUS RESTARTS AGE -# podinfo-xxxxxxxxx-xxxxx 1/1 Running 0 30s +# NAME READY STATUS RESTARTS AGE +# podinfo-xxx-xxx 1/1 Running 0 30s ``` --- -### 4. Verify the app is working - -Port-forward to test locally (inside the VM): +### 5. Controleer dat de app werkt ```bash kubectl port-forward svc/podinfo -n podinfo 9898:80 ``` -In another terminal (or using curl inside the VM): +In een ander terminal (of via curl): ```bash curl http://localhost:9898 # {"hostname":"podinfo-xxx","version":"6.6.2", ...} ``` -You can see `"version":"6.6.2"` — that matches the image tag in `deployment.yaml`. +Versie `6.6.2` — dat klopt met de image-tag in `deployment.yaml`. --- -### 5. Make a GitOps change +### 6. Maak een GitOps-wijziging -Let's change the UI color to prove the loop works. +Pas de UI-kleur aan om te bewijzen dat de loop werkt. -Edit `manifests/apps/podinfo/deployment.yaml` and change: +Verander in `manifests/apps/podinfo/deployment.yaml`: ```yaml value: "#6C48C5" ``` -to any hex color you like, e.g.: +naar bijv.: ```yaml value: "#2ecc71" ``` -Commit and push: +Commit en push: ```bash git add manifests/apps/podinfo/deployment.yaml -git commit -m "chore: change podinfo UI color" +git commit -m "chore: verander podinfo UI-kleur" git push ``` -Within ~3 minutes (ArgoCD's default poll interval) you'll see the pod restart and -the new color appear. You can also click **Refresh** in the ArgoCD UI to trigger -an immediate sync. +Binnen ~3 minuten (standaard poll-interval van ArgoCD) herstart de pod en zie je de nieuwe kleur. Je kunt ook op **Refresh** klikken in de UI voor direct effect. --- -## Expected outcome +## Verwacht resultaat ``` NAME SYNC STATUS HEALTH STATUS podinfo Synced Healthy ``` -```bash -curl http://localhost:9898 | jq .version -# "6.6.2" -``` +--- + +## Probleemoplossing + +| Symptoom | Oplossing | +|----------|-----------| +| Application blijft "Progressing" | `kubectl describe pod -n podinfo` — waarschijnlijk image pull | +| ArgoCD toont OutOfSync na push | Klik **Refresh** of wacht 3 minuten | --- -## Troubleshooting +## Volgende stap -| Symptom | Fix | -|---------|-----| -| Application stuck in "Progressing" | `kubectl describe pod -n podinfo` — usually image pull | -| `ImagePullBackOff` | Image was pre-pulled; run `kubectl get events -n podinfo` | -| ArgoCD shows OutOfSync after push | Click **Refresh** or wait 3 min for next poll | - ---- - -## What's next - -podinfo is running but only accessible via port-forward. In Exercise 03 you'll -expose it on your LAN using MetalLB (a real load balancer) and Ingress-Nginx, -so you can reach it from your laptop's browser without any port-forward. +podinfo draait maar is alleen bereikbaar via port-forward. In Oefening 03 stel je MetalLB en Ingress-Nginx in zodat je de app vanuit je browser op je laptop kunt bereiken — zonder port-forward. diff --git a/docs/03-metallb-ingress.md b/docs/03-metallb-ingress.md index e718ad3..9c96487 100644 --- a/docs/03-metallb-ingress.md +++ b/docs/03-metallb-ingress.md @@ -1,109 +1,240 @@ -# Exercise 03 — MetalLB + Ingress-Nginx (LAN exposure) +# Oefening 03 — MetalLB + Ingress-Nginx -**Time**: ~45 min -**Goal**: Expose podinfo and the ArgoCD UI on a real LAN IP — accessible from your laptop's browser without any port-forward. +**Tijd**: ~45 minuten +**Doel**: podinfo en de ArgoCD UI bereikbaar maken op een echt LAN-IP — vanuit je browser op je laptop, zonder port-forward. --- -## What you'll learn -- What MetalLB is and why you need it in a bare-metal / local Kubernetes cluster -- How a LoadBalancer service gets a real IP via L2 ARP -- How Ingress-Nginx routes HTTP traffic by hostname -- `nip.io` — a public wildcard DNS service for local development +## Wat je leert + +- Waarom je MetalLB nodig hebt op een bare-metal of lokaal Kubernetes-cluster +- Hoe een LoadBalancer-service een echt IP krijgt via L2 ARP +- Hoe Ingress-Nginx HTTP-verkeer routeert op basis van hostname +- nip.io: gratis wildcard-DNS voor lokale development --- -## Background +## Achtergrond -In cloud Kubernetes (EKS, GKE, AKS), `type: LoadBalancer` automatically provisions a cloud load balancer with a public IP. On bare metal or local VMs, nothing does that — so pods stay unreachable. +In cloud-Kubernetes (EKS, GKE, AKS) regelt `type: LoadBalancer` automatisch een load balancer met een extern IP. Op bare metal of lokale VMs doet niets dat — pods blijven onbereikbaar van buitenaf. -**MetalLB** fills that gap: it watches for `LoadBalancer` services and assigns IPs from a pool you define. In L2 mode it uses ARP to answer "who has 192.168.56.200?" — so your laptop routes directly to the VM. +**MetalLB** lost dit op: hij luistert naar LoadBalancer-services en kent IPs toe uit een pool die jij definieert. In L2-modus gebruikt hij ARP — jouw laptop vraagt "wie heeft 192.168.56.200?" en MetalLB antwoordt namens de VM. -**Ingress-Nginx** is a single LoadBalancer service that MetalLB gives one IP. All your apps share that IP — Nginx routes to the right service based on the `Host:` header. +**Ingress-Nginx** is één LoadBalancer-service die van MetalLB één IP krijgt. Al je apps delen dat IP — Nginx routeert op basis van de `Host:` header. -**nip.io** is a public DNS wildcard: `anything.192.168.56.200.nip.io` resolves to `192.168.56.200`. No `/etc/hosts` editing needed. +**nip.io** is publieke wildcard-DNS: `iets.192.168.56.200.nip.io` resolvet altijd naar `192.168.56.200`. Geen `/etc/hosts` aanpassen. --- -## Steps +## Stappen -### 1. Enable MetalLB +### 1. MetalLB installeren -The ArgoCD Application manifests for MetalLB are already in this repo. The root -App-of-Apps watches the `apps/` directory, which includes `apps/networking/`. -They are already being applied — MetalLB just needs a moment to become healthy. +Maak de volgende bestanden aan: -Check MetalLB is running: - -```bash -kubectl get pods -n metallb-system -# NAME READY STATUS RESTARTS AGE -# controller-xxx 1/1 Running 0 Xm -# speaker-xxx 1/1 Running 0 Xm +**`manifests/networking/metallb/values.yaml`** +```yaml +speaker: + tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule ``` -Check the IP pool is configured: +**`manifests/networking/metallb/metallb-config.yaml`** +```yaml +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: workshop-pool + namespace: metallb-system +spec: + addresses: + - 192.168.56.200-192.168.56.220 +--- +apiVersion: metallb.io/v1beta1 +kind: L2Advertisement +metadata: + name: workshop-l2 + namespace: metallb-system +spec: + ipAddressPools: + - workshop-pool +``` -```bash -kubectl get ipaddresspool -n metallb-system -# NAME AUTO ASSIGN AVOID BUGGY IPS ADDRESSES -# workshop-pool true false ["192.168.56.200-192.168.56.220"] +**`apps/networking/metallb.yaml`** +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: metallb + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "1" +spec: + project: workshop + sources: + - repoURL: https://metallb.github.io/metallb + chart: metallb + targetRevision: "0.14.9" + helm: + valueFiles: + - $values/manifests/networking/metallb/values.yaml + - repoURL: JOUW_FORK_URL + targetRevision: HEAD + ref: values + destination: + server: https://kubernetes.default.svc + namespace: metallb-system + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true +``` + +**`apps/networking/metallb-config.yaml`** +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: metallb-config + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "2" +spec: + project: workshop + source: + repoURL: JOUW_FORK_URL + targetRevision: HEAD + path: manifests/networking/metallb + directory: + include: "metallb-config.yaml" + destination: + server: https://kubernetes.default.svc + namespace: metallb-system + syncPolicy: + automated: + prune: true + selfHeal: true ``` --- -### 2. Enable Ingress-Nginx +### 2. Ingress-Nginx installeren -Similarly, `apps/networking/ingress-nginx.yaml` is already in the repo. Wait for it -to become Synced in ArgoCD, then: +**`manifests/networking/ingress-nginx/values.yaml`** +```yaml +controller: + ingressClassResource: + name: nginx + default: true + service: + type: LoadBalancer + loadBalancerIP: "192.168.56.200" + resources: + requests: + cpu: 100m + memory: 128Mi +``` + +**`apps/networking/ingress-nginx.yaml`** +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: ingress-nginx + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "3" +spec: + project: workshop + sources: + - repoURL: https://kubernetes.github.io/ingress-nginx + chart: ingress-nginx + targetRevision: "4.12.0" + helm: + valueFiles: + - $values/manifests/networking/ingress-nginx/values.yaml + - repoURL: JOUW_FORK_URL + targetRevision: HEAD + ref: values + destination: + server: https://kubernetes.default.svc + namespace: ingress-nginx + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true +``` + +--- + +### 3. Alles committen en pushen + +```bash +git add apps/networking/ manifests/networking/ +git commit -m "feat: MetalLB + Ingress-Nginx" +git push +``` + +Wacht tot beide applications Synced zijn, en controleer dan: ```bash kubectl get svc -n ingress-nginx -# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) -# ingress-nginx-controller LoadBalancer 10.43.x.x 192.168.56.200 80:xxx,443:xxx +# NAME TYPE EXTERNAL-IP PORT(S) +# ingress-nginx-controller LoadBalancer 192.168.56.200 80:xxx,443:xxx ``` -The `EXTERNAL-IP` column shows `192.168.56.200`. MetalLB assigned it. - -From your **laptop** (not the VM), verify: - +Vanuit je laptop: ```bash curl http://192.168.56.200 -# 404 from Nginx — correct! No ingress rule yet, but Nginx is reachable. +# 404 van Nginx — klopt, nog geen Ingress-regel ``` --- -### 3. Add a podinfo Ingress +### 4. Ingress voor podinfo toevoegen -The Ingress resource is already in `manifests/apps/podinfo/ingress.yaml`. -ArgoCD will sync it automatically. After sync: +**`manifests/apps/podinfo/ingress.yaml`** +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: podinfo + namespace: podinfo +spec: + ingressClassName: nginx + rules: + - host: podinfo.192.168.56.200.nip.io + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: podinfo + port: + name: http +``` ```bash -kubectl get ingress -n podinfo -# NAME CLASS HOSTS ADDRESS PORTS -# podinfo nginx podinfo.192.168.56.200.nip.io 192.168.56.200 80 +git add manifests/apps/podinfo/ingress.yaml +git commit -m "feat: voeg podinfo Ingress toe" +git push ``` -Open from your **laptop browser**: **http://podinfo.192.168.56.200.nip.io** - -You should see the podinfo UI with version 6.6.2. +Open vanuit je laptop: **http://podinfo.192.168.56.200.nip.io** --- -### 4. Enable the ArgoCD ingress +### 5. ArgoCD-ingress inschakelen -Now let's expose ArgoCD itself on a nice URL. Open `manifests/argocd/values.yaml` -and find the commented-out ingress block near the `server:` section: - -```yaml - # ── Exercise 03: uncomment this block after Ingress-Nginx is deployed ────── - # ingress: - # enabled: true - # ... -``` - -Uncomment the entire block (remove the `#` characters): +Pas `manifests/argocd/values.yaml` aan. Zoek het uitgecommentarieerde ingress-blok en verwijder de `#`-tekens: ```yaml ingress: @@ -115,52 +246,39 @@ Uncomment the entire block (remove the `#` characters): nginx.ingress.kubernetes.io/backend-protocol: "HTTP" ``` -Commit and push: - ```bash git add manifests/argocd/values.yaml -git commit -m "feat(ex03): enable ArgoCD ingress" +git commit -m "feat: schakel ArgoCD ingress in" git push ``` -ArgoCD will detect the change, upgrade its own Helm release, and create the Ingress. -Within a minute or two: - -```bash -kubectl get ingress -n argocd -# NAME CLASS HOSTS ADDRESS -# argocd-server nginx argocd.192.168.56.200.nip.io 192.168.56.200 -``` - -Open from your laptop: **http://argocd.192.168.56.200.nip.io** +ArgoCD detecteert de wijziging, past zijn eigen Helm-release aan en maakt de Ingress aan. +Open: **http://argocd.192.168.56.200.nip.io** --- -## Expected outcome +## Verwacht resultaat | URL | App | |-----|-----| | http://podinfo.192.168.56.200.nip.io | podinfo v6.6.2 | | http://argocd.192.168.56.200.nip.io | ArgoCD UI | -Both accessible from your laptop without any port-forward. +Beide bereikbaar vanaf je laptop zonder port-forward. --- -## Troubleshooting +## Probleemoplossing -| Symptom | Fix | -|---------|-----| -| `EXTERNAL-IP` is `` on ingress-nginx svc | MetalLB not ready yet — check `kubectl get pods -n metallb-system` | -| Curl to 192.168.56.200 times out from laptop | VirtualBox host-only adapter not configured; check `VBoxManage list hostonlyifs` | -| `nip.io` doesn't resolve | Temporary DNS issue; try again or use `/etc/hosts` with `192.168.56.200 podinfo.local` | -| ArgoCD ingress gives 502 | Wait for ArgoCD to restart after values change; ArgoCD now runs in insecure (HTTP) mode | +| Symptoom | Oplossing | +|----------|-----------| +| `EXTERNAL-IP` blijft `` | MetalLB is nog niet klaar — check `kubectl get pods -n metallb-system` | +| curl naar 192.168.56.200 time-out | VirtualBox host-only adapter niet geconfigureerd — zie vm-setup.md | +| nip.io resolvet niet | Tijdelijk DNS-probleem, probeer opnieuw of voeg toe aan `/etc/hosts` | +| ArgoCD ingress geeft 502 | Wacht tot ArgoCD herstart na de values-wijziging | --- -## What's next +## Volgende stap -In Exercise 04 you'll build a Tekton pipeline that: -1. Validates manifests -2. Bumps the podinfo image tag from `6.6.2` to `6.7.0` in `deployment.yaml` -3. Pushes the commit — and ArgoCD picks it up automatically +In Oefening 04 bouw je een Tekton-pipeline die automatisch de image-tag in Git aanpast, pusht, en laat ArgoCD de update uitrollen. diff --git a/docs/04-tekton-pipeline.md b/docs/04-tekton-pipeline.md index bccbda5..81ed657 100644 --- a/docs/04-tekton-pipeline.md +++ b/docs/04-tekton-pipeline.md @@ -1,216 +1,272 @@ -# Exercise 04 — Tekton Pipeline (GitOps Loop) +# Oefening 04 — Tekton Pipeline -**Time**: ~45 min -**Goal**: Build an automated pipeline that bumps the podinfo image tag in Git and watches ArgoCD roll out the new version — the full GitOps CI/CD loop. +**Tijd**: ~45 minuten +**Doel**: Een pipeline bouwen die automatisch de image-tag in Git aanpast en ArgoCD de update laat uitrollen — de volledige GitOps CI/CD-loop. --- -## What you'll learn -- Tekton concepts: Task, Pipeline, PipelineRun, Workspace -- How a pipeline commits to Git to trigger a GitOps deployment (no container registry needed) -- The full loop: pipeline push → ArgoCD detects → rolling update → new version in browser +## Wat je leert + +- Tekton-concepten: Task, Pipeline, PipelineRun, Workspace +- Hoe een pipeline via een Git-commit een GitOps-deployment triggert (geen container registry nodig) +- De volledige loop: pipeline push → ArgoCD detecteert → rolling update → nieuwe versie in browser --- -## The loop visualised +## De loop ``` -You trigger PipelineRun +Jij triggert een PipelineRun │ ▼ Task 1: clone repo -Task 2: validate manifests (kubectl dry-run) -Task 3: bump image tag → deployment.yaml: 6.6.2 → 6.7.0 +Task 2: valideer manifests (kubectl dry-run) +Task 3: pas image-tag aan → deployment.yaml: 6.6.2 → 6.7.0 Task 4: git commit + push │ ▼ -ArgoCD polls repo (or click Refresh) +ArgoCD detecteert de commit │ ▼ -ArgoCD syncs podinfo Deployment +ArgoCD synchroniseert de podinfo Deployment │ ▼ -Rolling update → podinfo v6.7.0 in your browser +Rolling update → podinfo v6.7.0 in je browser ``` --- -## Prerequisites +## Vereisten -Exercises 01–03 complete. podinfo is reachable at **http://podinfo.192.168.56.200.nip.io** and shows version **6.6.2**. +Oefeningen 01–03 afgerond. podinfo is bereikbaar via **http://podinfo.192.168.56.200.nip.io** en toont versie **6.6.2**. -You need: -- A GitHub account with write access to the `ops-demo` repo -- A GitHub Personal Access Token (PAT) with **repo** scope +Je hebt nodig: +- Een GitHub Personal Access Token (PAT) met **repo**-scope (lezen + schrijven) --- -## Steps +## Stappen -### 1. Verify Tekton is installed +### 1. Tekton installeren via ArgoCD -The `apps/ci/tekton.yaml` and `apps/ci/pipeline.yaml` ArgoCD Applications are -already in the repo. ArgoCD is installing Tekton via a kustomize remote reference. +**`manifests/ci/tekton/kustomization.yaml`** +```yaml +resources: + - https://storage.googleapis.com/tekton-releases/pipeline/previous/v0.65.1/release.yaml +``` -Wait for the install to complete (~3–5 min after the app appears in ArgoCD): +**`apps/ci/tekton.yaml`** +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: tekton + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "5" +spec: + project: workshop + source: + repoURL: JOUW_FORK_URL + targetRevision: HEAD + path: manifests/ci/tekton + kustomize: {} + destination: + server: https://kubernetes.default.svc + namespace: tekton-pipelines + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true +``` + +```bash +git add apps/ci/tekton.yaml manifests/ci/tekton/ +git commit -m "feat: installeer Tekton via ArgoCD" +git push +``` + +Wacht tot Tekton draait (~3–5 minuten): ```bash kubectl get pods -n tekton-pipelines -# NAME READY STATUS RESTARTS -# tekton-pipelines-controller-xxx 1/1 Running 0 -# tekton-pipelines-webhook-xxx 1/1 Running 0 -``` - -Also check that the pipeline resources are synced: - -```bash -kubectl get pipeline -n tekton-pipelines -# NAME AGE -# gitops-image-bump Xm +# tekton-pipelines-controller-xxx 1/1 Running +# tekton-pipelines-webhook-xxx 1/1 Running ``` --- -### 2. Set up Git credentials +### 2. Pipeline-resources aanmaken -The pipeline needs to push a commit to GitHub. Create a Personal Access Token: - -1. Go to **GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens** (or classic with `repo` scope) -2. Give it write access to the `ops-demo` repository - -Then create the Kubernetes Secret (this command replaces the placeholder Secret if it already exists): - -```bash -./scripts/set-git-credentials.sh +**`manifests/ci/pipeline/serviceaccount.yaml`** +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: pipeline-runner + namespace: tekton-pipelines ``` -Verify: +**`manifests/ci/pipeline/pipeline.yaml`** — zie de solution branch voor de volledige inhoud, of kopieer uit `reference-solution`: ```bash -kubectl get secret git-credentials -n tekton-pipelines -# NAME TYPE DATA AGE -# git-credentials Opaque 2 5s +git show origin/solution/04-tekton-pipeline:manifests/ci/pipeline/pipeline.yaml +``` + +**`manifests/ci/pipeline/pipelinerun.yaml`** +```yaml +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + name: bump-podinfo-to-670 + namespace: tekton-pipelines +spec: + pipelineRef: + name: gitops-image-bump + taskRunTemplate: + serviceAccountName: pipeline-runner + params: + - name: repo-url + value: JOUW_FORK_URL + - name: new-tag + value: "6.7.0" + workspaces: + - name: source + volumeClaimTemplate: + spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 1Gi + - name: git-credentials + secret: + secretName: git-credentials +``` + +**`apps/ci/pipeline.yaml`** +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: workshop-pipeline + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "6" +spec: + project: workshop + source: + repoURL: JOUW_FORK_URL + targetRevision: HEAD + path: manifests/ci/pipeline + destination: + server: https://kubernetes.default.svc + namespace: tekton-pipelines + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true +``` + +```bash +git add apps/ci/pipeline.yaml manifests/ci/pipeline/ +git commit -m "feat: voeg pipeline-resources toe" +git push ``` --- -### 3. Trigger the pipeline +### 3. Git-credentials instellen -Apply the PipelineRun (this is the only `kubectl apply` you'll run in this exercise): +De pipeline moet kunnen pushen naar jouw fork. Maak een GitHub PAT aan met `repo`-scope en voer dan uit: + +```bash +./scripts/set-git-credentials.sh +``` + +Dit maakt een Kubernetes Secret aan in de cluster — **het PAT komt niet in Git**. + +--- + +### 4. Pipeline triggeren ```bash kubectl apply -f manifests/ci/pipeline/pipelinerun.yaml ``` -Watch it run: +Volg de voortgang: ```bash kubectl get pipelinerun -n tekton-pipelines -w ``` -Or follow the logs with tkn (Tekton CLI, optional): - -```bash -# If tkn is installed: -tkn pipelinerun logs -f -n tekton-pipelines bump-podinfo-to-670 -``` - -Or follow individual TaskRun pods: +Of per pod: ```bash kubectl get pods -n tekton-pipelines -w -# Once a pod appears, you can: -kubectl logs -n tekton-pipelines -c step-bump --follow ``` -The PipelineRun should complete in ~2–3 minutes. +De PipelineRun duurt ~2–3 minuten. --- -### 4. Verify the commit +### 5. Controleer de commit ```bash -# Inside the VM — check the latest commit on the remote git fetch origin git log origin/main --oneline -3 -# You should see something like: -# a1b2c3d chore(pipeline): bump podinfo to 6.7.0 -# ... +# Je ziet: chore(pipeline): bump podinfo to 6.7.0 ``` -Or check GitHub directly in your browser. - --- -### 5. Watch ArgoCD sync +### 6. ArgoCD laten synchroniseren -In the ArgoCD UI, click **Refresh** on the **podinfo** application. - -ArgoCD will detect that `manifests/apps/podinfo/deployment.yaml` changed and -start a rolling update. +Klik **Refresh** op de **podinfo** application in ArgoCD, of wacht op het automatische poll-interval. ```bash kubectl rollout status deployment/podinfo -n podinfo -# deployment "podinfo" successfully rolled out ``` --- -### 6. Confirm in the browser +### 7. Controleer in de browser -Open **http://podinfo.192.168.56.200.nip.io** — you should now see **version 6.7.0**. +Open **http://podinfo.192.168.56.200.nip.io** — je ziet nu versie **6.7.0**. ```bash curl http://podinfo.192.168.56.200.nip.io | jq .version # "6.7.0" ``` -The full loop is complete. - --- -## Expected outcome +## Pipeline opnieuw uitvoeren -``` -PipelineRun STATUS: Succeeded -deployment.yaml image tag: 6.7.0 -podinfo UI version: 6.7.0 -``` - ---- - -## Re-running the pipeline - -The `PipelineRun` name must be unique. To run again: +De naam van een PipelineRun moet uniek zijn: ```bash -# Option A: delete and re-apply with same name kubectl delete pipelinerun bump-podinfo-to-670 -n tekton-pipelines kubectl apply -f manifests/ci/pipeline/pipelinerun.yaml - -# Option B: create a new run with a different name -kubectl create -f manifests/ci/pipeline/pipelinerun.yaml ``` --- -## Troubleshooting +## Probleemoplossing -| Symptom | Fix | -|---------|-----| -| PipelineRun stuck in "Running" forever | `kubectl describe pipelinerun -n tekton-pipelines bump-podinfo-to-670` | -| `git-credentials` Secret not found | Run `./scripts/set-git-credentials.sh` first | -| Push fails: 403 Forbidden | PAT has insufficient scope — needs `repo` write access | -| Push fails: remote already has this commit | Image tag already at 6.7.0; the pipeline is idempotent (nothing to push) | -| ArgoCD not syncing after push | Click **Refresh** in the UI; default poll interval is 3 min | -| Validate task fails | Check `kubectl apply --dry-run=client -f manifests/apps/podinfo/` manually | +| Symptoom | Oplossing | +|----------|-----------| +| PipelineRun blijft "Running" | `kubectl describe pipelinerun -n tekton-pipelines bump-podinfo-to-670` | +| Secret `git-credentials` niet gevonden | Voer `./scripts/set-git-credentials.sh` uit | +| Push mislukt: 403 Forbidden | PAT heeft onvoldoende rechten — `repo`-scope vereist | +| ArgoCD synchroniseert niet | Klik **Refresh** in de UI | --- -## What's next +## Volgende stap -Exercise 05 is a quick wrap-up: you'll look at the full picture of what you built -and optionally trigger another upgrade cycle to cement the GitOps loop. - -If you have time, try the **Bonus Exercise 06**: deploy Prometheus + Grafana and -see cluster and podinfo metrics in a live dashboard. +In Oefening 05 kijk je terug op wat je gebouwd hebt en experimenteer je met drift detection. diff --git a/docs/05-app-upgrade.md b/docs/05-app-upgrade.md index d1478b0..2720e57 100644 --- a/docs/05-app-upgrade.md +++ b/docs/05-app-upgrade.md @@ -1,80 +1,75 @@ -# Exercise 05 — App Upgrade via GitOps +# Oefening 05 — App upgrade en reflectie -**Time**: ~15 min (often done as the final step of Exercise 04) -**Goal**: Reflect on the complete GitOps loop you've built and optionally run another upgrade cycle. +**Tijd**: ~15 minuten +**Doel**: Terugkijken op wat je gebouwd hebt en de GitOps-loop nog een keer doorlopen. --- -## What you built - -You now have a fully functioning GitOps platform: +## Wat je gebouwd hebt ``` -Git repo (source of truth) +Git-repo (single source of truth) │ - │ ArgoCD polls every 3 min (or on Refresh) + │ ArgoCD pollt elke 3 minuten ▼ ArgoCD (GitOps engine) - │ detects drift between Git and cluster + │ detecteert drift tussen Git en cluster ▼ Kubernetes cluster - │ MetalLB assigns LAN IP to Ingress-Nginx + │ MetalLB kent LAN-IP toe aan Ingress-Nginx ▼ -Ingress-Nginx (routes by hostname) +Ingress-Nginx (routeert op hostname) │ ├─► podinfo.192.168.56.200.nip.io → podinfo Deployment └─► argocd.192.168.56.200.nip.io → ArgoCD UI ``` -And a CI pipeline that closes the loop: +En een CI-pipeline die de loop sluit: ``` Tekton PipelineRun │ - ├─ validate manifests - ├─ bump image tag in deployment.yaml + ├─ valideer manifests + ├─ pas image-tag aan in deployment.yaml └─ git push │ ▼ - ArgoCD detects commit → syncs → rolling update + ArgoCD detecteert commit → synchroniseert → rolling update ``` --- -## Reflect: What makes this "GitOps"? +## Waarom is dit "GitOps"? -1. **Git is the source of truth** — the cluster state is always derived from this repo -2. **No manual kubectl apply** — all cluster changes go through Git commits -3. **Drift detection** — if someone manually changes something in the cluster, ArgoCD reverts it -4. **Audit trail** — every cluster change has a corresponding Git commit -5. **Rollback = git revert** — no special tooling needed +1. **Git is de enige bron van waarheid** — de cluster-staat is altijd afgeleid van deze repo +2. **Geen handmatige `kubectl apply`** — alle cluster-wijzigingen gaan via Git-commits +3. **Drift detection** — iemand past iets handmatig aan in de cluster? ArgoCD draait het terug +4. **Auditlog** — elke cluster-wijziging heeft een bijbehorende Git-commit +5. **Rollback = `git revert`** — geen speciale tooling nodig --- -## Optional: Try a manual upgrade +## Probeer het: handmatige downgrade -If the pipeline already bumped podinfo to `6.7.0`, try a manual downgrade to see -the loop in reverse: +Als de pipeline podinfo al naar `6.7.0` heeft gebracht, probeer dan een handmatige downgrade: ```bash -# Edit the image tag back to 6.6.2 +# Pas de image-tag terug aan naar 6.6.2 vim manifests/apps/podinfo/deployment.yaml -# Change: ghcr.io/stefanprodan/podinfo:6.7.0 -# To: ghcr.io/stefanprodan/podinfo:6.6.2 git add manifests/apps/podinfo/deployment.yaml -git commit -m "chore: downgrade podinfo to 6.6.2 for demo" +git commit -m "chore: downgrade podinfo naar 6.6.2" git push ``` -Watch ArgoCD sync in the UI, then verify: +Kijk hoe ArgoCD synchroniseert, en verifieer: ```bash curl http://podinfo.192.168.56.200.nip.io | jq .version # "6.6.2" ``` -Now upgrade again via the pipeline: +En upgrade dan weer via de pipeline: ```bash kubectl delete pipelinerun bump-podinfo-to-670 -n tekton-pipelines @@ -83,40 +78,34 @@ kubectl apply -f manifests/ci/pipeline/pipelinerun.yaml --- -## Optional: Test drift detection +## Probeer het: drift detection -ArgoCD's `selfHeal: true` means it will automatically revert manual cluster changes. - -Try bypassing GitOps: +ArgoCD heeft `selfHeal: true` — hij draait handmatige cluster-wijzigingen automatisch terug. ```bash -# Change the image tag directly in the cluster (not via Git) +# Wijzig de image-tag direct in de cluster (buiten Git om) kubectl set image deployment/podinfo podinfo=ghcr.io/stefanprodan/podinfo:6.5.0 -n podinfo ``` -Watch the ArgoCD UI — within seconds you'll see the `podinfo` app go **OutOfSync**, -then ArgoCD reverts it back to whatever tag is in Git. The cluster drifted; GitOps corrected it. +Kijk in de ArgoCD UI — binnen seconden gaat de podinfo-app op **OutOfSync**, en daarna zet ArgoCD hem terug naar wat er in Git staat. --- -## Summary +## Samenvatting -| Component | Purpose | How deployed | -|-----------|---------|-------------| -| k3s | Kubernetes | Vagrantfile | -| ArgoCD | GitOps engine | bootstrap.sh → self-manages | -| MetalLB | LoadBalancer IPs | ArgoCD | -| Ingress-Nginx | HTTP routing | ArgoCD | -| podinfo | Demo app | ArgoCD | -| Tekton | CI pipeline | ArgoCD | +| Component | Rol | Hoe gedeployed | +|---------------|----------------------|--------------------| +| k3s | Kubernetes | Vagrantfile | +| ArgoCD | GitOps engine | bootstrap.sh | +| MetalLB | LoadBalancer IPs | ArgoCD | +| Ingress-Nginx | HTTP-routing | ArgoCD | +| podinfo | Demo-applicatie | ArgoCD | +| Tekton | CI-pipeline | ArgoCD | --- -## What's next +## Volgende stap -If you have time, try **Exercise 06 (Bonus)**: deploy Prometheus + Grafana and -observe your cluster and podinfo metrics in a live dashboard. +Als je nog tijd hebt: **Oefening 06 (bonus)** — Prometheus + Grafana deployen en cluster-metrics bekijken in een live dashboard. -Otherwise, join the **final presentation** for a discussion on: -- Why GitOps in production -- What comes next: Vault, ApplicationSets, Argo Rollouts +Anders: sluit af met de **presentatie** over GitOps in productie. diff --git a/docs/06-monitoring.md b/docs/06-monitoring.md index 82a18f5..a87a98b 100644 --- a/docs/06-monitoring.md +++ b/docs/06-monitoring.md @@ -1,138 +1,193 @@ -# Exercise 06 (Bonus) — Monitoring: Prometheus + Grafana +# Oefening 06 (Bonus) — Prometheus + Grafana -**Time**: ~60 min -**Goal**: Deploy a full observability stack via ArgoCD and explore cluster + application metrics in Grafana. +**Tijd**: ~60 minuten +**Doel**: Een volledige observability-stack deployen via ArgoCD en cluster- en applicatiemetrics bekijken in Grafana. --- -## What you'll learn -- How to deploy a complex multi-component stack (kube-prometheus-stack) purely via GitOps -- How Prometheus scrapes metrics from Kubernetes and applications -- How to navigate Grafana dashboards for cluster and pod-level metrics +## Wat je leert + +- Hoe je een complexe multi-component stack (kube-prometheus-stack) puur via GitOps deployet +- Hoe Prometheus metrics scrapt van Kubernetes en applicaties +- Navigeren door Grafana-dashboards voor cluster- en pod-metrics --- -## Prerequisites +## Vereisten -Exercises 01–03 complete. Ingress-Nginx is running and nip.io URLs are reachable from your laptop. +Oefeningen 01–03 afgerond. Ingress-Nginx draait en nip.io-URLs zijn bereikbaar vanaf je laptop. -**Note**: This exercise adds ~700 MB of additional memory usage. It works on an 8 GB VM but may be slow. If the VM feels sluggish, reduce `replicas` or skip Prometheus `storageSpec`. +> De monitoring-stack gebruikt extra ~700 MB geheugen. Op een 8 GB VM werkt het, maar kan wat traag aanvoelen. Als het te zwaar wordt, kun je `alertmanager` uitschakelen in de values. --- -## Steps +## Stappen -### 1. Enable the monitoring Application +### 1. Monitoring-Application aanmaken -The ArgoCD Application manifest for the monitoring stack is already in `apps/monitoring/`. -The root App-of-Apps watches this directory, so the application should already appear -in ArgoCD as **prometheus-grafana**. +**`manifests/monitoring/values.yaml`** +```yaml +grafana: + adminPassword: workshop123 + ingress: + enabled: true + ingressClassName: nginx + hosts: + - grafana.192.168.56.200.nip.io + resources: + requests: + cpu: 100m + memory: 256Mi -Check its sync status: +prometheus: + prometheusSpec: + resources: + requests: + cpu: 200m + memory: 512Mi + podMonitorSelectorNilUsesHelmValues: false + serviceMonitorSelectorNilUsesHelmValues: false + retention: 6h + retentionSize: "1GB" + storageSpec: + volumeClaimTemplate: + spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 2Gi -```bash -kubectl get application prometheus-grafana -n argocd +alertmanager: + enabled: false + +kubeStateMetrics: + resources: + requests: + cpu: 50m + memory: 64Mi + +nodeExporter: + resources: + requests: + cpu: 50m + memory: 64Mi ``` -The initial sync takes 5–8 minutes — the kube-prometheus-stack chart is large and -installs many CRDs. +**`apps/monitoring/prometheus-grafana.yaml`** +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: prometheus-grafana + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "10" +spec: + project: workshop + sources: + - repoURL: https://prometheus-community.github.io/helm-charts + chart: kube-prometheus-stack + targetRevision: "68.4.4" + helm: + valueFiles: + - $values/manifests/monitoring/values.yaml + - repoURL: JOUW_FORK_URL + targetRevision: HEAD + ref: values + destination: + server: https://kubernetes.default.svc + namespace: monitoring + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true +``` + +```bash +git add apps/monitoring/ manifests/monitoring/ +git commit -m "feat: Prometheus + Grafana via kube-prometheus-stack" +git push +``` + +De initiële sync duurt 5–8 minuten — de chart is groot en installeert veel CRDs. --- -### 2. Watch the stack come up +### 2. Wachten tot de stack klaar is ```bash kubectl get pods -n monitoring -w -# You'll see prometheus, grafana, kube-state-metrics, node-exporter pods appear ``` -Once all pods are Running: +Zodra alles Running is: ```bash kubectl get ingress -n monitoring -# NAME CLASS HOSTS ADDRESS -# grafana nginx grafana.192.168.56.200.nip.io 192.168.56.200 +# NAME HOSTS ADDRESS +# grafana grafana.192.168.56.200.nip.io 192.168.56.200 ``` --- -### 3. Open Grafana +### 3. Grafana openen -From your laptop: **http://grafana.192.168.56.200.nip.io** +Vanuit je laptop: **http://grafana.192.168.56.200.nip.io** Login: `admin` / `workshop123` --- -### 4. Explore dashboards +### 4. Dashboards verkennen -kube-prometheus-stack ships with pre-built dashboards. In the Grafana sidebar: -**Dashboards → Browse** +kube-prometheus-stack levert kant-en-klare dashboards mee. In de Grafana-sidebar: **Dashboards → Browse**. -Useful dashboards for this workshop: +Interessant voor deze workshop: -| Dashboard | What to look at | -|-----------|----------------| -| **Kubernetes / Compute Resources / Namespace (Pods)** | CPU + memory per pod in `podinfo` namespace | -| **Kubernetes / Compute Resources / Node (Pods)** | Node-level resource view | -| **Node Exporter / Full** | VM-level CPU, memory, disk, network | +| Dashboard | Wat je ziet | +|-----------|-------------| +| Kubernetes / Compute Resources / Namespace (Pods) | CPU + geheugen per pod in de `podinfo` namespace | +| Kubernetes / Compute Resources / Node (Pods) | Overzicht op node-niveau | +| Node Exporter / Full | VM-niveau: CPU, geheugen, schijf, netwerk | --- -### 5. Generate some load on podinfo - -In a new terminal, run a simple load loop: +### 5. Load genereren op podinfo ```bash -# Inside the VM +# In de VM while true; do curl -s http://podinfo.192.168.56.200.nip.io > /dev/null; sleep 0.2; done ``` -Switch back to Grafana → **Kubernetes / Compute Resources / Namespace (Pods)** → -set namespace to `podinfo`. You should see CPU usage climb for the podinfo pod. +Open in Grafana: **Kubernetes / Compute Resources / Namespace (Pods)** → namespace `podinfo`. Je ziet het CPU-gebruik stijgen. --- -### 6. Explore the GitOps aspect +### 6. GitOps ook hier -Every configuration change to the monitoring stack goes through Git. - -Try changing the Grafana admin password: +Probeer het Grafana-wachtwoord aan te passen: ```bash vim manifests/monitoring/values.yaml -# Change: adminPassword: workshop123 -# To: adminPassword: supersecret +# Verander: adminPassword: workshop123 +# Naar: adminPassword: nieuwwachtwoord + git add manifests/monitoring/values.yaml -git commit -m "chore(monitoring): update grafana admin password" +git commit -m "chore: pas Grafana-wachtwoord aan" git push ``` -Watch ArgoCD sync the Helm release, then try logging into Grafana with the new password. +ArgoCD synchroniseert de Helm-release en Grafana herstart. Log daarna in met het nieuwe wachtwoord. --- -## Expected outcome +## Probleemoplossing -- Grafana accessible at **http://grafana.192.168.56.200.nip.io** -- Prometheus scraping cluster metrics -- Pre-built Kubernetes dashboards visible and populated - ---- - -## Troubleshooting - -| Symptom | Fix | -|---------|-----| -| Pods in Pending state | VM may be low on memory; `kubectl describe pod` to confirm | -| Grafana 502 from Nginx | Grafana pod not ready yet; wait and retry | -| No data in dashboards | Prometheus needs ~2 min to scrape first metrics; wait and refresh | -| CRD conflict on sync | First sync installs CRDs; second sync applies resources — retry | - ---- - -## Going further (at home) - -- Add a podinfo `ServiceMonitor` so Prometheus scrapes podinfo's `/metrics` endpoint -- Create a custom Grafana dashboard for podinfo request rate and error rate -- Alert on high memory usage with Alertmanager (enable it in `values.yaml`) +| Symptoom | Oplossing | +|----------|-----------| +| Pods in Pending | VM heeft te weinig geheugen — `kubectl describe pod` voor details | +| Grafana 502 van Nginx | Pod is nog niet klaar, even wachten | +| Geen data in dashboards | Prometheus heeft ~2 minuten nodig voor de eerste scrape | +| CRD-conflict bij sync | Eerste sync installeert CRDs, tweede sync past resources toe — opnieuw proberen | diff --git a/docs/presentation/final-talk.md b/docs/presentation/final-talk.md index 6ff18f5..1a3c762 100644 --- a/docs/presentation/final-talk.md +++ b/docs/presentation/final-talk.md @@ -1,17 +1,17 @@ -# Final Talk — GitOps in Practice +# Final Talk — GitOps in de praktijk -**Duration**: ~20 min + Q&A -**Format**: Slides or whiteboard; optional live demo +**Duur**: ~20 min + Q&A +**Format**: Slides of whiteboard, optioneel met live demo --- -## 1. What We Built (7 min) +## 1. Wat we gebouwd hebben (7 min) -### Architecture diagram +### Architectuurdiagram ``` ┌─────────────────────────────────────────────────────────┐ -│ Your Laptop │ +│ Jouw laptop │ │ │ │ Browser ──────────────────────────────────────────► │ │ podinfo.192.168.56.200.nip.io │ @@ -25,13 +25,13 @@ │ │ │ ┌──────────────────┐ ┌───────────────────────────┐ │ │ │ Ingress-Nginx │ │ ArgoCD │ │ -│ │ (LB: .200) │ │ watches this Git repo │ │ +│ │ (LB: .200) │ │ kijkt naar deze Git repo │ │ │ └──────┬───────────┘ └───────────┬───────────────┘ │ -│ │ │ syncs │ +│ │ │ synct │ │ ▼ ▼ │ │ ┌──────────────────┐ ┌───────────────────────────┐ │ │ │ podinfo │ │ MetalLB │ │ -│ │ (Deployment) │ │ (assigns LAN IPs) │ │ +│ │ (Deployment) │ │ (geeft LAN IP's uit) │ │ │ └──────────────────┘ └───────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────┐ │ @@ -41,63 +41,63 @@ └─────────────────────────────────────────────────────────┘ ``` -### The GitOps loop (narrate this) +### De GitOps loop (vertel dit hardop) -1. Everything in the cluster is defined in **this Git repo** -2. ArgoCD watches the repo and reconciles the cluster to match -3. The Tekton pipeline is itself deployed by ArgoCD — and it pushes commits that ArgoCD then syncs -4. The only `kubectl apply` you ran today was: bootstrap ArgoCD + trigger PipelineRun +1. Alles in de cluster staat als declaratie in deze Git repo +2. ArgoCD kijkt naar de repo en reconcilet de cluster naar die gewenste state +3. De Tekton pipeline wordt zelf ook door ArgoCD gedeployed, en pusht commits die ArgoCD daarna weer synct +4. De enige `kubectl apply` die je vandaag deed: bootstrap van ArgoCD + PipelineRun triggeren ### Stack recap -| Component | Role | -|-----------|------| +| Component | Rol | +|-----------|-----| | k3s | Single-binary Kubernetes | | ArgoCD | GitOps engine (App-of-Apps) | | MetalLB | Bare-metal LoadBalancer | -| Ingress-Nginx | HTTP routing by hostname | +| Ingress-Nginx | HTTP routing op hostname | | Tekton | CI pipeline (in-cluster) | -| podinfo | Demo application | +| podinfo | Demo-applicatie | | kube-prometheus-stack | Observability (bonus) | --- -## 2. Why GitOps in Production (8 min) +## 2. Waarom GitOps in productie (8 min) -### The old way: imperative deploys +### De oude manier: imperatieve deploys ```bash -# Someone runs this on a Friday afternoon +# Iemand draait dit op vrijdagmiddag kubectl set image deployment/api api=company/api:v2.3.1-hotfix -# No review. No audit trail. No way to know who ran it at 16:47. +# Geen review. Geen audit trail. Niemand weet wie dit om 16:47 deed. ``` -### The GitOps way +### De GitOps manier ``` -PR: "bump API to v2.3.1-hotfix" +PR: "bump API naar v2.3.1-hotfix" → peer review → merge - → ArgoCD syncs - → deploy happens - → Git commit IS the audit trail + → ArgoCD synct + → deploy gebeurt + → Git commit IS de audit trail ``` -### Key benefits +### Belangrijkste voordelen -**Audit trail**: Every cluster change has a Git commit — who, what, when, why. +**Audit trail**: Elke clusterwijziging heeft een Git commit: wie, wat, wanneer, waarom. -**Drift detection**: If someone `kubectl apply`s directly, ArgoCD detects the drift and can auto-revert. The cluster always converges to what's in Git. +**Drift detection**: Als iemand direct `kubectl apply` doet, ziet ArgoCD drift en kan het automatisch terugdraaien. De cluster convergeert altijd terug naar wat in Git staat. -**Disaster recovery**: The cluster is destroyed? `vagrant up` + `./scripts/bootstrap.sh` + `kubectl apply -f apps/root.yaml` — and ArgoCD recreates everything. Git is the backup. +**Disaster recovery**: Cluster weg? `vagrant up` + `./scripts/bootstrap.sh` + `kubectl apply -f apps/root.yaml` en ArgoCD bouwt alles opnieuw op. Git is je backup. -**Multi-team collaboration**: Developers open PRs to deploy. Ops reviews the manifest changes. No SSH keys to production. +**Samenwerking tussen teams**: Developers openen PR's voor deploys. Ops reviewt manifest-wijzigingen. Geen SSH-sleutels op productie nodig. -**Rollback**: `git revert ` + `git push`. No special tooling. +**Rollback**: `git revert ` + `git push`. Geen speciale tooling nodig. -### The App-of-Apps pattern (brief) +### Het App-of-Apps pattern (kort) -One root Application manages all other Applications. Adding a new service = adding a single YAML file to `apps/`. The root app picks it up automatically. +Eén root Application beheert alle andere Applications. Nieuwe service toevoegen = één YAML-file in `apps/` toevoegen. De root app pakt die automatisch op. ``` apps/root.yaml ──manages──► apps/argocd.yaml @@ -111,27 +111,27 @@ apps/root.yaml ──manages──► apps/argocd.yaml --- -## 3. What's Next (5 min) +## 3. Wat is de volgende stap (5 min) ### Secrets management -Today: plain Kubernetes Secrets with GitHub PATs. -Production: **Vault + external-secrets-operator** +Vandaag: plain Kubernetes Secrets met GitHub PATs. +In productie: **Vault + external-secrets-operator** ``` Vault (secret store) - → external-secrets-operator pulls secrets - → creates Kubernetes Secrets - → ArgoCD syncs everything else + → external-secrets-operator haalt secrets op + → maakt Kubernetes Secrets aan + → ArgoCD synct de rest ``` -### Multi-cluster with ApplicationSets +### Multi-cluster met ApplicationSets -Today: one cluster, one repo. -Production: 10 clusters, one repo. +Vandaag: één cluster, één repo. +In productie: 10 clusters, één repo. ```yaml -# ArgoCD ApplicationSet: deploy podinfo to every cluster in a list +# ArgoCD ApplicationSet: deploy podinfo naar elke cluster uit de lijst generators: - list: elements: @@ -142,29 +142,29 @@ generators: ### Progressive delivery -Today: rolling update (all-or-nothing). -Production: **Argo Rollouts** with canary or blue/green strategies. +Vandaag: rolling update (all-or-nothing). +In productie: **Argo Rollouts** met canary of blue/green. ``` -New version → 5% of traffic - → metrics look good → 20% → 50% → 100% - → metrics bad → auto-rollback +Nieuwe versie → 5% van traffic + → metrics goed → 20% → 50% → 100% + → metrics slecht → auto-rollback ``` --- -## Optional live demo (~5 min) +## Optionele live demo (~5 min) -Make a one-line change to `manifests/apps/podinfo/deployment.yaml` (e.g. UI color), -push to GitHub, click **Refresh** in ArgoCD, and show the pod restart and new UI. +Doe een one-line wijziging in `manifests/apps/podinfo/deployment.yaml` (bijv. UI-kleur), +push naar GitHub, klik **Refresh** in ArgoCD en laat pod restart + nieuwe UI zien. -The audience has already done this — seeing it narrated makes the loop visceral. +De groep heeft dit al gedaan, maar live verteld landt de GitOps loop beter. --- -## Q&A prompts (if the room is quiet) +## Q&A prompts (als het stil valt) -- "How would you handle database migrations in a GitOps flow?" -- "What happens if two people push to Git at the same time?" -- "When is GitOps NOT the right tool?" (answer: local dev, scripts, one-off jobs) -- "How do you keep secrets out of Git at scale?" +- "Hoe pak je database migrations aan in een GitOps flow?" +- "Wat gebeurt er als twee mensen tegelijk naar Git pushen?" +- "Wanneer is GitOps NIET de juiste tool?" (antwoord: local dev, scripts, one-off jobs) +- "Hoe houd je secrets op schaal uit Git?" diff --git a/docs/vm-setup.md b/docs/vm-setup.md index 801c933..7304648 100644 --- a/docs/vm-setup.md +++ b/docs/vm-setup.md @@ -1,164 +1,128 @@ -# VM Setup — Getting Started +# VM-setup -Everything runs inside a VirtualBox VM provisioned by Vagrant. -This page walks you through starting the VM and verifying it is healthy before the workshop begins. +Alles draait in een VirtualBox-VM die Vagrant voor je opzet. Volg deze stappen voordat je aan de oefeningen begint. --- -## Requirements (install on your laptop before the workshop) +## Wat je nodig hebt -> **Do this the day before** — downloads can be slow on conference WiFi. +Doe dit de dag ervoor — niet op de ochtend van de workshop zelf. -| Tool | Version | Download | -|------|---------|----------| -| VirtualBox | 7.x | https://www.virtualbox.org/wiki/Downloads | -| Vagrant | 2.4.x | https://developer.hashicorp.com/vagrant/downloads | -| Git | any | https://git-scm.com/downloads | +| Tool | Versie | Download | +|----------------|-------------|----------| +| VirtualBox | 7.x | https://www.virtualbox.org/wiki/Downloads | +| Vagrant | 2.4.x | https://developer.hashicorp.com/vagrant/downloads | +| Git | willekeurig | https://git-scm.com/downloads | -**RAM**: The VM uses 8 GB. Your laptop should have at least 12 GB total RAM free. -**Disk**: ~15 GB free (Vagrant box ~1 GB + k3s images ~5 GB + workspace). +Minimaal 12 GB vrij RAM, ~15 GB schijfruimte. -> **Apple Silicon (M1/M2/M3/M4)**: VirtualBox 7.1+ supports Apple Silicon — make sure -> to download the **"macOS / Apple Silicon hosts"** build from the VirtualBox download page, -> not the Intel build. +**Na installatie van VirtualBox: herstart je laptop.** VirtualBox installeert een kernel-extensie en die werkt pas na een reboot. -### Verify your installs before continuing +Snelle check — alle drie moeten een versie tonen: ```bash -# All three must return a version number — if any fail, install it first -VBoxManage --version # e.g. 7.1.4r165100 -vagrant --version # e.g. Vagrant 2.4.3 -git --version # e.g. git version 2.x.x +VBoxManage --version && vagrant --version && git --version ``` -If `VBoxManage` is not found after installing VirtualBox, reboot your laptop — -VirtualBox installs a kernel extension that requires a restart. - --- -## Step 1 — Clone the repo +## Stap 1 — Fork en clone de repo + +Fork de repo naar je eigen GitHub-account via https://github.com/paulharkink/ops-demo → **Fork**. ```bash -git clone https://github.com/paulharkink/ops-demo.git +git clone https://github.com/JOUW_USERNAME/ops-demo.git cd ops-demo ``` --- -## Step 2 — Start the VM +## Stap 2 — VM opstarten ```bash vagrant up ``` -First run takes **10–15 minutes**: Vagrant downloads the Ubuntu 24.04 box, installs -k3s, Helm, yq, and pre-pulls the workshop container images. Subsequent `vagrant up` -calls start the existing VM in under a minute. +De eerste keer duurt dit 10–15 minuten. Vagrant downloadt de Ubuntu 24.04 box, installeert k3s, Helm en yq, en haalt de workshop-images alvast op. Daarna start de VM in een paar seconden. -You should see: +Aan het einde zie je: ``` ════════════════════════════════════════════════════════ VM provisioned successfully! - SSH: vagrant ssh - Next step: follow docs/vm-setup.md to verify, then - run scripts/bootstrap.sh to install ArgoCD ════════════════════════════════════════════════════════ ``` --- -## Step 3 — SSH into the VM +## Stap 3 — Inloggen ```bash vagrant ssh +cd /vagrant ``` -You are now inside the VM. All workshop commands run here unless stated otherwise. +Alle workshop-commando's voer je vanaf hier uit, tenzij anders aangegeven. --- -## Step 4 — Verify the setup +## Stap 4 — Controleer de setup ```bash -# 1. k3s is running kubectl get nodes # NAME STATUS ROLES AGE VERSION # ops-demo Ready control-plane,master Xm v1.31.x+k3s1 -# 2. Helm is available helm version # version.BuildInfo{Version:"v3.16.x", ...} -# 3. The workshop repo is mounted at /vagrant ls /vagrant -# apps/ docs/ manifests/ scripts/ Vagrantfile README.md - -# 4. The host-only interface has the right IP -ip addr show eth1 -# inet 192.168.56.10/24 +# Vagrantfile README.md apps/ docs/ manifests/ scripts/ ``` --- -## Step 5 — Verify host connectivity +## Stap 5 — Controleer bereikbaarheid vanaf je laptop -From your **laptop** (not the VM), confirm you can reach the VM's host-only IP: +Vanuit je laptop (niet de VM): ```bash ping 192.168.56.10 ``` -If this times out, check your VirtualBox host-only network adapter: +Werkt dit niet, controleer dan of de VirtualBox host-only adapter bestaat: ```bash -# macOS/Linux -VBoxManage list hostonlyifs -# Should show vboxnet0 with IP 192.168.56.1 - -# Windows VBoxManage list hostonlyifs +# Verwacht: vboxnet0 met IP 192.168.56.1 ``` -If no host-only adapter exists: +Bestaat hij niet: ```bash VBoxManage hostonlyif create VBoxManage hostonlyif ipconfig vboxnet0 --ip 192.168.56.1 --netmask 255.255.255.0 ``` -Then re-run `vagrant up`. +Dan `vagrant up` opnieuw. --- -## Working directory - -Inside the VM, the repo is available at `/vagrant` (a VirtualBox shared folder). -All workshop commands are run from `/vagrant`: +## Handige Vagrant-commando's ```bash -cd /vagrant +vagrant halt # afsluiten +vagrant up # opstarten +vagrant suspend # pauzeren +vagrant resume # hervatten +vagrant destroy # VM volledig verwijderen ``` --- -## Stopping and restarting the VM +## Probleemoplossing -```bash -vagrant halt # graceful shutdown (preserves state) -vagrant up # restart -vagrant suspend # pause (faster resume, uses disk space) -vagrant resume # resume from suspend -vagrant destroy # delete the VM entirely (start fresh) -``` - ---- - -## Troubleshooting - -| Symptom | Fix | -|---------|-----| -| `vagrant up`: "No usable default provider" | VirtualBox is not installed — install it and reboot, then retry | -| `vagrant up` fails: VT-x/AMD-V not enabled | Enable virtualisation in BIOS/UEFI settings | -| `vagrant up` fails: port conflict | Another VM may be using the host-only range; stop it | -| `kubectl get nodes` shows NotReady | k3s is still starting; wait 30–60 s | -| `/vagrant` is empty inside VM | Shared folder issue; try `vagrant reload` | -| Very slow image pulls | Images should be pre-pulled; if not, wait 5–10 min | +| Symptoom | Oplossing | +|----------|-----------| +| "No usable default provider" | VirtualBox niet geïnstalleerd of laptop niet herstart | +| VT-x/AMD-V niet beschikbaar | Schakel virtualisatie in via BIOS/UEFI | +| `kubectl get nodes` → NotReady | k3s start nog op, wacht 30–60 seconden | +| `/vagrant` is leeg | Shared folder probleem — probeer `vagrant reload` | diff --git a/manifests/apps/podinfo/deployment.yaml b/manifests/apps/podinfo/deployment.yaml deleted file mode 100644 index e64a2f6..0000000 --- a/manifests/apps/podinfo/deployment.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: podinfo - namespace: podinfo -spec: - replicas: 1 - selector: - matchLabels: - app: podinfo - template: - metadata: - labels: - app: podinfo - spec: - containers: - - name: podinfo - image: ghcr.io/stefanprodan/podinfo:6.6.2 - ports: - - containerPort: 9898 - name: http - env: - - name: PODINFO_UI_COLOR - value: "#6C48C5" - readinessProbe: - httpGet: - path: /readyz - port: 9898 - initialDelaySeconds: 5 - periodSeconds: 10 - livenessProbe: - httpGet: - path: /healthz - port: 9898 - initialDelaySeconds: 5 - periodSeconds: 30 - resources: - requests: - cpu: 50m - memory: 64Mi - limits: - memory: 128Mi diff --git a/manifests/apps/podinfo/ingress.yaml b/manifests/apps/podinfo/ingress.yaml deleted file mode 100644 index dc2efee..0000000 --- a/manifests/apps/podinfo/ingress.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: podinfo - namespace: podinfo - annotations: - nginx.ingress.kubernetes.io/rewrite-target: / -spec: - ingressClassName: nginx - rules: - - host: podinfo.192.168.56.200.nip.io - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: podinfo - port: - name: http diff --git a/manifests/apps/podinfo/namespace.yaml b/manifests/apps/podinfo/namespace.yaml deleted file mode 100644 index 5128776..0000000 --- a/manifests/apps/podinfo/namespace.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: podinfo diff --git a/manifests/apps/podinfo/service.yaml b/manifests/apps/podinfo/service.yaml deleted file mode 100644 index e070eed..0000000 --- a/manifests/apps/podinfo/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: podinfo - namespace: podinfo -spec: - selector: - app: podinfo - ports: - - port: 80 - targetPort: 9898 - name: http diff --git a/manifests/ci/pipeline/pipeline.yaml b/manifests/ci/pipeline/pipeline.yaml deleted file mode 100644 index b8682de..0000000 --- a/manifests/ci/pipeline/pipeline.yaml +++ /dev/null @@ -1,173 +0,0 @@ -apiVersion: tekton.dev/v1 -kind: Pipeline -metadata: - name: gitops-image-bump - namespace: tekton-pipelines -spec: - description: | - Validates manifests, bumps the podinfo image tag in deployment.yaml, - and pushes the commit back to the ops-demo repo. - ArgoCD then detects the change and rolls out the new image. - - params: - - name: repo-url - type: string - description: URL of the ops-demo git repository - default: https://github.com/paulharkink/ops-demo.git - - name: new-tag - type: string - description: New podinfo image tag to set (e.g. 6.7.0) - default: "6.7.0" - - name: git-user-name - type: string - description: Git author name for the bump commit - default: "Workshop Pipeline" - - name: git-user-email - type: string - description: Git author email for the bump commit - default: "pipeline@workshop.local" - - workspaces: - - name: source - description: Workspace for cloning the repo - - name: git-credentials - description: Secret with GitHub username + PAT (basic-auth) - - tasks: - # ── Task 1: Clone the repo ───────────────────────────────────────────── - - name: clone - taskSpec: - workspaces: - - name: source - - name: git-credentials - params: - - name: repo-url - - name: git-user-name - - name: git-user-email - steps: - - name: clone - image: alpine/git:latest - workingDir: /workspace/source - env: - - name: GIT_USERNAME - valueFrom: - secretKeyRef: - name: git-credentials - key: username - - name: GIT_PASSWORD - valueFrom: - secretKeyRef: - name: git-credentials - key: password - script: | - #!/bin/sh - set -eu - # Inject credentials into the clone URL - REPO=$(echo "$(params.repo-url)" | sed "s|https://|https://${GIT_USERNAME}:${GIT_PASSWORD}@|") - git clone "${REPO}" . - git config user.name "$(params.git-user-name)" - git config user.email "$(params.git-user-email)" - echo "Cloned $(git log --oneline -1)" - workspaces: - - name: source - workspace: source - - name: git-credentials - workspace: git-credentials - params: - - name: repo-url - value: $(params.repo-url) - - name: git-user-name - value: $(params.git-user-name) - - name: git-user-email - value: $(params.git-user-email) - - # ── Task 2: Validate manifests (dry-run) ────────────────────────────── - - name: validate - runAfter: [clone] - taskSpec: - workspaces: - - name: source - steps: - - name: dry-run - image: bitnami/kubectl:latest - workingDir: /workspace/source - script: | - #!/bin/sh - set -eu - echo "Running kubectl dry-run on manifests/apps/podinfo/" - kubectl apply --dry-run=client -f manifests/apps/podinfo/ - echo "Validation passed." - workspaces: - - name: source - workspace: source - - # ── Task 3: Bump image tag ───────────────────────────────────────────── - - name: bump-image-tag - runAfter: [validate] - taskSpec: - workspaces: - - name: source - params: - - name: new-tag - steps: - - name: bump - image: mikefarah/yq:4.44.3 - workingDir: /workspace/source - script: | - #!/bin/sh - set -eu - FILE="manifests/apps/podinfo/deployment.yaml" - CURRENT=$(yq '.spec.template.spec.containers[0].image' "${FILE}") - echo "Current image: ${CURRENT}" - yq -i '.spec.template.spec.containers[0].image = "ghcr.io/stefanprodan/podinfo:$(params.new-tag)"' "${FILE}" - UPDATED=$(yq '.spec.template.spec.containers[0].image' "${FILE}") - echo "Updated image: ${UPDATED}" - workspaces: - - name: source - workspace: source - params: - - name: new-tag - value: $(params.new-tag) - - # ── Task 4: Commit and push ──────────────────────────────────────────── - - name: git-commit-push - runAfter: [bump-image-tag] - taskSpec: - workspaces: - - name: source - - name: git-credentials - params: - - name: new-tag - steps: - - name: push - image: alpine/git:latest - workingDir: /workspace/source - env: - - name: GIT_USERNAME - valueFrom: - secretKeyRef: - name: git-credentials - key: username - - name: GIT_PASSWORD - valueFrom: - secretKeyRef: - name: git-credentials - key: password - script: | - #!/bin/sh - set -eu - git add manifests/apps/podinfo/deployment.yaml - git commit -m "chore(pipeline): bump podinfo to $(params.new-tag)" - - # Inject credentials for push - REMOTE_URL=$(git remote get-url origin | sed "s|https://|https://${GIT_USERNAME}:${GIT_PASSWORD}@|") - git push "${REMOTE_URL}" HEAD:main - echo "Pushed commit: $(git log --oneline -1)" - workspaces: - - name: source - workspace: source - - name: git-credentials - workspace: git-credentials - params: - - name: new-tag - value: $(params.new-tag) diff --git a/manifests/ci/pipeline/pipelinerun.yaml b/manifests/ci/pipeline/pipelinerun.yaml deleted file mode 100644 index e96f544..0000000 --- a/manifests/ci/pipeline/pipelinerun.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: tekton.dev/v1 -kind: PipelineRun -metadata: - # Change the name (e.g. bump-to-670-run2) each time you trigger the pipeline, - # or delete the old PipelineRun first — names must be unique. - name: bump-podinfo-to-670 - namespace: tekton-pipelines -spec: - pipelineRef: - name: gitops-image-bump - taskRunTemplate: - serviceAccountName: pipeline-runner - params: - - name: repo-url - value: https://github.com/paulharkink/ops-demo.git - - name: new-tag - value: "6.7.0" - - name: git-user-name - value: "Workshop Pipeline" - - name: git-user-email - value: "pipeline@workshop.local" - workspaces: - - name: source - volumeClaimTemplate: - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 1Gi - - name: git-credentials - secret: - secretName: git-credentials diff --git a/manifests/ci/pipeline/serviceaccount.yaml b/manifests/ci/pipeline/serviceaccount.yaml deleted file mode 100644 index 0007483..0000000 --- a/manifests/ci/pipeline/serviceaccount.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: pipeline-runner - namespace: tekton-pipelines -# The git-credentials Secret is NOT in this repo (it contains a real GitHub token). -# Create it before running the pipeline: -# ./scripts/set-git-credentials.sh diff --git a/manifests/ci/tekton/kustomization.yaml b/manifests/ci/tekton/kustomization.yaml deleted file mode 100644 index d1192d3..0000000 --- a/manifests/ci/tekton/kustomization.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# Installs Tekton Pipelines v0.65.1 via kustomize remote reference. -# ArgoCD applies this with its built-in kustomize support. -# Images are pre-pulled by the Vagrantfile, so this only needs network to -# fetch the manifest once (not the images). -resources: - - https://storage.googleapis.com/tekton-releases/pipeline/previous/v0.65.1/release.yaml diff --git a/manifests/monitoring/values.yaml b/manifests/monitoring/values.yaml deleted file mode 100644 index 42e363c..0000000 --- a/manifests/monitoring/values.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# kube-prometheus-stack Helm values (workshop — lightweight config) -# Chart: prometheus-community/kube-prometheus-stack 68.x - -grafana: - adminPassword: workshop123 - - ingress: - enabled: true - ingressClassName: nginx - hosts: - - grafana.192.168.56.200.nip.io - - # Lightweight for a workshop VM - resources: - requests: - cpu: 100m - memory: 256Mi - -prometheus: - prometheusSpec: - resources: - requests: - cpu: 200m - memory: 512Mi - - # Scrape everything in the cluster - podMonitorSelectorNilUsesHelmValues: false - serviceMonitorSelectorNilUsesHelmValues: false - - # Short retention for a workshop - retention: 6h - retentionSize: "1GB" - - storageSpec: - volumeClaimTemplate: - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 2Gi - -alertmanager: - enabled: false # not needed for the workshop - -# Reduce resource footprint -kubeStateMetrics: - resources: - requests: - cpu: 50m - memory: 64Mi - -nodeExporter: - resources: - requests: - cpu: 50m - memory: 64Mi diff --git a/manifests/networking/ingress-nginx/values.yaml b/manifests/networking/ingress-nginx/values.yaml deleted file mode 100644 index 9ae8582..0000000 --- a/manifests/networking/ingress-nginx/values.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Ingress-Nginx Helm values -# The controller's LoadBalancer service will get 192.168.56.200 from MetalLB. -# All workshop ingresses use IngressClass "nginx". -controller: - ingressClassResource: - name: nginx - default: true - - service: - type: LoadBalancer - # Request a specific IP so docs can reference it reliably - loadBalancerIP: "192.168.56.200" - - resources: - requests: - cpu: 100m - memory: 128Mi diff --git a/manifests/networking/metallb/metallb-config.yaml b/manifests/networking/metallb/metallb-config.yaml deleted file mode 100644 index cf7c298..0000000 --- a/manifests/networking/metallb/metallb-config.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# MetalLB L2 configuration -# IP pool: 192.168.56.200–192.168.56.220 (VirtualBox host-only subnet) -# First IP (200) will be claimed by Ingress-Nginx LoadBalancer service. -apiVersion: metallb.io/v1beta1 -kind: IPAddressPool -metadata: - name: workshop-pool - namespace: metallb-system -spec: - addresses: - - 192.168.56.200-192.168.56.220 ---- -apiVersion: metallb.io/v1beta1 -kind: L2Advertisement -metadata: - name: workshop-l2 - namespace: metallb-system -spec: - ipAddressPools: - - workshop-pool diff --git a/manifests/networking/metallb/values.yaml b/manifests/networking/metallb/values.yaml deleted file mode 100644 index 121aaff..0000000 --- a/manifests/networking/metallb/values.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# MetalLB Helm values -# No special configuration needed at chart level; -# IP pool is configured via metallb-config.yaml (CRDs). -speaker: - tolerations: - - key: node-role.kubernetes.io/control-plane - operator: Exists - effect: NoSchedule diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh index 9f63af7..4de5dcb 100755 --- a/scripts/bootstrap.sh +++ b/scripts/bootstrap.sh @@ -1,38 +1,59 @@ #!/usr/bin/env bash -# bootstrap.sh — Install ArgoCD via Helm and apply the root App-of-Apps -# Run this once inside the VM after `vagrant up`. +# bootstrap.sh — Installeer ArgoCD via Helm en genereer de root App-of-Apps +# Eénmalig uitvoeren in de VM na `vagrant up`. # -# Usage: +# Gebruik: # cd /vagrant # ./scripts/bootstrap.sh # -# What it does: -# 1. Creates the argocd namespace -# 2. Installs ArgoCD via Helm using manifests/argocd/values.yaml -# 3. Waits for ArgoCD server to be ready -# 4. Applies apps/root.yaml (App-of-Apps entry point) -# 5. Prints the initial admin password and a port-forward hint +# Wat het doet: +# 1. Detecteert de URL van jouw fork op basis van de git remote +# 2. Maakt de argocd namespace aan +# 3. Installeert ArgoCD via Helm (manifests/argocd/values.yaml) +# 4. Wacht tot ArgoCD klaar is +# 5. Past apps/project.yaml toe +# 6. Genereert apps/root.yaml met jouw fork-URL en past het toe +# 7. Print het admin-wachtwoord en een port-forward hint set -euo pipefail ARGOCD_NAMESPACE="argocd" -ARGOCD_CHART_VERSION="7.7.11" # ArgoCD chart 7.x → ArgoCD v2.13.x +ARGOCD_CHART_VERSION="7.7.11" REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" echo "══════════════════════════════════════════════" echo " ops-demo Bootstrap" echo "══════════════════════════════════════════════" -# ── 1. Namespace ────────────────────────────────────────────────────────────── -echo "→ Creating namespace: ${ARGOCD_NAMESPACE}" +# ── 1. Detecteer fork URL ───────────────────────────────────────────────────── +REMOTE_URL=$(git -C "${REPO_ROOT}" remote get-url origin 2>/dev/null || echo "") +if [[ -z "${REMOTE_URL}" ]]; then + echo "FOUT: geen git remote 'origin' gevonden." + echo " Clone de repo eerst via: git clone https://github.com/JOUW_USERNAME/ops-demo.git" + exit 1 +fi + +# Converteer SSH naar HTTPS als nodig (git@github.com:user/repo.git → https://...) +if [[ "${REMOTE_URL}" == git@* ]]; then + REPO_URL=$(echo "${REMOTE_URL}" | sed 's|git@github.com:|https://github.com/|') +else + REPO_URL="${REMOTE_URL}" +fi +# Zorg dat de URL eindigt op .git +[[ "${REPO_URL}" == *.git ]] || REPO_URL="${REPO_URL}.git" + +echo "→ Fork URL gedetecteerd: ${REPO_URL}" + +# ── 2. Namespace ────────────────────────────────────────────────────────────── +echo "→ Namespace aanmaken: ${ARGOCD_NAMESPACE}" kubectl create namespace "${ARGOCD_NAMESPACE}" --dry-run=client -o yaml | kubectl apply -f - -# ── 2. Helm install ArgoCD ──────────────────────────────────────────────────── -echo "→ Adding Argo Helm repo" +# ── 3. Helm install ArgoCD ──────────────────────────────────────────────────── +echo "→ Argo Helm-repo toevoegen" helm repo add argo https://argoproj.github.io/argo-helm --force-update helm repo update argo -echo "→ Installing ArgoCD (chart ${ARGOCD_CHART_VERSION})" +echo "→ ArgoCD installeren (chart ${ARGOCD_CHART_VERSION})" helm upgrade --install argocd argo/argo-cd \ --namespace "${ARGOCD_NAMESPACE}" \ --version "${ARGOCD_CHART_VERSION}" \ @@ -40,26 +61,53 @@ helm upgrade --install argocd argo/argo-cd \ --wait \ --timeout 5m -# ── 3. Apply root App-of-Apps ───────────────────────────────────────────────── -echo "→ Applying root App-of-Apps" +# ── 4. AppProject toepassen ─────────────────────────────────────────────────── +echo "→ AppProject 'workshop' aanmaken" kubectl apply -f "${REPO_ROOT}/apps/project.yaml" + +# ── 5. Genereer en pas apps/root.yaml toe ───────────────────────────────────── +echo "→ apps/root.yaml genereren voor ${REPO_URL}" +mkdir -p "${REPO_ROOT}/apps" +cat > "${REPO_ROOT}/apps/root.yaml" <