From 601a0328b815a89a5ed283beb0d54ed409fd65de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Wa=C5=82achowski?= Date: Tue, 31 Mar 2026 23:37:01 +0200 Subject: [PATCH] cicd: initial kubernetes infrastructure --- RUNBOOK.md | 228 +++++++ cluster-config.yml | 17 + k8s/argocd-apps/drawio.yaml | 23 + k8s/argocd-apps/nextcloud.yaml | 23 + k8s/deploy-infrastructure.sh | 53 ++ k8s/infrastructure/argocd/ingress.yaml | 26 + k8s/infrastructure/argocd/kustomization.yaml | 9 + k8s/infrastructure/argocd/namespace.yaml | 4 + .../cert-manager/cluster-issuer.yaml | 14 + .../cert-manager/kustomization.yaml | 5 + .../coredns/coredns-custom.yaml | 38 ++ k8s/infrastructure/coredns/kustomization.yaml | 5 + k8s/infrastructure/forgejo/deployment.yaml | 55 ++ k8s/infrastructure/forgejo/ingress.yaml | 24 + k8s/infrastructure/forgejo/kustomization.yaml | 10 + k8s/infrastructure/forgejo/namespace.yaml | 4 + k8s/infrastructure/forgejo/pv.yaml | 35 ++ .../forgejo/runner-deployment.yaml | 50 ++ k8s/infrastructure/forgejo/service.yaml | 12 + .../ingress-nginx/ingress-nginx.yaml | 582 ++++++++++++++++++ .../ingress-nginx/kustomization.yaml | 5 + k8s/infrastructure/kustomization.yaml | 10 + .../registry/kustomization.yaml | 9 + .../registry/registry-deployment.yaml | 55 ++ .../registry/registry-ingress.yaml | 27 + .../registry/registry-namespace.yaml | 6 + k8s/infrastructure/registry/registry-pv.yaml | 40 ++ .../registry/registry-service.yaml | 16 + 28 files changed, 1385 insertions(+) create mode 100644 RUNBOOK.md create mode 100644 cluster-config.yml create mode 100644 k8s/argocd-apps/drawio.yaml create mode 100644 k8s/argocd-apps/nextcloud.yaml create mode 100755 k8s/deploy-infrastructure.sh create mode 100644 k8s/infrastructure/argocd/ingress.yaml create mode 100644 k8s/infrastructure/argocd/kustomization.yaml create mode 100644 k8s/infrastructure/argocd/namespace.yaml create mode 100644 k8s/infrastructure/cert-manager/cluster-issuer.yaml create mode 100644 k8s/infrastructure/cert-manager/kustomization.yaml create mode 100644 k8s/infrastructure/coredns/coredns-custom.yaml create mode 100644 k8s/infrastructure/coredns/kustomization.yaml create mode 100644 k8s/infrastructure/forgejo/deployment.yaml create mode 100644 k8s/infrastructure/forgejo/ingress.yaml create mode 100644 k8s/infrastructure/forgejo/kustomization.yaml create mode 100644 k8s/infrastructure/forgejo/namespace.yaml create mode 100644 k8s/infrastructure/forgejo/pv.yaml create mode 100644 k8s/infrastructure/forgejo/runner-deployment.yaml create mode 100644 k8s/infrastructure/forgejo/service.yaml create mode 100644 k8s/infrastructure/ingress-nginx/ingress-nginx.yaml create mode 100644 k8s/infrastructure/ingress-nginx/kustomization.yaml create mode 100644 k8s/infrastructure/kustomization.yaml create mode 100644 k8s/infrastructure/registry/kustomization.yaml create mode 100644 k8s/infrastructure/registry/registry-deployment.yaml create mode 100644 k8s/infrastructure/registry/registry-ingress.yaml create mode 100644 k8s/infrastructure/registry/registry-namespace.yaml create mode 100644 k8s/infrastructure/registry/registry-pv.yaml create mode 100644 k8s/infrastructure/registry/registry-service.yaml diff --git a/RUNBOOK.md b/RUNBOOK.md new file mode 100644 index 0000000..e1a5c3d --- /dev/null +++ b/RUNBOOK.md @@ -0,0 +1,228 @@ +# Szymiserver Infrastructure Runbook + +## Overview + +This server runs a Kubernetes cluster (kind) hosting: + +| Service | URL | Notes | +|---------|-----|-------| +| Forgejo | https://git.szymi.ddns.net | Git + CI/CD | +| ArgoCD | https://argocd.szymi.ddns.net | GitOps deployment | +| Registry | https://registry.szymi.ddns.net | Private Docker registry | +| Nextcloud | https://nextcloud.szymi.ddns.net | File storage | +| Draw.io | https://drawio.szymi.ddns.net | Diagrams | + +All TLS certificates are automatically issued and renewed via Let's Encrypt (cert-manager). + +Apps are deployed via **ArgoCD GitOps** — push to Forgejo → ArgoCD syncs automatically. + +--- + +## Prerequisites + +- Linux machine with Docker installed +- `kind` installed (`go install sigs.k8s.io/kind@latest`) +- `kubectl` installed +- Ports 80, 443, 2222 open on the firewall/router +- DNS A records pointing to server's public IP for all subdomains +- `/media/ssd` mounted (SSD for persistent data) + +--- + +## Fresh Install From Scratch + +### Step 1: Clone the repo + +```bash +git clone https://git.szymi.ddns.net/szymi/szymiserver.git +cd szymiserver +``` + +### Step 2: Create the kind cluster + +```bash +kind create cluster --config cluster-config.yml +``` + +This creates a cluster named `szymicluster` with: +- Port 80, 443 exposed (for ingress HTTP/HTTPS) +- Port 2222 exposed (for Forgejo SSH) +- `/media/ssd` mounted into the kind container (persistent volumes) +- `/var/run/docker.sock` mounted (for Forgejo runner) + +### Step 3: Deploy infrastructure + +```bash +chmod +x k8s/deploy-infrastructure.sh +./k8s/deploy-infrastructure.sh +``` + +**What the script does, in order:** +1. Fixes kind container DNS (forces IPv4 — prevents ImagePullBackOff on servers with broken IPv6) +2. Creates required host directories on `/media/ssd` +3. Applies all infrastructure via kustomize (ingress-nginx, cert-manager, CoreDNS, registry, ArgoCD, Forgejo) +4. Waits for cert-manager to be ready, then applies the ClusterIssuer +5. Waits for ingress-nginx to be ready +6. Restarts CoreDNS to pick up internal DNS config +7. Applies ArgoCD Application definitions (deploys apps via GitOps) + +### Step 4: Wait for pods and certificates + +```bash +# Watch pods come up (takes 2-5 min) +kubectl get pods -A -w + +# Check TLS certificates (takes 1-3 min after pods are up) +kubectl get certificates -A +``` + +All certificates should show `READY = True`. + +### Step 5: Get ArgoCD admin password + +```bash +kubectl -n argocd get secret argocd-initial-admin-secret \ + -o jsonpath="{.data.password}" | base64 -d && echo +``` + +Login at https://argocd.szymi.ddns.net with `admin` / above password. + +--- + +## Manual Steps After Fresh Install + +These cannot be automated and must be done once: + +### Forgejo: Create admin account +1. Open https://git.szymi.ddns.net +2. Complete the setup wizard (first user becomes admin) +3. Create the repositories: `szymiserver`, `nextcloud`, `drawio` + +### Forgejo Runner: Register +The runner pod expects `/media/ssd/forgejo/runner-data/` to contain a registered config. + +```bash +# Get a runner registration token from Forgejo: +# Site Administration → Actions → Runners → Create new Runner + +# Register the runner (exec into the pod): +kubectl exec -it -n forgejo deployment/forgejo-runner -- \ + forgejo-runner register \ + --instance https://git.szymi.ddns.net \ + --token \ + --name szymiserver \ + --no-interactive +``` + +--- + +## Day-to-Day Operations + +### Deploying a new app via ArgoCD + +1. Create Kubernetes manifests in your app repo (see `drawio` or `nextcloud` repos as examples) +2. Add `nextcloud.szymi.ddns.net` to CoreDNS internal hosts: + ```yaml + # k8s/infrastructure/coredns/coredns-custom.yaml + 10.96.0.100 newapp.szymi.ddns.net + ``` +3. Create an ArgoCD Application in `k8s/argocd-apps/newapp.yaml` +4. Push to Forgejo and apply: + ```bash + kubectl apply -f k8s/infrastructure/coredns/coredns-custom.yaml + kubectl rollout restart deployment coredns -n kube-system + kubectl apply -f k8s/argocd-apps/newapp.yaml + ``` + +### After server restart (if pods have ImagePullBackOff) + +Kind container loses its DNS config on restart: + +```bash +docker exec szymicluster-control-plane bash -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf' +kubectl delete pods -n argocd --all +``` + +### Checking overall health + +```bash +# All pods status +kubectl get pods -A | grep -v Running | grep -v Completed + +# Certificate status +kubectl get certificates -A + +# ArgoCD app sync status +kubectl get applications -n argocd +``` + +### Forcing ArgoCD to re-sync + +```bash +kubectl patch application -n argocd \ + --type merge -p '{"metadata":{"annotations":{"argocd.argoproj.io/refresh":"hard"}}}' +``` + +--- + +## Architecture + +``` +Internet + │ + ▼ +Router (ports 80, 443, 2222 → server) + │ + ▼ +Linux Host (/media/ssd for persistent data) + │ + ▼ +kind container (szymicluster-control-plane) + │ + ├── ingress-nginx ← routes all HTTP/HTTPS traffic + ├── cert-manager ← issues Let's Encrypt TLS certs + ├── CoreDNS ← internal DNS (routes *.szymi.ddns.net to ingress) + ├── ArgoCD ← watches Forgejo, deploys apps automatically + ├── Forgejo ← git server + CI/CD runner + ├── Registry ← private Docker image registry + ├── Nextcloud ← deployed via ArgoCD + └── Draw.io ← deployed via ArgoCD +``` + +### Why CoreDNS has internal hosts +cert-manager needs to verify domain ownership via HTTP-01 challenge. Inside the cluster, `*.szymi.ddns.net` would resolve to the external IP, which doesn't route back in. CoreDNS overrides these to point directly to ingress-nginx's fixed ClusterIP (`10.96.0.100`). + +--- + +## File Structure + +``` +szymiserver/ +├── cluster-config.yml # kind cluster definition (run once) +├── RUNBOOK.md # this file +├── k8s/ +│ ├── deploy-infrastructure.sh # main deployment script +│ ├── infrastructure/ +│ │ ├── kustomization.yaml # applies all infrastructure +│ │ ├── coredns/ # internal DNS overrides +│ │ ├── ingress-nginx/ # ingress controller +│ │ ├── cert-manager/ # TLS certificates +│ │ ├── argocd/ # GitOps controller +│ │ ├── forgejo/ # git server + runner +│ │ └── registry/ # private Docker registry +│ └── argocd-apps/ +│ ├── drawio.yaml # ArgoCD app for draw.io +│ └── nextcloud.yaml # ArgoCD app for Nextcloud +``` + +--- + +## Persistent Data (on host at /media/ssd) + +| Path | Used by | +|------|---------| +| `/media/ssd/forgejo/forgejo-data` | Forgejo repos, config, DB | +| `/media/ssd/forgejo/runner-data` | Runner registration + config | +| `/media/ssd/registry` | Docker image layers | +| `/media/ssd/nextcloud` | Nextcloud files | +| `/media/ssd/mariadb` | Nextcloud database | diff --git a/cluster-config.yml b/cluster-config.yml new file mode 100644 index 0000000..dd3127a --- /dev/null +++ b/cluster-config.yml @@ -0,0 +1,17 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +name: szymicluster +nodes: + - role: control-plane + extraPortMappings: + - containerPort: 80 + hostPort: 80 + - containerPort: 443 + hostPort: 443 + - containerPort: 2222 + hostPort: 2222 + extraMounts: + - hostPath: /media/ssd + containerPath: /media/ssd + - hostPath: /var/run/docker.sock + containerPath: /var/run/docker.sock diff --git a/k8s/argocd-apps/drawio.yaml b/k8s/argocd-apps/drawio.yaml new file mode 100644 index 0000000..f3124e8 --- /dev/null +++ b/k8s/argocd-apps/drawio.yaml @@ -0,0 +1,23 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: drawio + namespace: argocd +spec: + project: default + + source: + repoURL: https://git.szymi.ddns.net/szymi/drawio.git + targetRevision: master + path: deployment + + destination: + server: https://kubernetes.default.svc + namespace: drawio + + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s/argocd-apps/nextcloud.yaml b/k8s/argocd-apps/nextcloud.yaml new file mode 100644 index 0000000..a5bb6b4 --- /dev/null +++ b/k8s/argocd-apps/nextcloud.yaml @@ -0,0 +1,23 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: nextcloud + namespace: argocd +spec: + project: default + + source: + repoURL: https://git.szymi.ddns.net/szymi/nextcloud.git + targetRevision: master + path: deployment + + destination: + server: https://kubernetes.default.svc + namespace: nextcloud + + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s/deploy-infrastructure.sh b/k8s/deploy-infrastructure.sh new file mode 100755 index 0000000..8fd3955 --- /dev/null +++ b/k8s/deploy-infrastructure.sh @@ -0,0 +1,53 @@ +#!/bin/bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR/.." + +echo "=== Deploying Infrastructure ===" + +echo "0. Fixing kind container DNS (prevents ImagePullBackOff on IPv6-only DNS)..." +docker exec szymicluster-control-plane bash -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf' + +echo "1. Creating required host data directories..." +mkdir -p /media/ssd/forgejo/forgejo-data +mkdir -p /media/ssd/forgejo/runner-data +mkdir -p /media/ssd/registry + +echo "2. Applying main infrastructure (coredns, ingress-nginx, cert-manager, registry, argocd, forgejo)..." +kubectl apply -k k8s/infrastructure/ + +echo "3. Waiting for cert-manager to be ready..." +kubectl wait --namespace cert-manager \ + --for=condition=ready pod \ + --selector=app.kubernetes.io/instance=cert-manager \ + --timeout=120s + +echo "4. Applying ClusterIssuer for Let's Encrypt..." +kubectl apply -f k8s/infrastructure/cert-manager/cluster-issuer.yaml + +echo "5. Waiting for ingress-nginx to be ready..." +kubectl wait --namespace ingress-nginx \ + --for=condition=ready pod \ + --selector=app.kubernetes.io/component=controller \ + --timeout=120s + +echo "6. Restarting CoreDNS to pick up internal DNS config..." +kubectl rollout restart deployment coredns -n kube-system +kubectl rollout status deployment coredns -n kube-system --timeout=60s + +echo "7. Applying ArgoCD applications..." +kubectl apply -f k8s/argocd-apps/ + +echo "=== Infrastructure deployment complete ===" +echo "" +echo "Checking certificate status (may take a few minutes to issue):" +kubectl get certificates -A +echo "" +echo "To check challenge status: kubectl get challenges -A" +echo "To check pods: kubectl get pods -A" +echo "" +echo "Services accessible at:" +echo " - https://git.szymi.ddns.net (Forgejo)" +echo " - https://argocd.szymi.ddns.net (ArgoCD)" +echo " - https://registry.szymi.ddns.net (Docker Registry)" diff --git a/k8s/infrastructure/argocd/ingress.yaml b/k8s/infrastructure/argocd/ingress.yaml new file mode 100644 index 0000000..1aea0ba --- /dev/null +++ b/k8s/infrastructure/argocd/ingress.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: argocd-server + namespace: argocd + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/ssl-passthrough: "true" + nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" +spec: + ingressClassName: nginx + tls: + - hosts: + - argocd.szymi.ddns.net + secretName: argocd-tls + rules: + - host: argocd.szymi.ddns.net + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: argocd-server + port: + name: https diff --git a/k8s/infrastructure/argocd/kustomization.yaml b/k8s/infrastructure/argocd/kustomization.yaml new file mode 100644 index 0000000..06c9c95 --- /dev/null +++ b/k8s/infrastructure/argocd/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: argocd + +resources: + - namespace.yaml + - https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml + - ingress.yaml diff --git a/k8s/infrastructure/argocd/namespace.yaml b/k8s/infrastructure/argocd/namespace.yaml new file mode 100644 index 0000000..a040f2b --- /dev/null +++ b/k8s/infrastructure/argocd/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: argocd diff --git a/k8s/infrastructure/cert-manager/cluster-issuer.yaml b/k8s/infrastructure/cert-manager/cluster-issuer.yaml new file mode 100644 index 0000000..a841571 --- /dev/null +++ b/k8s/infrastructure/cert-manager/cluster-issuer.yaml @@ -0,0 +1,14 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: admin@szymi.ddns.net + privateKeySecretRef: + name: letsencrypt-prod-account-key + solvers: + - http01: + ingress: + class: nginx diff --git a/k8s/infrastructure/cert-manager/kustomization.yaml b/k8s/infrastructure/cert-manager/kustomization.yaml new file mode 100644 index 0000000..c131bed --- /dev/null +++ b/k8s/infrastructure/cert-manager/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml diff --git a/k8s/infrastructure/coredns/coredns-custom.yaml b/k8s/infrastructure/coredns/coredns-custom.yaml new file mode 100644 index 0000000..dd738a2 --- /dev/null +++ b/k8s/infrastructure/coredns/coredns-custom.yaml @@ -0,0 +1,38 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: coredns + namespace: kube-system +data: + Corefile: | + .:53 { + errors + health { + lameduck 5s + } + ready + hosts { + 10.96.0.100 git.szymi.ddns.net + 10.96.0.100 argocd.szymi.ddns.net + 10.96.0.100 registry.szymi.ddns.net + 10.96.0.100 drawio.szymi.ddns.net + 10.96.0.100 nextcloud.szymi.ddns.net + fallthrough + } + kubernetes cluster.local in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + ttl 30 + } + prometheus :9153 + forward . /etc/resolv.conf { + max_concurrent 1000 + } + cache 30 { + disable success cluster.local + disable denial cluster.local + } + loop + reload + loadbalance + } diff --git a/k8s/infrastructure/coredns/kustomization.yaml b/k8s/infrastructure/coredns/kustomization.yaml new file mode 100644 index 0000000..fa4194c --- /dev/null +++ b/k8s/infrastructure/coredns/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - coredns-custom.yaml diff --git a/k8s/infrastructure/forgejo/deployment.yaml b/k8s/infrastructure/forgejo/deployment.yaml new file mode 100644 index 0000000..8b5deb1 --- /dev/null +++ b/k8s/infrastructure/forgejo/deployment.yaml @@ -0,0 +1,55 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: forgejo + namespace: forgejo +spec: + replicas: 1 + selector: + matchLabels: + app: forgejo + template: + metadata: + labels: + app: forgejo + spec: + containers: + - name: forgejo + image: codeberg.org/forgejo/forgejo:11 + ports: + - containerPort: 3000 + name: http + - containerPort: 22 + name: ssh + hostPort: 2222 + volumeMounts: + - name: forgejo-data + mountPath: /data + env: + - name: USER_UID + value: "1000" + - name: USER_GID + value: "1000" + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "1000m" + livenessProbe: + httpGet: + path: /api/healthz + port: 3000 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /api/healthz + port: 3000 + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: forgejo-data + persistentVolumeClaim: + claimName: forgejo-pvc diff --git a/k8s/infrastructure/forgejo/ingress.yaml b/k8s/infrastructure/forgejo/ingress.yaml new file mode 100644 index 0000000..49652e3 --- /dev/null +++ b/k8s/infrastructure/forgejo/ingress.yaml @@ -0,0 +1,24 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: forgejo + namespace: forgejo + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod +spec: + ingressClassName: nginx + tls: + - hosts: + - git.szymi.ddns.net + secretName: forgejo-tls + rules: + - host: git.szymi.ddns.net + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: forgejo + port: + number: 3000 diff --git a/k8s/infrastructure/forgejo/kustomization.yaml b/k8s/infrastructure/forgejo/kustomization.yaml new file mode 100644 index 0000000..ef8e736 --- /dev/null +++ b/k8s/infrastructure/forgejo/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - namespace.yaml + - pv.yaml + - deployment.yaml + - service.yaml + - ingress.yaml + - runner-deployment.yaml diff --git a/k8s/infrastructure/forgejo/namespace.yaml b/k8s/infrastructure/forgejo/namespace.yaml new file mode 100644 index 0000000..6521f89 --- /dev/null +++ b/k8s/infrastructure/forgejo/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: forgejo diff --git a/k8s/infrastructure/forgejo/pv.yaml b/k8s/infrastructure/forgejo/pv.yaml new file mode 100644 index 0000000..b31cead --- /dev/null +++ b/k8s/infrastructure/forgejo/pv.yaml @@ -0,0 +1,35 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: forgejo-pv +spec: + capacity: + storage: 50Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: local-storage + local: + path: /media/ssd/forgejo/forgejo-data + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - szymicluster-control-plane +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: forgejo-pvc + namespace: forgejo +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-storage + resources: + requests: + storage: 50Gi + volumeName: forgejo-pv diff --git a/k8s/infrastructure/forgejo/runner-deployment.yaml b/k8s/infrastructure/forgejo/runner-deployment.yaml new file mode 100644 index 0000000..38c3cc2 --- /dev/null +++ b/k8s/infrastructure/forgejo/runner-deployment.yaml @@ -0,0 +1,50 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: forgejo-runner + namespace: forgejo +spec: + replicas: 1 + selector: + matchLabels: + app: forgejo-runner + template: + metadata: + labels: + app: forgejo-runner + spec: + containers: + - name: runner + image: code.forgejo.org/forgejo/runner:6 + command: + - forgejo-runner + - daemon + - --config + - /data/config.yaml + env: + - name: ACT_RUNNER_APPEND_RUN_ARGS + value: "-v /var/run/docker.sock:/var/run/docker.sock" + volumeMounts: + - name: runner-data + mountPath: /data + - name: docker-sock + mountPath: /var/run/docker.sock + securityContext: + runAsUser: 0 + privileged: true + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" + volumes: + - name: runner-data + hostPath: + path: /media/ssd/forgejo/runner-data + type: Directory + - name: docker-sock + hostPath: + path: /var/run/docker.sock + type: Socket diff --git a/k8s/infrastructure/forgejo/service.yaml b/k8s/infrastructure/forgejo/service.yaml new file mode 100644 index 0000000..7d834c6 --- /dev/null +++ b/k8s/infrastructure/forgejo/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: forgejo + namespace: forgejo +spec: + selector: + app: forgejo + ports: + - name: http + port: 3000 + targetPort: 3000 diff --git a/k8s/infrastructure/ingress-nginx/ingress-nginx.yaml b/k8s/infrastructure/ingress-nginx/ingress-nginx.yaml new file mode 100644 index 0000000..02adfcd --- /dev/null +++ b/k8s/infrastructure/ingress-nginx/ingress-nginx.yaml @@ -0,0 +1,582 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: ingress-nginx +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx + namespace: ingress-nginx +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-admission + namespace: ingress-nginx +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx + namespace: ingress-nginx +rules: +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get +- apiGroups: + - "" + resources: + - configmaps + - pods + - secrets + - endpoints + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update +- apiGroups: + - networking.k8s.io + resources: + - ingressclasses + verbs: + - get + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + resourceNames: + - ingress-nginx-leader + verbs: + - get + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-admission + namespace: ingress-nginx +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx +rules: +- apiGroups: + - "" + resources: + - configmaps + - endpoints + - nodes + - pods + - secrets + - namespaces + verbs: + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - list + - watch +- apiGroups: + - "" + resources: + - nodes + verbs: + - get +- apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update +- apiGroups: + - networking.k8s.io + resources: + - ingressclasses + verbs: + - get + - list + - watch +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-admission +rules: +- apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + verbs: + - get + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx + namespace: ingress-nginx +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: ingress-nginx +subjects: +- kind: ServiceAccount + name: ingress-nginx + namespace: ingress-nginx +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-admission + namespace: ingress-nginx +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: ingress-nginx-admission +subjects: +- kind: ServiceAccount + name: ingress-nginx-admission + namespace: ingress-nginx +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: ingress-nginx +subjects: +- kind: ServiceAccount + name: ingress-nginx + namespace: ingress-nginx +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-admission +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: ingress-nginx-admission +subjects: +- kind: ServiceAccount + name: ingress-nginx-admission + namespace: ingress-nginx +--- +apiVersion: v1 +data: + allow-snippet-annotations: "false" +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-controller + namespace: ingress-nginx +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-controller + namespace: ingress-nginx +spec: + clusterIP: 10.96.0.100 + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - appProtocol: http + name: http + port: 80 + protocol: TCP + targetPort: http + - appProtocol: https + name: https + port: 443 + protocol: TCP + targetPort: https + selector: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + type: NodePort +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-controller-admission + namespace: ingress-nginx +spec: + ports: + - appProtocol: https + name: https-webhook + port: 443 + targetPort: webhook + selector: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + type: ClusterIP +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-controller + namespace: ingress-nginx +spec: + minReadySeconds: 0 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + template: + metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + spec: + containers: + - args: + - /nginx-ingress-controller + - --election-id=ingress-nginx-leader + - --controller-class=k8s.io/ingress-nginx + - --ingress-class=nginx + - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller + - --validating-webhook=:8443 + - --validating-webhook-certificate=/usr/local/certificates/cert + - --validating-webhook-key=/usr/local/certificates/key + - --watch-ingress-without-class=true + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: LD_PRELOAD + value: /usr/local/lib/libmimalloc.so + image: registry.k8s.io/ingress-nginx/controller:v1.9.5 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - /wait-shutdown + livenessProbe: + failureThreshold: 5 + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: controller + ports: + - containerPort: 80 + hostPort: 80 + name: http + protocol: TCP + - containerPort: 443 + hostPort: 443 + name: https + protocol: TCP + - containerPort: 8443 + name: webhook + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: + requests: + cpu: 100m + memory: 90Mi + securityContext: + allowPrivilegeEscalation: true + capabilities: + add: + - NET_BIND_SERVICE + drop: + - ALL + runAsUser: 101 + volumeMounts: + - mountPath: /usr/local/certificates/ + name: webhook-cert + readOnly: true + dnsPolicy: ClusterFirst + nodeSelector: + kubernetes.io/os: linux + serviceAccountName: ingress-nginx + terminationGracePeriodSeconds: 300 + volumes: + - name: webhook-cert + secret: + secretName: ingress-nginx-admission +--- +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: nginx +spec: + controller: k8s.io/ingress-nginx +--- +apiVersion: batch/v1 +kind: Job +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-admission-create + namespace: ingress-nginx +spec: + template: + metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-admission-create + spec: + containers: + - args: + - create + - --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc + - --namespace=$(POD_NAMESPACE) + - --secret-name=ingress-nginx-admission + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0 + imagePullPolicy: IfNotPresent + name: create + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 2000 + nodeSelector: + kubernetes.io/os: linux + restartPolicy: OnFailure + securityContext: + fsGroup: 2000 + runAsNonRoot: true + runAsUser: 2000 + serviceAccountName: ingress-nginx-admission +--- +apiVersion: batch/v1 +kind: Job +metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-admission-patch + namespace: ingress-nginx +spec: + template: + metadata: + labels: + app.kubernetes.io/component: admission-webhook + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/name: ingress-nginx + name: ingress-nginx-admission-patch + spec: + containers: + - args: + - patch + - --webhook-name=ingress-nginx-admission + - --namespace=$(POD_NAMESPACE) + - --patch-mutating=false + - --secret-name=ingress-nginx-admission + - --patch-failure-policy=Fail + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-8b53cabe0 + imagePullPolicy: IfNotPresent + name: patch + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 2000 + nodeSelector: + kubernetes.io/os: linux + restartPolicy: OnFailure + securityContext: + fsGroup: 2000 + runAsNonRoot: true + runAsUser: 2000 + serviceAccountName: ingress-nginx-admission diff --git a/k8s/infrastructure/ingress-nginx/kustomization.yaml b/k8s/infrastructure/ingress-nginx/kustomization.yaml new file mode 100644 index 0000000..e745795 --- /dev/null +++ b/k8s/infrastructure/ingress-nginx/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ingress-nginx.yaml diff --git a/k8s/infrastructure/kustomization.yaml b/k8s/infrastructure/kustomization.yaml new file mode 100644 index 0000000..2f0f0a2 --- /dev/null +++ b/k8s/infrastructure/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - coredns/ + - ingress-nginx/ + - cert-manager/ + - registry/ + - argocd/ + - forgejo/ diff --git a/k8s/infrastructure/registry/kustomization.yaml b/k8s/infrastructure/registry/kustomization.yaml new file mode 100644 index 0000000..ccdd0d9 --- /dev/null +++ b/k8s/infrastructure/registry/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - registry-namespace.yaml + - registry-pv.yaml + - registry-deployment.yaml + - registry-service.yaml + - registry-ingress.yaml diff --git a/k8s/infrastructure/registry/registry-deployment.yaml b/k8s/infrastructure/registry/registry-deployment.yaml new file mode 100644 index 0000000..6560990 --- /dev/null +++ b/k8s/infrastructure/registry/registry-deployment.yaml @@ -0,0 +1,55 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: registry + namespace: registry + labels: + app: registry +spec: + replicas: 1 + selector: + matchLabels: + app: registry + template: + metadata: + labels: + app: registry + spec: + containers: + - name: registry + image: registry:3.0 + ports: + - containerPort: 5000 + name: registry + protocol: TCP + env: + - name: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY + value: /var/lib/registry + - name: REGISTRY_STORAGE_DELETE_ENABLED + value: "true" + volumeMounts: + - name: registry-data + mountPath: /var/lib/registry + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: / + port: 5000 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: 5000 + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: registry-data + persistentVolumeClaim: + claimName: registry-pvc diff --git a/k8s/infrastructure/registry/registry-ingress.yaml b/k8s/infrastructure/registry/registry-ingress.yaml new file mode 100644 index 0000000..438d975 --- /dev/null +++ b/k8s/infrastructure/registry/registry-ingress.yaml @@ -0,0 +1,27 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: registry + namespace: registry + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/proxy-body-size: "0" + nginx.ingress.kubernetes.io/proxy-read-timeout: "600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "600" +spec: + ingressClassName: nginx + tls: + - hosts: + - registry.szymi.ddns.net + secretName: registry-tls + rules: + - host: registry.szymi.ddns.net + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: registry + port: + number: 5000 diff --git a/k8s/infrastructure/registry/registry-namespace.yaml b/k8s/infrastructure/registry/registry-namespace.yaml new file mode 100644 index 0000000..257e5a7 --- /dev/null +++ b/k8s/infrastructure/registry/registry-namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: registry + labels: + name: registry diff --git a/k8s/infrastructure/registry/registry-pv.yaml b/k8s/infrastructure/registry/registry-pv.yaml new file mode 100644 index 0000000..10cf399 --- /dev/null +++ b/k8s/infrastructure/registry/registry-pv.yaml @@ -0,0 +1,40 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: registry-pv + labels: + app: registry +spec: + capacity: + storage: 100Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: local-storage + hostPath: + path: /media/ssd/image-registry + type: DirectoryOrCreate + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - szymicluster-control-plane +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: registry-pvc + namespace: registry +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Gi + storageClassName: local-storage + selector: + matchLabels: + app: registry diff --git a/k8s/infrastructure/registry/registry-service.yaml b/k8s/infrastructure/registry/registry-service.yaml new file mode 100644 index 0000000..b5534df --- /dev/null +++ b/k8s/infrastructure/registry/registry-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: registry + namespace: registry + labels: + app: registry +spec: + type: ClusterIP + ports: + - port: 5000 + targetPort: 5000 + protocol: TCP + name: registry + selector: + app: registry