GitLab CI/CD: Architecting, Deploying, and Optimizing Pipelines
Auto DevOps
Staging Environment and Canaray Deployment with AutoDevOps part 1
In this guide, we’ll leverage GitLab’s Auto DevOps to set up a staging environment and execute a canary release into production. By the end, you’ll have a pipeline that automatically deploys to staging and promotes to production manually, with incremental rollouts.
GitLab Auto DevOps Deployment Strategy
GitLab Auto DevOps offers multiple deployment workflows. In this lesson, we use:
- Automatic deployment to staging
- Manual approval for production
Default Variables
By default, staging_enabled
is set to 1
and INCREMENTAL_ROLLOUT_MODE
is manual
. This means every commit goes to staging automatically, while production requires a manual trigger.
Canary (Incremental) Rollout
With a canary strategy, new releases go to a small subset of pods in steps. Monitor each phase before proceeding.
Rollout example with 10 replicas:
- 10% → 1 new pod
- 25% → 3 new pods
- 50% → 5 new pods
- 100% → all pods updated
If any phase fails, you can roll back completely before proceeding.
Demo: Configuring Auto DevOps
- Go to Settings > CI/CD in your project.
- Under Auto DevOps, choose Automatic deployment to staging and manual to production.
- Click Save changes.
No pipeline runs until you push new code. Next, create a feature branch and observe the behavior.
Viewing Pipelines and Environments
After pushing, navigate to CI/CD > Pipelines to see your jobs:
Protected branches (like main
) block automatic production deployment. Under Operations > Environments, you’ll notice that production requires manual approval:
Making Code Changes
Create a branch named feature/canary-deployment
and update these files:
index.html
- Switch to a static background
- Remove the spinning keyframes
<!-- index.html -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
#planetImage {
background: url('https://gitlab.com/sidd-harth/solar-system/-/raw/main/images/saturn.png') center no-repeat;
background-size: cover;
position: static;
width: 50vw;
height: 50vw;
/* animation: spin 25s linear infinite; */
}
@keyframes spin {
100% { transform: rotate(360deg); }
}
body {
display: flex;
align-items: center;
justify-content: center;
background: url('images/static-background.png');
}
</style>
app.js
- Comment out an extra
console.log
- Preserve existing Express & Mongoose setup
// app.js
const path = require('path');
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, '/')));
app.use(cors());
mongoose.connect(process.env.MONGO_URI, {
user: process.env.MONGO_USERNAME,
pass: process.env.MONGO_PASSWORD,
useNewUrlParser: true,
useUnifiedTopology: true
}, function(err) {
if (err) {
console.log("error!! " + err);
} else {
// console.log("MongoDB Connection Successful");
}
});
// Define schema and routes...
app-test.js
Enhance test logs for clarity:
// app-test.js
const mongoose = require('mongoose');
const server = require('../app');
const chai = require('chai');
const chaiHttp = require('chai-http');
chai.should();
chai.use(chaiHttp);
describe('Planets API Suite', () => {
describe('Fetching Planet Details', () => {
it('should fetch a planet named Mercury', (done) => {
const payload = { id: 1 };
chai.request(server)
.post('/planet')
.send(payload)
.end((err, res) => {
res.should.have.status(200);
res.body.should.have.property('id').eql(1);
res.body.should.have.property('name').eql('Mercury');
done();
});
});
it('should fetch a planet named Venus', (done) => {
const payload = { id: 2 };
chai.request(server)
.post('/planet')
.send(payload)
.end((err, res) => {
res.should.have.status(200);
res.body.should.have.property('id').eql(2);
done();
});
});
});
});
Commit and push to trigger Auto DevOps.
Observing the CI/CD Pipeline
The merge request or branch push triggers these stages:
Stage | Purpose |
---|---|
build | Build Docker image |
test | Run unit and integration tests |
review | Deploy to a dynamic review environment |
dast | Perform Dynamic Application Security Testing (DAST) |
performance | Execute performance and load tests |
cleanup | Tear down review resources |
Create a Merge Request; Auto DevOps reuses the same pipeline:
The MR pipeline kicks off with 12+ jobs:
Dependency & Container Scanning
With an Ultimate license, Auto DevOps includes Dependency Scanning and Container Scanning:
Example dependency scan logs:
$ git remote set-url origin ${CI_REPOSITORY_URL}
$ analyzer run
[Gemnasium] v4.10.5 using schema model 15
Cannot auto-remediate: package-lock.json
Uploading artifacts: gl-sbom-*.cdx.json (CycloneDX format)
Sample snippet from the CycloneDX SBOM artifact:
{
"bomFormat": "CycloneDX",
"specVersion": "1.4",
"metadata": { "timestamp": "2024-02-09T18:13:38Z" },
"tools": [{ "vendor": "GitLab", "name": "Gemnasium", "version": "4.10.5" }],
"components": [
{ "name": "@babel/core", "version": "7.22.20", "type": "library" },
{ "name": "@babel/code-frame", "version": "7.22.13", "type": "library" }
]
}
When all tests pass, the pipeline pauses at review stop:
Dynamic Application Security Testing (DAST)
DAST runs OWASP ZAP against the review URL:
$ export DAST_WEBSITE=$(cat environment_url.txt)
$ zap-cli quick-scan --self-contained -r report.html $DAST_WEBSITE
Example DAST output:
PASS: Application Error Disclosure [90222]
WARN: Content Security Policy (CSP) Header Not Set [10938] x 3
SKIP: Big Redirect Detected [10044]
...
Review the report.html
artifact. The live review environment now shows a static background:
Merge Request Overview & Reports
On the MR page, GitLab surfaces:
- License Compliance: e.g., Apache—20 packages
- Code Quality: Degradations and improvements
- Security Scans: SAST, DAST, Dependency Scanning, Container Scanning, Secret Detection
Detected vulnerabilities:
SAST flags a NoSQL injection:
Container Scanning reveals OS-level CVEs:
Merge ready, despite detected issues:
The Security Dashboard aggregates all findings:
Finally, merge into the protected main
branch. Watch the canary jobs promote your release in staged percentages, requiring manual approval at each step.
Links and References
Watch Video
Watch video content