feat(ex02): deploy podinfo via GitOps

- apps/apps/podinfo.yaml: ArgoCD Application for podinfo
- manifests/apps/podinfo/: namespace, deployment (6.6.2), service
- docs/01-argocd-bootstrap.md: Exercise 01 participant guide
- docs/02-deploy-podinfo.md: Exercise 02 participant guide
This commit is contained in:
Paul Harkink 2026-02-28 15:26:26 +01:00
parent 621d2cbcde
commit a5e57583b5
6 changed files with 355 additions and 0 deletions

22
apps/apps/podinfo.yaml Normal file
View file

@ -0,0 +1,22 @@
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/innspire/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

117
docs/01-argocd-bootstrap.md Normal file
View file

@ -0,0 +1,117 @@
# Exercise 01 — Bootstrap ArgoCD
**Time**: ~30 min
**Goal**: Get ArgoCD running on your local k3s cluster and apply the App-of-Apps root application.
---
## 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
---
## Prerequisites
Make sure your VM is up and you are SSHed in:
```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
```
---
## Steps
### 1. Run the bootstrap script
```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
At the end it prints the admin password. **Copy it now.**
---
### 2. Open the ArgoCD UI
In a second terminal on your laptop (not the VM), run:
```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` / `<password from script output>`
---
### 3. Explore the root Application
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.
```bash
# Confirm from the CLI too
kubectl get applications -n argocd
```
---
### 4. Check the self-managing ArgoCD app
Click the **argocd** application in the UI. It should show **Synced / Healthy**.
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.
---
## Expected outcome
```
NAME SYNC STATUS HEALTH STATUS
argocd Synced Healthy
root Synced Healthy
```
---
## Troubleshooting
| Symptom | Fix |
|---------|-----|
| `kubectl get nodes` shows NotReady | Wait 3060 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 |
---
## What's next
In Exercise 02 you will deploy your first application — **podinfo** — purely through
Git: no `kubectl apply`, just a YAML file committed to the repo.

158
docs/02-deploy-podinfo.md Normal file
View file

@ -0,0 +1,158 @@
# Exercise 02 — Deploy podinfo via GitOps
**Time**: ~30 min
**Goal**: Deploy a real application to the cluster purely through Git — no `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
---
## Prerequisites
Exercise 01 complete: ArgoCD is running and the root app is Synced.
---
## Background: what 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.
---
## Steps
### 1. Understand what already exists
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:
```yaml
image: ghcr.io/stefanprodan/podinfo:6.6.2
```
This is version **6.6.2**. Remember it — you'll upgrade it later.
---
### 2. Create the ArgoCD Application
This is the only thing you need to "deploy" the app — tell ArgoCD to watch the manifests:
```bash
cat apps/apps/podinfo.yaml
```
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.
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.
---
### 3. Watch it sync
```bash
kubectl get application podinfo -n argocd -w
```
Wait until you see `Synced` and `Healthy`. Then:
```bash
kubectl get pods -n podinfo
# NAME READY STATUS RESTARTS AGE
# podinfo-xxxxxxxxx-xxxxx 1/1 Running 0 30s
```
---
### 4. Verify the app is working
Port-forward to test locally (inside the VM):
```bash
kubectl port-forward svc/podinfo -n podinfo 9898:80
```
In another terminal (or using curl inside the VM):
```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`.
---
### 5. Make a GitOps change
Let's change the UI color to prove the loop works.
Edit `manifests/apps/podinfo/deployment.yaml` and change:
```yaml
value: "#6C48C5"
```
to any hex color you like, e.g.:
```yaml
value: "#2ecc71"
```
Commit and push:
```bash
git add manifests/apps/podinfo/deployment.yaml
git commit -m "chore: change podinfo UI color"
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.
---
## Expected outcome
```
NAME SYNC STATUS HEALTH STATUS
podinfo Synced Healthy
```
```bash
curl http://localhost:9898 | jq .version
# "6.6.2"
```
---
## Troubleshooting
| 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.

View file

@ -0,0 +1,42 @@
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

View file

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: podinfo

View file

@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: podinfo
namespace: podinfo
spec:
selector:
app: podinfo
ports:
- port: 80
targetPort: 9898
name: http