GitLab CI Runners, when configured with the Kubernetes executor, can dynamically provision pods on your Kubernetes cluster to run your CI/CD jobs. This means you don’t need a dedicated runner machine for every job; instead, Kubernetes handles the scaling and isolation.
Here’s a GitLab CI configuration that sets up a runner to use the Kubernetes executor. This example assumes you have a GitLab Runner registered with your project or group and that it’s configured to use the kubernetes executor.
stages:
- build
- deploy
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
build_app:
stage: build
script:
- echo "Building the application..."
- echo "Tagging image as $IMAGE_TAG"
- echo "Pushing image to registry..."
tags:
- kubernetes # This tag tells the runner to use a runner configured with the kubernetes executor
deploy_app:
stage: deploy
script:
- echo "Deploying the application to Kubernetes..."
- echo "Using image: $IMAGE_TAG"
tags:
- kubernetes
When a job runs with the kubernetes tag, the GitLab Runner will interact with your Kubernetes API. It will create a pod in a specified namespace. This pod will have the necessary environment variables and secrets injected from your GitLab CI/CD settings, and it will pull the Docker image defined in your .gitlab-ci.yml or the default image configured for the runner.
The beauty of the Kubernetes executor is its flexibility. You can define specific Kubernetes configurations directly within your .gitlab-ci.yml file. This allows you to tailor the pod’s characteristics for each job.
Let’s say you need a specific service account with particular RBAC permissions for a deployment job. You can specify this in your .gitlab-ci.yml:
deploy_app_with_specific_sa:
stage: deploy
script:
- echo "Deploying with a specific service account..."
- kubectl apply -f deployment.yaml
tags:
- kubernetes
kubernetes:
namespace: production # Target namespace for the job pod
service_account: ci-deployer-sa # The Kubernetes service account to use
image: registry.gitlab.com/gitlab-org/gitlab-runner:latest # Optional: override runner image
cpu_limit: "100m" # Optional: set CPU limits for the pod
memory_limit: "128Mi" # Optional: set memory limits for the pod
In this snippet, the kubernetes section within the job definition allows you to override default runner configurations.
namespace: This tells the runner to create the job pod in theproductionnamespace within your Kubernetes cluster.service_account: This specifies that the pod should run using theci-deployer-saKubernetes service account, granting it the permissions defined for that service account.image: This allows you to specify a different Docker image for the job pod than the one the runner might default to.cpu_limitandmemory_limit: These define resource constraints for the job pod, preventing it from consuming excessive resources in your cluster.
The most surprising true thing about the Kubernetes executor is that it can be configured to clean up after the job finishes, but the specific timing and conditions for this cleanup are often misunderstood. By default, the runner will delete the pod once the job completes (either successfully or with an error). However, if you need to retain logs or inspect the pod state after a failure, you can configure the runner’s helper_image and termination_message_path in the runner’s config.toml to influence this behavior, though it’s not directly controlled by the .gitlab-ci.yml per job.
The runner uses the Kubernetes API to create, monitor, and delete pods. When a job is triggered, the runner finds a suitable Kubernetes executor runner based on the tags. It then constructs a pod definition, including the job’s script, environment variables, and any specified Kubernetes configurations. This pod is submitted to the Kubernetes API. The Kubernetes scheduler then places the pod onto a node, and the container runtime starts the container. The runner continuously polls the Kubernetes API for the pod’s status until it completes, at which point the runner either deletes the pod or leaves it for inspection based on its configuration.
If you’re running jobs that require specific Kubernetes configurations, like mounting ConfigMaps or Secrets, you can also define these within the job’s kubernetes block:
deploy_with_config:
stage: deploy
script:
- echo "Applying deployments with specific configurations..."
- kubectl apply -f deployment.yaml
tags:
- kubernetes
kubernetes:
namespace: staging
volumes:
- name: config-volume
configMap:
name: my-app-config
volume_mounts:
- name: config-volume
mount_path: /etc/config
Here, volumes and volume_mounts directly map to Kubernetes pod specifications. This allows you to inject configuration data or secrets into your job pods dynamically, making your CI/CD pipelines more adaptable to different environments and application requirements.
The next concept you’ll likely encounter is managing Kubernetes secrets and credentials securely within your GitLab CI/CD pipelines when using the Kubernetes executor.