Add ArgoCD self-management and app-of-apps pattern

- Add argocd CLI to Brewfile
- Create argocd.yaml for ArgoCD self-management (manual sync)
- Create apps.yaml app-of-apps root (auto-sync for Application resources)
- Convert tailscale-operator to kustomize
- Update READMEs with bootstrap steps and ArgoCD CLI commands
- Change all workload Applications to manual sync policy

App-of-apps auto-syncs to pick up new Application manifests, but child
apps require manual sync for actual workload deployments.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Erich Blume 2026-01-19 07:54:04 -08:00
commit 32d5927838
7 changed files with 195 additions and 30 deletions

View file

@ -1,4 +1,5 @@
# CLI tools for blumeops management
brew "argocd" # ArgoCD CLI for GitOps management
brew "bat" # Syntax-highlighted file concatenation
brew "tea" # Gitea/Forgejo CLI for forge.tail8d86e.ts.net
brew "podman" # Container CLI (uses VM on macOS, for building/pushing images)

24
argocd/apps/apps.yaml Normal file
View file

@ -0,0 +1,24 @@
# App-of-apps root Application
# Watches argocd/apps/ and creates/manages all Application resources
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: apps
namespace: argocd
spec:
project: default
source:
repoURL: ssh://forgejo@indri.tail8d86e.ts.net:2200/eblume/blumeops.git
targetRevision: feature/k8s-phase1-kickoff
path: argocd/apps
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
# Auto-sync enabled: new/changed Application manifests appear automatically
# but child apps still require manual sync (they have manual sync policy)

20
argocd/apps/argocd.yaml Normal file
View file

@ -0,0 +1,20 @@
# ArgoCD self-management Application
# After bootstrap, ArgoCD manages its own deployment
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: argocd
namespace: argocd
spec:
project: default
source:
repoURL: ssh://forgejo@indri.tail8d86e.ts.net:2200/eblume/blumeops.git
targetRevision: feature/k8s-phase1-kickoff
path: argocd/manifests/argocd
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
syncOptions:
- CreateNamespace=true
# Manual sync only - no automated sync on git push

View file

@ -7,6 +7,12 @@ metadata:
namespace: argocd
spec:
project: default
# Tailscale operator mutates externalName from "placeholder" to actual proxy service
ignoreDifferences:
- group: ""
kind: Service
jsonPointers:
- /spec/externalName
source:
repoURL: ssh://forgejo@indri.tail8d86e.ts.net:2200/eblume/blumeops.git
targetRevision: feature/k8s-phase1-kickoff
@ -15,8 +21,6 @@ spec:
server: https://kubernetes.default.svc
namespace: tailscale
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
# Manual sync only - no automated sync on git push

View file

