Application Deployment Guide¶
Dieser Guide beschreibt den End-to-End-Workflow für Anwendungsentwickler zum Deployment von Anwendungen auf der ayedo Software Delivery Plattform mittels ohMyHelm, Harbor und ArgoCD.
Zielgruppe
Dieser Guide richtet sich an Anwendungsentwickler, die ihre Software auf der ayedo SDP deployen möchten.
Überblick¶
Der Deployment-Workflow umfasst folgende Schritte:
- GitLab für Kollaboration - Source Code Management, Merge Requests, Code Reviews
- Anwendung mit ohMyHelm paketieren - Helm Chart mit Guardrails-Compliance erstellen
- GitLab CI/CD Pipeline - Automatisierte Tests, Image-Builds (Kaniko/Buildah), Chart-Packaging
- Secrets Management - Platform Admin stellt PostgreSQL bereit, Credentials in Vault
- External Secrets Operator - Automatischer Secrets-Sync von Vault zu Kubernetes
- ArgoCD Application - GitOps-basiertes Deployment auf Downstream-Cluster
- Deployment überwachen - Application-Status in ArgoCD und Grafana
graph TB
subgraph "Development"
A[GitLab Repository] --> B[Merge Request]
B --> C[Code Review]
C --> D[GitLab CI/CD Pipeline]
end
subgraph "CI/CD Pipeline"
D --> E[Tests ausführen]
E --> F[Kaniko: Build Image]
F --> G[Push zu Harbor]
D --> H[Package Helm Chart]
H --> G
end
subgraph "Platform Admin"
I[Polycrate: Deploy CloudNativePG] --> J[PostgreSQL Cluster]
J --> K[Credentials in Vault]
end
subgraph "Application Deployment"
G --> L[ArgoCD Application]
K --> M[External Secrets Operator]
M --> N[Kubernetes Secrets]
L --> N
N --> O[Application Pod]
end
subgraph "Monitoring"
O --> P[Grafana Dashboards]
end Voraussetzungen¶
Zugriff auf Platform-Komponenten¶
Stellen Sie sicher, dass Sie Zugriff auf folgende Komponenten haben:
- GitLab: Source Code Management und CI/CD
- Harbor: Container Registry für Images und Helm Charts
- HashiCorp Vault: Secrets Management für Application-Konfiguration
- ArgoCD: GitOps-basierte Application Delivery
- Kubernetes Cluster: Ziel-Cluster für Deployments
Zugriffsrechte anfordern:
Kontaktieren Sie Ihren Platform Administrator, um Zugriff zu erhalten. Siehe Access Control.
Lokale Tools¶
Installieren Sie folgende Tools lokal:
# Docker (für Image-Builds)
docker --version
# Helm (für Chart-Management)
helm version
# kubectl (für Cluster-Zugriff)
kubectl version --client
# argocd CLI (für ArgoCD-Interaktion)
argocd version --client
# Optional: ohMyHelm CLI
npm install -g @ayedo/ohmyhelm
Schritt 0: GitLab - Kollaborative Entwicklung¶
0.1 GitLab Repository Setup¶
GitLab ist die zentrale Plattform für Source Code Management, Code Reviews und CI/CD auf der ayedo SDP.
Neues Projekt erstellen¶
- Via GitLab UI:
- Navigate zu Projects → New project
- Project name:
my-application - Visibility Level:
PrivateoderInternal -
Initialize repository with a README: ✓
-
Via Git CLI:
# Lokales Repository initialisieren
git init my-application
cd my-application
# README erstellen
echo "# My Application" > README.md
git add README.md
git commit -m "Initial commit"
# Remote hinzufügen
git remote add origin https://gitlab.example.com/my-team/my-application.git
# Pushen
git push -u origin main
Repository-Struktur¶
Empfohlene Struktur für ein Application-Repository:
my-application/
├── .gitlab-ci.yml # CI/CD Pipeline
├── Dockerfile # Container Image Definition
├── README.md
├── helm/ # Helm Chart
│ ├── Chart.yaml
│ ├── values.yaml
│ ├── values-staging.yaml
│ ├── values-production.yaml
│ └── templates/
├── src/ # Application Source Code
│ ├── server.js
│ └── ...
├── tests/ # Unit & Integration Tests
│ └── ...
└── docs/ # Application Documentation
0.2 Kollaborativer Entwicklungs-Workflow¶
Feature Branch Workflow¶
# Neuen Feature Branch erstellen
git checkout -b feature/add-user-authentication
# Änderungen vornehmen
# ... code ...
# Commit
git add .
git commit -m "feat: add user authentication with JWT"
# Push zu GitLab
git push origin feature/add-user-authentication
Merge Request erstellen¶
- Via GitLab UI:
- Navigate zu Merge Requests → New merge request
- Source branch:
feature/add-user-authentication - Target branch:
main - Title:
feat: Add user authentication - Description: Beschreiben Sie die Änderungen
- Assignee: Team-Mitglied für Code Review
-
Create merge request
-
Via Git CLI mit GitLab CLI:
# GitLab CLI installieren (falls nicht vorhanden)
# brew install glab # macOS
# apt install glab # Debian/Ubuntu
# Merge Request erstellen
glab mr create \
--title "feat: Add user authentication" \
--description "Implements JWT-based authentication" \
--assignee @team-lead \
--label "feature,needs-review"
Code Review¶
Best Practices für Code Reviews:
- Reviewer prüfen:
- Code-Qualität und Readability
- Einhaltung von Team-Standards
- Security-Aspekte (keine Secrets im Code!)
- Test-Coverage
-
Guardrails-Compliance
-
Kommentare hinzufügen:
- Konstruktives Feedback
- Konkrete Verbesserungsvorschläge
-
Fragen bei Unklarheiten
-
Approval geben:
- Approve → Merge Request kann gemergt werden
- Request changes → Anpassungen erforderlich
Merge in Main Branch¶
# Nach Approval: Merge via GitLab UI
# Oder via CLI:
glab mr merge
# Pipeline läuft automatisch nach Merge
0.3 GitLab CI/CD Pipeline¶
Die .gitlab-ci.yml definiert die automatisierte CI/CD-Pipeline.
Pipeline-Stages¶
# .gitlab-ci.yml
stages:
- test # Unit & Integration Tests
- build # Docker Image & Helm Chart Build
- security # Security Scans
- deploy # Deploy zu Staging/Production
variables:
# Harbor Registry
HARBOR_REGISTRY: harbor.example.com
HARBOR_PROJECT: my-team
IMAGE_NAME: $HARBOR_REGISTRY/$HARBOR_PROJECT/$CI_PROJECT_NAME
# Semantic Versioning
VERSION: "1.0.$CI_PIPELINE_ID"
IMAGE_TAG: $CI_COMMIT_SHORT_SHA
# Kaniko Cache
KANIKO_CACHE_REPO: $HARBOR_REGISTRY/$HARBOR_PROJECT/cache
Stage 1: Tests¶
unit-tests:
stage: test
image: node:18
before_script:
- npm ci
script:
- npm run test:unit
- npm run test:coverage
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
integration-tests:
stage: test
image: node:18
services:
- postgres:14
variables:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
DATABASE_URL: postgresql://testuser:testpass@postgres:5432/testdb
before_script:
- npm ci
script:
- npm run test:integration
lint:
stage: test
image: node:18
before_script:
- npm ci
script:
- npm run lint
- npm run format:check
Stage 2: Build mit Kaniko¶
Kaniko ist ein Tool zum Bauen von Container-Images ohne Docker Daemon - perfekt für Kubernetes-native CI/CD.
build-image-kaniko:
stage: build
image:
name: gcr.io/kaniko-project/executor:v1.23.0-debug
entrypoint: [""]
before_script:
# Harbor Login für Kaniko
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$HARBOR_REGISTRY\":{\"auth\":\"$(echo -n $HARBOR_USERNAME:$HARBOR_PASSWORD | base64)\"}}}" > /kaniko/.docker/config.json
script:
# Image bauen und pushen mit Kaniko
- /kaniko/executor
--context $CI_PROJECT_DIR
--dockerfile $CI_PROJECT_DIR/Dockerfile
--destination $IMAGE_NAME:$IMAGE_TAG
--destination $IMAGE_NAME:latest
--cache=true
--cache-repo=$KANIKO_CACHE_REPO
--snapshot-mode=redo
--compressed-caching=false
--use-new-run
only:
- main
- develop
Alternative: Buildah
Buildah ist eine weitere Option für rootless Container-Builds:
build-image-buildah:
stage: build
image: quay.io/buildah/stable:latest
before_script:
# Harbor Login
- buildah login -u $HARBOR_USERNAME -p $HARBOR_PASSWORD $HARBOR_REGISTRY
script:
# Image bauen
- buildah bud -t $IMAGE_NAME:$IMAGE_TAG -f Dockerfile .
# Tags erstellen
- buildah tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:latest
# Images pushen
- buildah push $IMAGE_NAME:$IMAGE_TAG
- buildah push $IMAGE_NAME:latest
only:
- main
- develop
Kaniko vs. Buildah
- Kaniko: Purpose-built für Kubernetes, exzellenter Layer-Caching
- Buildah: Mehr Features, kompatibel mit Dockerfile und Buildah-Script-Syntax
- Beide sind rootless und daemonless - ideal für sichere CI/CD
Stage 2: Helm Chart Packaging¶
package-helm-chart:
stage: build
image: alpine/helm:latest
before_script:
# Helm Registry Login
- helm registry login $HARBOR_REGISTRY -u $HARBOR_USERNAME -p $HARBOR_PASSWORD
script:
# Chart Version aktualisieren
- sed -i "s/^version:.*/version: $VERSION/" helm/Chart.yaml
- sed -i "s/^appVersion:.*/appVersion: $IMAGE_TAG/" helm/Chart.yaml
# Chart-Dependencies aktualisieren
- helm dependency update helm/
# Chart paketieren
- helm package helm/ --destination .
# Chart zu Harbor pushen
- helm push $CI_PROJECT_NAME-$VERSION.tgz oci://$HARBOR_REGISTRY/$HARBOR_PROJECT
artifacts:
paths:
- "*.tgz"
expire_in: 1 week
only:
- main
- develop
Stage 3: Security Scans¶
trivy-image-scan:
stage: security
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
# Image auf Vulnerabilities scannen
- trivy image
--severity HIGH,CRITICAL
--exit-code 0
--no-progress
$IMAGE_NAME:$IMAGE_TAG
allow_failure: true
only:
- main
- develop
# Harbor führt automatisch einen zusätzlichen Scan durch
Stage 4: Deployment¶
deploy-staging:
stage: deploy
image: argoproj/argocd:latest
before_script:
# ArgoCD Login
- argocd login argocd.example.com --auth-token $ARGOCD_TOKEN --insecure
script:
# ArgoCD Application aktualisieren
- |
argocd app set my-application-staging \
--revision $VERSION \
--helm-set ohmyhelm.chart.container.image=$IMAGE_TAG
# Sync triggern
- argocd app sync my-application-staging --prune
# Auf Healthy warten
- argocd app wait my-application-staging --health --timeout 300
environment:
name: staging
url: https://my-app-staging.example.com
only:
- develop
deploy-production:
stage: deploy
image: argoproj/argocd:latest
before_script:
- argocd login argocd.example.com --auth-token $ARGOCD_TOKEN --insecure
script:
- |
argocd app set my-application-production \
--revision $VERSION \
--helm-set ohmyhelm.chart.container.image=$IMAGE_TAG
- argocd app sync my-application-production --prune
- argocd app wait my-application-production --health --timeout 600
environment:
name: production
url: https://my-app.example.com
only:
- main
when: manual # Manuelles Approval für Production
0.4 GitLab CI/CD Variables¶
Konfigurieren Sie CI/CD-Variables in GitLab:
- Navigate zu Settings → CI/CD → Variables
- Fügen Sie folgende Variables hinzu:
| Variable | Value | Protected | Masked |
|---|---|---|---|
HARBOR_USERNAME | <your-username> | ✓ | ✓ |
HARBOR_PASSWORD | <harbor-cli-secret> | ✓ | ✓ |
ARGOCD_TOKEN | <argocd-token> | ✓ | ✓ |
ArgoCD Token generieren:
# Login
argocd login argocd.example.com --sso
# Token erstellen (24h gültig, für CI/CD besser: 90d)
argocd account generate-token --account ci-pipeline --expires-in 90d
0.5 Pipeline-Monitoring¶
Überwachen Sie Pipeline-Runs:
# Via GitLab UI:
# Project → CI/CD → Pipelines
# Via GitLab CLI:
glab ci view
# Pipeline-Logs anzeigen
glab ci trace
# Pipeline-Status
glab ci status
Schritt 1: Anwendung mit ohMyHelm paketieren¶
ohMyHelm ist ein Helm Helper-Framework, das automatisch Guardrails-compliant Deployments erstellt.
1.1 Neues ohMyHelm Chart erstellen¶
# Chart-Verzeichnis erstellen
mkdir my-application
cd my-application
# Chart.yaml erstellen
cat <<EOF > Chart.yaml
apiVersion: v2
name: my-application
description: My Application on ayedo SDP
type: application
version: 1.0.0
appVersion: "1.0.0"
dependencies:
- name: ohmyhelm
version: "1.13.0"
repository: "https://gitlab.com/ayedocloudsolutions/ohmyhelm"
EOF
# Dependencies downloaden
helm dependency update
ohMyHelm Repository
ohMyHelm wird als Git-Repository hosted. Die neueste Version finden Sie unter: https://gitlab.com/ayedocloudsolutions/ohmyhelm
1.2 values.yaml konfigurieren¶
Erstellen Sie eine values.yaml mit ohMyHelm-Konfiguration:
# values.yaml
ohmyhelm:
chart:
enabled: true
fullnameOverride: "my-application"
replicaCount: 2 # Minimum 2 für Production (Guardrail)
# Container Configuration
container:
image: harbor.example.com/my-team/my-application:1.0.0 # Niemals 'latest' (Guardrail)
ports:
- name: http
containerPort: 8080
protocol: TCP
imageConfig:
pullPolicy: IfNotPresent
# Security Context (Guardrail: runAsNonRoot required)
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
# Resource Requests (Guardrail: requests required)
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
# Liveness & Readiness Probes
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 3
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
# Environment Variables aus ExternalSecret
envFrom:
- secretRef:
name: my-application-secrets
# Service Configuration
service:
enabled: true
type: ClusterIP # Guardrail: LoadBalancer nicht erlaubt
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
# Ingress Configuration
ingress:
enabled: true
ingressClassName: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: my-app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: my-app-tls
hosts:
- my-app.example.com
# PodDisruptionBudget (Guardrail: empfohlen für HA)
pdb:
enabled: true
minAvailable: 1
# Horizontal Pod Autoscaler (optional)
hpa:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
# ServiceMonitor für Prometheus (optional)
serviceMonitor:
enabled: true
endpoints:
- port: metrics
path: /metrics
interval: 30s
# Network Policy (Guardrail: empfohlen)
networkPolicy:
enabled: true
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
egress:
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 443
- protocol: TCP
port: 5432 # PostgreSQL
# ConfigMap (optional)
configMap:
enabled: true
data:
APP_ENV: production
LOG_LEVEL: info
# Secrets (via Vault, empfohlen)
externalSecrets:
enabled: true
backend: vault
vaultPath: secret/data/my-team/my-application
data:
- key: database-password
name: DB_PASSWORD
- key: api-key
name: API_KEY
1.3 Guardrails-Compliance validieren¶
Testen Sie Ihr Chart lokal gegen Kyverno-Policies:
# Chart rendern
helm template my-application . > manifests.yaml
# Mit kyverno CLI validieren (falls installiert)
kyverno apply policies/ --resource manifests.yaml
# Oder: Direkt in Staging-Namespace testen
helm upgrade --install my-application . \
--namespace my-team-staging \
--create-namespace \
--dry-run
Schritt 2: Secrets Management mit Vault und External Secrets Operator¶
2.1 Überblick: Secrets Management auf der ayedo SDP¶
Die ayedo SDP bietet ein compliance-freundliches Secrets Management durch die Integration von:
- HashiCorp Vault: Zentrale Secrets-Speicherung mit Audit-Logging
- External Secrets Operator (ESO): Automatische Synchronisation von Vault → Kubernetes Secrets
- CloudNativePG: Platform Admin stellt PostgreSQL-Cluster bereit, Credentials werden automatisch in Vault gespeichert
graph LR
subgraph "Platform Admin"
A[Polycrate: Deploy CloudNativePG] --> B[PostgreSQL Cluster]
B --> C[Auto-Generate Credentials]
C --> D[Store in Vault]
end
subgraph "Anwendungsentwickler"
E[Define ExternalSecret] --> F[Reference Vault Path]
end
subgraph "Kubernetes Cluster"
D --> G[External Secrets Operator]
F --> G
G --> H[Kubernetes Secret]
H --> I[Application Pod]
end 2.2 Rolle des Platform Administrators¶
Platform Admins stellen Infrastruktur-Komponenten (z.B. Datenbanken) über Polycrate bereit:
Beispiel: PostgreSQL mit CloudNativePG¶
Platform Admin deployed PostgreSQL-Cluster via Polycrate:
# workspace.poly (Platform Admin)
blocks:
- name: cloudnative-pg
from: cargo.ayedo.cloud/ayedo/k8s/cloudnative-pg:1.24.0
kubeconfig:
from: production-cluster
config:
clusters:
- name: my-team-postgres
namespace: my-team-production
instances: 3
storage: 100Gi
backup:
enabled: true
schedule: "0 2 * * *"
# Vault-Integration
vault:
enabled: true
path: secret/data/my-team/postgres/my-team-postgres
Was passiert automatisch:
- Polycrate deployed CloudNativePG Cluster
- CloudNativePG generiert PostgreSQL-Credentials (User, Password, Connection String)
- Credentials werden automatisch in Vault gespeichert unter:
secret/data/my-team/postgres/my-team-postgres - Platform Admin informiert Anwendungsentwickler über Vault-Path
2.3 Vault: Secrets für Anwendungen speichern¶
Als Anwendungsentwickler speichern Sie Ihre Application-Secrets (API-Keys, Tokens, etc.) in Vault.
Vault-CLI installieren¶
# macOS
brew install hashicorp/tap/vault
# Linux
wget https://releases.hashicorp.com/vault/1.18.0/vault_1.18.0_linux_amd64.zip
unzip vault_1.18.0_linux_amd64.zip
sudo mv vault /usr/local/bin/
Vault-Login via OIDC¶
# Vault-Adresse setzen
export VAULT_ADDR=https://vault.example.com
# Login via OIDC (öffnet Browser)
vault login -method=oidc role=developer
# Alternativ: Via Vault UI
# https://vault.example.com → Method: OIDC → Sign in
Secrets in Vault speichern¶
# Namespace-Struktur: secret/data/<team>/<app>/<secret-key>
# API-Keys speichern
vault kv put secret/my-team/my-application \
stripe-api-key="sk_test_..." \
sendgrid-api-key="SG...." \
jwt-secret="your-jwt-secret"
# Secrets auslesen (zur Verifikation)
vault kv get secret/my-team/my-application
# Einzelne Keys auslesen
vault kv get -field=stripe-api-key secret/my-team/my-application
Vault-Secrets-Struktur¶
Empfohlene Ordner-Struktur in Vault:
secret/
├── my-team/
│ ├── postgres/
│ │ └── my-team-postgres # Von Platform Admin bereitgestellt
│ │ ├── username
│ │ ├── password
│ │ └── connection-string
│ ├── my-application/
│ │ ├── stripe-api-key # Von Entwickler erstellt
│ │ ├── sendgrid-api-key
│ │ └── jwt-secret
│ └── my-other-app/
│ └── ...
Secrets via Vault UI verwalten¶
- Navigate zu
https://vault.example.com/ui/vault/secrets/secret/list - Create secret → Path:
my-team/my-application - Fügen Sie Key-Value-Paare hinzu
- Save
2.4 External Secrets Operator: Vault → Kubernetes¶
External Secrets Operator synchronisiert automatisch Secrets von Vault nach Kubernetes.
ExternalSecret Resource definieren¶
Fügen Sie ExternalSecret zu Ihrem Helm Chart hinzu:
# helm/templates/external-secret.yaml
{{- if .Values.ohmyhelm.externalSecrets.enabled }}
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: {{ include "my-application.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "my-application.labels" . | nindent 4 }}
spec:
# Refresh-Intervall
refreshInterval: 1h
# SecretStore Reference
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
# Ziel-Secret
target:
name: {{ include "my-application.fullname" . }}-secrets
creationPolicy: Owner
# Secrets von Vault holen
data:
# Application Secrets
- secretKey: STRIPE_API_KEY
remoteRef:
key: secret/data/my-team/my-application
property: stripe-api-key
- secretKey: SENDGRID_API_KEY
remoteRef:
key: secret/data/my-team/my-application
property: sendgrid-api-key
- secretKey: JWT_SECRET
remoteRef:
key: secret/data/my-team/my-application
property: jwt-secret
# PostgreSQL Connection (von Platform Admin bereitgestellt)
- secretKey: DATABASE_URL
remoteRef:
key: secret/data/my-team/postgres/my-team-postgres
property: connection-string
- secretKey: DB_USERNAME
remoteRef:
key: secret/data/my-team/postgres/my-team-postgres
property: username
- secretKey: DB_PASSWORD
remoteRef:
key: secret/data/my-team/postgres/my-team-postgres
property: password
{{- end }}
ohMyHelm values.yaml konfigurieren¶
# helm/values.yaml
ohmyhelm:
# External Secrets aktivieren
externalSecrets:
enabled: true
backend: vault
refreshInterval: 1h
# Deployment muss ExternalSecret referenzieren
deployment:
envFrom:
- secretRef:
name: my-application-secrets # Erstellt von ExternalSecret
ClusterSecretStore (von Platform Admin bereitgestellt)¶
Platform Admins stellen einen ClusterSecretStore bereit, der ESO mit Vault verbindet:
# Bereitgestellt von Platform Admin
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "external-secrets-operator"
serviceAccountRef:
name: external-secrets
namespace: external-secrets-system
2.5 Application-Deployment mit Secrets¶
Mit External Secrets Operator wird das Secret automatisch erstellt und synchronisiert:
# Deployment referenziert das Secret
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-application
spec:
template:
spec:
containers:
- name: app
image: harbor.example.com/my-team/my-application:1.0.0
# Alle Secrets als Env-Vars
envFrom:
- secretRef:
name: my-application-secrets
# Oder: Einzelne Secrets als Env-Vars
env:
- name: STRIPE_API_KEY
valueFrom:
secretKeyRef:
name: my-application-secrets
key: STRIPE_API_KEY
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: my-application-secrets
key: DATABASE_URL
2.6 Secrets-Management-Workflow¶
sequenceDiagram
participant PA as Platform Admin
participant V as Vault
participant ESO as External Secrets Operator
participant K8s as Kubernetes
participant Dev as Anwendungsentwickler
PA->>K8s: Deploy CloudNativePG via Polycrate
K8s-->>V: Auto-Store DB Credentials
PA->>Dev: Informiert über Vault-Path
Dev->>V: Store Application-Secrets
Dev->>K8s: Deploy ExternalSecret via Helm
ESO->>V: Fetch Secrets (RefreshInterval)
ESO->>K8s: Create/Update Kubernetes Secret
K8s->>K8s: Mount Secret in Pod
K8s-->>Dev: Application läuft mit Secrets 2.7 Secrets-Rotation¶
External Secrets Operator synchronisiert Secrets automatisch:
- Secret in Vault aktualisieren:
# Neues Stripe API-Key
vault kv put secret/my-team/my-application \
stripe-api-key="sk_live_new_key_..."
# Oder via Vault UI
-
ESO synchronisiert automatisch (innerhalb
refreshInterval) -
Application-Pods müssen neu gestartet werden:
# Rollout triggern
kubectl rollout restart deployment/my-application -n my-team-production
# Oder: Reloader aktivieren (siehe unten)
Automatic Pod Restart mit Reloader¶
Stakater Reloader startet Pods automatisch bei Secret-Änderungen:
Platform Admin deployed Reloader via Polycrate:
2.8 Secrets-Security Best Practices¶
Niemals Secrets im Code!¶
# ❌ FALSCH: Secret im Code
const stripeKey = "sk_test_...";
# ✅ RICHTIG: Secret aus Env-Var
const stripeKey = process.env.STRIPE_API_KEY;
Git-Ignore für lokale Secrets¶
Least Privilege in Vault¶
Anwendungsentwickler sollten nur Zugriff auf ihre Team-Secrets haben:
# vault-policy (von Platform Admin konfiguriert)
# Developers können nur ihre Team-Secrets verwalten
path "secret/data/my-team/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "secret/metadata/my-team/*" {
capabilities = ["list"]
}
# Keine Zugriffsrechte auf andere Teams
path "secret/data/other-team/*" {
capabilities = ["deny"]
}
Audit-Logging¶
Vault loggt automatisch alle Secret-Zugriffe:
# Vault Audit-Logs anzeigen (Platform Admin)
kubectl logs -n vault vault-0 | grep audit
# Audit-Logs in Grafana
# Dashboard: "Vault Audit Logs"
2.9 Troubleshooting Secrets¶
Problem: ExternalSecret bleibt in "SecretSyncedError"¶
# ExternalSecret-Status prüfen
kubectl get externalsecrets -n my-team-production
# Details anzeigen
kubectl describe externalsecret my-application -n my-team-production
# ESO-Logs prüfen
kubectl logs -n external-secrets-system -l app.kubernetes.io/name=external-secrets
Häufige Ursachen:
- Vault-Path existiert nicht → Secret in Vault erstellen
- Keine Vault-Berechtigung → Platform Admin kontaktieren
- ClusterSecretStore falsch konfiguriert → Platform Admin kontaktieren
Problem: Secret existiert, aber Application kann nicht darauf zugreifen¶
# Secret existiert?
kubectl get secret my-application-secrets -n my-team-production
# Secret-Inhalt prüfen (base64-encoded)
kubectl get secret my-application-secrets -n my-team-production -o yaml
# Deployment referenziert richtiges Secret?
kubectl get deployment my-application -n my-team-production -o yaml | grep -A 5 secretRef
Schritt 3: Container-Image bauen und in Harbor pushen¶
3.1 Dockerfile erstellen¶
Erstellen Sie ein Guardrails-compliant Dockerfile:
# Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Production image
FROM node:18-alpine
# Create non-root user (Guardrail: runAsNonRoot)
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
WORKDIR /app
# Copy dependencies and built app
COPY --from=builder --chown=1000:1000 /app/node_modules ./node_modules
COPY --from=builder --chown=1000:1000 /app/dist ./dist
COPY --chown=1000:1000 package*.json ./
# Switch to non-root user
USER 1000
EXPOSE 8080
# Health check endpoint
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:8080/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"
CMD ["node", "dist/server.js"]
3.2 Image lokal bauen¶
# Image bauen
docker build -t harbor.example.com/my-team/my-application:1.0.0 .
# Lokal testen (als non-root)
docker run --rm -p 8080:8080 harbor.example.com/my-team/my-application:1.0.0
# Verifizieren: Läuft als non-root?
docker inspect harbor.example.com/my-team/my-application:1.0.0 | jq '.[0].Config.User'
# Output sollte "1000" sein
3.3 Harbor Login und Image Push¶
# Harbor-Login
docker login harbor.example.com
# Username: <your-oidc-username>
# Password: <your-harbor-cli-secret>
# Image pushen (Guardrail: kein 'latest' Tag!)
docker push harbor.example.com/my-team/my-application:1.0.0
# Harbor-Vulnerability-Scan abwarten
# Prüfen Sie in Harbor UI: Project → my-team → Repository → my-application → Tag 1.0.0
Harbor CLI Secret
Harbor CLI-Secrets können Sie in Harbor UI generieren: User Profile → User Settings → CLI Secret
Schritt 4: Helm Chart in Harbor pushen¶
Harbor unterstützt Helm Charts als OCI-Artefakte.
4.1 Helm Chart paketieren¶
# Chart paketieren
helm package .
# Output: my-application-1.0.0.tgz
# Helm Registry Login
helm registry login harbor.example.com
# Username: <your-oidc-username>
# Password: <your-harbor-cli-secret>
# Chart nach Harbor pushen (Guardrail: explizite Version)
helm push my-application-1.0.0.tgz oci://harbor.example.com/my-team
4.2 Chart in Harbor verifizieren¶
Prüfen Sie in Harbor UI:
- Navigate zu Projects → my-team
- Helm Charts Tab
- Chart my-application mit Version 1.0.0 sollte sichtbar sein
Schritt 5: ArgoCD Application konfigurieren¶
4.1 Zugriff auf ArgoCD¶
ArgoCD wird von Platform Administratoren verwaltet. Anwendungsentwickler haben Zugriff basierend auf OIDC-Groups:
# ArgoCD Login via OIDC
argocd login argocd.example.com --sso
# Alternativ: Via Web UI
# https://argocd.example.com → "LOG IN VIA KEYCLOAK"
# Verfügbare Cluster auflisten
argocd cluster list
# Verfügbare Repositories prüfen
argocd repo list
4.2 ArgoCD Application erstellen¶
Option 1: Via ArgoCD UI
- Navigate zu Applications → + NEW APP
- Application Name:
my-application-production - Project:
my-team(vom Platform Admin konfiguriert) - Sync Policy:
Automatic - Source:
- Repository:
oci://harbor.example.com/my-team - Chart:
my-application - Version:
1.0.0 - Destination:
- Cluster:
production-cluster - Namespace:
my-team-production - Helm Values: (Überschreiben Sie Werte falls nötig)
- CREATE
Option 2: Via ArgoCD CLI
argocd app create my-application-production \
--project my-team \
--repo oci://harbor.example.com/my-team \
--helm-chart my-application \
--revision 1.0.0 \
--dest-server https://production-cluster-api.example.com \
--dest-namespace my-team-production \
--sync-policy automated \
--auto-prune \
--self-heal \
--helm-set ohmyhelm.chart.container.image=1.0.0
Option 3: Via GitOps (empfohlen für Teams)
Erstellen Sie ein Git-Repository mit ArgoCD Application-Manifesten:
# argocd-apps/my-application-production.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-application-production
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: my-team
source:
repoURL: oci://harbor.example.com/my-team
chart: my-application
targetRevision: 1.0.0
helm:
values: |
ohmyhelm:
deployment:
image:
tag: "1.0.0"
replicas: 3
ingress:
hosts:
- host: my-app.example.com
destination:
server: https://production-cluster-api.example.com
namespace: my-team-production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
4.3 ApplicationSet für Multi-Cluster (optional)¶
Für Deployments auf mehrere Cluster (z.B. Staging + Production):
# argocd-apps/my-application-appset.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: my-application
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: production
url: https://production-cluster-api.example.com
replicas: "3"
host: my-app.example.com
- cluster: staging
url: https://staging-cluster-api.example.com
replicas: "2"
host: my-app-staging.example.com
template:
metadata:
name: 'my-application-{{cluster}}'
spec:
project: my-team
source:
repoURL: oci://harbor.example.com/my-team
chart: my-application
targetRevision: 1.0.0
helm:
values: |
ohmyhelm:
deployment:
replicas: {{replicas}}
ingress:
hosts:
- host: {{host}}
destination:
server: '{{url}}'
namespace: 'my-team-{{cluster}}'
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Schritt 6: Deployment überwachen¶
5.1 ArgoCD UI¶
- Navigate zu Applications → my-application-production
- Application Status sollte Healthy und Synced sein
- Resource Tree zeigt alle deployed Ressourcen
5.2 ArgoCD CLI¶
# Application-Status
argocd app get my-application-production
# Application-Logs
argocd app logs my-application-production
# Sync-Status überwachen
argocd app wait my-application-production --health
5.3 Kubectl¶
# Pods prüfen
kubectl get pods -n my-team-production
# Deployment-Status
kubectl rollout status deployment/my-application -n my-team-production
# Application-Logs
kubectl logs -n my-team-production -l app.kubernetes.io/name=my-application --tail=100 -f
# Events
kubectl get events -n my-team-production --sort-by='.lastTimestamp'
5.4 Grafana Dashboards¶
Die ayedo SDP stellt automatisch Grafana-Dashboards bereit:
- Application Metrics:
https://grafana.example.com/d/application-overview - Pod Resources: CPU, Memory, Network
- Application Logs: Via VictoriaLogs-Integration
Guardrails-Compliance im Deployment¶
Die ayedo SDP erzwingt automatisch Guardrails während des Deployments:
Typische Guardrails-Violations¶
1. Fehlende Resource Requests¶
Fehler:
Error from server: admission webhook "validate.kyverno.svc" denied the request:
policy Deployment/my-team/my-application for resource violation:
require-requests-limits:
validate-resources: 'validation error: CPU and memory resource requests are required.
rule validate-resources failed at path /spec/template/spec/containers/0/resources/requests/memory/'
Lösung:
2. Latest Tag verwendet¶
Fehler:
Error from server: admission webhook "validate.kyverno.svc" denied the request:
policy Deployment/my-team/my-application for resource violation:
disallow-latest-tag:
validate-image-tag: 'validation error: Image tag "latest" is not allowed.'
Lösung:
3. Nicht-vertrauenswürdige Registry¶
Fehler:
Error from server: admission webhook "validate.kyverno.svc" denied the request:
policy Deployment/my-team/my-application for resource violation:
restrict-image-registries:
validate-registries: 'validation error: Images must be from harbor.example.com'
Lösung:
Continuous Deployment mit GitLab CI/CD¶
Automatisieren Sie den Deployment-Prozess mit GitLab CI/CD:
.gitlab-ci.yml¶
stages:
- build
- package
- deploy
variables:
HARBOR_REGISTRY: harbor.example.com
HARBOR_PROJECT: my-team
IMAGE_NAME: $HARBOR_REGISTRY/$HARBOR_PROJECT/$CI_PROJECT_NAME
CHART_NAME: $CI_PROJECT_NAME
build-image:
stage: build
image: docker:24
services:
- docker:24-dind
before_script:
- echo "$HARBOR_PASSWORD" | docker login $HARBOR_REGISTRY -u "$HARBOR_USERNAME" --password-stdin
script:
# Build Image mit Git Commit SHA als Tag
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHORT_SHA .
- docker tag $IMAGE_NAME:$CI_COMMIT_SHORT_SHA $IMAGE_NAME:latest
# Push Image
- docker push $IMAGE_NAME:$CI_COMMIT_SHORT_SHA
- docker push $IMAGE_NAME:latest
# Warten auf Harbor Vulnerability Scan
- sleep 30 # Harbor braucht Zeit für Scan
only:
- main
- develop
package-chart:
stage: package
image: alpine/helm:latest
before_script:
- helm registry login $HARBOR_REGISTRY -u "$HARBOR_USERNAME" -p "$HARBOR_PASSWORD"
script:
# Update Chart Version
- sed -i "s/^version:.*/version: 0.1.$CI_PIPELINE_ID/" Chart.yaml
- sed -i "s/^appVersion:.*/appVersion: $CI_COMMIT_SHORT_SHA/" Chart.yaml
# Package und Push Chart
- helm package .
- helm push $CHART_NAME-0.1.$CI_PIPELINE_ID.tgz oci://$HARBOR_REGISTRY/$HARBOR_PROJECT
only:
- main
- develop
deploy-staging:
stage: deploy
image: argoproj/argocd:latest
before_script:
- argocd login argocd.example.com --auth-token $ARGOCD_TOKEN --insecure
script:
# Update ArgoCD Application
- |
argocd app set my-application-staging \
--revision 0.1.$CI_PIPELINE_ID \
--helm-set ohmyhelm.chart.container.image=$CI_COMMIT_SHORT_SHA
# Trigger Sync
- argocd app sync my-application-staging
# Wait for Healthy
- argocd app wait my-application-staging --health --timeout 300
only:
- develop
environment:
name: staging
url: https://my-app-staging.example.com
deploy-production:
stage: deploy
image: argoproj/argocd:latest
before_script:
- argocd login argocd.example.com --auth-token $ARGOCD_TOKEN --insecure
script:
- |
argocd app set my-application-production \
--revision 0.1.$CI_PIPELINE_ID \
--helm-set ohmyhelm.chart.container.image=$CI_COMMIT_SHORT_SHA
- argocd app sync my-application-production
- argocd app wait my-application-production --health --timeout 600
only:
- main
when: manual # Manuelles Approval für Production
environment:
name: production
url: https://my-app.example.com
Zusammenarbeit mit Platform Administratoren¶
Wann Platform Administratoren kontaktieren?¶
Plattform Administratoren sind verantwortlich für:
- Cluster-Onboarding: Neue Cluster zu ArgoCD hinzufügen
- RBAC-Konfiguration: Zugriff auf Namespaces und Cluster konfigurieren
- Project-Setup: ArgoCD Projects für Teams erstellen
- Guardrails-Anpassung: Policy Exceptions für berechtigte Sonderfälle
Kontaktieren Sie Platform Administratoren wenn:
- Sie Zugriff auf einen neuen Cluster benötigen
- Ihre OIDC-Gruppe keinen Zugriff auf ArgoCD hat
- Ein Guardrail Ihr legitimes Use-Case blockiert
- Sie Harbor-Projects oder Grafana-Dashboards benötigen
Siehe: Platform Operations für mehr Details.
Best Practices¶
1. Nutzen Sie Semantic Versioning¶
2. Separate Environments via ApplicationSets¶
Nutzen Sie ArgoCD ApplicationSets für Staging → Production Promotion.
3. Implementieren Sie Health Checks¶
# values.yaml
ohmyhelm:
deployment:
livenessProbe:
httpGet:
path: /health
port: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
4. Nutzen Sie Vault für Secrets¶
# values.yaml
ohmyhelm:
externalSecrets:
enabled: true
backend: vault
vaultPath: secret/data/my-team/my-application
5. Aktivieren Sie Monitoring¶
6. Definieren Sie PodDisruptionBudgets¶
7. Testen Sie in Staging zuerst¶
Deployen Sie immer zuerst in Staging, bevor Sie in Production gehen.
Troubleshooting¶
Problem: Image kann nicht gepullt werden¶
Symptom: ImagePullBackOff
Lösung:
# Image-Pull-Secret prüfen
kubectl get secret -n my-team-production | grep docker
# Falls fehlt: Platform Admin kontaktieren
Problem: Guardrail blockiert Deployment¶
Symptom: Kyverno Admission Webhook Fehler
Lösung:
- Lesen Sie die Fehlermeldung genau
- Passen Sie Ihre Konfiguration an (siehe Guardrails)
- Falls legitimer Sonderfall: Policy Exception via Platform Admin anfragen
Problem: ArgoCD Sync schlägt fehl¶
Symptom: Application Status OutOfSync oder Degraded
Lösung:
# Sync-Fehler anzeigen
argocd app get my-application-production
# Manual Sync
argocd app sync my-application-production
# Events prüfen
kubectl get events -n my-team-production --sort-by='.lastTimestamp'
Problem: Harbor Vulnerability Scan blockiert Pull¶
Symptom: Image hat kritische CVEs
Lösung:
- Prüfen Sie den Scan-Report in Harbor UI
- Updaten Sie Base Images im Dockerfile
- Rebuilden und pushen Sie das Image
- Falls false-positive: CVE in Harbor als "nicht zutreffend" markieren
Weiterführende Ressourcen¶
- Guardrails - Alle Guardrails im Detail
- ohMyHelm - Helm Helper-Framework
- Access Control - Zugriffssteuerung
- ArgoCD Best Practices - Offizielle ArgoCD Dokumentation
Support¶
Bei Fragen zum Application Deployment:
- E-Mail: support@ayedo.de
- Website: ayedo.de
- Discord: ayedo Discord