GitLab CI/CD: Architecting, Deploying, and Optimizing Pipelines
Architecture Core Concepts
Using needs Keyword
In this guide, you’ll enhance an existing GitLab CI/CD pipeline by:
- Renaming jobs for clarity
- Introducing a dedicated Docker stage
- Controlling execution order with the
needs
keyword
By the end, you’ll understand how to build a Directed Acyclic Graph (DAG) of jobs that enforces logical progression and efficient failure handling.
1. Original Workflow
Here’s the starting pipeline, which builds ASCII art, runs tests, and deploys:
workflow:
name: Generate ASCII Artwork
stages:
- build
- test
- deploy
build_job_1:
stage: build
before_script:
- gem install cowsay
- sleep 30s
script:
- >
cowsay -f dragon "Run for cover,
I am a DRAGON....RAWR!" >> dragon.txt
artifacts:
name: Dragon Text File
paths:
- dragon.txt
when: on_success
expire_in: 3 days
test_job_2:
stage: test
script:
- echo "Running tests..."
deploy_job_3:
stage: deploy
script:
- echo "Deploying to AWS EC2"
2. Renaming Jobs and Cleaning Up
Rename jobs for readability and remove the unnecessary sleep
command:
workflow:
name: Generate ASCII Artwork
stages:
- build
- test
- deploy
build_file:
stage: build
before_script:
- gem install cowsay
script:
- >
cowsay -f dragon "Run for cover,
I am a DRAGON....RAWR" >> dragon.txt
artifacts:
name: Dragon Text File
paths:
- dragon.txt
when: on_success
expire_in: 3 days
test_file:
stage: test
script:
- echo "Running tests..."
deploy_ec2:
stage: deploy
script:
- echo "Deploying to AWS EC2"
At this point, the pipeline runs three sequential stages: build, test, then deploy.
3. Introducing a Docker Stage
Add a new docker stage with three placeholder jobs:
stages:
- build
- test
- docker
- deploy
build_file: &build_file
stage: build
before_script:
- gem install cowsay
script:
- >
cowsay -f dragon "Run for cover,
I am a DRAGON....RAWR" >> dragon.txt
artifacts:
name: Dragon Text File
paths:
- dragon.txt
when: on_success
expire_in: 3 days
test_file: &test_file
stage: test
script:
- echo "Running tests..."
docker_build:
stage: docker
script:
- echo "docker build -t docker.io/dockerUsername/imageName:version"
- sleep 15s
docker_testing:
stage: docker
script:
- echo "docker run -p 80:80 docker.io/dockerUsername/imageName:version"
- sleep 10s
- exit 1
docker_push:
stage: docker
script:
- echo "docker login --username=dockerUsername --password=s3cUrePaSsW0rd"
- echo "docker push docker.io/dockerUsername/imageName:version"
deploy_ec2:
<<: *build_file
script:
- echo "Deploying to AWS EC2"
By default, GitLab runs all three Docker jobs in parallel once the test stage completes.
Warning
When jobs share the same stage, GitLab CI/CD executes them in parallel. This may cause docker_push
to run before docker_build
, or allow failures in docker_testing
without halting docker_push
.
4. Docker Jobs Overview
Job Name | Stage | Purpose |
---|---|---|
docker_build | docker | Builds the Docker image |
docker_testing | docker | Runs container and performs health checks |
docker_push | docker | Logs in and pushes the image to the registry |
5. Sequencing with needs
Use the needs
keyword to enforce a DAG of dependencies and ensure correct ordering:
docker_build:
stage: docker
needs:
- test_file
script:
- echo "docker build -t docker.io/dockerUsername/imageName:version"
- sleep 15s
docker_testing:
stage: docker
needs:
- docker_build
script:
- echo "docker run -p 80:80 docker.io/dockerUsername/imageName:version"
- sleep 10s
- exit 1
docker_push:
stage: docker
needs:
- docker_testing
script:
- echo "docker login --username=dockerUsername --password=s3cUrePaSsW0rd"
- echo "docker push docker.io/dockerUsername/imageName:version"
After committing, the UI will reflect this sequence:
build → test → docker_build → docker_testing → docker_push.
If docker_testing
fails, docker_push
is automatically skipped.
Console output for docker_testing
:
$ echo "docker run -p 80:80 docker.io/dockerUsername/imageName:version"
docker run -p 80:80 docker.io/dockerUsername/imageName:version
$ sleep 10s
$ exit 1
ERROR: Job failed: exit code 1
6. Ignoring Stage Order
You can also launch jobs as soon as their dependencies complete, even if they’re in later stages. For example:
docker_build:
stage: docker
needs:
- build_file
script:
- echo "docker build -t docker.io/dockerUsername/imageName:version"
Here, docker_build starts immediately after build_file, running in parallel with test_file.
7. Conclusion
Using the needs
keyword allows you to:
- Sequence jobs within the same stage
- Override default stage ordering for earlier execution
- Visualize your pipeline as a clear DAG
This gives you precise control over dependencies and failure handling in your GitLab CI/CD workflows.
Links and References
Watch Video
Watch video content
Practice Lab
Practice lab