Continuous Integration in GitOps geht über klassische Code-Integration hinaus. CI-Pipelines bauen nicht nur die Anwendung, sondern generieren auch die Kubernetes-Manifeste und Container-Images, die später deployed werden.
Klassische CI:
Code → Build → Test → Artifact
GitOps CI:
Code → Build → Test → Container Image + K8s Manifests → Registry
Häufige Integration
Entwickler commiten mindestens täglich. Kleine Änderungen reduzieren
Merge-Konflikte und machen Probleme schneller sichtbar.
Automatisierte Builds
Jeder Commit löst automatisch einen Build aus. Keine manuellen Schritte,
keine “es funktioniert auf meinem Rechner” Probleme.
Reproduzierbare Ergebnisse
Gleicher Code = gleiche Artifacts. Build-Umgebungen sind standardisiert
und deterministisch.
| Stufe | Zweck | Tools | Dauer |
|---|---|---|---|
| Lint | Code-Qualität prüfen | ESLint, SonarQube | 1-2 min |
| Build | Artifacts erstellen | npm, Maven, Go | 2-5 min |
| Test | Funktionalität validieren | Jest, JUnit | 5-15 min |
| Security | Vulnerabilities finden | Snyk, Trivy | 2-5 min |
| Package | Container Image bauen | Docker, Buildah | 3-10 min |
Direkt in GitHub integriert, einfache YAML-Konfiguration:
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install & Test
run: |
npm ci
npm test
- name: Build Container
run: |
docker build -t myapp:${{ github.sha }} .
docker push myregistry/myapp:${{ github.sha }}Flexibler, aber komplexer in der Konfiguration:
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'docker build -t myapp:${BUILD_NUMBER} .'
}
}
stage('Test') {
parallel {
stage('Unit Tests') {
steps {
sh 'npm test'
}
}
stage('Security Scan') {
steps {
sh 'trivy image myapp:${BUILD_NUMBER}'
}
}
}
}
}
}YAML-basiert mit starker Docker-Integration:
stages:
- build
- test
- package
build:
stage: build
script:
- npm ci
- npm run build
test:
stage: test
script:
- npm test
coverage: '/Coverage: \d+\.\d+%/'
package:
stage: package
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
only:
- main# Multi-stage Build für kleinere Images
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-alpine AS runtime
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
WORKDIR /app
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --chown=nextjs:nodejs . .
USER nextjs
EXPOSE 3000
CMD ["npm", "start"]# Semantische Versionierung
docker tag myapp:latest myapp:v1.2.3
# Git-Commit Hash (empfohlen für GitOps)
docker tag myapp:latest myapp:${GITHUB_SHA}
# Build-Nummer
docker tag myapp:latest myapp:build-${BUILD_NUMBER} E2E Tests (wenige, langsam)
/ \
Integration Tests (moderate)
/ \
Unit Tests (viele, schnell)
Unit Tests
// Jest Beispiel
test('should calculate total price', () => {
const items = [{ price: 10 }, { price: 20 }];
expect(calculateTotal(items)).toBe(30);
});Integration Tests
# API Tests mit Newman (Postman CLI)
newman run api-tests.json --environment staging.jsonContract Tests
// Pact Beispiel
const { Pact } = require('@pact-foundation/pact');
const provider = new Pact({
consumer: 'frontend',
provider: 'user-service'
});# SonarQube Quality Gate
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
- name: Check Quality Gate
run: |
if [ "$SONAR_QUALITY_GATE_STATUS" != "PASSED" ]; then
echo "Quality gate failed"
exit 1
fi// package.json
{
"jest": {
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}
}- name: Audit Dependencies
run: npm audit --audit-level high
- name: Snyk Security Scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}# Trivy für Container-Vulnerabilities
trivy image --severity HIGH,CRITICAL myapp:latest
# Hadolint für Dockerfile-Qualität
hadolint Dockerfile- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push Image
run: |
docker push ghcr.io/myorg/myapp:${{ github.sha }}
docker push ghcr.io/myorg/myapp:latestNach erfolgreichem Build müssen die Kubernetes-Manifeste aktualisiert werden:
# Automatisches Update der Image-Tags
sed -i "s|image: myapp:.*|image: myapp:${GITHUB_SHA}|" k8s/deployment.yaml
git add k8s/deployment.yaml
git commit -m "Update image to ${GITHUB_SHA}"
git push| Trigger | Zweck | Beispiel |
|---|---|---|
| Push to main | Production Build | Vollständige Pipeline + Deployment |
| Pull Request | Validation | Tests + Security Scan |
| Schedule | Dependency Updates | Nightly builds |
| Manual | Hotfixes | Emergency deployments |
on:
push:
branches: [main]
paths: ['src/**', 'Dockerfile']
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * *' # Täglich um 2 Uhr
workflow_dispatch: # Manueller Trigger- name: Slack Notification
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: "Build ${{ job.status }} for ${{ github.ref }}"
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
if: always()CI in GitOps automatisiert nicht nur das Testen und Bauen von Code, sondern auch die Erstellung der Deployment-Artifacts. Wichtige Erfolgsfaktoren:
Eine gut konfigurierte CI-Pipeline reduziert manuelle Arbeit und macht Deployments vorhersagbar und sicher.