Orchestrating Automation Controllers via GitLab Runner and Ansible Configuration-as-Code

The integration of GitLab Runner with Ansible represents a paradigm shift in how infrastructure is managed, moving away from manual, imperative configurations toward a robust Configuration-as-Code (CaC) methodology. At its core, a GitLab Runner is a specialized application designed to work in tandem with GitLab CI/CD to execute jobs within a pipeline. When this runner is specifically tasked with managing an Ansible automation controller, it acts as the execution engine that translates version-controlled Ansible playbooks into real-world infrastructure states. This synergy allows organizations to treat their automation controllers not as static appliances, but as dynamic, versioned entities that can be rebuilt, updated, and audited through a single Git repository.

By utilizing GitLab Runner, the automation process gains several architectural advantages. Unlike traditional methods that might rely on webhooks or direct manual execution, using a CI/CD pipeline provides tight integration with the source of truth: the Git repository. This enables the reuse of existing CI/CD processes, provides a clear audit trail of every change made to the controller, and allows for the easy re-running of failed jobs, which is critical for maintaining high availability in complex automation environments. Whether the environment is a highly connected cloud setup or a strictly disconnected, air-gapped enterprise network, the GitLab Runner provides the necessary compute capability to bridge the gap between code and execution.

Architectural Requirements and Environmental Prerequisites

Before initiating the deployment of a GitLab Runner to manage Ansible, several foundational components must be established to ensure a successful pipeline execution. The reliability of the automation depends on the connectivity and availability of both the GitLab instance and the target automation controller.

To facilitate a successful deployment, the following prerequisites must be met:

  • An automation controller must be running with administrative access privileges to allow for full configuration changes.
  • A GitLab instance must be operational with admin access to manage runners and CI/CD pipelines.
  • Network connectivity must be explicitly provided over ports 80 or 443 from the GitLab Runner node to the automation controller API.
  • A container registry must be available to host and store the various images required for the pipeline.
  • An Ansible Execution Environment (EE) image must be prepared and uploaded to the container registry. This specific image must come pre-configured with the ansible.controller and infra.controller_configuration collections to ensure the runner has the necessary modules to interact with the controller.

The choice of execution environment is vital. While many environments default to Docker, modern enterprise security standards often necessitate the use of Podman, particularly in RHEL-based systems. The ability to swap these executors determines how the runner interacts with the underlying host and manages containerized workloads.

Technical Specifications of the GitLab Runner Ansible Role

For administrators seeking to automate the installation and management of the runner itself, specialized Ansible roles provide a structured way to handle versioning and configuration. Utilizing a role like riemers/ansible-gitlab-runner allows for consistent deployment across multiple nodes.

The following table outlines the key variables and configuration parameters required for managing the GitLab Runner via Ansible:

Variable Name Description Usage Notes
gitlab_runner_package_name The name of the RPM/package to be installed. Defaults to gitlab-runner. For GitLab versions earlier than 10.x, use gitlab-ci-multi-runner.
gitlab_runner_package_version The specific version of the runner to install on Linux. Used to target specific releases for stability.
gitlab_runner_wanted_version The version of the runner for macOS or Windows. For example, 12.4.1 can be used for non-Linux platforms.
gitlab_runner_concurrent The maximum number of jobs that can run at once. Defaults to the number of available processor cores.
gitlab_runner_registration_token The token used to register the runner with GitLab. Can be used globally if gitlab_runner_registration_token_type is set to registration-token.
gitlab_runner_runners A list defining individual runner configurations. Often used to specify unique tokens for each runner item.

When managing these variables, it is important to note that the package naming convention changed in GitLab 10.x. Historically, the package was known as gitlab-ci-multi-runner, but it has since been renamed to gitlab-runner. Correctly identifying the target platform (Linux vs. macOS/Windows) is essential for selecting the correct versioning variable.

Manual Registration and Executor Configuration

Once the GitLab Runner software is obtained—typically as an RPM for RHEL 8 environments—it must be registered with the GitLab instance. This process links the compute resource to the CI/CD orchestrator.

The registration process follows a specific technical workflow:

  1. Navigate to the CI/CD section within the GitLab interface.
  2. Select "Runners" from the left-hand sidebar.
  3. Click "Register a group runner" located at the top right.
  4. Copy the generated registration token for use in the terminal.
  5. Execute the registration command on the runner node: sudo gitlab-runner register --url https://gitlab_url.
  6. When prompted, enter the registration token.
  7. Provide a description for the runner to identify its purpose.
  8. Define tags for the runner. Tags are critical because they allow the GitLab CI/CD process to match specific jobs to specific runners. For this specific automation workflow, a tag such as controller-system-ee is used to ensure the job is picked up by a runner equipped with the correct Ansible EE image.
  9. Select the executor. To utilize containerized environments, the docker executor is chosen (though this will be transitioned to podman in advanced configurations).
  10. Specify the default Docker image, which should point to the pre-configured Ansible EE image, such as quay.chrislab.internal/aap2/controller-system-ee:dev.

