diff --git a/assets/images/blog/cicd-devx/tk-blogpost-cicd-devx-3000x2000.png b/assets/images/blog/cicd-devx/tk-blogpost-cicd-devx-3000x2000.png new file mode 100644 index 00000000..0120b0bf Binary files /dev/null and b/assets/images/blog/cicd-devx/tk-blogpost-cicd-devx-3000x2000.png differ diff --git a/content/de/blog/2025-cicd-devx.md b/content/de/blog/2025-cicd-devx.md new file mode 100644 index 00000000..98efc6bf --- /dev/null +++ b/content/de/blog/2025-cicd-devx.md @@ -0,0 +1,193 @@ +--- +title: "Effiziente CI/CD-Pipelines mit GitLab: Produktivität und Sicherheit vereint" +slug: "cicd-devx" +description: "" +date: 2025-01-29T00:00:00+00:00 +lastmod: 2025-01-29T00:00:00+00:00 +draft: false +images: ["images/blog/cicd-devx/tk-blogpost-cicd-devx-3000x2000.png"] +img_border: true +Sitemap: +Priority: 0.91 + +categories: ["Technologie", "GitlabCI", "Kubernetes"] +authors: ['raffael-hertle'] +post_img: "images/blog/cicd-devx/tk-blogpost-cicd-devx-3000x2000.png" +lead: "GitLab wird in breiter Masse als beliebtes VCS Tool und Continuous Integration Tool verwendet. In diesem Beitrag möchten wir einen Erfahrungsbericht vorstellen, wie wir die Möglichkeiten der Plattform ausschöpfen, um die Developer Produktivität massgebend zu verbessern und zudem durch Automatisierung die Supply Chain Security zu erhöhen." +--- + + +Automatisierte Code-Integration ist längst ein unverzichtbarer Bestandteil der modernen Cloud-Native Welt. Doch gerade in grossen Enterprise-Umgebungen stellt sich oft die Frage: Wie behält man den Überblick über Pipelines und Jobs, ohne dabei die Produktivität oder den Spass an der Entwicklung zu verlieren? Entwickler:innen wollen in einem initialen Setup ihren Stack für die Entwicklung einsatzbereit haben, ohne in einer ersten Phase die ganze CI/CD Landschaft aufzusetzen. Für unseren Kunden war das definierte Ziel klar: Änderungen sollen zeitnah integriert, für das Deployment bereitgestellt werden und automatisch ausgerollt werden. Durch Automatisierung wird direkt die Produktivität erhöht, bietet jedoch viel Angriffsfläche für Supply Chain Attacks. In einem früheren Blogpost haben wir die Grundlagen der Supply Chain Security, sowie Toolings und deren Wichtigkeit in modernen Umgebungen vorgestellt. Diese Grundlage haben wir weiterverwendet um unsere Supply Chain in GitLab Pipelines zu sichern. + +In diesem Beitrag möchten wir unsere Erfahrungen teilen, die wir bei einem Kunden im Bankensektor gemacht haben. Das deklarierte Ziel war es eine DevSecOps Landschaft zu gestalten, die vollautomatisiert und ohne Securitybedenken auf alle Systeme mit Continuous Deployment ausrollen kann. + +### DevSecOps als Motivation + +Um DevSecOps zu leben und damit auch von allen Benefits von Microservicearchitekturen und agiler Entwicklungsmethode zu profitieren, muss Software zeitnah und regelmässig auf die Zielsysteme ausgerollt werden. Dies erfordert ein hohes Mass an Automatisierung und Standardisierung. +Entwickler:innen sollen sich auf ihre tägliche Tätigkeit fokussieren können und ihre Projekte mit wenig Zusatzaufwand in den CI/CD Prozess integrieren können. + +### Standardisierung mit GitLab CI/CD Components + +GitLab führte in Version 17.0 das Konzept der CI/CD Components ein. Diese wiederverwendbaren Templates bieten eine Möglichkeit, Pipelineelemente als Template zur Verfügung zu stellen. Diese Komponenten definieren in einer leicht zu verstehenden Templatesprache CI/CD Komponenten, die über Variablen parametrisiert in Projekten verwendet werden können. CI/CD Components können aus dem zentralen Hub (https://gitlab.com/components) bezogen werden, oder in einer eigenen Komponente gepflegt werden. In unserem Fall wurden die Komponenten selbst entwickelt, um die Kontrolle und Variabilität für projektspezifische Anforderungen gerecht zu werden. +Grundsätzlich ist eine CI/CD Component ein GitLab Repository mit einem /templates Verzeichnis. In diesem Verzeichnis liegen die Templates für die Komponenten. Eine Komponente hat folgende Struktur: + +```yaml +# component/templates/test.yml +spec: + inputs: + stage: + default: test +--- +unit-test: + stage: $[[ inputs.stage ]] + script: echo unit tests + +integration-test: + stage: $[[ inputs.stage ]] + script: echo integration tests +``` + +Dieses Template kann über den include Block in einer Pipeline integriert und parametrisiert werden: + +```yaml +# other-project/.gitlab-ci.yml +stages: [verify, release] + +include: + - component: $CI_SERVER_FQDN/compnent/test@main + inputs: + stage: verify +``` + +So können Pipeline Templates über zentrale Komponenten verwaltet und gepflegt werden. Dies erhöht die Wiederverwendbarkeit und die Wartbarkeit, verringert gleichzeitig den Aufwand für neue Projekte. + +### Renovate + +Grundsätzlich sollen Projekte ihre Dependencies up-to-date halten. Dies bringt einen enormen Aufwand mit sich, der den meisten Entwickler:innen selten Spass bereitet und reine Fleissarbeit ist. Ein idealer Fall für eine Automatisierungslösung! Hier verwenden wir eine zentrale Renovate CI/CD Component, die verwendet werden kann, um automatisch Pullrequests mit neuen Dependencies zu erhalten. Renovate mit einem Service Account bestückt, der in andere Projekte eingeladen werden kann, um diese mit Dependency Updates zu versehen. Durch eine Autodiscovery Funktion wird Renovate alle Projekte, auf die der Service Account Zugriff hat, Pull Requests mit neuen Dependencies verfassen. +Somit haben wir schon einen wichtigen Punkt für eine sichere Pipeline erledigt. + +### Signieren von Images mit Cosign + +Um sicherzustellen, dass nur Images publiziert und verwendet werden, die wir auch selber gebaut haben, benötigen wir Image Signaturen. Mit dem Cosign Image bekommen wir die benötigten Binaries, die wir benötigen um Signaturen von publizierten Images zu erstellen und in der Container Registry abzulegen. + +```yaml +# components/cosign/templates/cosign.yml +spec: + inputs: + image: + description: "The image to be signed." + registry-user: + description: "The username to authenticate with the docker registry" + registry-password: + description: "The password to authenticate with the docker registry" + needs: + type: array + default: ["image:build"] + description: "The jobs that this job depends on" + stage: + default: image:sign + description: "The stage in which the job will run" +--- +cosign-sign: + stage: $[[ inputs.stage ]] + needs: $[[ inputs.needs ]] + image: ghcr.io/sigstore/cosign/cosign + script: + - ./cosign sign + --registry-username="$[[inputs.registry-user]]" + --registry-password="$[[inputs.registry-password]]" + --yes + $[[ inputs.image ]] +``` + +Mit dieser CI/CD Component können gebaute Images durch cosign signiert werden. Nachdem unsere Dependencies mit Renovate mit Updates versehen werden und unsere Images durch cosign signiert werden, widmen wir uns noch dem Deployment mit ArgoCD. + +### Deployments mit dynamischen Pipelines + +Das Deployment mit ArgoCD kann je nach Anzahl Umgebungen und Clustern zu einer Herausforderung werden. Um die Herausforderung mit mehreren Umgebungen und eventuell auch anderen ArgoCD Instanzen zu meistern benötigen wir ein weiteres Konstrukt: Downstream Pipelines. GitLab erlaubt es uns in der Laufzeit einer Pipeline weitere Pipelines dynamisch mit generierten gitlab-ci.yml Manifesten zu triggern. +Das zentrale Problem ist, dass Teams ihre Applikationen auf unterschiedlichen Stages in jeweils unterschiedlichen Kubernetes Umgebungen sowie ArgoCD Instanzen deployen. Die Lösung besteht darin, dass wir diese Deploymentstages während der Laufzeit der Pipeline dynamisch erzeugen und eine Child-Pipeline mit einem generierten Manifest triggern. +Das Projekt mit den zu deployenden Umgebungen besitzt ein Config File environments.yml mit den verschiedenen Umgebung und deren Parametern definiert: + +```yaml +# environments.yml +dev: + stage: dev + argoUrl: argo.local + argoApp: demo-app-dev + kubernetesUrl: kubernetes.local + kubernetesToken: token +int: + stage: int + argoUrl: argo.local + argoApp: demo-app-int + kubernetesUrl: kubernetes.local + kubernetesToken: token +``` + +Zudem haben wir ein Boilerplate File für die Downstream Pipeline downstream-template.yml, welches wir in der Pipeline befüllen werden: + +```yaml +# downstream-template.yml +STAGE:deploy: + image: bash + stage: argo + script: + - echo "Deploying argoAPP" + +``` + +Einfachheitshalber haben wir noch das zu befüllende Pipelinefile rendered-downstream.yml vorbereitet und nur mit der initialen Stagedefinition im Repository eingecheckt: + +```yaml +# rendered-downstream.yml +stages: + - argo + +``` + +Die effektiv laufende Pipeline erzeugt in einer ersten Stage das resultierende Pipelinefile und erzeugt ein Artefakt daraus. Dieses Artefakt wird direkt verwendet um die Childpipelines zu erzeugen und somit die einzelnen Umgebungen zu deployen. + +```yaml +# .gitlab-ci.yml +stages: + - template + - deploy + +deploy:createDownstream: + stage: template + image: + name: bash + before_script: + - apk add yq + script: + - echo "Reading environments" + - readarray envs < <(yq e -o=j -I=0 '.[]' environments.yml) + - for env in "${envs[@]}"; do stage=$(echo "$env" | yq e '.stage' -); argoApp=$(echo "$env" | yq e '.argoApp' -); sed "s/STAGE/$stage/g; s/argoAPP/$argoApp/g" downstream-template.yml >> rendered-downstream.yml; done + artifacts: + paths: + - rendered-downstream.yml + +deploy:triggerDownstream: + stage: deploy + trigger: + include: + - artifact: rendered-downstream.yml + job: deploy:createDownstream + strategy: depend + +``` + +Die resultierende Pipeline erzeugt nun mit dieser Konfiguration zwei Childpipelines, die jeweils verschiedene Umgebungen deployen können. Einfachheitshalber ist die Deploymentlogik weggelassen. + +{{< custom-image "../images/cicd-devx/pipeline.png" >}} +
+ +Wie wir sehen, erzeugt der erste Pipeline Job `deploy:createDownstream` das Template in der Stage `template` und in der `deploy` Stage werden durch die zwei generierten Artefakte die Downstream Pipelines erzeugt. Somit können wir dynamisch aus einer Konfigurationsmatrix Umgebungen mit verschiedenen Parametern ausrollen. Falls ihr dieses Beispiel live sehen oder testen möchtet, ihr findet es hier auf [GitLab](https://gitlab.com/g1raffi/cicd-example). + +### Fazit + +Mit vielen kleinen Verbesserungen kann das Potenzial der CI/CD Umgebung maximiert werden! Durch den Einsatz von CI Components werden GitLab Pipelines besser wartbar und verwaltbar. Teams können durch einfache Templates ihre gewünschten Pipeline zusammenstellen und müssen keine Entwicklungsgeschwindigkeit einbüssen. Mit Hilfe der dynamischen Downstream Pipelines von GitLab konnten komplexere Continuous Deployment Prozesse mit ArgoCD in Pipelines integriert werden. + +### Können wir auch dich unterstützen + +Möchtest auch du deine CI/CD Landschaft auf das nächste Level bringen oder hast du Fragen zu den verwendeten Komponenten? Dann melde dich bei uns! diff --git a/content/de/blog/images/cicd-devx/pipeline.png b/content/de/blog/images/cicd-devx/pipeline.png new file mode 100644 index 00000000..af4cef38 Binary files /dev/null and b/content/de/blog/images/cicd-devx/pipeline.png differ diff --git a/content/en/blog/2025-cicd-devx.md b/content/en/blog/2025-cicd-devx.md new file mode 100644 index 00000000..1ae98eff --- /dev/null +++ b/content/en/blog/2025-cicd-devx.md @@ -0,0 +1,192 @@ +--- +title: "Efficient CI/CD Pipelines with GitLab: Productivity and Security combined" +slug: "cicd-devx" +description: "" +date: 2025-01-29T00:00:00+00:00 +lastmod: 2025-01-29T00:00:00+00:00 +draft: false +images: ["images/blog/cicd-devx/tk-blogpost-cicd-devx-3000x2000.png"] +img_border: true +Sitemap: +Priority: 0.91 +categories: ["Technology", "GitlabCI", "Kubernetes"] +authors: ['raffael-hertle'] +post_img: "images/blog/cicd-devx/tk-blogpost-cicd-devx-3000x2000.png" +lead: "GitLab is widely used as a popular VCS tool and continuous integration tool. In this post, we would like to present a field report on how we exploit the possibilities of the platform to significantly improve developer productivity and also increase supply chain security through automation." +--- + + +Automated code integration has long been an indispensable part of the modern cloud-native world. But especially in large enterprise environments, the question often arises: How do you keep track of pipelines and jobs without losing productivity or the fun of development? Developers want to have their stack ready for development in an initial setup without setting up the entire CI/CD landscape in the first phase. For our customer, the defined goal was clear: changes should be integrated promptly, made ready for deployment, and rolled out automatically. Automation directly increases productivity, but offers a lot of room for supply chain attacks. In an earlier blog post, we introduced the basics of supply chain security, as well as toolings and their importance in modern environments. We used this foundation to secure our supply chain in GitLab Pipelines. + +In this post, we would like to share our experiences that we had with a client in the banking sector. The declared goal was to design a DevSecOps landscape that can roll out fully automated and without security concerns to all systems with continuous deployment. + +### DevSecOps as Motivation + +To live DevSecOps and thus benefit from all the benefits of microservice architectures and agile development methods, software must be rolled out to the target systems promptly and regularly. This requires a high degree of automation and standardization. +Developers should be able to focus on their daily work and integrate their projects into the CI/CD process with little additional effort. + +### Standardization with GitLab CI/CD Components + +GitLab introduced the concept of CI/CD Components in version 17.0. These reusable templates provide a way to make pipeline elements available as templates. These components define CI/CD components in an easy-to-understand template language, which can be used in projects via parameterized variables. CI/CD components can be obtained from the central hub (https://gitlab.com/components), or maintained in your own component. In our case, the components were developed by ourselves in order to do justice to the control and variability for project-specific requirements. +Basically, a CI/CD Component is a GitLab repository with a /templates directory. The templates for the components are located in this directory. A component has the following structure: + +```yaml +# component/templates/test.yml +spec: + inputs: + stage: + default: test +--- +unit-test: + stage: $[[ inputs.stage ]] + script: echo unit tests + +integration-test: + stage: $[[ inputs.stage ]] + script: echo integration tests +``` + +This template can be integrated and parameterized via the include block in a pipeline: + +```yaml +# other-project/.gitlab-ci.yml +stages: [verify, release] + +include: + - component: $CI_SERVER_FQDN/compnent/test@main + inputs: + stage: verify +``` + +In this way, pipeline templates can be managed and maintained via central components. This increases reusability and maintainability while reducing the effort required for new projects. + +### Renovate + +Projects should keep their dependencies up-to-date. This involves an enormous amount of effort, which most developers rarely enjoy and is pure drudgery. An ideal case for an automation solution! Here we use a central Renovate CI/CD component that can be used to automatically receive pull requests with new dependencies. Renovate with a service account that can be invited to other projects to provide them with dependency updates. Through an autodiscovery function, Renovate will all projects to which the service account has access, write pull requests with new dependencies. +So we have already done an important point for a secure pipeline. + +### Signing Images with Cosign + +To ensure that only images that we have built ourselves are published and used, we need image signatures. With the Cosign image we get the binaries we need to create signatures of published images and store them in the container registry. + +```yaml +# components/cosign/templates/cosign.yml +spec: + inputs: + image: + description: "The image to be signed." + registry-user: + description: "The username to authenticate with the docker registry" + registry-password: + description: "The password to authenticate with the docker registry" + needs: + type: array + default: ["image:build"] + description: "The jobs that this job depends on" + stage: + default: image:sign + description: "The stage in which the job will run" +--- +cosign-sign: + stage: $[[ inputs.stage ]] + needs: $[[ inputs.needs ]] + image: ghcr.io/sigstore/cosign/cosign + script: + - ./cosign sign + --registry-username="$[[inputs.registry-user]]" + --registry-password="$[[inputs.registry-password]]" + --yes + $[[ inputs.image ]] +``` + +With this CI/CD component, built images can be signed by cosign. After our dependencies are updated with Renovate and our images are signed by cosign, we also dedicate ourselves to the deployment with ArgoCD. + +### Deployments with dynamic pipelines + +Deployment with ArgoCD can be a challenge depending on the number of environments and clusters. To master the challenge with multiple environments and possibly also other ArgoCD instances, we need another construct: Downstream Pipelines. GitLab allows us to dynamically trigger additional pipelines with generated gitlab-ci.yml manifests at runtime of a pipeline. +The central problem is that teams deploy their applications on different stages in different Kubernetes environments and ArgoCD instances. The solution is that we dynamically create these deployment stages during the pipeline runtime and trigger a child pipeline with a generated manifest. +The project with the environments to be deployed has a config file environments.yml with the different environments and their parameters defined: + +```yaml +# environments.yml +dev: + stage: dev + argoUrl: argo.local + argoApp: demo-app-dev + kubernetesUrl: kubernetes.local + kubernetesToken: token +int: + stage: int + argoUrl: argo.local + argoApp: demo-app-int + kubernetesUrl: kubernetes.local + kubernetesToken: token +``` + +In addition, we have a boilerplate file for the downstream pipeline downstream-template.yml, which we will fill in the pipeline: + +```yaml +# downstream-template.yml +STAGE:deploy: + image: bash + stage: argo + script: + - echo "Deploying argoAPP" + +``` + +For the sake of simplicity, we have also prepared the pipeline file to be filled rendered-downstream.yml and checked it into the repository with only the initial stage definition: + +```yaml +# rendered-downstream.yml +stages: + - argo + +``` + +The effectively running pipeline first generates the resulting pipeline file in a first stage and creates an artifact from it. This artifact is used directly to create the child pipelines and thus deploy the individual environments. + +```yaml +# .gitlab-ci.yml +stages: + - template + - deploy + +deploy:createDownstream: + stage: template + image: + name: bash + before_script: + - apk add yq + script: + - echo "Reading environments" + - readarray envs < <(yq e -o=j -I=0 '.[]' environments.yml) + - for env in "${envs[@]}"; do stage=$(echo "$env" | yq e '.stage' -); argoApp=$(echo "$env" | yq e '.argoApp' -); sed "s/STAGE/$stage/g; s/argoAPP/$argoApp/g" downstream-template.yml >> rendered-downstream.yml; done + artifacts: + paths: + - rendered-downstream.yml + +deploy:triggerDownstream: + stage: deploy + trigger: + include: + - artifact: rendered-downstream.yml + job: deploy:createDownstream + strategy: depend + +``` + +The resulting pipeline now creates two child pipelines with this configuration, each of which can deploy different environments. For simplicity, the deployment logic is omitted. + +{{< custom-image "../images/cicd-devx/pipeline.png" >}} +
+ +As we can see, the first pipeline job `deploy:createDownstream` creates the template in the `template` stage and in the `deploy` stage the downstream pipelines are created by the two generated artifacts. Thus, we can dynamically roll out environments with different parameters from a configuration matrix. If you would like to see or test this example live, you can find it here on [GitLab](https://gitlab.com/g1raffi/cicd-example). + +### Conclusion + +With many small improvements, the potential of the CI/CD environment can be maximized! The use of CI components makes GitLab pipelines easier to maintain and manage. Teams can put together their desired pipeline using simple templates and do not have to lose any development speed. With the help of GitLab's dynamic downstream pipelines, more complex continuous deployment processes with ArgoCD could be integrated into pipelines. + +### Can we support you too + +Would you also like to take your CI/CD landscape to the next level or do you have questions about the components used? Then contact us! diff --git a/content/en/blog/images/cicd-devx/pipeline.png b/content/en/blog/images/cicd-devx/pipeline.png new file mode 100644 index 00000000..af4cef38 Binary files /dev/null and b/content/en/blog/images/cicd-devx/pipeline.png differ