Hands-onLesson 13 of 16

Build a Full CI/CD Pipeline

Assemble an end-to-end Azure DevOps pipeline that validates code, builds a Docker image, pushes it to ACR, and deploys it to AKS with Helm across controlled environments.

🧒 Simple Explanation (ELI5)

This lesson is the full factory tour. Instead of learning individual machines one by one, you are now running the whole production line from raw code to live application.

The full line works like this: developer pushes code, pipeline checks quality, packages the app into a container, stores it in ACR, then deploys it into AKS using Helm. If one checkpoint fails, the pipeline stops before production is affected.

🔧 Technical Explanation

Pipeline Design Goals

Lab Pipeline Flow
PR / Push
Lint + Test
Build + Push
Staging Deploy
Production Deploy

🛠️ Hands-on

Folder Assumptions

text
repo/
  src/
  charts/webapp/
  Dockerfile
  package.json
  azure-pipelines.yml

Step 1: Create the Full YAML Pipeline

yaml
trigger:
- main

pr:
- main

variables:
- group: aks-shared
- name: imageRepository
  value: webapp
- name: imageTag
  value: $(Build.BuildId)

stages:
- stage: Validate
  displayName: 'Lint and Test'
  jobs:
  - job: AppValidation
    pool:
      vmImage: ubuntu-latest
    steps:
    - task: NodeTool@0
      inputs:
        versionSpec: '20.x'
    - script: npm ci
      displayName: 'Install dependencies'
    - script: npm run lint
      displayName: 'Run lint'
    - script: npm test -- --ci
      displayName: 'Run unit tests'

- stage: Build
  displayName: 'Build and Push Image'
  dependsOn: Validate
  jobs:
  - job: DockerBuild
    pool:
      vmImage: ubuntu-latest
    steps:
    - task: Docker@2
      inputs:
        command: buildAndPush
        repository: $(imageRepository)
        dockerfile: Dockerfile
        containerRegistry: sc-acr-prod
        tags: |
          $(imageTag)
    - publish: charts
      artifact: helm-chart

- stage: Deploy_Staging
  dependsOn: Build
  jobs:
  - deployment: DeployStaging
    environment: staging
    strategy:
      runOnce:
        deploy:
          steps:
          - download: current
            artifact: helm-chart
          - task: AzureCLI@2
            inputs:
              azureSubscription: sc-aks-staging
              scriptType: bash
              scriptLocation: inlineScript
              inlineScript: |
                az aks get-credentials --resource-group $(resourceGroup) --name $(stagingClusterName) --overwrite-existing
                helm upgrade --install webapp ./charts/webapp \
                  --namespace staging \
                  --set image.repository=$(acrLoginServer)/$(imageRepository) \
                  --set image.tag=$(imageTag) \
                  --wait --timeout 10m

- stage: Deploy_Production
  dependsOn: Deploy_Staging
  condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
  jobs:
  - deployment: DeployProd
    environment: production
    strategy:
      runOnce:
        deploy:
          steps:
          - task: AzureCLI@2
            inputs:
              azureSubscription: sc-aks-prod
              scriptType: bash
              scriptLocation: inlineScript
              inlineScript: |
                az aks get-credentials --resource-group $(resourceGroup) --name $(prodClusterName) --overwrite-existing
                helm upgrade --install webapp ./charts/webapp \
                  --namespace production \
                  --set image.repository=$(acrLoginServer)/$(imageRepository) \
                  --set image.tag=$(imageTag) \
                  --wait --timeout 10m

Step 2: Add Environment Controls

  1. Create staging and production environments.
  2. Require approval for production.
  3. Authorize only this pipeline to use the production environment.

Step 3: Validate the Release

After each staging deployment, run smoke tests against the service endpoint before approving production.

bash
kubectl get pods -n staging
kubectl rollout status deployment/webapp -n staging
curl https://staging.example.com/health

Try It Yourself

🐛 Debugging Scenarios

Scenario 1: Lint Passes Locally but Fails in CI

Check Node version mismatch, missing lockfile updates, or OS-specific path issues. CI is valuable precisely because it runs in a clean environment.

Scenario 2: Image Build Succeeds but Helm Deploy Uses Old Tag

Inspect Helm values and live deployment spec. ACR push success does not mean the chart consumed the new tag.

Scenario 3: Production Is Waiting Forever

Look for pending environment approval, failed check, or missing authorization to use the production environment.

Operational Habit

For every deployment run, know the commit, build ID, image tag, Helm release revision, and namespace. Those four references shorten almost every incident investigation.

📋 Interview Questions

Beginner

What stages would you expect in a basic Azure DevOps CI/CD pipeline?

Typically validate, build/package, and one or more deployment stages such as staging and production.

Why publish one image and promote it instead of rebuilding later?

Promotion of one immutable build avoids drift and ensures what was tested is what gets deployed.

Why use environments for deployment stages?

They provide approvals, checks, and deployment history for each target environment.

Where does Helm fit in the pipeline?

Helm handles packaging and applying the Kubernetes deployment configuration during the release stage.

What is the role of ACR here?

ACR stores the built container image so the cluster can pull and run it.

Intermediate

How do you make a full CI/CD pipeline production-safe?

Fail fast in validation, use immutable artifacts, protect environments, restrict identities, and keep rollbacks practical.

Why would you separate staging and production service connections?

To reduce blast radius and apply stronger controls to production credentials and resources.

What signals should block promotion to production?

Failed tests, failed smoke checks, unresolved change approvals, environment health issues, or failing policy checks.

How do you keep the pipeline maintainable as it grows?

Use templates, shared variables, clean stage boundaries, and a clear separation between validation, packaging, and deployment concerns.

What would you log for good release traceability?

Commit SHA, build ID, image tag, environment name, approval record, and Helm release revision.

Scenario-Based

A release reached staging, passed smoke tests, then failed in production because of a secret. What lesson do you take?

Environment parity matters. I review secret injection, environment-specific config validation, and add early checks for required values before rollout.

How would you demonstrate rollback readiness in this pipeline?

I would show immutable image tags, Helm release history, environment deployment records, and a tested rollback command or pipeline path.

A team wants production deployment directly after build to save time. How do you respond?

I challenge the risk model. Skipping lower-environment validation may be acceptable only if the service is low risk, tests are exceptional, and rollback plus monitoring are very strong.

What would you change first if this pipeline became slow?

I would profile dependency restore, test runtime, image build caching, and unnecessary sequential steps before restructuring the whole design.

How do you explain this pipeline to a product manager?

It automatically checks code quality, packages the app, tests it in staging, and then safely promotes the same verified version into production with approvals and traceability.

🌍 Real-World Usage

This lab reflects a common enterprise pattern: Azure DevOps handles CI, container packaging, promotion controls, and release logging while AKS and Helm provide the runtime platform. That is enough to support both day-to-day deployments and incident response.

🧾 Summary

You now have the full delivery chain in one pipeline. The engineering value is not just automation. It is predictable promotion, traceable releases, and faster recovery when something goes wrong.