The use of tags like controller-system-ee is not merely organizational; it is a functional requirement. It ensures that the job, which requires specific Ansible collections, is routed to a container environment that actually contains those collections.

Transitioning from Docker to Podman for Enhanced Security

In many high-security or air-gapped enterprise environments, Podman is preferred over Docker due to its daemonless architecture and improved security posture. Configuring a GitLab Runner to use a Podman socket requires direct manipulation of the configuration files.

To facilitate this transition, the config.toml file must be modified. The process is as follows:

  1. Access the configuration file using a text editor: sudo vim /etc/gitlab-runner/config.toml.
  2. Locate the [runners.docker] section near the end of the file.
  3. Add the host parameter to point to the Podman socket. In the provided example, the socket is located at /run/user/1001/podman/podman.sock.
  4. The entry must be formatted as: host = "unix:///run/user/1001/podman/podman.sock".
  5. Ensure the unix:/// prefix is included to instruct the runner to look for the socket on the local machine.

An example of a completed [runners.docker] configuration block for a Podman-based runner is provided below:

toml [runners.docker] tls_verify = false image = "aapah.chrislab.internal/controller-system-ee:latest" privileged = false disable_entrypoint_overwrite = false oom_kill_disable = false disable_cache = false volumes = ["/cache"] shm_size = 0 host = "unix:///run/user/1001/podman/podman.sock"

In air-gapped or disconnected environments, an additional step is required: the config.toml must be adjusted to point to a local helper image. This is because the runner cannot reach out to the internet to pull the default helper images during job execution.

Securing the Pipeline with SAST, IaC Scanning, and Linting

A professional Configuration-as-Code pipeline does not just deploy; it validates. By integrating security scanning into the GitLab CI/CD pipeline, the automation ensures that the Ansible playbooks and Terraform files do not introduce vulnerabilities.

The security architecture of the pipeline includes:

  • SAST IaC (Static Application Security Testing for Infrastructure as Code): This scanner detects vulnerabilities within the Terraform and Ansible code itself.
  • Container Scanning: This process is applied to the Execution Environment (EE) image to identify security issues and generate a Software Bill of Materials (SBOM).
  • Ansible Linting: This integrates Ansible's linting capabilities with GitLab's Code Quality reports.

The integration of ansible-lint is achieved through a specific job definition within the .gitlab-ci.yml file. This job runs the linter and converts the output into a format that GitLab can display as a Code Quality report.

yaml 🔍 ansible-lint: stage: 🚀 ansible-deploy image: ${CI_REGISTRY_IMAGE}/${EE_IMAGE_NAME}:${EE_IMAGE_TAG} needs: [] script: - ansible-lint ansible/playbook.yml -f codeclimate | python3 -m json.tool | tee gl-code-quality-report.json || true artifacts: reports: codequality: - gl-code-quality-report.json

Following a successful deployment, the pipeline performs critical health checks. For instance, if the automation is provisioning a Tomcat server, the health-check job will attempt to connect to the server's HTTP port. A successful response confirms that the deployment is not only complete but also functional and accessible via the public IP address of the provisioned instance.

Analysis of the Integrated Automation Lifecycle

The convergence of GitLab Runner, Podman, and Ansible creates a highly resilient automation loop. The transition from manual configuration to a fully orchestrated pipeline allows for a "self-healing" infrastructure approach. By treating the automation controller as code, the complexity of managing large-scale environments is significantly reduced.

The shift to Podman via the host configuration in config.toml addresses a critical need in modern DevOps: running containerized workloads without the security risks associated with a root-level Docker daemon. Furthermore, the implementation of SAST and IaC scanning ensures that security is not an afterthought but a fundamental component of the deployment lifecycle. This "Shift Left" approach, where security and validation are performed early in the pipeline, prevents the propagation of misconfigurations from the Git repository to the production automation controller.

Ultimately, the ability to run these pipelines in disconnected, air-gapped environments proves the versatility of the GitLab Runner. It provides a standardized way to manage even the most sensitive enterprise systems, ensuring that the configuration of the automation controller remains consistent, repeatable, and highly secure.

Sources

  1. Red Hat - GitLab Runner and Ansible Configuration
  2. GitHub - ansible-gitlab-runner Role
  3. GitLab - Using Ansible and GitLab as Infrastructure for Code

Related Posts