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 the production namespace within your Kubernetes cluster.
  • service_account: This specifies that the pod should run using the ci-deployer-sa Kubernetes 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_limit and memory_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.

Want structured learning?

Take the full Gitlab-ci course →