Application Deployment Guide
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.
Ü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
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
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:
# helm/values.yaml
ohmyhelm:
deployment:
annotations:
reloader.stakater.com/auto: "true"
Platform Admin deployed Reloader via Polycrate:
# workspace.poly
blocks:
- name: reloader
from: cargo.ayedo.cloud/ayedo/k8s/reloader:1.0.0
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
# .gitignore
.env
.env.local
secrets/
*.pem
*.key
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
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)
ohmyhelm:
deployment:
image:
tag: "1.0.0"
ingress:
hosts:
- host: my-app.example.com
- 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:
# values.yaml
ohmyhelm:
deployment:
resources:
requests:
cpu: 100m
memory: 128Mi
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:
# values.yaml
ohmyhelm:
deployment:
image:
tag: "1.0.0" # Explizite Version
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:
# values.yaml
ohmyhelm:
deployment:
image:
registry: harbor.example.com # Trusted Registry
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
# Chart.yaml
version: 1.2.3 # MAJOR.MINOR.PATCH
appVersion: "1.2.3"
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
# values.yaml
ohmyhelm:
serviceMonitor:
enabled: true
6. Definieren Sie PodDisruptionBudgets
# values.yaml
ohmyhelm:
podDisruptionBudget:
enabled: true
minAvailable: 1
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