Mission 5: Bring it all together with Gitlab CI/CD
In this mission, we integrate everything we've learned into a GitLab CI/CD pipeline, creating a fully automated network automation workflow. Throughout the previous missions, we have:
- Automated network configurations using Ansible
- Managed network hierarchy and inventory dynamically with NetBox
- Prepared to ensured device configuration by verifying with pyATS
Now, we take these automation capabilities to the next level by orchestrating them into a GitLab CI/CD pipeline. This will allow us to:
- Automate deployments whenever network changes are committed
- Ensure consistency across network environments by integrating NetBox, Ansible, Catalyst Center, and pyATS into a structured, repeatable workflow
What do we need to have a functional Gitlab CI/CD pipeline?
To establish a fully operational GitLab CI/CD pipeline, three essential components must be in place:
- Runner: A host responsible for executing the pipeline jobs. The GitLab Runner can be installed on a physical server, virtual machine, Docker or Kubernetes cluster. It processes tasks defined in the pipeline configuration and executes them accordingly.
- Image: If the GitLab Runner operates within a Docker environment, a Docker image must be specified. This image provides the necessary dependencies and runtime environment required for executing automation tasks efficiently. You can specify a public available image or upload your custom image to the Gitlab container registry.
- Pipeline Configuration: The pipeline’s structure and execution logic are defined in a
.gitlab-ci.yml
file located at the root of the repository. This file dictates how jobs are executed, including steps for validation, deployment and testing.
We already have a shared runner in place, so we don't need to add an additional runner. But we still need to work on our image and the pipeline configuration.
Step 1: Create image and push it to the Gitlab container registry
-
Have a look at the
Dockerfile
in the directorydocker
. The build file specifies the following:- Use
ubuntu:24.04
as base image - Install
python3
andpip3
- Copy the requirements files into the containers working directory
- Install the required python packages and ansible collections specified in the requirements files
- Use
-
Let's build the image:
docker build -t 198.18.134.23:5050/ltrops-2341/pod-2/ansible ./docker/.
Warning
If the build was not successful in the first try, just do it again. Could be a temporary connection issues between your workstation and the internet
-
After the build we want to push it to our container registry:
docker push 198.18.134.23:5050/ltrops-2341/pod-2/ansible
-
Navigate to your Registry and verify that the image got pushed sucessfully.
Key Value User user2 Password C1sco12345 Example Pod-1:
Understanding pipeline configuration
The .gitlab-ci.yml
file serves as the blueprint for our GitLab CI/CD pipeline, defining its structure and execution logic. The pipeline is organized into stages, each representing a distinct phase of the automation process, with jobs that execute specific tasks within those stages.
To ensure reliability and prevent unintended changes, each stage must complete successfully before the pipeline proceeds to the next. This approach guarantees that configurations are validated and tested before being applied to the network.
Each job runs within a GitLab Runner, leveraging a predefined Docker image to provide a consistent execution environment.
In this lab, we are using a simple pipeline, performing only minimal validation before deployment. However, in real-world scenarios, it is strongly recommended to implement comprehensive testing, including:
- Deploying to a test environment first before making changes in production.
- Performing impact analysis to assess potential risks before applying configurations to live networks.
- Implementing an approval process to add a layer of governance by requiring manual validation before changes are promoted to production.
Below is a simplified version of our pipeline configuration, designed for learning purposes and to provide a foundational understanding of CI/CD workflows.
- Verification (verify stage): A dry-run using Ansible to preview potential changes before deployment.
- Deployment (deploy stage): Executes automation tasks to provision and configure network infrastructure.
- Testing (test stage): Validates the deployed changes by verifying the actual network state through automated tests.
default:
image: 198.18.134.23:5050/ltrops-2341/pod-2/ansible:latest
stages:
- verify
- deploy
- test
ansible_dry-run:
stage: verify
script:
- ansible-playbook ansible/create-site-hierarchy.yml --check
deploy_infastructure:
stage: deploy
script:
- ansible-playbook ansible/create-site-hierarchy.yml
test_network_state:
stage: test
script:
- pyats run job pyATS/job.py
This configuration results into the following Gitlab pipeline:
Key Pipeline Features Explained
To better understand how our pipeline functions, let's explore some essential GitLab CI/CD concepts used in the configuration. These features help optimize, control, and reuse configurations across different stages and jobs.
- Artifacts:
artifacts
store generated files from a job so they can be downloaded or used later - Extends:
extends
allows jobs to inherit settings from predefined templates, reducing duplication - Rules:
rules
define when a job should execute based on conditions - When: The
when: manual
setting pauses execution until triggered manually by a user
Step 2: Review and edit the pipeline configuration
-
Open the file
.gitlab-ci.yml
. -
Go to Gitlab and copy the image path by click on the icon:
-
Edit the image on top to match your newly added Docker image:
default: image: path_to_your_image
Pipeline triggers
By default, GitLab pipelines are triggered by code changes, such as:
- Pushing commits to a branch.
- Creating or updating a merge request.
This approach is commonly used in Infrastructure as Code (IaC) workflows, where changes to network configurations are managed via Git. Merge request pipelines allow for extensive testing and approval before deployment, ensuring infrastructure stability.
In a NetBox-Driven Environment
Unlike traditional IaC, our network infrastructure is managed through NetBox rather than direct code changes. This means:
- Code does not change, but deployment data does.
- NetBox updates (e.g., adding devices, modifying configurations) must trigger automation.
Alternative Triggering Methods
Since we cannot rely on Git changes, we use manual pipeline triggers:
- Web Triggers (UI-Based Execution)
- API Trigger with Tokens
Step 3: Update your git repository
git add .
git commit -m "mission-5 gitlab-ci"
git push
Step 4: Trigger the pipeline via Web for site-creation
The pipeline has been structured into two distinct workflows to separate site creation from switch onboarding. This approach reflects real-world network automation scenarios, where:
- A site can be pre-provisioned independently, without immediately adding a switch
- A switch can be added later to an existing site without requiring a full site deployment
Since our sites have already been created and we don't want to delete and recreate them, we will manually trigger the site-creation pipeline via the GitLab Web UI.
-
Navigate to your Repository
-
In the left menu click on Build > Pipelines
-
In the top right corner, click on New pipeline
-
Add a variable named
SITE_PIPELINE
and add the the valuetrue
to it. Click onNew pipeline
: -
Wait for the site-creation_dry-run to sucessfully complete and then you have to option to verify by clicking on it.
-
Navigate back and trigger the deploy job:
-
As soon as the job successfully executed, check in Catalyst Center if the site hierarchy was created sucessfully.
Example Pod-1:
Step 5: Create a pipeline trigger token
-
Navigate to Settings > CI/CD > Pipeline trigger tokens in your Gitlab repository.
-
Create a new token by clicking on Add new token. Add a description and click on Create pipeline trigger token.
Step 6: Adjust your netbox custom script to trigger the pipeline
-
Open the Netbox custom-script
./netbox/pod2_switches.py
and add the following to the end of the script.if commit: self.log_info("Commit is set. Triggering GitLab pipeline...") project = "8" url = f"http://198.18.134.23/api/v4/projects/{project}/ref/master/trigger/pipeline" token = "YOUR TOKEN HERE" # replace with your token params = { "token": token, "variables[SWITCH_PIPELINE]": "true" } try: response = requests.post(url, params=params) response.raise_for_status() if response.status_code == 201: self.log_success("Pipeline triggered successfully.") else: self.log_warning( f"Unexpected response status: {response.status_code}. Response: {response.text}" ) except requests.exceptions.RequestException as e: self.log_failure(f"Failed to trigger pipeline: {e}") else: self.log_info("Commit is not set. Skipping GitLab pipeline trigger.")
-
Copy your newly created trigger Token.
-
Replace the token in your script.
-
Commit and push your changes to git:
git add . git commit -m "mission-5 custom script" git push
-
In Netbox Devices > Devices make sure to delete your previously created switch.
-
Navigate to Operations > Data Sources, click on your data source and then on Sync.
-
Recreate the switch by navigating to Customization > Scripts and run your custom script Add Switches to Site.
Switch creation variables
Key Value Tenant pod2 Site $YOUR_SITE
Device Type C9KV-UADP-8P Device Role Branch Switch Device Uplink GigabitEthernet1/0/1 Serial Numbers CML12SW1 Commit changes true -
Check the pipeline status in your Repository, by clicking on the running pipeline. Once the dry-run went through, you can verify the output by clicking on it.
-
Trigger the deploy stage. The pipeline can easily take up to 15 minutes, as we are doing a PnP and a switch provision before moving forward. Feel free to take a break!
-
Meanwhile you can check the status also in Catalyst Center. You should see your switch there in different states before reaching
Provisioned
-
When the pipeline successfully went through, your switch should be in
active
state in Netbox and also added to your site in Catalyst Center Inventory.