[{"data":1,"prerenderedAt":825},["ShallowReactive",2],{"/de-de/blog/how-to-build-ci-cd-observability-at-scale":3,"navigation-de-de":39,"banner-de-de":462,"footer-de-de":472,"blog-post-authors-de-de-Paul Meresanu":707,"blog-related-posts-de-de-how-to-build-ci-cd-observability-at-scale":722,"blog-promotions-de-de":763,"next-steps-de-de":815},{"id":4,"title":5,"authorSlugs":6,"authors":8,"body":10,"category":11,"categorySlug":11,"config":12,"content":16,"date":20,"description":17,"extension":25,"externalUrl":26,"featured":13,"heroImage":19,"isFeatured":13,"meta":27,"navigation":28,"path":29,"publishedDate":20,"rawbody":30,"seo":31,"slug":15,"stem":34,"tagSlugs":35,"tags":37,"template":14,"updatedDate":26,"__hash__":38},"blogPosts/de-de/blog/how-to-build-ci-cd-observability-at-scale.yml","CI/CD-Observability im Unternehmensmaßstab aufbauen",[7],"paul-meresanu",[9],"Paul Meresanu","CI/CD-Optimierung beginnt mit Transparenz. Eine erfolgreiche DevOps-Plattform\nim Unternehmensmaßstab umfasst das Verständnis von Pipeline-Performance,\nJob-Ausführungsmustern und quantifizierbaren operationalen Insights – insbesondere\nfür Unternehmen, die GitLab Self-Managed betreiben.\n\nUm GitLab-Kund(inn)en dabei zu helfen, den vollen Nutzen ihrer Plattform\nauszuschöpfen, haben wir die GitLab CI/CD Observability-Lösung als Teil unseres\nPlatform Excellence-Programms entwickelt. Sie verwandelt rohe Pipeline-Metriken\nin handlungsrelevante operationale Erkenntnisse.\n\nEin führendes Finanzdienstleistungsunternehmen hat gemeinsam mit GitLabs Customer\nSuccess Architect Transparenz über seine GitLab Self-Managed-Deployments\ngewonnen. Gemeinsam haben wir eine containerisierte Observability-Lösung\nimplementiert, die den Open-Source-gitlab-ci-pipelines-exporter mit\nunternehmensgerechter Prometheus- und Grafana-Infrastruktur kombiniert.\n\nIn diesem Artikel werden die Herausforderungen beim Pipeline-Management im\nUnternehmensmaßstab erläutert – und wie GitLab CI/CD Observability diese mit\neiner praxisnahen End-to-End-Implementierung adressiert.\n\n\n## Die Herausforderung: CI/CD-Performance messen\n\nVor der Implementierung einer Observability-Lösung sollte die\nMessdimension klar definiert sein:\n\n* **Welche Metriken sind relevant?** Pipeline-Dauer, Job-Erfolgsraten,\n  Queue-Zeiten, Runner-Auslastung\n* **Wer braucht Transparenz?** Entwickler(innen), DevOps-Engineers,\n  Plattformteams, Führungsebene\n* **Welche Entscheidungen werden damit getroffen?** Infrastrukturinvestitionen,\n  Engpass-Behebung, Kapazitätsplanung\n\n\n## Lösungsarchitektur: Ein vollständiges Dashboard-Set für Observability\n\nNach dem Deployment stellt der Observability-Stack ein Set von\nGrafana-Dashboards bereit, das Echtzeit- und historische Transparenz über die\nCI/CD-Plattform bietet. Ein typisches Deployment umfasst:\n\n* **Pipeline Overview Dashboard:** Eine übergeordnete Ansicht mit Gesamtzahl\n  der Pipeline-Läufe, Erfolgs-/Fehlerquoten über die Zeit (als gestapelte\n  Balken- oder Zeitreihencharts) und Trends bei der durchschnittlichen\n  Pipeline-Dauer. Panels verwenden farbcodierte Statusindikatoren (Grün für\n  Erfolg, Rot für Fehler, Gelb für Abbruch), damit Plattformteams\n  Verschlechterungen auf einen Blick erkennen.\n* **Job Performance Dashboard:** Drill-down-Panels mit Verteilungen der\n  einzelnen Job-Dauern (Histogramm), den 10 langsamsten Jobs nach\n  Durchschnittsdauer und Job-Fehler-Heatmaps nach Projekt und Stage. Hier\n  identifizieren Teams konkrete Engpass-Jobs, die sich zu optimieren lohnen.\n* **Runner & Infrastructure Dashboard:** Kombiniert Node-Exporter-Host-Metriken\n  (CPU, Arbeitsspeicher, Disk) mit Pipeline-Queue-Zeit-Daten, um\n  Infrastruktur-Sättigung mit Pipeline-Wartezeiten zu korrelieren. Nützlich\n  für Kapazitätsplanungsentscheidungen wie die Skalierung von Runner-Pools oder\n  das Upgrade von Instanzgrößen.\n* **Deployment Frequency Dashboard:** Verfolgt Deployment-Anzahl und\n  -Dauer über die Zeit pro Umgebung, abgestimmt auf DORA-Metriken. Hilft\n  der Engineering-Führungsebene, Lieferdurchsatz und Environment-Drift\n  (Commits hinter main) zu bewerten.\n\nJedes Dashboard wird automatisch über Grafanas dateibasiertes Provisioning\nbereitgestellt, sodass es konsistent über alle Umgebungen hinweg deployed wird.\nDie Dashboards lassen sich über Grafana-Variablen weiter anpassen, um nach\nProjekt, Ref/Branch oder Zeitraum zu filtern.\n\n![Lösungsarchitektur](https://res.cloudinary.com/about-gitlab-com/image/upload/v1777382608/Blog/Imported/blog-building-ci-cd-observability-stack-for-gitlab-self-managed/image1.png)\n\nDie Lösung benötigt zwei Exporter:\n\n* **Pipeline Exporter:** Erfasst CI/CD-Metriken über die GitLab API\n  (Pipeline-Dauer, Job-Status, Deployments)\n* **Node Exporter:** Erfasst Host-Metriken (CPU, Arbeitsspeicher, Disk)\n  für die Infrastruktur-Korrelation\n\n**Voraussetzungen:**\n\n* GitLab Self-Managed Version 18.1+\n* **Container-Orchestrierungsplattform:** Ein Kubernetes-Cluster (empfohlen\n  für Unternehmens-Deployments) oder eine Container-Runtime wie Docker/Podman\n  für kleinere Umgebungen oder Proof-of-Concept-Deployments. Die primäre\n  Deployment-Anleitung unten zielt auf Kubernetes; eine Docker-Compose-Alternative\n  ist im Anhang für lokales Testen und Evaluation verfügbar\n* GitLab Personal Access Token (Scope **read_api**)\n\nDie vollständige Implementierungsanleitung mit allen Kubernetes-Manifesten\nfolgt direkt im Anschluss.\n\n\n## Kubernetes-Deployment (empfohlen)\n\nFür Unternehmensumgebungen wird jede Komponente als separates Deployment in\neinem dedizierten Namespace deployed. Dieser Ansatz integriert sich in\nbestehende Cluster-Infrastruktur, Secrets-Management und Network-Policies.\n\n### 1. Namespace und Secret erstellen\n\n```bash\nkubectl create namespace gitlab-observability\n\n# GitLab-Token-Secret erstellen (siehe Abschnitt Secrets-Management\n# für unternehmensgerechte Ansätze mit externen Secret-Operatoren)\nkubectl create secret generic gitlab-token \\\n  --from-literal=token=glpat-xxxxxxxxxxxx \\\n  -n gitlab-observability\n```\n\n### 2. Pipeline Exporter deployen\n\n```yaml\n# exporter-deployment.yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: gitlab-ci-pipelines-exporter\n  namespace: gitlab-observability\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: gitlab-ci-pipelines-exporter\n  template:\n    metadata:\n      labels:\n        app: gitlab-ci-pipelines-exporter\n    spec:\n      containers:\n        - name: exporter\n          image: mvisonneau/gitlab-ci-pipelines-exporter:latest\n          ports:\n            - containerPort: 8080\n          env:\n            - name: GCPE_GITLAB_TOKEN\n              valueFrom:\n                secretKeyRef:\n                  name: gitlab-token\n                  key: token\n            - name: GCPE_CONFIG\n              value: /etc/gcpe/config.yml\n          volumeMounts:\n            - name: config\n              mountPath: /etc/gcpe\n      volumes:\n        - name: config\n          configMap:\n            name: gcpe-config\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: gitlab-ci-pipelines-exporter\n  namespace: gitlab-observability\nspec:\n  selector:\n    app: gitlab-ci-pipelines-exporter\n  ports:\n    - port: 8080\n      targetPort: 8080\n```\n\n### 3. Node Exporter deployen (DaemonSet)\n\n```yaml\n# node-exporter-daemonset.yaml\napiVersion: apps/v1\nkind: DaemonSet\nmetadata:\n  name: node-exporter\n  namespace: gitlab-observability\nspec:\n  selector:\n    matchLabels:\n      app: node-exporter\n  template:\n    metadata:\n      labels:\n        app: node-exporter\n    spec:\n      containers:\n        - name: node-exporter\n          image: prom/node-exporter:latest\n          ports:\n            - containerPort: 9100\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: node-exporter\n  namespace: gitlab-observability\nspec:\n  selector:\n    app: node-exporter\n  ports:\n    - port: 9100\n      targetPort: 9100\n```\n\n### 4. Prometheus deployen\n\n```yaml\n# prometheus-deployment.yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: prometheus\n  namespace: gitlab-observability\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: prometheus\n  template:\n    metadata:\n      labels:\n        app: prometheus\n    spec:\n      containers:\n        - name: prometheus\n          image: prom/prometheus:latest\n          ports:\n            - containerPort: 9090\n          volumeMounts:\n            - name: config\n              mountPath: /etc/prometheus\n      volumes:\n        - name: config\n          configMap:\n            name: prometheus-config\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: prometheus\n  namespace: gitlab-observability\nspec:\n  selector:\n    app: prometheus\n  ports:\n    - port: 9090\n      targetPort: 9090\n```\n\n### 5. Grafana deployen\n\nDas folgende Grafana-Deployment startet mit deaktivierter Authentifizierung\n(`GF_AUTH_ANONYMOUS_ENABLED: true`) für den einfachen Einstieg.\n\n**Diese Einstellung erlaubt jedem mit Netzwerkzugang, alle Dashboards ohne\nAnmeldung einzusehen.** Für Produktions-Deployments diese Variable entfernen\noder auf false setzen und einen geeigneten Authentifizierungs-Provider\n(LDAP, SAML/SSO oder OAuth) konfigurieren, um den Zugriff auf autorisierte\nNutzende zu beschränken.\n\n```yaml\n# grafana-deployment.yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: grafana\n  namespace: gitlab-observability\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: grafana\n  template:\n    metadata:\n      labels:\n        app: grafana\n    spec:\n      containers:\n        - name: grafana\n          image: grafana/grafana:10.0.0\n          ports:\n            - containerPort: 3000\n          env:\n            # Für Produktion ENTFERNEN oder auf 'false' setzen.\n            # Bei 'true' können alle Nutzenden mit Netzwerkzugang\n            # Dashboards ohne Authentifizierung einsehen.\n            - name: GF_AUTH_ANONYMOUS_ENABLED\n              value: 'true'\n          volumeMounts:\n            - name: dashboards-provider\n              mountPath: /etc/grafana/provisioning/dashboards\n            - name: datasources\n              mountPath: /etc/grafana/provisioning/datasources\n            - name: dashboards\n              mountPath: /var/lib/grafana/dashboards\n      volumes:\n        - name: dashboards-provider\n          configMap:\n            name: grafana-dashboards-provider\n        - name: datasources\n          configMap:\n            name: grafana-datasources\n        - name: dashboards\n          configMap:\n            name: grafana-dashboards\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: grafana\n  namespace: gitlab-observability\nspec:\n  selector:\n    app: grafana\n  ports:\n    - port: 3000\n      targetPort: 3000\n```\n\n### 6. Network Policy setzen\n\nDen Inter-Pod-Traffic auf die erforderlichen Kommunikationspfade beschränken:\n\n```yaml\n# network-policy.yaml\napiVersion: networking.k8s.io/v1\nkind: NetworkPolicy\nmetadata:\n  name: observability-policy\n  namespace: gitlab-observability\nspec:\n  podSelector: {}\n  policyTypes:\n    - Ingress\n  ingress:\n    # Prometheus scrapt Exporter und Node-Exporter\n    - from:\n        - podSelector:\n            matchLabels:\n              app: prometheus\n      ports:\n        - port: 8080\n        - port: 9100\n    # Grafana fragt Prometheus ab\n    - from:\n        - podSelector:\n            matchLabels:\n              app: grafana\n      ports:\n        - port: 9090\n```\n\n### 7. Validieren\n\n```bash\nkubectl get pods -n gitlab-observability\nkubectl port-forward svc/grafana 3000:3000 -n gitlab-observability\ncurl http://localhost:3000/api/health\n```\n\n\n## Konfigurationsreferenz\n\n### Exporter-Konfiguration\n\n```yaml\n# gitlab-ci-pipelines-exporter.yml (ConfigMap: gcpe-config)\nlog:\n  level: info\ngitlab:\n  url: https://gitlab.your-domain.com\n  maximum_requests_per_second: 10\nproject_defaults:\n  pull:\n    pipeline:\n      jobs:\n        enabled: true\nwildcards:\n  - owner:\n      name: your-group-name\n      kind: group\n    archived: false\n```\n\n### Prometheus-Konfiguration\n\n```yaml\n# prometheus.yml (ConfigMap: prometheus-config)\nglobal:\n  scrape_interval: 15s\nscrape_configs:\n  - job_name: 'gitlab-ci-pipelines-exporter'\n    static_configs:\n      - targets: ['gitlab-ci-pipelines-exporter:8080']\n  - job_name: 'node-exporter'\n    static_configs:\n      - targets: ['node-exporter:9100']\n```\n\n### Grafana-Datenquellen\n\n```yaml\n# datasources.yml (ConfigMap: grafana-datasources)\napiVersion: 1\ndatasources:\n  - name: Prometheus\n    type: prometheus\n    access: proxy\n    url: http://prometheus:9090\n    isDefault: true\n# dashboards.yml (ConfigMap: grafana-dashboards-provider)\napiVersion: 1\nproviders:\n  - name: 'default'\n    folder: 'GitLab CI/CD'\n    type: file\n    options:\n      path: /var/lib/grafana/dashboards\n```\n\n\n## Wichtige Metriken\n\n### Pipeline-Exporter-Metriken\n\n| Metrik | Beschreibung |\n| :---- | :---- |\n| `gitlab_ci_pipeline_duration_seconds` | Pipeline-Ausführungszeit |\n| `gitlab_ci_pipeline_status` | Pipeline-Erfolg/-Fehler nach Projekt |\n| `gitlab_ci_pipeline_job_duration_seconds` | Einzelne Job-Ausführungszeit |\n| `gitlab_ci_pipeline_job_status` | Job-Erfolgs-/-Fehlerstatus |\n| `gitlab_ci_pipeline_job_artifact_size_bytes` | Artifact-Speicherverbrauch |\n| `gitlab_ci_pipeline_coverage` | Code-Coverage-Prozentsatz |\n| `gitlab_ci_environment_deployment_count` | Deployment-Häufigkeit |\n| `gitlab_ci_environment_deployment_duration_seconds` | Deployment-Ausführungszeit |\n| `gitlab_ci_environment_behind_commits_count` | Environment-Drift gegenüber main |\n\n### Node-Exporter-Metriken\n\n| Metrik | Beschreibung |\n| :---- | :---- |\n| `node_cpu_seconds_total` | CPU-Auslastung |\n| `node_memory_MemAvailable_bytes` | Verfügbarer Arbeitsspeicher |\n| `node_filesystem_avail_bytes` | Verfügbarer Festplattenspeicher |\n| `node_load1` | 1-Minuten-Lastdurchschnitt |\n\n\n## Fehlerbehebung\n\n### Grafana-Plugin-Installation in Air-gapped-Umgebungen\n\nFür Offline-Umgebungen Plugins manuell installieren. Beispiel für Kubernetes:\n\n```bash\n# Plugin-ZIP in den Grafana-Pod kopieren\nkubectl cp grafana-polystat-panel-2.1.16.zip \\\n  gitlab-observability/grafana-\u003Cpod-id>:/tmp/\n# Plugin entpacken\nkubectl exec -it -n gitlab-observability deploy/grafana -- \\\n  sh -c \"unzip /tmp/grafana-polystat-panel-2.1.16.zip -d /var/lib/grafana/plugins/\"\n# Grafana-Pod neu starten\nkubectl rollout restart deployment/grafana -n gitlab-observability\n# Installation prüfen\nkubectl exec -it -n gitlab-observability deploy/grafana -- \\\n  ls -al /var/lib/grafana/plugins/\n```\n\n\n## Unternehmensaspekte\n\nFür regulierte Branchen gilt:\n\n* **Token-Sicherheit:** GitLab Personal Access Tokens in einem dedizierten\n  Secrets-Manager speichern, nicht hartcodiert in ConfigMaps. Token-Rotation\n  durchsetzen und den Scope auf **read\\_api** beschränken.\n* **Netzwerksegmentierung:** Hinter einem Reverse Proxy mit TLS-Terminierung\n  deployen. In Kubernetes einen Ingress-Controller mit automatisierter\n  Zertifikatsbereitstellung verwenden.\n* **Authentifizierung:** Grafana mit dem Identity Provider der Organisation\n  konfigurieren (SAML, LDAP oder OAuth/OIDC), um rollenbasierte\n  Zugriffskontrolle auf Dashboards durchzusetzen.\n\n\n## Warum GitLab?\n\nGitLabs API-First-Design ermöglicht individuelle Observability-Lösungen, die\nnative Funktionen wie Value Stream Analytics und DORA-Metriken ergänzen. Die\noffene Architektur erlaubt es Unternehmen, bewährte Open-Source-Werkzeuge –\nwie den gitlab-ci-pipelines-exporter – direkt in bestehende\nUnternehmensinfrastruktur zu integrieren, ohne etablierte Workflows zu\nunterbrechen.\n\nMit wachsender Observability-Reife bieten GitLabs eingebaute\nObservability-Funktionen einen natürlichen nächsten Schritt – tiefere,\nintegrierte Transparenz ohne zusätzliche Werkzeuge. Mehr zu den nativen\nPlattformfunktionen unter\n[GitLab Observability](https://docs.gitlab.com/operations/observability/observability/).\n","engineering",{"featured":13,"template":14,"slug":15},false,"BlogPost","how-to-build-ci-cd-observability-at-scale",{"title":5,"description":17,"authors":18,"heroImage":19,"date":20,"body":10,"category":11,"tags":21},"Dieser Praxisleitfaden zu GitLab Pipeline Analytics hilft Self-Managed-Nutzern, mit Prometheus und Grafana operationale Insights zu gewinnen.",[9],"https://res.cloudinary.com/about-gitlab-com/image/upload/v1774465167/n5hlvrsrheadeccyr1oz.png","2026-04-28",[22,23,24],"CI/CD","product","tutorial","yml",null,{},true,"/de-de/blog/how-to-build-ci-cd-observability-at-scale","seo:\n  config:\n    noIndex: false\n  title: 'CI/CD-Observability im Unternehmensmaßstab aufbauen'\n  description: 'Praxisleitfaden zu GitLab Pipeline Analytics für Self-Managed-Instanzen: Operationale Insights mit Prometheus und Grafana gewinnen.'\ncontent:\n  title: 'CI/CD-Observability im Unternehmensmaßstab aufbauen'\n  description: 'Dieser Praxisleitfaden zu GitLab Pipeline Analytics hilft Self-Managed-Nutzern, mit Prometheus und Grafana operationale Insights zu gewinnen.'\n  authors:\n    - Paul Meresanu\n  heroImage: https://res.cloudinary.com/about-gitlab-com/image/upload/v1774465167/n5hlvrsrheadeccyr1oz.png\n  date: '2026-04-28'\n  body: |\n    CI/CD-Optimierung beginnt mit Transparenz. Eine erfolgreiche DevOps-Plattform\n    im Unternehmensmaßstab umfasst das Verständnis von Pipeline-Performance,\n    Job-Ausführungsmustern und quantifizierbaren operationalen Insights – insbesondere\n    für Unternehmen, die GitLab Self-Managed betreiben.\n\n    Um GitLab-Kund(inn)en dabei zu helfen, den vollen Nutzen ihrer Plattform\n    auszuschöpfen, haben wir die GitLab CI/CD Observability-Lösung als Teil unseres\n    Platform Excellence-Programms entwickelt. Sie verwandelt rohe Pipeline-Metriken\n    in handlungsrelevante operationale Erkenntnisse.\n\n    Ein führendes Finanzdienstleistungsunternehmen hat gemeinsam mit GitLabs Customer\n    Success Architect Transparenz über seine GitLab Self-Managed-Deployments\n    gewonnen. Gemeinsam haben wir eine containerisierte Observability-Lösung\n    implementiert, die den Open-Source-gitlab-ci-pipelines-exporter mit\n    unternehmensgerechter Prometheus- und Grafana-Infrastruktur kombiniert.\n\n    In diesem Artikel werden die Herausforderungen beim Pipeline-Management im\n    Unternehmensmaßstab erläutert – und wie GitLab CI/CD Observability diese mit\n    einer praxisnahen End-to-End-Implementierung adressiert.\n\n\n    ## Die Herausforderung: CI/CD-Performance messen\n\n    Vor der Implementierung einer Observability-Lösung sollte die\n    Messdimension klar definiert sein:\n\n    * **Welche Metriken sind relevant?** Pipeline-Dauer, Job-Erfolgsraten,\n      Queue-Zeiten, Runner-Auslastung\n    * **Wer braucht Transparenz?** Entwickler(innen), DevOps-Engineers,\n      Plattformteams, Führungsebene\n    * **Welche Entscheidungen werden damit getroffen?** Infrastrukturinvestitionen,\n      Engpass-Behebung, Kapazitätsplanung\n\n\n    ## Lösungsarchitektur: Ein vollständiges Dashboard-Set für Observability\n\n    Nach dem Deployment stellt der Observability-Stack ein Set von\n    Grafana-Dashboards bereit, das Echtzeit- und historische Transparenz über die\n    CI/CD-Plattform bietet. Ein typisches Deployment umfasst:\n\n    * **Pipeline Overview Dashboard:** Eine übergeordnete Ansicht mit Gesamtzahl\n      der Pipeline-Läufe, Erfolgs-/Fehlerquoten über die Zeit (als gestapelte\n      Balken- oder Zeitreihencharts) und Trends bei der durchschnittlichen\n      Pipeline-Dauer. Panels verwenden farbcodierte Statusindikatoren (Grün für\n      Erfolg, Rot für Fehler, Gelb für Abbruch), damit Plattformteams\n      Verschlechterungen auf einen Blick erkennen.\n    * **Job Performance Dashboard:** Drill-down-Panels mit Verteilungen der\n      einzelnen Job-Dauern (Histogramm), den 10 langsamsten Jobs nach\n      Durchschnittsdauer und Job-Fehler-Heatmaps nach Projekt und Stage. Hier\n      identifizieren Teams konkrete Engpass-Jobs, die sich zu optimieren lohnen.\n    * **Runner & Infrastructure Dashboard:** Kombiniert Node-Exporter-Host-Metriken\n      (CPU, Arbeitsspeicher, Disk) mit Pipeline-Queue-Zeit-Daten, um\n      Infrastruktur-Sättigung mit Pipeline-Wartezeiten zu korrelieren. Nützlich\n      für Kapazitätsplanungsentscheidungen wie die Skalierung von Runner-Pools oder\n      das Upgrade von Instanzgrößen.\n    * **Deployment Frequency Dashboard:** Verfolgt Deployment-Anzahl und\n      -Dauer über die Zeit pro Umgebung, abgestimmt auf DORA-Metriken. Hilft\n      der Engineering-Führungsebene, Lieferdurchsatz und Environment-Drift\n      (Commits hinter main) zu bewerten.\n\n    Jedes Dashboard wird automatisch über Grafanas dateibasiertes Provisioning\n    bereitgestellt, sodass es konsistent über alle Umgebungen hinweg deployed wird.\n    Die Dashboards lassen sich über Grafana-Variablen weiter anpassen, um nach\n    Projekt, Ref/Branch oder Zeitraum zu filtern.\n\n    ![Lösungsarchitektur](https://res.cloudinary.com/about-gitlab-com/image/upload/v1777382608/Blog/Imported/blog-building-ci-cd-observability-stack-for-gitlab-self-managed/image1.png)\n\n    Die Lösung benötigt zwei Exporter:\n\n    * **Pipeline Exporter:** Erfasst CI/CD-Metriken über die GitLab API\n      (Pipeline-Dauer, Job-Status, Deployments)\n    * **Node Exporter:** Erfasst Host-Metriken (CPU, Arbeitsspeicher, Disk)\n      für die Infrastruktur-Korrelation\n\n    **Voraussetzungen:**\n\n    * GitLab Self-Managed Version 18.1+\n    * **Container-Orchestrierungsplattform:** Ein Kubernetes-Cluster (empfohlen\n      für Unternehmens-Deployments) oder eine Container-Runtime wie Docker/Podman\n      für kleinere Umgebungen oder Proof-of-Concept-Deployments. Die primäre\n      Deployment-Anleitung unten zielt auf Kubernetes; eine Docker-Compose-Alternative\n      ist im Anhang für lokales Testen und Evaluation verfügbar\n    * GitLab Personal Access Token (Scope **read_api**)\n\n    Die vollständige Implementierungsanleitung mit allen Kubernetes-Manifesten\n    folgt direkt im Anschluss.\n\n\n    ## Kubernetes-Deployment (empfohlen)\n\n    Für Unternehmensumgebungen wird jede Komponente als separates Deployment in\n    einem dedizierten Namespace deployed. Dieser Ansatz integriert sich in\n    bestehende Cluster-Infrastruktur, Secrets-Management und Network-Policies.\n\n    ### 1. Namespace und Secret erstellen\n\n    ```bash\n    kubectl create namespace gitlab-observability\n\n    # GitLab-Token-Secret erstellen (siehe Abschnitt Secrets-Management\n    # für unternehmensgerechte Ansätze mit externen Secret-Operatoren)\n    kubectl create secret generic gitlab-token \\\n      --from-literal=token=glpat-xxxxxxxxxxxx \\\n      -n gitlab-observability\n    ```\n\n    ### 2. Pipeline Exporter deployen\n\n    ```yaml\n    # exporter-deployment.yaml\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: gitlab-ci-pipelines-exporter\n      namespace: gitlab-observability\n    spec:\n      replicas: 1\n      selector:\n        matchLabels:\n          app: gitlab-ci-pipelines-exporter\n      template:\n        metadata:\n          labels:\n            app: gitlab-ci-pipelines-exporter\n        spec:\n          containers:\n            - name: exporter\n              image: mvisonneau/gitlab-ci-pipelines-exporter:latest\n              ports:\n                - containerPort: 8080\n              env:\n                - name: GCPE_GITLAB_TOKEN\n                  valueFrom:\n                    secretKeyRef:\n                      name: gitlab-token\n                      key: token\n                - name: GCPE_CONFIG\n                  value: /etc/gcpe/config.yml\n              volumeMounts:\n                - name: config\n                  mountPath: /etc/gcpe\n          volumes:\n            - name: config\n              configMap:\n                name: gcpe-config\n    ---\n    apiVersion: v1\n    kind: Service\n    metadata:\n      name: gitlab-ci-pipelines-exporter\n      namespace: gitlab-observability\n    spec:\n      selector:\n        app: gitlab-ci-pipelines-exporter\n      ports:\n        - port: 8080\n          targetPort: 8080\n    ```\n\n    ### 3. Node Exporter deployen (DaemonSet)\n\n    ```yaml\n    # node-exporter-daemonset.yaml\n    apiVersion: apps/v1\n    kind: DaemonSet\n    metadata:\n      name: node-exporter\n      namespace: gitlab-observability\n    spec:\n      selector:\n        matchLabels:\n          app: node-exporter\n      template:\n        metadata:\n          labels:\n            app: node-exporter\n        spec:\n          containers:\n            - name: node-exporter\n              image: prom/node-exporter:latest\n              ports:\n                - containerPort: 9100\n    ---\n    apiVersion: v1\n    kind: Service\n    metadata:\n      name: node-exporter\n      namespace: gitlab-observability\n    spec:\n      selector:\n        app: node-exporter\n      ports:\n        - port: 9100\n          targetPort: 9100\n    ```\n\n    ### 4. Prometheus deployen\n\n    ```yaml\n    # prometheus-deployment.yaml\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: prometheus\n      namespace: gitlab-observability\n    spec:\n      replicas: 1\n      selector:\n        matchLabels:\n          app: prometheus\n      template:\n        metadata:\n          labels:\n            app: prometheus\n        spec:\n          containers:\n            - name: prometheus\n              image: prom/prometheus:latest\n              ports:\n                - containerPort: 9090\n              volumeMounts:\n                - name: config\n                  mountPath: /etc/prometheus\n          volumes:\n            - name: config\n              configMap:\n                name: prometheus-config\n    ---\n    apiVersion: v1\n    kind: Service\n    metadata:\n      name: prometheus\n      namespace: gitlab-observability\n    spec:\n      selector:\n        app: prometheus\n      ports:\n        - port: 9090\n          targetPort: 9090\n    ```\n\n    ### 5. Grafana deployen\n\n    Das folgende Grafana-Deployment startet mit deaktivierter Authentifizierung\n    (`GF_AUTH_ANONYMOUS_ENABLED: true`) für den einfachen Einstieg.\n\n    **Diese Einstellung erlaubt jedem mit Netzwerkzugang, alle Dashboards ohne\n    Anmeldung einzusehen.** Für Produktions-Deployments diese Variable entfernen\n    oder auf false setzen und einen geeigneten Authentifizierungs-Provider\n    (LDAP, SAML/SSO oder OAuth) konfigurieren, um den Zugriff auf autorisierte\n    Nutzende zu beschränken.\n\n    ```yaml\n    # grafana-deployment.yaml\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: grafana\n      namespace: gitlab-observability\n    spec:\n      replicas: 1\n      selector:\n        matchLabels:\n          app: grafana\n      template:\n        metadata:\n          labels:\n            app: grafana\n        spec:\n          containers:\n            - name: grafana\n              image: grafana/grafana:10.0.0\n              ports:\n                - containerPort: 3000\n              env:\n                # Für Produktion ENTFERNEN oder auf 'false' setzen.\n                # Bei 'true' können alle Nutzenden mit Netzwerkzugang\n                # Dashboards ohne Authentifizierung einsehen.\n                - name: GF_AUTH_ANONYMOUS_ENABLED\n                  value: 'true'\n              volumeMounts:\n                - name: dashboards-provider\n                  mountPath: /etc/grafana/provisioning/dashboards\n                - name: datasources\n                  mountPath: /etc/grafana/provisioning/datasources\n                - name: dashboards\n                  mountPath: /var/lib/grafana/dashboards\n          volumes:\n            - name: dashboards-provider\n              configMap:\n                name: grafana-dashboards-provider\n            - name: datasources\n              configMap:\n                name: grafana-datasources\n            - name: dashboards\n              configMap:\n                name: grafana-dashboards\n    ---\n    apiVersion: v1\n    kind: Service\n    metadata:\n      name: grafana\n      namespace: gitlab-observability\n    spec:\n      selector:\n        app: grafana\n      ports:\n        - port: 3000\n          targetPort: 3000\n    ```\n\n    ### 6. Network Policy setzen\n\n    Den Inter-Pod-Traffic auf die erforderlichen Kommunikationspfade beschränken:\n\n    ```yaml\n    # network-policy.yaml\n    apiVersion: networking.k8s.io/v1\n    kind: NetworkPolicy\n    metadata:\n      name: observability-policy\n      namespace: gitlab-observability\n    spec:\n      podSelector: {}\n      policyTypes:\n        - Ingress\n      ingress:\n        # Prometheus scrapt Exporter und Node-Exporter\n        - from:\n            - podSelector:\n                matchLabels:\n                  app: prometheus\n          ports:\n            - port: 8080\n            - port: 9100\n        # Grafana fragt Prometheus ab\n        - from:\n            - podSelector:\n                matchLabels:\n                  app: grafana\n          ports:\n            - port: 9090\n    ```\n\n    ### 7. Validieren\n\n    ```bash\n    kubectl get pods -n gitlab-observability\n    kubectl port-forward svc/grafana 3000:3000 -n gitlab-observability\n    curl http://localhost:3000/api/health\n    ```\n\n\n    ## Konfigurationsreferenz\n\n    ### Exporter-Konfiguration\n\n    ```yaml\n    # gitlab-ci-pipelines-exporter.yml (ConfigMap: gcpe-config)\n    log:\n      level: info\n    gitlab:\n      url: https://gitlab.your-domain.com\n      maximum_requests_per_second: 10\n    project_defaults:\n      pull:\n        pipeline:\n          jobs:\n            enabled: true\n    wildcards:\n      - owner:\n          name: your-group-name\n          kind: group\n        archived: false\n    ```\n\n    ### Prometheus-Konfiguration\n\n    ```yaml\n    # prometheus.yml (ConfigMap: prometheus-config)\n    global:\n      scrape_interval: 15s\n    scrape_configs:\n      - job_name: 'gitlab-ci-pipelines-exporter'\n        static_configs:\n          - targets: ['gitlab-ci-pipelines-exporter:8080']\n      - job_name: 'node-exporter'\n        static_configs:\n          - targets: ['node-exporter:9100']\n    ```\n\n    ### Grafana-Datenquellen\n\n    ```yaml\n    # datasources.yml (ConfigMap: grafana-datasources)\n    apiVersion: 1\n    datasources:\n      - name: Prometheus\n        type: prometheus\n        access: proxy\n        url: http://prometheus:9090\n        isDefault: true\n    # dashboards.yml (ConfigMap: grafana-dashboards-provider)\n    apiVersion: 1\n    providers:\n      - name: 'default'\n        folder: 'GitLab CI/CD'\n        type: file\n        options:\n          path: /var/lib/grafana/dashboards\n    ```\n\n\n    ## Wichtige Metriken\n\n    ### Pipeline-Exporter-Metriken\n\n    | Metrik | Beschreibung |\n    | :---- | :---- |\n    | `gitlab_ci_pipeline_duration_seconds` | Pipeline-Ausführungszeit |\n    | `gitlab_ci_pipeline_status` | Pipeline-Erfolg/-Fehler nach Projekt |\n    | `gitlab_ci_pipeline_job_duration_seconds` | Einzelne Job-Ausführungszeit |\n    | `gitlab_ci_pipeline_job_status` | Job-Erfolgs-/-Fehlerstatus |\n    | `gitlab_ci_pipeline_job_artifact_size_bytes` | Artifact-Speicherverbrauch |\n    | `gitlab_ci_pipeline_coverage` | Code-Coverage-Prozentsatz |\n    | `gitlab_ci_environment_deployment_count` | Deployment-Häufigkeit |\n    | `gitlab_ci_environment_deployment_duration_seconds` | Deployment-Ausführungszeit |\n    | `gitlab_ci_environment_behind_commits_count` | Environment-Drift gegenüber main |\n\n    ### Node-Exporter-Metriken\n\n    | Metrik | Beschreibung |\n    | :---- | :---- |\n    | `node_cpu_seconds_total` | CPU-Auslastung |\n    | `node_memory_MemAvailable_bytes` | Verfügbarer Arbeitsspeicher |\n    | `node_filesystem_avail_bytes` | Verfügbarer Festplattenspeicher |\n    | `node_load1` | 1-Minuten-Lastdurchschnitt |\n\n\n    ## Fehlerbehebung\n\n    ### Grafana-Plugin-Installation in Air-gapped-Umgebungen\n\n    Für Offline-Umgebungen Plugins manuell installieren. Beispiel für Kubernetes:\n\n    ```bash\n    # Plugin-ZIP in den Grafana-Pod kopieren\n    kubectl cp grafana-polystat-panel-2.1.16.zip \\\n      gitlab-observability/grafana-\u003Cpod-id>:/tmp/\n    # Plugin entpacken\n    kubectl exec -it -n gitlab-observability deploy/grafana -- \\\n      sh -c \"unzip /tmp/grafana-polystat-panel-2.1.16.zip -d /var/lib/grafana/plugins/\"\n    # Grafana-Pod neu starten\n    kubectl rollout restart deployment/grafana -n gitlab-observability\n    # Installation prüfen\n    kubectl exec -it -n gitlab-observability deploy/grafana -- \\\n      ls -al /var/lib/grafana/plugins/\n    ```\n\n\n    ## Unternehmensaspekte\n\n    Für regulierte Branchen gilt:\n\n    * **Token-Sicherheit:** GitLab Personal Access Tokens in einem dedizierten\n      Secrets-Manager speichern, nicht hartcodiert in ConfigMaps. Token-Rotation\n      durchsetzen und den Scope auf **read\\_api** beschränken.\n    * **Netzwerksegmentierung:** Hinter einem Reverse Proxy mit TLS-Terminierung\n      deployen. In Kubernetes einen Ingress-Controller mit automatisierter\n      Zertifikatsbereitstellung verwenden.\n    * **Authentifizierung:** Grafana mit dem Identity Provider der Organisation\n      konfigurieren (SAML, LDAP oder OAuth/OIDC), um rollenbasierte\n      Zugriffskontrolle auf Dashboards durchzusetzen.\n\n\n    ## Warum GitLab?\n\n    GitLabs API-First-Design ermöglicht individuelle Observability-Lösungen, die\n    native Funktionen wie Value Stream Analytics und DORA-Metriken ergänzen. Die\n    offene Architektur erlaubt es Unternehmen, bewährte Open-Source-Werkzeuge –\n    wie den gitlab-ci-pipelines-exporter – direkt in bestehende\n    Unternehmensinfrastruktur zu integrieren, ohne etablierte Workflows zu\n    unterbrechen.\n\n    Mit wachsender Observability-Reife bieten GitLabs eingebaute\n    Observability-Funktionen einen natürlichen nächsten Schritt – tiefere,\n    integrierte Transparenz ohne zusätzliche Werkzeuge. Mehr zu den nativen\n    Plattformfunktionen unter\n    [GitLab Observability](https://docs.gitlab.com/operations/observability/observability/).\n  category: engineering\n  tags:\n    - CI/CD\n    - product\n    - tutorial\nconfig:\n  featured: false\n  template: BlogPost\n  slug: how-to-build-ci-cd-observability-at-scale\n",{"config":32,"title":5,"description":33},{"noIndex":13},"Praxisleitfaden zu GitLab Pipeline Analytics für Self-Managed-Instanzen: Operationale Insights mit Prometheus und Grafana gewinnen.","de-de/blog/how-to-build-ci-cd-observability-at-scale",[36,23,24],"cicd",[22,23,24],"t81TpES0_MVA2N3gS1BHgaU2RRwiPQKR3uyp0PUVx4A",{"logo":40,"freeTrial":45,"sales":50,"login":55,"items":60,"search":380,"minimal":414,"duo":432,"switchNav":441,"pricingDeployment":452},{"config":41},{"href":42,"dataGaName":43,"dataGaLocation":44},"/de-de/","gitlab logo","header",{"text":46,"config":47},"Kostenlose Testversion anfordern",{"href":48,"dataGaName":49,"dataGaLocation":44},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/de-de&glm_content=default-saas-trial/","free trial",{"text":51,"config":52},"Vertrieb kontaktieren",{"href":53,"dataGaName":54,"dataGaLocation":44},"/de-de/sales/","sales",{"text":56,"config":57},"Anmelden",{"href":58,"dataGaName":59,"dataGaLocation":44},"https://gitlab.com/users/sign_in/","sign in",[61,90,191,196,299,360],{"text":62,"config":63,"menu":65},"Plattform",{"dataNavLevelOne":64},"platform",{"type":66,"columns":67},"cards",[68,74,82],{"title":62,"description":69,"link":70},"Die intelligente Orchestrierungsplattform für DevSecOps",{"text":71,"config":72},"Die Plattform erkunden",{"href":73,"dataGaName":64,"dataGaLocation":44},"/de-de/platform/",{"title":75,"description":76,"link":77},"GitLab Duo Agent Platform","Agentische KI für den gesamten Software-Lebenszyklus",{"text":78,"config":79},"Lerne GitLab Duo kennen",{"href":80,"dataGaName":81,"dataGaLocation":44},"/de-de/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":83,"description":84,"link":85},"Warum GitLab?","Erfahre, warum sich Unternehmen für GitLab entscheiden",{"text":86,"config":87},"Mehr erfahren",{"href":88,"dataGaName":89,"dataGaLocation":44},"/de-de/why-gitlab/","why gitlab",{"text":91,"left":28,"config":92,"menu":94},"Produkt",{"dataNavLevelOne":93},"solutions",{"type":95,"link":96,"columns":100,"feature":170},"lists",{"text":97,"config":98},"Alle Lösungen anzeigen",{"href":99,"dataGaName":93,"dataGaLocation":44},"/de-de/solutions/",[101,125,148],{"title":102,"description":103,"link":104,"items":109},"Automatisierung","CI/CD und Automatisierung zur Beschleunigung der Bereitstellung",{"config":105},{"icon":106,"href":107,"dataGaName":108,"dataGaLocation":44},"AutomatedCodeAlt","/de-de/solutions/delivery-automation/","automated software delivery",[110,113,116,121],{"text":22,"config":111},{"href":112,"dataGaLocation":44,"dataGaName":22},"/de-de/solutions/continuous-integration/",{"text":75,"config":114},{"href":80,"dataGaLocation":44,"dataGaName":115},"gitlab duo agent platform - product menu",{"text":117,"config":118},"Quellcodeverwaltung",{"href":119,"dataGaLocation":44,"dataGaName":120},"/de-de/solutions/source-code-management/","Source Code Management",{"text":122,"config":123},"Automatische Softwarebereitstellung",{"href":107,"dataGaLocation":44,"dataGaName":124},"Automated software delivery",{"title":126,"description":127,"link":128,"items":133},"Sicherheit","Entwickle Code schneller ohne Abstriche bei der Sicherheit",{"config":129},{"href":130,"dataGaName":131,"dataGaLocation":44,"icon":132},"/de-de/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[134,138,143],{"text":135,"config":136},"Anwendungssicherheitstests",{"href":130,"dataGaName":137,"dataGaLocation":44},"Application security testing",{"text":139,"config":140},"Schutz der Software-Lieferkette",{"href":141,"dataGaLocation":44,"dataGaName":142},"/de-de/solutions/supply-chain/","Software supply chain security",{"text":144,"config":145},"Software-Compliance",{"href":146,"dataGaName":147,"dataGaLocation":44},"/de-de/solutions/software-compliance/","software compliance",{"title":149,"link":150,"items":155},"Auswertung",{"config":151},{"icon":152,"href":153,"dataGaName":154,"dataGaLocation":44},"DigitalTransformation","/de-de/solutions/visibility-measurement/","visibility and measurement",[156,160,165],{"text":157,"config":158},"Sichtbarkeit und Auswertung",{"href":153,"dataGaLocation":44,"dataGaName":159},"Visibility and Measurement",{"text":161,"config":162},"Wertstrommanagement",{"href":163,"dataGaLocation":44,"dataGaName":164},"/de-de/solutions/value-stream-management/","Value Stream Management",{"text":166,"config":167},"Analysen und Einblicke",{"href":168,"dataGaLocation":44,"dataGaName":169},"/de-de/solutions/analytics-and-insights/","Analytics and insights",{"title":171,"type":95,"items":172},"GitLab für",[173,179,185],{"text":174,"config":175},"Enterprise",{"icon":176,"href":177,"dataGaLocation":44,"dataGaName":178},"Building","/de-de/enterprise/","enterprise",{"text":180,"config":181},"Kleinunternehmen",{"icon":182,"href":183,"dataGaLocation":44,"dataGaName":184},"Work","/de-de/small-business/","small business",{"text":186,"config":187},"Öffentlicher Sektor",{"icon":188,"href":189,"dataGaLocation":44,"dataGaName":190},"Organization","/de-de/solutions/public-sector/","public sector",{"text":192,"config":193},"Preise",{"href":194,"dataGaName":195,"dataGaLocation":44,"dataNavLevelOne":195},"/de-de/pricing/","pricing",{"text":197,"config":198,"menu":200},"Ressourcen",{"dataNavLevelOne":199},"resources",{"type":95,"link":201,"columns":205,"feature":285},{"text":202,"config":203},"Alle Ressourcen anzeigen",{"href":204,"dataGaName":199,"dataGaLocation":44},"/de-de/resources/",[206,239,257],{"title":207,"items":208},"Erste Schritte",[209,214,219,224,229,234],{"text":210,"config":211},"Installieren",{"href":212,"dataGaName":213,"dataGaLocation":44},"/de-de/install/","install",{"text":215,"config":216},"Kurzanleitungen",{"href":217,"dataGaName":218,"dataGaLocation":44},"/de-de/get-started/","quick setup checklists",{"text":220,"config":221},"Lernen",{"href":222,"dataGaLocation":44,"dataGaName":223},"https://university.gitlab.com/","learn",{"text":225,"config":226},"Produktdokumentation",{"href":227,"dataGaName":228,"dataGaLocation":44},"https://docs.gitlab.com/","product documentation",{"text":230,"config":231},"Best-Practice-Videos",{"href":232,"dataGaName":233,"dataGaLocation":44},"/de-de/getting-started-videos/","best practice videos",{"text":235,"config":236},"Integrationen",{"href":237,"dataGaName":238,"dataGaLocation":44},"/de-de/integrations/","integrations",{"title":240,"items":241},"Entdecken",[242,247,252],{"text":243,"config":244},"Kundenerfolge",{"href":245,"dataGaName":246,"dataGaLocation":44},"/de-de/customers/","customer success stories",{"text":248,"config":249},"Blog",{"href":250,"dataGaName":251,"dataGaLocation":44},"/de-de/blog/","blog",{"text":253,"config":254},"Remote",{"href":255,"dataGaName":256,"dataGaLocation":44},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":258,"items":259},"Vernetzen",[260,265,270,275,280],{"text":261,"config":262},"GitLab Services",{"href":263,"dataGaName":264,"dataGaLocation":44},"/de-de/services/","services",{"text":266,"config":267},"Community",{"href":268,"dataGaName":269,"dataGaLocation":44},"/community/","community",{"text":271,"config":272},"Forum",{"href":273,"dataGaName":274,"dataGaLocation":44},"https://forum.gitlab.com/","forum",{"text":276,"config":277},"Veranstaltungen",{"href":278,"dataGaName":279,"dataGaLocation":44},"/events/","events",{"text":281,"config":282},"Partner",{"href":283,"dataGaName":284,"dataGaLocation":44},"/de-de/partners/","partners",{"config":286,"text":289,"image":290,"link":294},{"background":287,"textColor":288},"#2f2a6b","#fff","Perspektiven für die Softwareentwicklung der Zukunft",{"altText":291,"config":292},"The Source Promo-Karte",{"src":293},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":295,"config":296},"Aktuelles",{"href":297,"dataGaName":298,"dataGaLocation":44},"/de-de/the-source/","the source",{"text":300,"config":301,"menu":303},"Unternehmen",{"dataNavLevelOne":302},"company",{"type":95,"columns":304},[305],{"items":306},[307,312,318,320,325,330,335,340,345,350,355],{"text":308,"config":309},"Über",{"href":310,"dataGaName":311,"dataGaLocation":44},"/de-de/company/","about",{"text":313,"config":314,"footerGa":317},"Karriere",{"href":315,"dataGaName":316,"dataGaLocation":44},"/jobs/","jobs",{"dataGaName":316},{"text":276,"config":319},{"href":278,"dataGaName":279,"dataGaLocation":44},{"text":321,"config":322},"Geschäftsführung",{"href":323,"dataGaName":324,"dataGaLocation":44},"/company/team/e-group/","leadership",{"text":326,"config":327},"Team",{"href":328,"dataGaName":329,"dataGaLocation":44},"/company/team/","team",{"text":331,"config":332},"Handbuch",{"href":333,"dataGaName":334,"dataGaLocation":44},"https://handbook.gitlab.com/","handbook",{"text":336,"config":337},"Investor Relations",{"href":338,"dataGaName":339,"dataGaLocation":44},"https://ir.gitlab.com/","investor relations",{"text":341,"config":342},"Trust Center",{"href":343,"dataGaName":344,"dataGaLocation":44},"/de-de/security/","trust center",{"text":346,"config":347},"AI Transparency Center",{"href":348,"dataGaName":349,"dataGaLocation":44},"/de-de/ai-transparency-center/","ai transparency center",{"text":351,"config":352},"Newsletter",{"href":353,"dataGaName":354,"dataGaLocation":44},"/company/contact/#contact-forms","newsletter",{"text":356,"config":357},"Presse",{"href":358,"dataGaName":359,"dataGaLocation":44},"/press/","press",{"text":361,"config":362,"menu":363},"Kontakt",{"dataNavLevelOne":302},{"type":95,"columns":364},[365],{"items":366},[367,370,375],{"text":51,"config":368},{"href":53,"dataGaName":369,"dataGaLocation":44},"talk to sales",{"text":371,"config":372},"Support-Portal",{"href":373,"dataGaName":374,"dataGaLocation":44},"https://support.gitlab.com","support portal",{"text":376,"config":377},"Kundenportal",{"href":378,"dataGaName":379,"dataGaLocation":44},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":381,"login":382,"suggestions":389},"Schließen",{"text":383,"link":384},"Um Repositorys und Projekte zu durchsuchen, melde dich an bei",{"text":385,"config":386},"gitlab.com",{"href":58,"dataGaName":387,"dataGaLocation":388},"search login","search",{"text":390,"default":391},"Vorschläge",[392,394,399,401,406,411],{"text":75,"config":393},{"href":80,"dataGaName":75,"dataGaLocation":388},{"text":395,"config":396},"Codevorschläge (KI)",{"href":397,"dataGaName":398,"dataGaLocation":388},"/de-de/solutions/code-suggestions/","Code Suggestions (AI)",{"text":22,"config":400},{"href":112,"dataGaName":22,"dataGaLocation":388},{"text":402,"config":403},"GitLab auf AWS",{"href":404,"dataGaName":405,"dataGaLocation":388},"/de-de/partners/technology-partners/aws/","GitLab on AWS",{"text":407,"config":408},"GitLab auf Google Cloud",{"href":409,"dataGaName":410,"dataGaLocation":388},"/de-de/partners/technology-partners/google-cloud-platform/","GitLab on Google Cloud",{"text":83,"config":412},{"href":88,"dataGaName":413,"dataGaLocation":388},"Why GitLab?",{"freeTrial":415,"mobileIcon":420,"desktopIcon":425,"secondaryButton":428},{"text":416,"config":417},"Kostenlos testen",{"href":418,"dataGaName":49,"dataGaLocation":419},"https://gitlab.com/-/trials/new/","nav",{"altText":421,"config":422},"GitLab-Symbol",{"src":423,"dataGaName":424,"dataGaLocation":419},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":421,"config":426},{"src":427,"dataGaName":424,"dataGaLocation":419},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":207,"config":429},{"href":430,"dataGaName":431,"dataGaLocation":419},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/de-de/get-started/","get started",{"freeTrial":433,"mobileIcon":437,"desktopIcon":439},{"text":434,"config":435},"Mehr über GitLab Duo erfahren",{"href":80,"dataGaName":436,"dataGaLocation":419},"gitlab duo",{"altText":421,"config":438},{"src":423,"dataGaName":424,"dataGaLocation":419},{"altText":421,"config":440},{"src":427,"dataGaName":424,"dataGaLocation":419},{"button":442,"mobileIcon":447,"desktopIcon":449},{"text":443,"config":444},"/Option",{"href":445,"dataGaName":446,"dataGaLocation":419},"#contact","switch",{"altText":421,"config":448},{"src":423,"dataGaName":424,"dataGaLocation":419},{"altText":421,"config":450},{"src":451,"dataGaName":424,"dataGaLocation":419},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":453,"mobileIcon":458,"desktopIcon":460},{"text":454,"config":455},"Zurück zur Preisübersicht",{"href":194,"dataGaName":456,"dataGaLocation":419,"icon":457},"back to pricing","GoBack",{"altText":421,"config":459},{"src":423,"dataGaName":424,"dataGaLocation":419},{"altText":421,"config":461},{"src":427,"dataGaName":424,"dataGaLocation":419},{"title":463,"button":464,"config":469},"Sieh dir an, wie agentische KI die Softwarebereitstellung transformiert",{"text":465,"config":466},"Für GitLab Transcend am 10. Juni anmelden",{"href":467,"dataGaName":468,"dataGaLocation":44},"/de-de/releases/whats-new/#sign-up","transcend event",{"layout":470,"icon":471,"disabled":13},"release","AiStar",{"data":473},{"text":474,"source":475,"edit":481,"contribute":486,"config":491,"items":496,"minimal":698},"Git ist eine Marke von Software Freedom Conservancy und unsere Verwendung von „GitLab“ erfolgt unter Lizenz.",{"text":476,"config":477},"Quelltext der Seite anzeigen",{"href":478,"dataGaName":479,"dataGaLocation":480},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":482,"config":483},"Diese Seite bearbeiten",{"href":484,"dataGaName":485,"dataGaLocation":480},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":487,"config":488},"Beteilige dich",{"href":489,"dataGaName":490,"dataGaLocation":480},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":492,"facebook":493,"youtube":494,"linkedin":495},"https://x.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[497,542,594,636,663],{"title":192,"links":498,"subMenu":513},[499,503,508],{"text":500,"config":501},"Tarife anzeigen",{"href":194,"dataGaName":502,"dataGaLocation":480},"view plans",{"text":504,"config":505},"Vorteile von Premium",{"href":506,"dataGaName":507,"dataGaLocation":480},"/de-de/pricing/premium/","why premium",{"text":509,"config":510},"Vorteile von Ultimate",{"href":511,"dataGaName":512,"dataGaLocation":480},"/de-de/pricing/ultimate/","why ultimate",[514],{"title":361,"links":515},[516,518,520,522,527,532,537],{"text":51,"config":517},{"href":53,"dataGaName":54,"dataGaLocation":480},{"text":371,"config":519},{"href":373,"dataGaName":374,"dataGaLocation":480},{"text":376,"config":521},{"href":378,"dataGaName":379,"dataGaLocation":480},{"text":523,"config":524},"Status",{"href":525,"dataGaName":526,"dataGaLocation":480},"https://status.gitlab.com/","status",{"text":528,"config":529},"Nutzungsbedingungen",{"href":530,"dataGaName":531,"dataGaLocation":480},"/terms/","terms of use",{"text":533,"config":534},"Datenschutzerklärung",{"href":535,"dataGaName":536,"dataGaLocation":480},"/de-de/privacy/","privacy statement",{"text":538,"config":539},"Cookie-Einstellungen",{"dataGaName":540,"dataGaLocation":480,"id":541,"isOneTrustButton":28},"cookie preferences","ot-sdk-btn",{"title":91,"links":543,"subMenu":552},[544,548],{"text":545,"config":546},"DevSecOps-Plattform",{"href":73,"dataGaName":547,"dataGaLocation":480},"devsecops platform",{"text":549,"config":550},"KI-unterstützte Entwicklung",{"href":80,"dataGaName":551,"dataGaLocation":480},"ai-assisted development",[553],{"title":554,"links":555},"Themen",[556,559,564,569,574,579,584,589],{"text":22,"config":557},{"href":558,"dataGaName":36,"dataGaLocation":480},"/de-de/topics/ci-cd/",{"text":560,"config":561},"GitOps",{"href":562,"dataGaName":563,"dataGaLocation":480},"/de-de/topics/gitops/","gitops",{"text":565,"config":566},"DevOps",{"href":567,"dataGaName":568,"dataGaLocation":480},"/de-de/topics/devops/","devops",{"text":570,"config":571},"Versionskontrolle",{"href":572,"dataGaName":573,"dataGaLocation":480},"/de-de/topics/version-control/","version control",{"text":575,"config":576},"DevSecOps",{"href":577,"dataGaName":578,"dataGaLocation":480},"/de-de/topics/devsecops/","devsecops",{"text":580,"config":581},"Cloud-nativ",{"href":582,"dataGaName":583,"dataGaLocation":480},"/de-de/topics/cloud-native/","cloud native",{"text":585,"config":586},"KI für das Programmieren",{"href":587,"dataGaName":588,"dataGaLocation":480},"/de-de/topics/devops/ai-for-coding/","ai for coding",{"text":590,"config":591},"Agentische KI",{"href":592,"dataGaName":593,"dataGaLocation":480},"/de-de/topics/agentic-ai/","agentic ai",{"title":595,"links":596},"Lösungen",[597,600,602,607,611,614,617,620,622,624,626,631],{"text":135,"config":598},{"href":130,"dataGaName":599,"dataGaLocation":480},"Application Security Testing",{"text":122,"config":601},{"href":107,"dataGaName":108,"dataGaLocation":480},{"text":603,"config":604},"Agile Entwicklung",{"href":605,"dataGaName":606,"dataGaLocation":480},"/de-de/solutions/agile-delivery/","agile delivery",{"text":608,"config":609},"SCM",{"href":119,"dataGaName":610,"dataGaLocation":480},"source code management",{"text":22,"config":612},{"href":112,"dataGaName":613,"dataGaLocation":480},"continuous integration & delivery",{"text":161,"config":615},{"href":163,"dataGaName":616,"dataGaLocation":480},"value stream management",{"text":560,"config":618},{"href":619,"dataGaName":563,"dataGaLocation":480},"/de-de/solutions/gitops/",{"text":174,"config":621},{"href":177,"dataGaName":178,"dataGaLocation":480},{"text":180,"config":623},{"href":183,"dataGaName":184,"dataGaLocation":480},{"text":186,"config":625},{"href":189,"dataGaName":190,"dataGaLocation":480},{"text":627,"config":628},"Bildungswesen",{"href":629,"dataGaName":630,"dataGaLocation":480},"/de-de/solutions/education/","education",{"text":632,"config":633},"Finanzdienstleistungen",{"href":634,"dataGaName":635,"dataGaLocation":480},"/de-de/solutions/finance/","financial services",{"title":197,"links":637},[638,640,642,644,647,649,651,653,655,657,659,661],{"text":210,"config":639},{"href":212,"dataGaName":213,"dataGaLocation":480},{"text":215,"config":641},{"href":217,"dataGaName":218,"dataGaLocation":480},{"text":220,"config":643},{"href":222,"dataGaName":223,"dataGaLocation":480},{"text":225,"config":645},{"href":227,"dataGaName":646,"dataGaLocation":480},"docs",{"text":248,"config":648},{"href":250,"dataGaName":251,"dataGaLocation":480},{"text":243,"config":650},{"href":245,"dataGaName":246,"dataGaLocation":480},{"text":253,"config":652},{"href":255,"dataGaName":256,"dataGaLocation":480},{"text":261,"config":654},{"href":263,"dataGaName":264,"dataGaLocation":480},{"text":266,"config":656},{"href":268,"dataGaName":269,"dataGaLocation":480},{"text":271,"config":658},{"href":273,"dataGaName":274,"dataGaLocation":480},{"text":276,"config":660},{"href":278,"dataGaName":279,"dataGaLocation":480},{"text":281,"config":662},{"href":283,"dataGaName":284,"dataGaLocation":480},{"title":300,"links":664},[665,667,669,671,673,675,677,682,687,689,691,693],{"text":308,"config":666},{"href":310,"dataGaName":302,"dataGaLocation":480},{"text":313,"config":668},{"href":315,"dataGaName":316,"dataGaLocation":480},{"text":321,"config":670},{"href":323,"dataGaName":324,"dataGaLocation":480},{"text":326,"config":672},{"href":328,"dataGaName":329,"dataGaLocation":480},{"text":331,"config":674},{"href":333,"dataGaName":334,"dataGaLocation":480},{"text":336,"config":676},{"href":338,"dataGaName":339,"dataGaLocation":480},{"text":678,"config":679},"Nachhaltigkeit",{"href":680,"dataGaName":681,"dataGaLocation":480},"/sustainability/","Sustainability",{"text":683,"config":684},"Vielfalt, Inklusion und Zugehörigkeit",{"href":685,"dataGaName":686,"dataGaLocation":480},"/de-de/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":341,"config":688},{"href":343,"dataGaName":344,"dataGaLocation":480},{"text":351,"config":690},{"href":353,"dataGaName":354,"dataGaLocation":480},{"text":356,"config":692},{"href":358,"dataGaName":359,"dataGaLocation":480},{"text":694,"config":695},"Transparenzerklärung zu moderner Sklaverei",{"href":696,"dataGaName":697,"dataGaLocation":480},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":699},[700,702,705],{"text":528,"config":701},{"href":530,"dataGaName":531,"dataGaLocation":480},{"text":703,"config":704},"Cookies",{"dataGaName":540,"dataGaLocation":480,"id":541,"isOneTrustButton":28},{"text":533,"config":706},{"href":535,"dataGaName":536,"dataGaLocation":480},[708],{"id":709,"title":9,"body":26,"config":710,"content":713,"description":26,"extension":25,"meta":717,"navigation":28,"path":718,"seo":719,"stem":720,"__hash__":721},"blogAuthors/en-us/blog/authors/paul-meresanu.yml",{"template":711,"gitlabHandle":712},"BlogAuthor","pmeresanu",{"name":9,"role":714,"config":715},"",{"headshot":716},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1750267141/qpw5ayteg0sewyh7s8xi.png",{},"/en-us/blog/authors/paul-meresanu",{},"en-us/blog/authors/paul-meresanu","S41feQ9U81y4EVDx5pEECPqpIEiKTCDLBOmNwjLyE1E",[723,737,749],{"content":724,"config":735},{"body":725,"title":726,"description":727,"authors":728,"heroImage":730,"date":731,"category":11,"tags":732},"## Abschnitt 1: Das Modell verstehen\n*Für Engineering-Leads und Entscheidungsträger: Konzept, Anwendungsfälle und Architekturprinzipien. Konfigurationsdetails folgen in Abschnitt 2.*\n\nDie meisten CI/CD-Werkzeuge können einen Build ausführen und ein Deployment anstoßen. Der Unterschied zeigt sich erst dann, wenn die Delivery-Anforderungen komplexer werden: ein Monorepo mit einem Dutzend Services, Microservices über mehrere Repositories verteilt, Deployments in Dutzende von Umgebungen gleichzeitig – oder ein Platform-Team, das organisationsweite Standards durchsetzen will, ohne dabei zum Engpass zu werden.\n\nGitLabs Pipeline-Modell wurde für genau diese Komplexität entwickelt. Parent-Child-Pipelines, DAG-Execution, dynamische Pipeline-Generierung, Multi-Project-Trigger, Merge-Request-Pipelines mit Merged-Results-Verarbeitung und CI/CD Components lösen jeweils eine eigene Klasse von Problemen. Da sich diese Bausteine kombinieren lassen, erschließt das vollständige Modell mehr als nur kürzere Pipeline-Laufzeiten.\n\nDieser Artikel beschreibt die fünf Muster, bei denen das Modell seine Stärken deutlich zeigt – jeweils zugeordnet zu einem konkreten Engineering-Szenario. Konfigurationen und Implementierungsdetails folgen in Abschnitt 2.\n\n### 1. Monorepos: Parent-Child-Pipelines und DAG-Execution\n\n**Das Problem:** Ein Monorepo enthält Frontend, Backend und Dokumentation. Jeder Commit löst einen vollständigen Rebuild aller Komponenten aus – auch wenn sich nur eine README-Datei geändert hat.\n\nGitLab kombiniert zwei sich ergänzende Mechanismen: [Parent-Child-Pipelines](https://docs.gitlab.com/ci/pipelines/downstream_pipelines/#parent-child-pipelines) ermöglichen es einer übergeordneten Pipeline, isolierte Child-Pipelines zu starten. [DAG-Execution via `needs`](https://docs.gitlab.com/ci/yaml/#needs) bricht die starre Stage-Reihenfolge auf und startet Jobs, sobald ihre Abhängigkeiten abgeschlossen sind – nicht erst, wenn alle Jobs einer Stage fertig sind.\n\nEine Parent-Pipeline erkennt, welche Teile des Repos sich geändert haben, und löst ausschließlich die betroffenen Child-Pipelines aus. Jeder Service verwaltet seine eigene Pipeline-Konfiguration; Änderungen in einem Service können keine anderen beeinflussen. Damit bleibt die Komplexität beherrschbar, während das Repository und das Team wachsen.\n\nEinen technischen Aspekt gilt es dabei zu kennen: Wenn mehrere Dateien an einen einzelnen `trigger: include:`-Block übergeben werden, fusioniert GitLab sie zu einer einzigen Child-Pipeline-Konfiguration. Jobs aus diesen Dateien teilen denselben Pipeline-Kontext und können sich gegenseitig per `needs:` referenzieren – das ist die Voraussetzung für die DAG-Optimierung. Werden die Dateien stattdessen auf separate Trigger-Jobs aufgeteilt, entsteht jeweils eine isolierte Pipeline, und dateiübergreifende `needs:`-Referenzen funktionieren nicht.\n\nIn großen Monorepos lassen sich Pipeline-Laufzeiten durch DAG-Execution deutlich reduzieren, da Jobs nicht mehr auf unabhängige Arbeitsschritte in derselben Stage warten.\n\n### 2. Microservices: Cross-Repo-Pipelines über mehrere Projekte\n\n**Das Problem:** Frontend und Backend leben in separaten Repositories. Wenn das Frontend-Team eine Änderung ausliefert, ist nicht erkennbar, ob sie die Backend-Integration beeinträchtigt – und umgekehrt.\n\n[Multi-Project-Pipelines](https://docs.gitlab.com/ci/pipelines/downstream_pipelines/#multi-project-pipelines) ermöglichen es, aus einem Projekt heraus eine Pipeline in einem anderen Projekt auszulösen und auf das Ergebnis zu warten. Das auslösende Projekt sieht die verknüpfte Downstream-Pipeline direkt in seiner eigenen Pipeline-Ansicht.\n\nIn der Praxis erstellt die Frontend-Pipeline ein API-Contract-Artifact und veröffentlicht es, bevor die Backend-Pipeline ausgelöst wird. Das Backend ruft dieses Artifact über die [Jobs API](https://docs.gitlab.com/api/jobs/#download-a-single-artifact-file-from-specific-tag-or-branch) ab und validiert es, bevor weitere Schritte erlaubt sind. Wird eine Breaking Change erkannt, schlägt die Backend-Pipeline fehl – und mit ihr die Frontend-Pipeline. Probleme, die bisher erst in der Produktion sichtbar wurden, werden damit im Pipeline-Prozess abgefangen. Die Abhängigkeit zwischen Services wird sichtbar, nachvollziehbar und aktiv verwaltbar.\n\n![Cross-project pipelines](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738762/Blog/Imported/hackathon-fake-blog-post-s/image4_h6mfsb.png \"Cross-project pipelines\") *Cross-project pipelines*\n\n### 3. Multi-Tenant/Matrix-Deployments: Dynamische Child-Pipelines\n\n**Das Problem:** Dieselbe Anwendung wird in 15 Kundenumgebungen, drei Cloud-Regionen oder den Stages Dev/Staging/Prod deployed. Manuelle Anpassungen je Umgebung führen zu Konfigurationsdrift. Eine separate Pipeline pro Umgebung ist von Anfang an nicht wartbar.\n\n[Dynamische Child-Pipelines](https://docs.gitlab.com/ci/pipelines/downstream_pipelines/#dynamic-child-pipelines) generieren die Pipeline-Struktur zur Laufzeit. Ein Job führt ein Skript aus, das eine YAML-Datei erzeugt – und diese YAML-Datei wird zur Pipeline für den nächsten Schritt. Die Pipeline-Struktur selbst wird damit zu Daten.\n\nDas Generierungsskript iteriert über eine `ENVIRONMENTS`-Variable, statt jede Umgebung fest zu kodieren. Eine neue Umgebung lässt sich durch Anpassen der Variable hinzufügen – ohne Änderungen an der Pipeline-Konfiguration selbst. Trigger-Jobs erben mit `extends:` eine gemeinsame Template-Konfiguration, sodass `strategy: depend` einmal definiert und nicht für jeden Trigger-Job wiederholt wird. Ein `when: manual`-Gate für das Produktions-Deployment ist direkt in den Pipeline-Graph integriert.\n\nPlatform-Teams nutzen dieses Muster, um Dutzende von Umgebungen zu verwalten, ohne Pipeline-Logik zu duplizieren.\n\n![Dynamic pipeline](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738765/Blog/Imported/hackathon-fake-blog-post-s/image7_wr0kx2.png \"Dynamic pipeline\")\n\n### 4. MR-First-Delivery: Merge-Request-Pipelines, Merged-Results und Workflow-Routing\n\n**Das Problem:** Die Pipeline läuft bei jedem Push auf jeden Branch. Aufwändige Tests werden auf Feature-Branches ausgeführt, die nie gemergt werden. Gleichzeitig gibt es keine Garantie, dass das Getestete dem entspricht, was nach dem Merge auf `main` tatsächlich landet.\n\nGitLab kombiniert drei ineinandergreifende Mechanismen: [Merge-Request-Pipelines](https://docs.gitlab.com/ci/pipelines/merge_request_pipelines/) laufen ausschließlich dann, wenn ein Merge Request existiert – nicht bei jedem Branch-Push. Allein dadurch entfällt ein erheblicher Anteil unnötiger Compute-Ausführungen. [Merged-Results-Pipelines](https://docs.gitlab.com/ci/pipelines/merged_results_pipelines/) gehen einen Schritt weiter: GitLab erstellt einen temporären Merge-Commit aus dem Branch und dem aktuellen Ziel-Branch und führt die Pipeline dagegen aus. Getestet wird damit das tatsächliche Ergebnis des Merges – nicht der Branch in Isolation. [Workflow-Rules](https://docs.gitlab.com/ci/yaml/workflow/) definieren schließlich, welcher Pipeline-Typ unter welchen Bedingungen ausgeführt wird. Die `$CI_OPEN_MERGE_REQUESTS`-Guard verhindert dabei, dass für einen Branch mit offenem MR doppelte Pipelines ausgelöst werden.\n\nDas Ergebnis ist ein Pipeline-Verhalten, das sich je nach Kontext unterscheidet: Ein Push auf einen Feature-Branch ohne offenen MR führt nur Lint und Unit-Tests aus. Sobald ein MR geöffnet wird, wechseln die Workflow-Rules auf eine MR-Pipeline mit der vollständigen Test-Suite gegen das Merged-Result. Ein Merge auf `main` stellt ein manuelles Produktions-Deployment in die Warteschlange. Der Nightly-Scan läuft einmalig als geplante Pipeline – nicht bei jedem Commit.\n\nMerged-Results-Pipelines fangen dabei die Klasse von Fehlern ab, die erst nach einem Merge sichtbar werden – bevor sie `main` erreichen.\n\n### 5. Governed Pipelines: CI/CD Components\n\n**Das Problem:** Das Platform-Team hat den richtigen Weg für Build, Test und Deploy definiert. Jedes Anwendungsteam pflegt jedoch eine eigene `.gitlab-ci.yml` mit subtilen Abweichungen. Security-Scanning wird übersprungen. Deployment-Standards driften. Audits werden aufwändig.\n\n[CI/CD Components](https://docs.gitlab.com/ci/components/) ermöglichen es Platform-Teams, versionierte, wiederverwendbare Pipeline-Bausteine zu veröffentlichen. Anwendungsteams binden sie mit einer einzigen `include:`-Zeile ein – kein Copy-Paste, kein Drift. Components sind über den [CI/CD Catalog](https://docs.gitlab.com/ci/components/#cicd-catalog) auffindbar, sodass Teams bewährte Bausteine finden und übernehmen können, ohne das Platform-Team direkt einschalten zu müssen.\n\nDrei Zeilen `include:` ersetzen hunderte von duplizierten YAML-Zeilen. Das Platform-Team kann einen Security-Fix in einer neuen Komponentenversion veröffentlichen – Teams steigen auf ihrem eigenen Zeitplan um, oder das Platform-Team fixiert alle auf eine Mindestversion. In beiden Fällen propagiert eine Änderung organisationsweit, statt repo-für-repo angewendet zu werden.\n\nKombiniert mit [Resource Groups](https://docs.gitlab.com/ci/resource_groups/) zur Vermeidung konkurrierender Deployments und [Protected Environments](https://docs.gitlab.com/ci/environments/protected_environments/) für Freigabe-Gates entsteht eine governed Delivery-Plattform, auf der **Compliance der Standard ist, nicht die Ausnahme**. Platform-Teams setzen Vorgaben durch, ohne zum Engpass zu werden.\n\n![Component pipeline (imported jobs)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738776/Blog/Imported/hackathon-fake-blog-post-s/image2_pizuxd.png \"Component pipeline (imported jobs)\")\n\n## Das Modell als Ganzes\n\nKeines dieser Muster existiert isoliert. Der Wert von GitLabs Pipeline-Modell liegt in der Kombinierbarkeit seiner Bausteine:\n\n- Ein Monorepo nutzt Parent-Child-Pipelines, und jede Child-Pipeline nutzt DAG-Execution.\n- Eine Microservices-Plattform nutzt Multi-Project-Pipelines, und jedes Projekt nutzt MR-Pipelines mit Merged-Results.\n- Eine governed Plattform nutzt CI/CD Components, um die obigen Muster organisationsweit zu standardisieren.\n\nDie meisten Teams entdecken eines dieser Muster, wenn sie auf ein konkretes Problem stoßen. Teams, die das vollständige Modell verstehen, entwickeln daraus eine Delivery-Infrastruktur, die tatsächlich abbildet, wie ihre Engineering-Organisation arbeitet – und mit ihr wächst.\n\n## Weitere Muster\n\nDas Pipeline-Modell geht über die fünf vorgestellten Muster hinaus:\n\n- [Review Apps mit dynamischen Umgebungen](https://docs.gitlab.com/ci/environments/) erstellen für jeden Feature-Branch eine Live-Vorschau und räumen sie automatisch auf, wenn der MR geschlossen wird.\n- [Caching- und Artifact-Strategien](https://docs.gitlab.com/ci/caching/) sind nach der strukturellen Arbeit häufig der direkteste Weg zur weiteren Laufzeitoptimierung – ohne die Pipeline-Struktur zu verändern.\n- [Geplante und API-ausgelöste Pipelines](https://docs.gitlab.com/ci/pipelines/schedules/) eignen sich für Workloads, die nicht bei jedem Code-Push laufen sollten: Nightly-Security-Scans, Compliance-Reports und Release-Automatisierung lassen sich als geplante oder [API-ausgelöste](https://docs.gitlab.com/ci/triggers/) Pipelines mit `$CI_PIPELINE_SOURCE`-Routing modellieren.\n\n> [GitLab Ultimate kostenlos testen](https://about.gitlab.com/de-de/free-trial/) und Pipeline-Logik ab heute einsetzen.\n\n## Für deutsche Unternehmen: Regulatorischer Kontext\n\nTeams, die Pipeline-Governance nach Muster 5 einführen, adressieren dabei möglicherweise auch Anforderungen, die regulatorische Frameworks an sichere Softwareentwicklungsprozesse stellen.\n\nCI/CD Components mit erzwungenen Security-Gates könnten Anforderungen an sichere Entwicklungsprozesse betreffen – beispielsweise in Bereichen, die Frameworks wie NIS2, ISO 27001 oder BSI IT-Grundschutz an den Software-Entwicklungslebenszyklus adressieren. Protected Environments und Resource Groups betreffen ähnliche Themen im Bereich Änderungskontrolle und Umgebungstrennung, wie sie in Governance-Frameworks typischerweise explizit formuliert sind.\n\nMulti-Project-Pipelines mit API-Contract-Validierung (Muster 2) schaffen Sichtbarkeit über Service-Abhängigkeiten hinweg – ein Aspekt, den Frameworks zur Lieferkettensicherheit adressieren.\n\nMerged-Results-Pipelines (Muster 4) dokumentieren automatisch, dass das tatsächliche Merge-Ergebnis getestet wurde, nicht nur der Feature-Branch in Isolation. Dies könnte Anforderungen an nachvollziehbare Änderungsprozesse betreffen, wie sie in Change-Management-Kontrollen verschiedener Sicherheitsframeworks formuliert sind.\n\nFür konkrete Compliance-Anforderungen im eigenen regulatorischen Umfeld empfiehlt sich Rücksprache mit entsprechender Fachberatung.\n\n## Abschnitt 2: Konfiguration und Implementierung\n\n*Für Entwicklungsteams und DevOps-Praktiker: ausgewählte Konfigurationsbeispiele zu den Mustern 1, 4 und 5. Für vollständige Konfigurationen aller Muster: [englischer Originalartikel](https://about.gitlab.com/blog/5-ways-gitlab-pipeline-logic-solves-real-engineering-problems/).*\n\nDie folgenden Konfigurationen sind illustrativ aufgebaut. Die Skripte verwenden `echo`-Befehle, um das Wesentliche sichtbar zu halten. Für den produktiven Einsatz werden die `echo`-Befehle durch die tatsächlichen Build-, Test- und Deploy-Schritte ersetzt.\n\n### Muster 1: Parent-Child-Pipelines und DAG-Execution\n\nEine Parent-Pipeline erkennt Änderungen und löst nur die betroffenen Child-Pipelines aus:\n\n```yaml # .gitlab-ci.yml stages:\n  - trigger\n\ntrigger-services:\n  stage: trigger\n  trigger:\n    include:\n      - local: '.gitlab/ci/api-service.yml'\n      - local: '.gitlab/ci/web-service.yml'\n      - local: '.gitlab/ci/worker-service.yml'\n    strategy: depend\n```\n\nInnerhalb der Child-Pipeline ermöglicht `needs:` DAG-Execution – der Test startet, sobald der Build abgeschlossen ist, ohne auf andere Jobs in derselben Stage zu warten:\n\n```yaml # .gitlab/ci/api-service.yml stages:\n  - build\n  - test\n\nbuild-api:\n  stage: build\n  script:\n    - echo \"Building API service\"\n\ntest-api:\n  stage: test\n  needs: [build-api]\n  script:\n    - echo \"Running API tests\"\n```\n\n![Local downstream pipelines](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738759/Blog/Imported/hackathon-fake-blog-post-s/image3_vwj3rz.png \"Local downstream pipelines\")\n\n### Muster 4: MR-First-Delivery\n\nWorkflow-Rules, MR-Pipelines und Merged-Results zusammen ergeben ein kontextabhängiges Pipeline-Verhalten:\n\n```yaml # .gitlab-ci.yml workflow:\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS\n      when: never\n    - if: $CI_COMMIT_BRANCH\n    - if: $CI_PIPELINE_SOURCE == \"schedule\"\n\nstages:\n  - fast-checks\n  - expensive-tests\n  - deploy\n\nlint-code:\n  stage: fast-checks\n  script:\n    - echo \"Running linter\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"push\"\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\nunit-tests:\n  stage: fast-checks\n  script:\n    - echo \"Running unit tests\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"push\"\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\nintegration-tests:\n  stage: expensive-tests\n  script:\n    - echo \"Running integration tests (15 min)\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\ne2e-tests:\n  stage: expensive-tests\n  script:\n    - echo \"Running E2E tests (30 min)\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH == \"main\"\n\nnightly-comprehensive-scan:\n  stage: expensive-tests\n  script:\n    - echo \"Running full nightly suite (2 hours)\"\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"schedule\"\n\ndeploy-production:\n  stage: deploy\n  script:\n    - echo \"Deploying to production\"\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"\n      when: manual\n```\n\n![Conditional pipelines (within a branch with no MR)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738768/Blog/Imported/hackathon-fake-blog-post-s/image6_dnfcny.png \"Conditional pipelines (within a branch with no MR)\")\n\n![Conditional pipelines (within an MR)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738772/Blog/Imported/hackathon-fake-blog-post-s/image1_wyiafu.png \"Conditional pipelines (within an MR)\")\n\n![Conditional pipelines (on the main branch)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1775738774/Blog/Imported/hackathon-fake-blog-post-s/image5_r6lkfd.png \"Conditional pipelines (on the main branch)\")\n\n### Muster 5: CI/CD Components\n\nEine Komponentendefinition aus einer gemeinsamen Bibliothek:\n\n```yaml # templates/deploy.yml spec:\n  inputs:\n    stage:\n      default: deploy\n    environment:\n      default: production\n--- deploy-job:\n  stage: $[[ inputs.stage ]]\n  script:\n    - echo \"Deploying $APP_NAME to $[[ inputs.environment ]]\"\n    - echo \"Deploy URL: $DEPLOY_URL\"\n  environment:\n    name: $[[ inputs.environment ]]\n```\n\nSo bindet ein Anwendungsteam die Komponenten ein:\n\n```yaml # Application repo: .gitlab-ci.yml variables:\n  APP_NAME: \"my-awesome-app\"\n  DEPLOY_URL: \"https://api.example.com\"\n\ninclude:\n  - component: gitlab.com/my-org/component-library/build@v1.0.6\n  - component: gitlab.com/my-org/component-library/test@v1.0.6\n  - component: gitlab.com/my-org/component-library/deploy@v1.0.6\n    inputs:\n      environment: staging\n\nstages:\n  - build\n  - test\n  - deploy\n```\n\n### Orientierung zu den Mustern 2 und 3\n\n**Muster 2 (Multi-Project-Pipelines):** Das Frontend-Repository publiziert ein API-Contract-Artifact und löst anschließend die Backend-Pipeline aus. Das Backend ruft das Artifact über die GitLab Jobs API ab und validiert es. Der `integration-test`-Job läuft dabei nur dann, wenn er von einer Upstream-Pipeline ausgelöst wurde (`$CI_PIPELINE_SOURCE == \"pipeline\"`), nicht bei einem eigenständigen Push. Die Frontend-Projekt-ID wird als CI/CD-Variable gesetzt, um Hardcoding zu vermeiden. Vollständige Konfigurationen beider Repositories: [englischer Originalartikel](https://about.gitlab.com/blog/5-ways-gitlab-pipeline-logic-solves-real-engineering-problems/#2-microservices-cross-repo-multi-project-pipelines).\n\n**Muster 3 (Dynamische Child-Pipelines):** Ein `generate-config`-Job erzeugt zur Laufzeit environment-spezifische YAML-Dateien. Trigger-Jobs nutzen `extends:` für gemeinsam genutzte Konfiguration und `needs:` für sequenzielle Promotion (dev → staging → prod mit manuellem Gate). Vollständige Konfiguration: [englischer Originalartikel](https://about.gitlab.com/blog/5-ways-gitlab-pipeline-logic-solves-real-engineering-problems/#3-multi-tenant--matrix-deployments-dynamic-child-pipelines).\n\n## Weiterführende Artikel\n\n- [Variable and artifact sharing in GitLab parent-child pipelines](https://about.gitlab.com/blog/variable-and-artifact-sharing-in-gitlab-parent-child-pipelines/)\n- [CI/CD inputs: Secure and preferred method to pass parameters to a pipeline](https://about.gitlab.com/blog/ci-cd-inputs-secure-and-preferred-method-to-pass-parameters-to-a-pipeline/)\n- [Tutorial: How to set up your first GitLab CI/CD component](https://about.gitlab.com/blog/tutorial-how-to-set-up-your-first-gitlab-ci-cd-component/)\n- [How to include file references in your CI/CD components](https://about.gitlab.com/blog/how-to-include-file-references-in-your-ci-cd-components/)\n- [FAQ: GitLab CI/CD Catalog](https://about.gitlab.com/blog/faq-gitlab-ci-cd-catalog/)\n- [Building a GitLab CI/CD pipeline for a monorepo the easy way](https://about.gitlab.com/blog/building-a-gitlab-ci-cd-pipeline-for-a-monorepo-the-easy-way/)\n- [A CI/CD component builder's journey](https://about.gitlab.com/blog/a-ci-component-builders-journey/)\n- [CI/CD Catalog goes GA: No more building pipelines from scratch](https://about.gitlab.com/blog/ci-cd-catalog-goes-ga-no-more-building-pipelines-from-scratch/)","5 GitLab-Pipeline-Muster für komplexe Engineering-Herausforderungen","Wie Parent-Child-Pipelines, DAG-Execution, MR-Pipelines und CI/CD Components komplexe Delivery-Probleme lösen – von Monorepos bis zur governed Plattform.",[729],"Omid Khan","https://res.cloudinary.com/about-gitlab-com/image/upload/v1772721753/frfsm1qfscwrmsyzj1qn.png","2026-04-09",[22,733,24,734],"DevOps platform","features",{"featured":28,"template":14,"slug":736},"5-ways-gitlab-pipeline-logic-solves-real-engineering-problems",{"content":738,"config":747},{"title":739,"description":740,"authors":741,"heroImage":743,"date":744,"body":745,"category":11,"tags":746},"GitLab Container Virtual Registry mit Docker Hardened Images einrichten","Mehrere Registries hinter einem Endpunkt – GitLab Container Virtual Registry mit Docker Hardened Images, Caching und Audit-Trail.",[742],"Tim Rizzi","https://res.cloudinary.com/about-gitlab-com/image/upload/v1772111172/mwhgbjawn62kymfwrhle.png","2026-03-12","Wer im Plattformteam arbeitet, kennt solche Gespräche:\n\n*„Security sagt: Wir müssen gehärtete Base-Images verwenden.\"*\n\n*„Prima – wo trage ich jetzt die Credentials für noch eine weitere Registry ein?\"*\n\n*„Und wie stellen wir sicher, dass alle sie auch wirklich nutzen?\"*\n\nOder diese hier:\n\n*„Warum sind unsere Builds so langsam?\"*\n\n*„Wir pullen dasselbe 500-MB-Image in jedem einzelnen Job neu von Docker Hub.\"*\n\n*„Kann man die nicht irgendwo cachen?\"*\n\nIch arbeite bei GitLab an der [Container Virtual Registry](https://docs.gitlab.com/user/packages/virtual_registry/container/) – einem Pull-Through-Cache, der vor den vorgelagerten Registries sitzt: Docker Hub, dhi.io (Docker Hardened Images), MCR und Quay. Teams erhalten einen einzigen Endpunkt zum Pullen. Images werden beim ersten Abruf gecacht; alle nachfolgenden Pulls kommen aus dem Cache. Das Entwicklungsteam muss nicht wissen, aus welchem Upstream ein bestimmtes Image stammt.\n\nDieser Artikel zeigt die Einrichtung der Container Virtual Registry – mit Docker Hardened Images als konkretem Anwendungsfall, da diese Kombination für Teams mit Sicherheitsanforderungen besonders naheliegt.\n\n## Das Problem: Registry-Wildwuchs im Plattformteam\n\nDie Plattformteams, mit denen ich spreche, verwalten Container-Images über drei bis fünf Registries:\n\n- **Docker Hub** für die meisten Base-Images\n- **dhi.io** für Docker Hardened Images (sicherheitskritische Workloads)\n- **MCR** für .NET- und Azure-Tooling\n- **Quay.io** für das Red-Hat-Ökosystem\n- **Interne Registries** für proprietäre Images\n\nJede davon hat eigene Authentifizierungsmechanismen, unterschiedliche Netzwerklatenz und eine eigene Pfadstruktur für Images.\n\nCI/CD-Konfigurationen füllen sich mit registry-spezifischer Logik. Credential-Management wird zum eigenständigen Projekt. Und jeder Pipeline-Job lädt dieselben Base-Images erneut über das Netz – obwohl sie sich seit Wochen nicht geändert haben.\n\nContainer Virtual Registry konsolidiert das: eine Registry-URL, ein Authentifizierungsfluss über GitLab, gecachte Images aus GitLab-Infrastruktur statt wiederholter Internet-Traversierung.\n\n## Funktionsweise\n\nDas Modell ist geradlinig:\n\n```text\n\nPipeline ruft ab:\n  gitlab.com/virtual_registries/container/1000016/python:3.13\n\nVirtual Registry prüft:\n  1. Im Cache vorhanden? → Direkt zurückgeben\n  2. Nein? → Vom Upstream laden, cachen, zurückgeben\n\n\n```\n\nUpstreams werden in Prioritätsreihenfolge konfiguriert. Bei einem eingehenden Pull-Request durchsucht die Virtual Registry die Upstreams der Reihe nach, bis das Image gefunden wird. Das Ergebnis wird für einen konfigurierbaren Zeitraum gecacht – standardmäßig 24 Stunden.\n\n\n```text\n\n┌─────────────────────────────────────────────────────────┐ │                    CI/CD Pipeline                       │ │                          │                              │ │                          ▼                              │ │   gitlab.com/virtual_registries/container/\u003Cid>/image   │ └─────────────────────────────────────────────────────────┘\n                           │\n                           ▼\n┌─────────────────────────────────────────────────────────┐ │            Container Virtual Registry                   │ │                                                         │ │  Upstream 1: Docker Hub ────────────────┐               │ │  Upstream 2: dhi.io (Hardened) ────────┐│               │ │  Upstream 3: MCR ─────────────────────┐││               │ │  Upstream 4: Quay.io ────────────────┐│││               │ │                                      ││││               │ │                    ┌─────────────────┴┴┴┴──┐            │ │                    │        Cache          │            │ │                    │  (manifests + layers) │            │ │                    └───────────────────────┘            │ └─────────────────────────────────────────────────────────┘\n\n```\n\n## Was das konkret bringt – besonders mit Docker Hardened Images\n\n[Docker Hardened Images](https://docs.docker.com/dhi/) zeichnen sich durch minimale Angriffsfläche, nahezu keine bekannten CVEs, vollständige Software Bills of Materials (SBOMs) und SLSA-Provenance aus. Für Teams, die Base-Images für sicherheitskritische Workloads evaluieren, gehören sie auf die Shortlist.\n\nDer Wechsel zu dhi.io erzeugt jedoch dieselbe operative Reibung wie jede neue Registry:\n\n- **Credential-Verteilung**: Docker-Credentials müssen auf alle Systeme verteilt werden, die Images von dhi.io abrufen.\n- **CI/CD-Anpassungen**: Jede Pipeline muss für die Authentifizierung mit dhi.io aktualisiert werden.\n- **Akzeptanzproblem**: Ohne zentrale Steuerung greifen Teams weiterhin auf reguläre Images zurück.\n- **Fehlende Transparenz**: Ob Teams tatsächlich die gehärteten Varianten nutzen, ist kaum nachvollziehbar.\n\nDie Virtual Registry löst jeden dieser Punkte:\n\n**Einzelne Credential**: Teams authentifizieren sich bei GitLab. Die Virtual Registry übernimmt die Upstream-Authentifizierung. Docker-Credentials werden einmalig auf Registry-Ebene konfiguriert und gelten für alle Pulls.\n\n**Keine per-Team-CI/CD-Änderungen**: Pipelines auf die Virtual Registry zeigen lassen – fertig. Die Upstream-Konfiguration ist zentralisiert.\n\n**Schrittweise Einführung**: Da Images mit ihrem vollständigen Pfad gecacht werden, ist im Cache sichtbar, was tatsächlich abgerufen wird. Wird `library/python:3.11` statt der gehärteten Variante gepullt, ist das erkennbar.\n\n**Audit-Trail**: Der Cache zeigt exakt, welche Images aktiv genutzt werden – nachvollziehbar für Compliance-Zwecke und als Grundlage für das Verständnis der tatsächlichen Infrastruktur-Abhängigkeiten.\n\nWer das Konzept verstanden hat und die Einrichtung zu einem späteren Zeitpunkt in Angriff nimmt: Die wesentlichen Konzepte sind damit abgedeckt. Die technische Konfiguration folgt im nächsten Abschnitt.\n\n## Einrichtung\n\nDie folgende Einrichtung nutzt den Python-Client aus dem Demo-Projekt.\n\n### Virtual Registry erstellen\n\n```python\nfrom virtual_registry_client import VirtualRegistryClient\nclient = VirtualRegistryClient()\nregistry = client.create_virtual_registry(\n    group_id=\"785414\",  # ID der obersten Gruppe\n    name=\"platform-images\",\n    description=\"Cached container images for platform teams\"\n)\nprint(f\"Registry ID: {registry['id']}\") # Diese ID wird für die Pull-URL benötigt\n```\n\n### Docker Hub als Upstream hinzufügen\n\nFür offizielle Images wie Alpine, Python usw.:\n\n```python\n\ndocker_upstream = client.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://registry-1.docker.io\",\n    name=\"Docker Hub\",\n    cache_validity_hours=24\n)\n\n```\n\n### Docker Hardened Images (dhi.io) hinzufügen\n\nDocker Hardened Images werden auf `dhi.io` gehostet – einer separaten Registry mit Authentifizierungspflicht:\n\n```python\n\ndhi_upstream = client.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://dhi.io\",\n    name=\"Docker Hardened Images\",\n    username=\"your-docker-username\",\n    password=\"your-docker-access-token\",\n    cache_validity_hours=24\n)\n\n```\n\n### Weitere Upstreams hinzufügen\n\n```python\n\n# MCR für .NET-Teams client.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://mcr.microsoft.com\",\n    name=\"Microsoft Container Registry\",\n    cache_validity_hours=48\n)\n# Quay für das Red-Hat-Ökosystem client.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://quay.io\",\n    name=\"Quay.io\",\n    cache_validity_hours=24\n)\n\n```\n\n### CI/CD aktualisieren\n\nEine `.gitlab-ci.yml`, die über die Virtual Registry pullt:\n\n```yaml\n\nvariables:\n  VIRTUAL_REGISTRY_ID: \u003Cyour_virtual_registry_ID>\n\n  \nbuild:\n  image: docker:24\n  services:\n    - docker:24-dind\n  before_script:\n    # Authentifizierung bei GitLab – Upstream-Auth wird übernommen\n    - echo \"${CI_JOB_TOKEN}\" | docker login -u gitlab-ci-token --password-stdin gitlab.com\n  script:\n    # Alle Pulls laufen über die zentrale Virtual Registry\n    \n    # Offizielle Docker Hub Images (library/-Präfix erforderlich)\n    - docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/library/alpine:latest\n    \n    # Docker Hardened Images von dhi.io (kein Präfix nötig)\n    - docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/python:3.13\n    \n    # .NET von MCR\n    - docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/dotnet/sdk:8.0\n\n\n```\n\n### Image-Pfadformate\n\nVerschiedene Registries verwenden unterschiedliche Pfadkonventionen:\n\n| Registry | Beispiel-Pull-URL |\n|----------|-------------------|\n| Docker Hub (offiziell) | `.../library/python:3.11-slim` |\n| Docker Hardened Images (dhi.io) | `.../python:3.13` |\n| MCR | `.../dotnet/sdk:8.0` |\n| Quay.io | `.../prometheus/prometheus:latest` |\n\n### Funktionsprüfung\n\nNach einigen Pulls lässt sich der Cache überprüfen:\n\n```python\n\nupstreams = client.list_registry_upstreams(registry['id']) for upstream in upstreams:\n    entries = client.list_cache_entries(upstream['id'])\n    print(f\"{upstream['name']}: {len(entries)} cached entries\")\n\n\n```\n\n## Messergebnisse\n\nTestergebnisse beim Pullen über die Virtual Registry:\n\n| Messgröße | Ohne Cache | Mit warmem Cache |\n|-----------|------------|-----------------|\n| Pull-Zeit (Alpine) | 10,3 s | 4,2 s |\n| Pull-Zeit (Python 3.13 DHI) | 11,6 s | ~4 s |\n| Netzwerk-Roundtrips zum Upstream | Jeder Pull | Nur Cache-Misses |\n\nDer erste Pull hat dieselbe Dauer – das Image muss vom Upstream geladen werden. Jeder weitere Pull innerhalb der Cache-Gültigkeitsdauer kommt direkt aus GitLab-Storage: kein Netzwerk-Hop zu Docker Hub, dhi.io, MCR oder einer anderen Registry.\n\nBei Teams mit vielen Pipeline-Jobs pro Tag summiert sich das zu einem messbaren Gewinn bei den Build-Laufzeiten.\n\n## Praktische Hinweise\n\n### Cache-Gültigkeit\n\nDer Standard sind 24 Stunden. Für sicherheitskritische Images, bei denen Patches schnell verfügbar sein sollen, empfiehlt sich ein kürzeres Intervall:\n\n```python\n\nclient.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://dhi.io\",\n    name=\"Docker Hardened Images\",\n    username=\"your-username\",\n    password=\"your-token\",\n    cache_validity_hours=12\n)\n\n```\n\nFür stabile Images mit fixen Versions-Tags ist ein längeres Intervall problemlos.\n\n### Upstream-Priorität\n\nUpstreams werden der Reihe nach geprüft. Bei gleichnamigen Images in verschiedenen Registries gewinnt der erste passende Upstream.\n\n### Limits\n\n- Maximal 20 Virtual Registries pro Gruppe\n- Maximal 20 Upstreams pro Virtual Registry\n\n## Konfiguration über die Oberfläche\n\nVirtual Registries und Upstreams lassen sich auch direkt in der GitLab-Oberfläche einrichten – ohne API-Aufrufe. Unter **Einstellungen > Pakete und Registries > Virtual Registry** der jeweiligen Gruppe stehen folgende Optionen zur Verfügung:\n\n- Virtual Registries erstellen und verwalten\n- Upstreams hinzufügen, bearbeiten und neu anordnen\n- Cache anzeigen und verwalten\n- Überblick, welche Images abgerufen werden\n\n## Ausblick\n\nIn Entwicklung:\n\n- **Allow/Deny-Listen**: Regex-basierte Steuerung, welche Images aus welchen Upstreams abgerufen werden dürfen.\n\nContainer Virtual Registry befindet sich in der Beta-Phase. Die Funktion wird produktiv eingesetzt und wird weiterentwickelt – Feedback fließt direkt in die Priorisierung ein.\n\n## Feedback\n\nWer als Plattformteam mit Registry-Wildwuchs zu kämpfen hat: Ich möchte verstehen, wie die aktuelle Situation aussieht.\n\n- Wie viele Upstream-Registries werden verwaltet?\n- Wo liegt der größte Schmerzpunkt?\n- Würde ein solcher Ansatz helfen – und falls nicht: Was fehlt?\n\nErfahrungen und Rückmeldungen gerne im [Container Virtual Registry Feedback-Issue](https://gitlab.com/gitlab-org/gitlab/-/work_items/589630) teilen.\n\n## Weiterführende Ressourcen\n\n- [Neue GitLab-Metriken und Registry-Funktionen zur Optimierung von CI/CD-Pipelines](https://about.gitlab.com/de-de/blog/new-gitlab-metrics-and-registry-features-help-reduce-ci-cd-bottlenecks/)\n- [Container Virtual Registry – Dokumentation](https://docs.gitlab.com/user/packages/virtual_registry/container/)\n- [Container Virtual Registry – API](https://docs.gitlab.com/api/container_virtual_registries/)\n\n## Für deutsche Unternehmen könnte dies folgende Themen betreffen\n\nTeams, die sicherheitsgehärtete Base-Images mit vollständigen SBOMs und SLSA-Provenance einsetzen, haben möglicherweise auch Compliance-Überlegungen – beispielsweise in Bereichen wie Sicherheit der Software-Lieferkette, Nachvollziehbarkeit von Image-Abhängigkeiten und zentralem Audit-Trail.\n\nRegulatorische Frameworks wie NIS2 und der Cyber Resilience Act adressieren ähnliche Themen rund um Software-Lieferketten und SBOM-Transparenz. Für konkrete Compliance-Anforderungen empfiehlt sich Rücksprache mit entsprechender Fachberatung.",[24,23,734],{"featured":13,"template":14,"slug":748},"using-gitlab-container-virtual-registry-with-docker-hardened-images",{"content":750,"config":761},{"category":11,"tags":751,"body":753,"date":754,"heroImage":755,"authors":756,"title":759,"description":760},[24,752,22],"git","Enterprise-Migrationen in regulierten Branchen wie Finanzwesen und Automobilindustrie erfordern systematisches Risikomanagement gemäß NIS2-Richtlinie Artikel 21. Die mehrstufige Migrationsstruktur mit Testläufen vor Produktions-Wellen und kontrollierten Change-Freezes demonstriert Business-Continuity-Management in der Praxis. GitLab Professional Services organisiert Migrationen in Wellen von 200-300 Projekten, um Komplexität zu managen und API-Rate-Limits zu respektieren.\n\n## Überblick\n\nGitLab bietet [Congregate](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/) (maintained by GitLab Professional Services) und [eingebauten Git-Repository-Import](https://docs.gitlab.com/user/project/import/repo_by_url/) für Migrationen von Azure DevOps (ADO). Beide Optionen unterstützen Repository-basierte oder Bulk-Migration und erhalten Git-Commit-History, Branches und Tags. Mit Congregate werden zusätzliche Assets wie Wikis, Work Items, CI/CD-Variablen, Container-Images, Packages und Pipelines migriert (siehe [Feature-Matrix](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/blob/master/customer/ado-migration-features-matrix.md)).\n\nEnterprises folgen typischerweise einem mehrstufigen Ansatz:\n\n- Repositories von ADO zu GitLab migrieren (Congregate oder eingebauter Import)\n- Pipelines von Azure Pipelines zu GitLab CI/CD migrieren\n- Verbleibende Assets wie Boards, Work Items und Artifacts zu GitLab Issues, Epics und Package/Container Registries migrieren\n\nMehrstufige Migrationsphasen (siehe [Diagramm](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/#overview)):\n\n- Prerequisites (IdP, Runners, Change Management)\n- Migration Phase (Source Code, History, Work Items)\n- Post-Migration (Pipelines, Assets, Security)\n\n## Migration planen\n\n**Zentrale Planungsfragen:**\n\n- Wie schnell muss die Migration abgeschlossen werden?\n- Was genau wird migriert?\n- Wer führt die Migration durch?\n- Welche Organisationsstruktur wird in GitLab benötigt?\n- Welche Einschränkungen, Limitierungen oder Fallstricke müssen berücksichtigt werden?\n\nDie Timeline bestimmt weitgehend den Migrationsansatz. Identifizierung von Champions oder Teams mit ADO- und GitLab-Erfahrung unterstützt Adoption und Guidance.\n\n**Inventar erstellen:**\n\nDas GitLab Professional Services [Evaluate](https://gitlab.com/gitlab-org/professional-services-automation/tools/utilities/evaluate#beta-azure-devops)-Tool produziert ein vollständiges Inventar der Azure DevOps Organisation: Repositories, PR-Counts, Contributors, Pipelines, Work Items, CI/CD-Variablen. Bei Professional Services Engagements wird dieser Report mit Engagement Manager oder Technical Architect geteilt für Migrationsplanung.\n\nMigrations-Timing wird primär bestimmt durch Pull-Request-Count, Repository-Größe und Contribution-Menge. Beispiel: 1.000 kleine Repositories mit wenigen PRs migrieren schneller als wenige Repositories mit Zehntausenden PRs. Inventar-Daten ermöglichen Aufwands-Schätzung und Test-Run-Planung.\n\nIn Professional Services Engagements werden Migrationen in Wellen von 200-300 Projekten organisiert, um Komplexität zu managen und API-Rate-Limits zu respektieren (sowohl [GitLab](https://docs.gitlab.com/security/rate_limits/) als auch [ADO](https://learn.microsoft.com/en-us/azure/devops/integrate/concepts/rate-limits?view=azure-devops)).\n\n**Tool-Auswahl:**\n\nGitLabs [eingebauter Repository-Importer](https://docs.gitlab.com/user/project/import/repo_by_url/) migriert Git-Repositories (Commits, Branches, Tags) einzeln. Congregate erhält Pull Requests (in GitLab: Merge Requests), Kommentare und Metadata; der eingebaute Import fokussiert auf Git-Daten (History, Branches, Tags).\n\nAssets die typischerweise separate Migration oder manuelle Neuerstellung erfordern:\n\n- Azure Pipelines → GitLab CI/CD Pipelines (siehe [CI/CD YAML](https://docs.gitlab.com/ci/yaml/) oder [CI/CD Components](https://docs.gitlab.com/ci/components/)). Alternativ: AI-basierte Pipeline-Konvertierung in Congregate.\n- Work Items und Boards → GitLab Issues, Epics, Issue Boards\n- Artifacts, Container-Images (ACR) → GitLab Package/Container Registry\n- Service Hooks, externe Integrationen → in GitLab neu erstellen\n\n[Permissions-Modelle](https://docs.gitlab.com/user/permissions/) unterscheiden sich zwischen ADO und GitLab. Permissions-Mapping planen statt exakter Preservation zu erwarten.\n\n**Organisationsstruktur in GitLab:**\n\nEmpfohlener Ansatz: ADO-Organisationen auf GitLab-Groups mappen (nicht viele kleine Groups). Migration als Gelegenheit nutzen, GitLab-Struktur zu rationalisieren (siehe [Struktur-Diagramm](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/#planning-your-migration)):\n\n- **ADO Organization** → GitLab Top-level Group\n- **ADO Project** → GitLab Subgroup (optional)\n- **ADO Repository** → GitLab Project\n\nWeitere Empfehlungen:\n\n- Subgroups und Project-Level-Permissions für verwandte Repositories nutzen\n- Zugriff über GitLab Groups und Group-Membership managen\n- GitLab [Permissions](https://docs.gitlab.com/user/permissions/) und [SAML Group Links](https://docs.gitlab.com/user/group/saml_sso/group_sync/) für Enterprise-RBAC-Modell evaluieren\n\n**ADO Work Items Migration:**\n\nADO Boards und Work Items mappen zu GitLab Issues, Epics und Issue Boards. ADO Epics und Features werden GitLab Epics. Andere Work-Item-Typen (User Stories, Tasks, Bugs) werden project-scoped Issues. Standard-Felder bleiben erhalten; ausgewählte Custom Fields können migriert werden. Parent-Child-Relationships bleiben erhalten. Links zu Pull Requests werden zu Merge-Request-Links konvertiert.\n\n![Migration eines Work Items zu GitLab Issue](https://res.cloudinary.com/about-gitlab-com/image/upload/v1764769188/ztesjnxxfbwmfmtckyga.png)\n\n**Pipelines-Migration:**\n\nCongregate bietet [AI-basierte Konvertierung](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/merge_requests/1298) für multi-stage YAML-Pipelines von Azure DevOps zu GitLab CI/CD. Die automatisierte Konvertierung funktioniert optimal für einfache Single-File-Pipelines und liefert einen funktionalen Ausgangspunkt, nicht ein produktionsreifes `.gitlab-ci.yml`-File. Das Tool generiert funktional äquivalente GitLab-Pipelines zur weiteren Optimierung.\n\n- Konvertiert Azure Pipelines YAML zu `.gitlab-ci.yml` automatisch\n- Geeignet für straightforward Single-File-Pipeline-Konfigurationen\n- Liefert Boilerplate zur Migrations-Beschleunigung, nicht finales Production-Artifact\n- Erfordert Review und Anpassung für komplexe Szenarien, Custom Tasks oder Enterprise-Requirements\n- Unterstützt keine Azure DevOps Classic Release Pipelines\n\nRepository-Owner sollten [GitLab CI/CD Dokumentation](https://docs.gitlab.com/ci/) konsultieren für weitere Pipeline-Optimierung nach initialer Konvertierung.\n\n## Migrationen durchführen\n\nNach der Planung werden Migrationen in Stages durchgeführt, beginnend mit Trial Runs. Trial Migrations identifizieren organisations-spezifische Issues frühzeitig und ermöglichen Duration-Messung, Outcome-Validierung und Approach-Finetuning vor Production.\n\n**Was Trial Migrations validieren:**\n\n- Ob Repository und Assets erfolgreich migrieren (History, Branches, Tags; plus MRs/Comments bei Congregate)\n- Ob Destination sofort nutzbar ist (Permissions, Runners, CI/CD-Variablen, Integrationen)\n- Wie lange jeder Batch benötigt für Schedule- und Stakeholder-Expectations\n\n**Downtime-Guidance:**\n\nGitLabs eingebauter Git-Import und Congregate erfordern inhärent keine Downtime. Für Production-Wellen: Changes in ADO freezen (Branch-Protections oder Read-only), um verpasste Commits, PR-Updates oder mid-migration Work Items zu vermeiden. Trial Runs erfordern keine Freezes.\n\n**Batching-Guidance:**\n\nTrial-Batches back-to-back durchführen für kürzere elapsed Time. Teams validieren Results asynchron. Geplante Group/Subgroup-Struktur für Batch-Definition nutzen und API-Rate-Limits respektieren.\n\n**Empfohlene Schritte:**\n\n1. Test-Destination in GitLab erstellen (GitLab.com: dedicated Group/Namespace; Self-managed: Top-level Group)\n2. Authentication vorbereiten (Azure DevOps PAT, GitLab Personal Access Token mit api und read_repository Scopes)\n3. Trial Migrations durchführen (Repos only: eingebauter Import; Repos + PRs/MRs: Congregate)\n4. Post-Trial Follow-up (Repo-History, Branches, Tags, Merge Requests, Issues/Epics, Labels, Relationships verifizieren)\n5. Permissions/Roles, Protected Branches, Runners/Tags, Variables/Secrets, Integrations/Webhooks prüfen\n6. Pipelines (`.gitlab-ci.yml`) oder konvertierte Pipelines validieren\n7. User-Validierung für Functionality und Data-Fidelity\n8. Production-Migrationen in Waves durchführen (Change-Freezes in ADO erzwingen, Progress und Logs monitoren)\n\n\u003Cfigure class=\"video_container\">\n  \u003Ciframe src=\"https://www.youtube.com/embed/ibIXGfrVbi4?si=ZxOVnXjCF-h4Ne0N\" frameborder=\"0\" allowfullscreen=\"true\">\u003C/iframe>\n\u003C/figure>\n\n## Zentrale ADO-zu-GitLab-Mappings\n\nWichtigste Struktur-Mappings für Migrationsplanung:\n\n- **ADO Organization** → GitLab Group (Top-level Namespace)\n- **ADO Project** → GitLab Group oder Subgroup (Permissions-Boundary)\n- **ADO Repository** → GitLab Project (Git-Repo plus Issues, CI/CD, Wiki)\n- **Pull Request** → Merge Request (Code Review, Approvals)\n- **Azure Pipelines** → GitLab CI/CD (`.gitlab-ci.yml`)\n- **Agent Pools** → GitLab Runners (Job-Execution)\n- **Work Items** → GitLab Issues/Epics (Planning-Funktionalität)\n- **Variable Groups** → CI/CD Variables (Project/Group/Instance Level)\n\nFür vollständige Terminologie-Referenztabelle mit allen Mappings siehe [englische Originalversion](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/).\n\n## Professional Services Migrationsmuster\n\nGitLab Professional Services nutzt bewährte Muster für Enterprise-Migrationen:\n\n**Systematische Planung:** Evaluate-Tool liefert vollständiges Inventar (Repositories, PRs, Contributors, Pipelines, Work Items). Klassifikation nach Komplexität (Work Items = Planning-Team-Involvement; Pipelines = Conversion-Requirements) ermöglicht Timeline-Schätzung und Batch-Definition.\n\n**Controlled Execution:** Wellen von 200-300 Projekten managen Komplexität und respektieren API-Rate-Limits. Trial-Migrationen vor Production-Waves identifizieren Issues frühzeitig. Change-Freezes in ADO während Production-Wellen vermeiden mid-migration Updates.\n\n**Risikominimierung:** Multi-Phase-Ansatz (Prerequisites → Migration → Post-migration) mit Validierungs-Checkpoints an jeder Phase. Trial-Runs ermöglichen asynchrone Team-Validierung ohne Production-Impact.\n\n## Praktische Implementierung\n\nFür vollständige Implementierungsdetails, Konfigurationsbeispiele, Pipeline-Konvertierungs-Patterns und detaillierte Post-Migration-Checklisten siehe [englische Originalversion](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/).\n\nWeitere Professional Services Ressourcen:\n\n- [Professional Services Full Catalog](https://about.gitlab.com/professional-services/catalog/)\n- [Congregate Migration Tool](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/)\n- [Evaluate Inventory Tool](https://gitlab.com/gitlab-org/professional-services-automation/tools/utilities/evaluate)\n","2025-12-03","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749658924/Blog/Hero%20Images/securitylifecycle-light.png",[757,758],"Evgeny Rudinsky","Michael Leopard","Migration von Azure DevOps zu GitLab systematisch planen","Professional Services Migrationsansatz mit mehrstufiger Struktur, 200-300 Projekt-Wellen und systematischem Risikomanagement für Enterprise-Migrationen.",{"featured":28,"template":14,"slug":762},"migration-from-azure-devops-to-gitlab",{"promotions":764},[765,779,790,802],{"id":766,"categories":767,"header":769,"text":770,"button":771,"image":776},"ai-modernization",[768],"ai-ml","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":772,"config":773},"Get your AI maturity score",{"href":774,"dataGaName":775,"dataGaLocation":251},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":777},{"src":778},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":780,"categories":781,"header":782,"text":770,"button":783,"image":787},"devops-modernization",[23,578],"Are you just managing tools or shipping innovation?",{"text":784,"config":785},"Get your DevOps maturity score",{"href":786,"dataGaName":775,"dataGaLocation":251},"/assessments/devops-modernization-assessment/",{"config":788},{"src":789},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":791,"categories":792,"header":794,"text":770,"button":795,"image":799},"security-modernization",[793],"security","Are you trading speed for security?",{"text":796,"config":797},"Get your security maturity score",{"href":798,"dataGaName":775,"dataGaLocation":251},"/assessments/security-modernization-assessment/",{"config":800},{"src":801},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":803,"paths":804,"header":806,"text":807,"button":808,"image":813},"github-azure-migration",[762,805],"integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":809,"config":810},"See how GitLab compares to GitHub",{"href":811,"dataGaName":812,"dataGaLocation":251},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":814},{"src":789},{"header":816,"blurb":817,"button":818,"secondaryButton":823},"Beginne noch heute, schneller zu entwickeln","Entdecke, was dein Team mit der intelligenten Orchestrierungsplattform für DevSecOps erreichen kann.\n",{"text":819,"config":820},"Kostenlosen Test starten",{"href":821,"dataGaName":49,"dataGaLocation":822},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/de-de/","feature",{"text":51,"config":824},{"href":53,"dataGaName":54,"dataGaLocation":822},1777934809522]