@ -1,28 +1,99 @@
# ArgoCD
GitOps continuous delivery for Kubernetes.
GitOps continuous delivery for Kubernetes, with self-management via ArgoCD.
## Installation
## Prerequisites
Uses kustomize with remote base from upstream ArgoCD.
- Tailscale operator deployed (see `argocd/manifests/tailscale-operator/README.md`)
- Deploy key added to forge for SSH access to blumeops repo
## Manual Bootstrap
Bootstrap is required when setting up a new cluster. After bootstrap, ArgoCD manages itself.
```bash
# Create namespace
# 1. Create namespace
kubectl create namespace argocd
# Apply manifests
# 2. Apply ArgoCD manifests via kustomize
kubectl apply -k argocd/manifests/argocd/
# 3. Wait for ArgoCD to be ready
kubectl wait --for=condition=available deployment/argocd-server -n argocd --timeout=300s
# 4. Get initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d && echo
# 5. Login and change password
argocd login argocd.tail8d86e.ts.net --username admin --grpc-web
argocd account update-password
# 6. Apply repo-forge secret for SSH access to forge
PRIV_KEY=$(op read "op://vg6xf6vvfmoh5hqjjhlhbeoaie/csjncynh6htjvnh2l2da65y32q/private key?ssh-format=openssh")$'\n' && \
kubectl create secret generic repo-forge -n argocd \
--from-literal=type=git \
--from-literal=url='ssh://forgejo@indri.tail8d86e.ts.net:2200/eblume/blumeops.git' \
--from-literal=insecure=true \
--from-literal=sshPrivateKey="$PRIV_KEY" && \
kubectl label secret repo-forge -n argocd argocd.argoproj.io/secret-type=repository
# 7. Apply ArgoCD Applications (self-management + app-of-apps)
kubectl apply -f argocd/apps/argocd.yaml
kubectl apply -f argocd/apps/apps.yaml
```
After step 7, ArgoCD manages itself and all applications defined in `argocd/apps/`.
## Access
- URL: https://argocd.tail8d86e.ts.net
- Username: `admin`
- Password: Get from secret (see below)
- Password: Stored in 1Password after initial setup
## ArgoCD CLI Commands
```bash
# Get initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# Check all applications
argocd app list
# Sync a specific application
argocd app sync <app-name>
# Check application status
argocd app get <app-name>
# Hard refresh (clear git cache)
argocd app get <app-name> --hard-refresh
```
## Adding New Applications
1. Create an Application manifest in `argocd/apps/<app-name>.yaml`
2. Commit and push to forge
3. ArgoCD (via app-of-apps) automatically picks it up
Example Application:
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: ssh://forgejo@indri.tail8d86e.ts.net:2200/eblume/blumeops.git
targetRevision: main
path: argocd/manifests/my-app
destination:
server: https://kubernetes.default.svc
namespace: my-app
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
```
## Files
@ -32,10 +103,12 @@ kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.pas
| `kustomization.yaml` | References upstream install.yaml + local customizations |
| `service-tailscale.yaml` | Tailscale Ingress for external access with Let's Encrypt TLS |
| `argocd-cmd-params-cm.yaml` | Patch to disable HTTPS redirect (TLS terminates at Ingress) |
| `repo-forge-secret.yaml.tpl` | Template documenting the forge SSH secret (manual) |
| `README.md` | This file |
## Post-Setup
## Notes
1. Login and change the admin password
2. Store new password in 1Password
3. Configure git repository connection to `github.com/eblume/blumeops`
- **TODO:** Secrets (`repo-forge`) are not managed by ArgoCD and must be applied manually.
Future improvement: integrate with a secrets operator (e.g., External Secrets).
- ArgoCD uses Tailscale Ingress with Let's Encrypt for TLS termination.
- The `--grpc-web` flag is required for CLI access through the Tailscale ingress.

View file

@ -1,8 +1,6 @@
# Tailscale Kubernetes Operator
Manifests for the Tailscale Kubernetes Operator, sourced from Tailscale's official repository.
**Note:** These are currently raw manifests from Tailscale, not yet kustomized. Once kustomized, this directory will include a `kustomization.yaml` and any necessary patches.
Manifests for the Tailscale Kubernetes Operator, managed via ArgoCD.
## Source
@ -18,26 +16,43 @@ Manifests for the Tailscale Kubernetes Operator, sourced from Tailscale's offici
- Services: Write
2. ACL with `tag:k8s-operator` owning `tag:k8s` (so operator can tag resources it creates)
## Deployment
## Manual Bootstrap (Before ArgoCD)
Tailscale operator must be deployed before ArgoCD since ArgoCD uses Tailscale for ingress.
```bash
# 1. Create namespace
kubectl create namespace tailscale
# 2. Apply the OAuth secret (uses 1Password for credential resolution)
# 2. Apply OAuth secret (uses 1Password)
op inject -i argocd/manifests/tailscale-operator/secret.yaml.tpl | kubectl apply -f -
# 3. Apply the operator
kubectl apply -f argocd/manifests/tailscale-operator/operator.yaml
# 4. Apply the ProxyClass (required for CRI-O image compatibility)
kubectl apply -f argocd/manifests/tailscale-operator/proxyclass.yaml
# 3. Apply manifests via kustomize
kubectl apply -k argocd/manifests/tailscale-operator/
```
**Important:** Services using the Tailscale LoadBalancer must reference the ProxyClass:
```yaml
annotations:
tailscale.com/proxy-class: "default"
## Ongoing Management (After ArgoCD)
Once ArgoCD is running, the operator is managed by the `tailscale-operator` ArgoCD Application.
ArgoCD pulls manifests from forge and applies them automatically.
## ArgoCD CLI Commands
```bash
# Check application status
argocd app get tailscale-operator
# Trigger a sync (pull latest from forge and apply)
argocd app sync tailscale-operator
# Preview what would change without applying
argocd app diff tailscale-operator
# View deployment history
argocd app history tailscale-operator
# Hard refresh (clear cache and re-fetch from git)
argocd app get tailscale-operator --hard-refresh
```
## Verification
@ -54,7 +69,22 @@ kubectl logs -n tailscale -l app.kubernetes.io/name=operator
| File | Description |
|------|-------------|
| `kustomization.yaml` | Kustomize configuration for all manifests |
| `operator.yaml` | Operator deployment, CRDs, RBAC (secret removed) |
| `proxyclass.yaml` | ProxyClass with fully-qualified images for CRI-O |
| `secret.yaml.tpl` | 1Password template for OAuth credentials |
| `dnsconfig.yaml` | DNSConfig for cluster-to-tailnet name resolution |
| `egress-forge.yaml` | Egress proxy for accessing forge on indri |
| `secret.yaml.tpl` | 1Password template for OAuth credentials (manual) |
| `README.md` | This file |
## Notes
- **TODO:** The OAuth secret (`operator-oauth`) is not managed by ArgoCD and must be applied
manually. Future improvement: integrate with a secrets operator (e.g., External Secrets).
- Services using the Tailscale LoadBalancer must reference the ProxyClass:
```yaml
annotations:
tailscale.com/proxy-class: "default"
```
- The egress proxy for forge targets `indri.tail8d86e.ts.net` directly (not `forge.tail8d86e.ts.net`)
because Tailscale Serve hostnames are virtual and only work via the Tailscale client.

View file

@ -0,0 +1,13 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: tailscale
resources:
- operator.yaml
- proxyclass.yaml
- dnsconfig.yaml
- egress-forge.yaml
# Note: OAuth secret (operator-oauth) is NOT included here.
# It must be manually applied before deploying - see README.md