Note that the traditional GitOps with the agent for Kubernetes has been deprecated with GitLab 16.2 (July 2023), and replaced with Flux, as shown in this tutorial.
But still, a traditional approach can still work, with a bridge between the CI/CD pipeline and the GitOps workflow:
As part of your CI/CD pipeline, whenever you merge a project file, create or update the manifest files accordingly. That could be based on templates, dynamic values, or any other logic appropriate for your application.
Once the manifests are generated or updated, you should commit them back to the repository. That step is crucial because the GitLab Kubernetes Agent is watching the repository for changes in manifest files.
You should add [skip ci]
to the commit messages for auto-commits to prevent infinite loops of CI/CD pipeline runs.
For instance:
stages:
- build
- deploy
build:
stage: build
script:
- echo "Build steps for your application"
# Potentially other build steps ...
generate_and_commit_manifests:
stage: deploy
script:
- echo "Generating/Updating manifests for $CI_COMMIT_SHORT_SHA"
- ./generate-or-update-manifests.sh $CI_COMMIT_SHORT_SHA
- git add path/to/manifests/*
- git commit -m "Auto-update manifests for $CI_COMMIT_SHORT_SHA [skip ci]"
- git push origin $CI_COMMIT_REF_NAME
deploy_stage:
stage: deploy
environment:
name: stage
script:
- echo "That is where you would traditionally deploy to staging, but it is now handled by the GitOps process"
when: manual
deploy_production:
stage: deploy
environment:
name: production
script:
- echo "That is where you would traditionally deploy to production, but it is now handled by the GitOps process"
when: manual
The build job will run, building your application.
The generate_and_commit_manifests
job will run, updating the manifest files as necessary and committing them back to the repository. That commit will be noticed by the GitLab Kubernetes Agent, which will then apply the manifests to your Kubernetes cluster.
The deploy_stage
and deploy_production
jobs will be available to run manually, but they are more symbolic in this setup, since the actual deployment is handled by the GitLab Kubernetes Agent.
With this strategy, you are making use of both traditional CI/CD and GitOps workflows. The actual application build and preparation happens in the CI/CD pipeline, while the deployment is managed in a GitOps manner by the GitLab Kubernetes Agent.
On the other hand, as of today, this is what we are doing:
Let's suppose we need to merge a code change, by code change, I mean some feature code, as if we need to change Payment.java
or Payment.py
file, some project related code.
As of today, imagine an angry manager, who would force us:
"for each and every comit/merge on a code file, you also need to manually change the kubernetes/staging/payment-staging-manifest.yml
".
He would also say:
"if I see a commit, merge, without the manual change, fired!"
Your hypothetical scenario with the "angry manager" makes the challenge clearer: essentially, your team is being asked to ensure that every commit or merge involving a code change must also involve a corresponding change to the Kubernetes manifest file, or else... there are consequences.
That requirement calls for automation, as the ideal way to address this requirement without increasing the manual overhead for developers.
A first good practice is to embed the version/commit info in the application: Each time the code is built (e.g., during a CI job), embed the commit hash or another unique identifier in the application. That could be an environment variable, a file in the build, etc.
Then, after the application is built and before it is deployed, have a step in your CI/CD pipeline that automatically updates the Kubernetes manifest to use this new version.
- For instance, if you are using Docker and Kubernetes, every build of your application could produce a new Docker image with a tag corresponding to the commit hash.
- Your CI/CD pipeline would then automatically update the manifest to use this new image.
And once the manifest is updated, auto-commit and push this change back to your repository. Ensure you use a commit message tag like [skip ci]
to prevent infinite CI loops.
Since the GitLab Kubernetes Agent is already watching for changes in the manifest, it will pick up this auto-committed change and deploy it.
Something like:
update-manifest:
stage: deploy
script:
- COMMIT_HASH=$(git rev-parse --short HEAD)
- docker build -t myapp:$COMMIT_HASH .
- docker push myapp:$COMMIT_HASH
- sed -i "s/myapp:[a-z0-9]*/myapp:$COMMIT_HASH/" kubernetes/staging/payment-staging-manifest.yml
- git add kubernetes/staging/payment-staging-manifest.yml
- git commit -m "Auto-update manifest with image myapp:$COMMIT_HASH [skip ci]"
- git push origin $CI_COMMIT_REF_NAME
By using this approach, developers can continue their workflow of just committing code changes. The CI/CD pipeline handles the requirement of updating the Kubernetes manifest for each code change, ensuring you stay in the "good books" of the HAM (Hypothetical Angry Manager).
Some kind of:
deploy_stage:
stage: deploy
environment:
name: stage
script:
- trigger the already configured gitlab kubernetes agent here without changing the manifest file.
I understand the need: you want to trigger a deployment in the GitLab Kubernetes Agent without having to modify a manifest file or make any other changes in your repository.
I do not remember a GitLab Kubernetes Agent with an out-of-the-box "trigger" command or API call, as its design philosophy revolves around watching Git repositories for changes ("pull").
Still, as a workaround, you can use a GitLab CI/CD pipeline to force a synchronization.
Rather than making "dummy" changes to the manifest file, maintain an environment variable within the Kubernetes manifest that is set to be the commit hash (or some other unique value) of the pipeline.
Then modify the environment variable in the manifest file with the commit hash or a timestamp during the CI/CD pipeline run.
Commit this change and push it back to the repository (again, use [skip ci]
in the commit message to prevent an endless loop of pipelines). That ensures that the agent sees the change and gets "triggered".
Something like:
deploy_stage:
stage: deploy
environment:
name: stage
script:
- COMMIT_HASH=$(git rev-parse --short HEAD)
- sed -i "s/COMMIT_ENV_VAR=.*/COMMIT_ENV_VAR=$COMMIT_HASH/" path/to/manifest.yml
- git config user.email "ci@mydomain.com"
- git config user.name "GitLab CI"
- git add path/to/manifest.yml
- git commit -m "Update manifest with commit hash [skip ci]"
- git push origin $CI_COMMIT_REF_NAME
The downside is that you are still making a change to the repo, but it is now a meaningful change: recording the commit hash of the build triggering the deployment, which can be helpful for tracking purposes.