GitLab CI/CD Fundamentals¶
Overview¶
Explain what CI/CD solves, map GitLab’s architecture to pipelines and runners, and define stage, job, and pipeline in ops language.
Continuous Integration (CI) builds and tests every change in Git. Continuous Delivery / Deployment (CD) promotes those builds toward production with gates you control. GitLab CI/CD stores the automation definition as .gitlab-ci.yml next to the application code, so review, history, and merge requests share one system.
This course is GitLab CI/CD for Cloud & DevOps Engineers — production pipelines, not toy demos.
This is a core tutorial in Module 1 · GitLab CI/CD Fundamentals of the REBASH Academy GitLab CI/CD for Cloud & DevOps Engineers series — written for Cloud, DevOps, Platform, and SRE engineers.
Prerequisites¶
Learning Objectives¶
By the end of this tutorial, you will be able to:
- Define CI, CD, pipeline, stage, and job
- Sketch GitLab → Runner → job execution
- Contrast Free / Premium / Ultimate and SaaS vs self-managed
- Name when shared runners are enough vs when you need dedicated ones
Architecture¶
This topic’s control points and relationships are shown below.
Theory¶
What it is¶
A pipeline is one run of your CI/CD definition for a commit, merge request, tag, or schedule. Pipelines contain stages (ordered phases such as build, test, deploy). Each stage holds jobs — isolated units of work with a script that a runner executes. GitLab coordinates: the GitLab instance schedules jobs; runners pick them up and report status and logs.
| Term | Meaning |
|---|---|
| Pipeline | One automation run for a Git event |
| Stage | Ordered group of jobs (next stage waits by default) |
| Job | Single unit of work (script, image, tags) |
| Runner | Agent that executes jobs |
Why it matters¶
Manual builds and “works on my laptop” deploys fail at scale. Pipelines make every change reviewable (MR pipeline), repeatable (same YAML, different $CI_* context), and auditable (job logs and artefacts). Platform and SRE teams standardise templates so product squads inherit lint, test, and deploy patterns instead of inventing CI per repo.
How it works¶
Mental model: Git event → GitLab creates pipeline → jobs queued → runners execute → artefacts / status back to GitLab.
- You push or open a merge request; GitLab reads
.gitlab-ci.yml. - GitLab expands includes, evaluates
rules/workflow, and builds the job graph. - Eligible runners (shared, group, or project) claim jobs matching tags and capacity.
- The executor (Docker, shell, Kubernetes, …) runs the job script and streams logs.
- Job status rolls up to the pipeline; optional artefacts and environments update the UI.
You do not need a paid GitLab instance for early labs: use GitLab.com free tier, or lint locally with glab ci lint / gitlab-ci-local.
Key concepts and comparisons¶
| Edition | Typical CI/CD focus |
|---|---|
| Free | Core pipelines, shared runners (minutes limits on SaaS) |
| Premium | Deeper compliance, approvals, advanced environments |
| Ultimate | Security scanning suites and governance at scale |
| Hosting | You operate |
|---|---|
| GitLab.com (SaaS) | Projects and CI; GitLab runs the control plane |
| Self-managed | Full stack — GitLab, runners, upgrades, HA |
Common pitfalls¶
- CI is not “the runner” — GitLab schedules; runners execute.
- A green pipeline is not a production release unless you designed deploy jobs and gates that way.
- Free-tier minutes are finite on SaaS — lint and local runners save quota.
- Stages are not the only ordering model; later modules cover
needsDAGs.
Hands-on Lab¶
Objective¶
Create a minimal Python app, a two-stage .gitlab-ci.yml with needs, and prove both YAML validity and local script execution before pushing to GitLab.
Prerequisites¶
- Python 3 with PyYAML (
pip install pyyaml) - Optional: GitLab.com project or self-managed instance to run the pipeline on a runner
Lab environment¶
Workspace: ~/rebash-gitlab/module-01
File-first lab. Push to GitLab only when you want a runner to execute jobs.
Real-world scenario¶
Your squad is onboarding a new microservice repository. The platform team requires every project to ship a .gitlab-ci.yml with explicit lint and test stages before the first merge request. You author the files locally, validate YAML offline, and simulate job scripts on your laptop to save runner minutes.
Step-by-step tasks¶
Task 1 – Create the application source¶
Create src/app.py:
Verify the script runs:
cd ~/rebash-gitlab/module-01
python3 src/app.py | tee app-out.txt
grep -q '^ok$' app-out.txt
Expected output
app-out.txt contains exactly ok.
Task 2 – Author the pipeline with stages and needs¶
Create .gitlab-ci.yml:
stages:
- lint
- test
lint:
stage: lint
image: python:3.12-alpine
script:
- python -m py_compile src/app.py
test:
stage: test
image: python:3.12-alpine
needs:
- lint
script:
- python src/app.py
Validate offline:
cd ~/rebash-gitlab/module-01
python3 -c "
import yaml
d = yaml.safe_load(open('.gitlab-ci.yml'))
assert d['stages'] == ['lint', 'test']
assert d['test']['needs'] == ['lint']
print('OK', sorted(k for k in d if k != 'stages'))
"
Expected output
Prints OK followed by job keys (lint, test); no YAML exception.
Task 3 – Simulate job scripts locally¶
Run the same commands the runner would execute:
cd ~/rebash-gitlab/module-01
python3 -m py_compile src/app.py
python3 src/app.py | tee simulate-out.txt
test "$(cat simulate-out.txt)" = 'ok'
Expected output
Compile succeeds with no output; simulate-out.txt is ok.
Validation steps¶
-
src/app.pyprintsokwhen run directly -
.gitlab-ci.ymlparses with PyYAML -
stageslistslintthentest -
testjob declaresneeds: [lint] - Local compile and run match job scripts
Common errors and fixes¶
| Error | Cause | Fix |
|---|---|---|
yaml.scanner.ScannerError | Wrong indentation in .gitlab-ci.yml | Use 2-space indent; re-validate with PyYAML |
needs: job not found | Typo in job name | Job keys are case-sensitive; align needs with the lint job key |
Job stuck pending on GitLab | No runner available | Register a runner or enable shared runners; check tags |
python: command not found locally | Alpine image vs host Python | Local simulation uses python3; CI job uses the pinned python:3.12-alpine image |
Challenge exercise¶
Add a top-level default: block with image: python:3.12-alpine and remove duplicate image: lines from each job. Re-validate YAML and confirm both jobs still parse.
Learning outcomes¶
- Modelled pipeline stages and job ordering with
needs - Pinned container images for reproducible CI runs
- Validated GitLab CI YAML offline before pushing
- Simulated runner scripts locally to catch failures early
Cleanup¶
rm -f ~/rebash-gitlab/module-01/app-out.txt ~/rebash-gitlab/module-01/simulate-out.txt
# Keep src/ and .gitlab-ci.yml for the next module
Validation¶
- Lab commands run under
~/rebash-gitlab/module-01/ - You can explain each Theory section in your own words
- You used modern tooling where it applies to this topic
- You can describe one production failure mode for this topic
Code Walkthrough¶
Production practice for GitLab CI/CD Fundamentals always combines:
- Inspect before you change (status, plan, logs, dry-run)
- Prefer reversible, documented changes (Git, IaC, drop-ins, version pins)
- Capture evidence (command output, pipeline logs) for handovers
- Prefer current tools and APIs over legacy shortcuts
- Least privilege — escalate credentials only when required
Keep runbooks short enough to follow under pressure. Automate checks; keep humans for judgement.
Security Considerations¶
- Treat credentials and tokens for gitlab as privileged — never commit them
- Prefer short-lived auth (OIDC, roles, SSO) over long-lived keys
- Validate blast radius before apply/deploy/delete operations
- Restrict who can approve production changes
- Collect audit logs; limit who can read sensitive traces
Common Mistakes¶
CI is not “the runner” — GitLab schedules; runners execute.
Validate assumptions against the Theory section and official docs before changing production.
A green pipeline is not a production release unless you designed deploy jobs and gates tha
Lab shortcuts (open security groups, admin roles, skip approvals) must not ship unchanged.
Changing production without a rollback path
Always know how to revert (previous artefact, prior release, state rollback, DNS failback).
Best Practices¶
- Encode GitLab CI/CD Fundamentals changes as code and review them in pull requests
- Pin versions (images, modules, actions, provider plugins)
- Separate environments with clear promotion gates
- Alert on symptoms with runbooks attached
- Destroy lab resources; tag everything with owner and expiry where possible
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
| Auth / permission denied | Wrong identity, policy, or scope | Check caller identity, roles, and least-privilege policies |
| Timeout / no route | Network, DNS, security group, or endpoint | Trace path, DNS, and allow-lists before retrying |
| Drift / unexpected plan | Manual change or wrong state/workspace | Reconcile desired vs actual; avoid click-ops on managed resources |
| Pipeline/job red | Flaky step, cache, or missing secret | Read failing step logs; bisect recent workflow/config changes |
| Cost spike | Idle load balancer, NAT, oversized compute | Inventory billable resources; stop/delete labs promptly |
Summary¶
GitLab CI/CD Fundamentals is essential for Cloud and DevOps engineers working with gitlab. Practise the lab until the inspection and change path is muscle memory, then continue the track.
Interview Questions¶
- What are stages versus jobs in GitLab CI, and why does order matter?
- A pipeline is green in your mind but red on GitLab — what do you check first?
- When would you split verify and package into separate stages?
- How should secrets be handled in a first GitLab CI pipeline?
- How do artifacts help downstream jobs without rebuilding everything?
Sample answer — question 2
Compare the job image and script with your laptop: missing packages, wrong shell, and different CI variables explain most first-pipeline failures. Open the failing job log at the first error line.
Sample answer — question 4
Keep non-secrets in YAML variables; put credentials in masked/protected CI variables or OIDC. Never commit tokens, and never echo secret values in job logs.