DevOps: From Commit to Container · Year 4 Study Hub
DevOpsWeeks 1, 2, 4, 6 · Lab 1

From Commit to Container

Hi. Writing code is only half of shipping software. Someone has to build it, test it, put it on a server and keep it running, and for a long time that was a different team who found out about your change last. DevOps is how those two halves became one process. This page follows a single change from your editor to a running container, and stops at every tool and command along the way.

Think of it like… Real world Key idea Careful In the lab

Pictures come from your slides, and each caption says which week and slide, so you can flip back to it. Tap any picture to see it larger. The code is the code from Lab 1 and the Week 4 deck. The demos run in your browser, so nothing here touches a real server. There are no Week 3 or Week 5 decks in the folder, so the page goes from testing straight to containers, and from Docker to microservices.

The plan
  • Chapter 1 says what DevOps is, where it came from, and the eight stages every change goes through. (Week 1)
  • Chapter 2 shows how a Git push starts the automation, and what CI and CD each automate. (Week 1)
  • Chapter 3 covers testing: the kinds, the levels, linting and coverage. (Week 2)
  • Chapter 4 walks through Lab 1 line by line and runs its pipeline. (Lab 1)
  • Chapter 5 goes from physical servers to virtual machines to containers. (Week 4)
  • Chapter 6 gets hands-on with Docker. (Week 4)
  • Chapter 7 zooms out to architecture: monoliths vs microservices, how services talk, and how to roll out a new version safely. (Week 6)
Chapter 1

What Is DevOps?

Wk 1 · s.4–22

1.1 The definition

Definition

DevOps is the practice of operations and development engineers working together through the whole life of a service, from design and development to running it in production.

The name is the two job titles joined. Dev writes the features. Ops runs the servers. Before DevOps they usually sat in separate teams with opposite goals: developers were rewarded for shipping changes, and operations was rewarded for keeping things stable, and every change is a risk to stability. So releases were rare, large and stressful.

The slide's point is that DevOps is more than tools. You can install Jenkins and Docker and still have two teams throwing work over a wall. It's a way of working where the same people care about the code and about how it runs.

The DevOps infinity loop: plan, code, build, test on the Dev side, then release, deploy, operate, monitor on the Ops side, looping back to plan
Wk 1 · s.13The loop never ends: what you learn from monitoring goes back into planning.

1.2 Where it came from

Waterfall ran each phase once, in order: requirements, design, implementation, testing, then deployment. You found out whether it worked at the very end. Agile cut the work into short sprints so the software is built and checked in small pieces. Agile fixed the development side, but deployment was still slow and manual. DevOps extends the same idea, small and frequent, all the way to production.

A waterfall staircase from requirements down to deployment and maintenance, with an arrow to three repeating agile sprints
Wk 1 · s.5Waterfall runs once, top to bottom. Agile repeats short sprints.
A timeline: DevOps origins 2007, Agile Infrastructure talk 2008, the term DevOps 2009, the Continuous Delivery book 2010, The Phoenix Project 2013, State of DevOps report 2015, The DevOps Handbook 2016, ten years of DevOpsDays 2019
Wk 1 · s.6The milestones on the slide.
Real world

The 2009 point on that timeline is a conference talk. At Velocity 2009, John Allspaw and Paul Hammond from Flickr presented "10+ Deploys per Day: Dev and Ops Cooperation at Flickr", at a time when many companies released a few times a year. Patrick Debois, who had been pushing the same idea in Belgium, then organised the first DevOpsDays in Ghent that autumn, and the name stuck.

What do teams actually get from it? The slide lists four things: better collaboration, more efficiency, faster time to market, and higher quality and security. The last one surprises people, because shipping more often sounds riskier. It isn't, because each release is small and goes through the same automated checks.

Wk 1 · s.4–7

1.3 The Three Ways

Gene Kim, author of The Phoenix Project and The DevOps Handbook, sums the principles up as three "ways".

Three small diagrams: an arrow from Dev to Ops for systems thinking; a loop from Ops back to Dev for feedback; a loop with many small cycles for experimentation
Wk 1 · s.8Flow left to right, feedback right to left, and lots of small loops.
WayWhat it meansIn practice
1. Flow / systems thinkingOptimise the whole path from idea to production, not one team's part of it.Small batches, remove bottlenecks such as a manual approval that takes a week.
2. Amplify feedback loopsProblems should travel back to the people who caused them, quickly.CI tells you in minutes that your commit broke a test. Monitoring alerts the team that shipped.
3. Continual experimentation and learningMake it safe to try things and to fail.Blameless post-mortems, feature flags, trying a change on 1% of users first.
Wk 1 · s.8–12

1.4 The lifecycle

Every change goes round the loop in eight stages. Here's one real change, "add a /health endpoint", going all the way round. It's the change you make at the end of Lab 1.

Try it

Follow one change round the loop


    
Wk 1 · s.13–21

1.5 Tools at each stage

StageGoalTools on the slides
PlanEveryone knows what to buildJira, Trello, Asana, Azure DevOps
CodeWrite and review code togetherGit, GitHub, GitLab, Bitbucket
BuildPackage the code into something runnableMaven, Gradle, npm, Docker
TestCatch bugs before users doSelenium, JUnit, TestNG, Postman, Cypress
ReleaseVersion, tag and approveJenkins, GitLab CI, Bamboo
DeployPut it on servers reliablyDocker, Kubernetes, Ansible, Terraform, AWS/GCP/Azure
OperateKeep it available and scaledPrometheus, Grafana, Nagios, ELK
MonitorNotice problems, collect feedbackPrometheus, Grafana, ELK, New Relic, Datadog

Don't memorise the whole wheel below. Learn one tool per stage well. The ones this course actually uses are Git, GitHub Actions and Docker.

The DevOps tools stack wheel: dozens of tool logos arranged around the plan, code, build, test, release, deploy, operate and monitor segments
Wk 1 · s.22The tools wheel. Tap to zoom in.
Wk 1 · s.14–22

Chapter 1 recap

  • DevOps: developers and operations own the whole life of the service together.
  • It's a culture first. Tools support it.
  • Waterfall: once, in order. Agile: short sprints. DevOps: small changes all the way to production.
  • Three Ways: flow, feedback, experimentation.
  • Lifecycle: plan, code, build, test, release, deploy, operate, monitor, and back to plan.

Check yourself: Chapter 1

Chapter 2

Git and CI/CD

Wk 1 · s.23–37

2.1 The DevOps engineer

The roadmap on the slide is the skill list for the job, in roughly the order you learn it: Linux and networking, then Git, CI/CD pipelines, containers and Kubernetes, infrastructure as code, cloud, then security, monitoring and reliability engineering. This course covers the middle of it: Git, CI/CD and containers.

A nine-step DevOps roadmap: fundamentals, version control with Git, CI/CD pipelines, containers and Kubernetes, infrastructure as code, cloud computing, security, observability and monitoring, site reliability engineering
Wk 1 · s.25The DevOps roadmap. Tap to zoom in.

2.2 Git triggers everything

In DevOps, Git is the single source of truth. If it isn't committed, it doesn't exist: the code, the tests, the pipeline file and even the server configuration all live in the repository. That matters because Git events are what start the automation.

GitHub at the centre: fork, clone, commit, push and pull request arrows between a developer's copy and the repository, with a GitHub Workflow badge
Wk 1 · s.27Each arrow is a Git event that can start a workflow.

The four events on the slide are push, pull_request, merge and tag. A workflow file chooses which ones it listens to with its on: block. This is the one from Lab 1:

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
Try it

Does this event start the workflow?

Careful

GitHub has no separate merge event. Merging a pull request into main creates a commit on main, so it arrives as a push to main. That's why Lab 1's workflow runs twice for one feature: once on the pull request, and once after you merge it.

2.3 The pull request workflow

The master branch runs along the top; a feature branch splits off, takes commits, becomes a pull request with a template, gets reviewed, and merges back, with CI running tests at the pull request and at the merge
Wk 1 · s.28CI runs twice: when the pull request opens, and when it merges.
  1. Create a feature branch from main: git checkout -b feature/health-route.
  2. Commit your changes on that branch and push it.
  3. Open a pull request (PR). CI runs the tests on it automatically.
  4. A teammate reviews the code. The PR can't merge while CI is red, if the repository is set up that way.
  5. Merge into main. CI runs again on the merged code.

The point of the branch is that main always works. Nobody's half-finished code lands there, and nothing lands there untested.

2.4 Continuous integration

Definition

Continuous integration (CI) means merging everyone's changes into the shared repository often, and automatically building and testing each change, usually with unit and integration tests, to prove it hasn't broken the app.

"Integration" is the hard part it's named after. If five people each work alone for a month and then merge, you get a month of conflicts at once. If they merge every day, each merge is small, and when the build breaks you know it was today's change.

The slide's five steps to set it up:

  1. Write tests for the critical parts of your code.
  2. Get a CI service to run them on every push.
  3. Integrate your changes every day.
  4. Fix the build as soon as it breaks.
  5. Write tests for every new feature.

Step 4 is the cultural one. A red build that everyone ignores is worse than no CI at all, because people stop trusting the signal.

Wk 1 · s.29–31

2.5 Continuous delivery vs continuous deployment

CD has two meanings, and the difference is one step.

Three boxes: continuous integration is build, test, merge; continuous delivery automatically releases to a repository; continuous deployment automatically deploys to production
Wk 1 · s.33The same pipeline. The only difference is the last box.
  • Continuous delivery: every change that passes is automatically packaged and released to a repository, ready to deploy. A person presses the button to put it in production.
  • Continuous deployment: no button. If it passes, it goes to production automatically.
Try it

Delivery or deployment?

Why would anyone keep the button? Some releases need a human decision: a marketing launch date, an app store review, a bank's change approval. Continuous deployment needs a lot of trust in your tests, because nothing stands between them and your users.

Real world

On 1 August 2012, Knight Capital, a US trading firm, deployed new code to its eight trading servers by hand. A technician missed one server. That server still had old, retired code on it, which the new release accidentally switched back on, and it sent millions of unintended orders. The firm lost over $400 million in about 45 minutes. An automated deploy does the same thing on every server every time, and that is the whole reason the Deploy stage is automated.

A detailed CI/CD workflow: source code management, build tools, quality analysis, unit testing, deploy to test environment, integration testing, acceptance testing, packaging, and deploy to production, with example tools at each stop
Wk 1 · s.34A fuller pipeline with real tool names. Tap to zoom in.

The benefits on the slide follow from automation: fewer bugs reach users, less manual work, faster and less risky releases, and quicker user feedback.

Wk 1 · s.32–35

2.6 CI/CD vs DevOps

CI/CD is a part of DevOps. CI/CD covers getting code from a commit to a release: version control, integration, testing and deployment. DevOps covers the whole loop, including planning, running the service and monitoring it, plus the culture around all of it.

A board of CI/CD tool logos: GitHub Actions, GitLab CI/CD, Jenkins, Argo, Bamboo, CircleCI, Axify, Buddy, Spinnaker, TeamCity, Azure and Concourse
Wk 1 · s.36CI/CD tools. Lab 1 uses GitHub Actions.
Wk 1 · s.36–37

Chapter 2 recap

  • Git is the source of truth, and Git events start the automation.
  • Work on a branch, open a PR, let CI test it, review, merge.
  • CI: integrate often, and build and test every change automatically.
  • Continuous delivery stops at a release button. Continuous deployment goes straight to production.
  • CI/CD is one part of DevOps.

Check yourself: Chapter 2

Chapter 3

Testing in DevOps

Wk 2 · s.2–43

3.1 Why test, and when

Definition

Software testing checks that software works correctly, securely and efficiently against its requirements.

The big change in DevOps is when testing happens. It used to be a phase at the end. Now it runs through the whole lifecycle, which is called continuous testing. Two phrases on the slides describe the direction:

  • Shift left: test earlier, while you write the code. A bug found by a unit test on your laptop costs you a minute. The same bug found in production costs an incident.
  • Shift right: keep checking after release, by monitoring real use in production.

The slide's example of a costly defect is Delta Air Lines in 2024: a faulty update crashed systems worldwide and cost Delta about $500 million.

Real world

That update wasn't Delta's own code. On 19 July 2024 the security company CrowdStrike pushed a faulty content update to its Falcon software, and Microsoft estimated that about 8.5 million Windows machines crashed and couldn't restart: airlines, banks and hospitals at once. Delta cancelled thousands of flights over several days. Part of the lesson is shift right: roll an update out to a small group first and watch it, instead of to every machine at the same moment.

Wk 2 · s.2–7

3.2 Manual vs automated

A person labelled manual testing versus a robot labelled automated testing
Wk 2 · s.8The two broad categories.
ManualAutomated
HowA person clicks through the app or calls the APIScripts and tools run the tests
CostExpensive every single timeCosts effort to write once, then nearly free to rerun
ErrorsTypos and skipped stepsDoes the same thing every time
Good forExploring, usability, one-off checksAnything you'll run again, which in CI is every commit
Wk 2 · s.8–10

3.3 TDD and BDD

Test-driven development (TDD) writes the test before the code, in a short loop: red (write a test, watch it fail), green (write just enough code to pass), refactor (tidy the code while the test keeps you safe).

A cycle of three arrows: write a failing test, make the test pass, refactor
Wk 2 · s.12The TDD loop.

Try the loop. The tests below are already written, and the function is an empty stub, so every test starts red. Write the code until they're all green.

Try it

Red, green, refactor


      
      
      

      

Behaviour-driven development (BDD) writes the expected behaviour in plain language first, so non-programmers on the team can read and agree on it. It uses Given / When / Then:

Given the user has entered a valid username and password,
When the user clicks the "Login" button,
Then the system should display a "Login Successful" message.

Tools like Cucumber turn each of those lines into an automated step. So BDD is TDD where the test is written as a sentence.

Wk 2 · s.11–13

3.4 Functional and non-functional testing

Two arrows: functional testing asks is it working as expected, for example unit, system and regression testing; non-functional testing asks how well is it working, for example load, performance and security testing
Wk 2 · s.14The question each one asks.

Functional: does it work?

  • White-box: you can see the code.
  • Black-box: you can't, only inputs and outputs.
  • API: the interfaces between components.
  • Regression: new changes didn't break old features.
  • Smoke: the basics work, so the build is worth testing further.
  • Sanity: one area works, when there's no time for a full run.
  • Ad hoc / exploratory: try to break it without a script.
  • UAT: real users confirm it meets their needs.

Non-functional: how well?

  • Performance: speed under a given workload.
  • Load: performance under realistic traffic.
  • Stress: how much it takes before it fails.
  • Recovery: does it come back cleanly after a crash?
  • Security: can an attacker get in?
  • Usability: can a user finish the task easily?
  • Compatibility: other browsers, devices, operating systems.

The functional checks follow six steps: identify the functions, create input data, work out the expected outputs, run the test cases, compare actual with expected, and decide whether it passes.

Wk 2 · s.14–20

3.5 The four levels

Four circles: 01 unit testing of individual components, 02 integration testing of component groups, 03 system testing of the integrated system, 04 acceptance testing of the final system
Wk 2 · s.21Each level tests a bigger piece.
A table: unit testing is done by developers, integration and system testing by testers, acceptance testing by end users
Wk 2 · s.22Who usually does each level.
LevelWhat it testsExampleSpeed and cost
UnitOne function, method or class, on its ownadd(2, 3) returns 5Milliseconds. Run thousands.
IntegrationModules or services working togetherThe API really saves to the databaseSlower: needs several parts running
SystemThe whole app, as a black box, like productionSign up, log in, place an orderSlow
AcceptanceDoes it meet the business need? Users or the client decideThe shop owner checks the checkout flowSlowest, partly manual
What is unit testing: input goes into a function and actual is compared with expected; an annotated JUnit test; and Jest output reporting one passed test
Wk 2 · s.23A unit test, and what a passing run prints.

The five steps of unit testing on the slide: identify the unit, pick manual or a framework, set up the environment (test data, dependencies and mock objects that stand in for the real database), write the test cases, then debug and rerun. The frameworks listed: Jest and Mocha for JavaScript, JUnit for Java, NUnit for .NET, pytest for Python. Lab 1 uses Mocha.

Acceptance testing comes in several kinds: user (UAT), business (BAT), contract (CAT, against the agreed terms), regulation (RAT, such as health-data law), operational (OAT, is it ready to run), plus alpha testing in-house and beta testing by outside users before release.

Try it

Which kind of test is this?

Wk 2 · s.21–37

3.6 Integration techniques

If you're testing how parts fit together, which parts do you put together first?

TechniqueOrderMissing parts are replaced byTrade-off
Top-downMain module first, then the modules below itStubs: fake lower modules that return canned answersCan start early
Bottom-upLow-level modules first, then upwardDrivers: fake higher modules that call the real low onesThe basics are proven first
Sandwich (mixed)Both ends at onceStubs and driversNo forced order
Big bangEverything together at one timeNothingQuick answer, but when it fails you can't tell which part broke
Think of it like…

A stub is a cardboard cut-out of a module: it stands where the real one will go and gives fixed replies. A driver is the opposite: a fake caller that pokes the real module to see what it does.

Tools on the slides: Postman for API tests, Katalon (built on Selenium), SoapUI, and Citrus for Java.

Wk 2 · s.26–32

3.7 Linting

A linter reads your code without running it (static analysis) and flags mistakes and style problems: an unused variable, a misspelled name, == where you meant ===, inconsistent formatting. It runs in two places:

Working directory, git add to staging, git commit runs a pre-commit check including secret scanning and linting, then the local repo, then git push to the remote repo
Wk 2 · s.39A pre-commit hook lints before the commit is even created.
  • Before committing, as a pre-commit hook, so bad code never reaches the repository.
  • In the CI pipeline, so nobody can skip it. Lab 1 does this with yarn lint.
// ESLint would flag both of these:
const unused = 42;          // 'unused' is assigned a value but never used
if (user = admin) { … }     // assignment where a comparison was meant
Wk 2 · s.38–39

3.8 Code coverage

Code coverage measures how much of your code ran while the tests ran. Tools report it four or five ways:

  • Function: % of functions called.
  • Statement: % of statements executed.
  • Branch: % of the paths through each if/else taken.
  • Condition: % of boolean conditions seen both true and false.
  • Line: % of lines executed.

Pick which tests to run and watch the numbers.

Try it

What did the tests touch?


      
Careful: 100% isn't "bug-free"

Coverage only says the code ran. A test that calls shippingFee(80, false) and never checks the answer still counts. Lab 1 says the same: full coverage means the measured code was exercised, not that every behaviour was tested.

Wk 2 · s.40–42Lab 1 · Step 10

Chapter 3 recap

  • Continuous testing: shift left to catch bugs early, shift right to watch production.
  • Automate anything you'll run more than once.
  • TDD: red, green, refactor. BDD: Given, When, Then.
  • Functional asks "does it work?" Non-functional asks "how well?"
  • Levels: unit, integration, system, acceptance.
  • Stubs replace lower modules, drivers replace upper ones.
  • Lint before commit and in CI. Coverage shows what ran, not what's correct.

Check yourself: Chapter 3

Chapter 4

Lab 1: A CI Pipeline with GitHub Actions

Lab 1 · Steps 1–14

4.1 What you build

A tiny Node.js web app whose only page says Hello, GitHub Actions!, plus a pipeline that lints, tests and measures coverage on every push and pull request. The app is deliberately boring. The pipeline is the point.

ci-cd-lab/
├── .github/
│   └── workflows/
│       └── ci.yml          ← the pipeline
├── test/
│   └── test.js             ← the tests
├── .gitignore
├── .yarnrc.yml
├── app.js                  ← the Express app
├── eslint.config.js        ← lint rules
├── package.json            ← dependencies and scripts
├── README.md
└── server.js               ← starts the app

The stack: Node.js 24, Yarn 4, Express 5, Mocha and Chai for tests, Supertest to call the app over HTTP, ESLint for linting, and c8 for coverage.

4.2 The app and its test

The app is split over two files on purpose. app.js defines the app and exports it:

import express from 'express';

const app = express();

app.get('/', (req, res) => {
  res.status(200).send('Hello, GitHub Actions!');
});

export default app;

server.js starts it on a port:

import app from './app.js';

const port = process.env.PORT ?? 3000;

app.listen(port, () => {
  console.log(`Server listening on port ${port}`);
});
Why two files?

The test imports app.js and calls it directly with Supertest. No server has to be running, no port can clash, and the test runs the same way on your laptop and on GitHub's machine. If app.js called listen() itself, every test run would try to open port 3000.

import { expect } from 'chai';
import request from 'supertest';
import app from '../app.js';

describe('GET /', () => {
  it('returns HTTP 200', async () => {
    const response = await request(app).get('/');
    expect(response.status).to.equal(200);
  });

  it('returns the expected greeting', async () => {
    const response = await request(app).get('/');
    expect(response.text).to.equal('Hello, GitHub Actions!');
  });
});

describe and it come from Mocha, expect from Chai, and request(app) from Supertest. Both tests are unit-sized, but because they go through HTTP they're also a small integration test of Express and your route.

4.3 Lint, test, coverage

package.json names the commands, so everybody and CI run exactly the same thing:

"type": "module",
"packageManager": "yarn@4.18.1",
"scripts": {
  "start": "node server.js",
  "test": "mocha",
  "lint": "eslint .",
  "coverage": "c8 mocha"
}
  • "type": "module" turns on import/export (ES modules).
  • "packageManager" pins the Yarn version, so everyone installs the same way.
  • c8 mocha runs the same tests while recording which lines ran, then prints the table and writes an HTML report to coverage/.
yarn install      # creates yarn.lock: commit it
yarn start        # open http://localhost:3000/
yarn lint
yarn test         # 2 passing
yarn coverage
In the lab

Step 9 · break it on purpose

Change the greeting in app.js and run yarn test. The first test still passes, because the status is still 200. The second fails and shows you both strings:

  GET /
    ✔ returns HTTP 200
    1) returns the expected greeting

  1 passing (24ms)
  1 failing

  1) GET /
       returns the expected greeting:
     AssertionError: expected 'Hello, GitHub!' to equal 'Hello, GitHub Actions!'

Put the greeting back before you commit. The pipeline in 4.5 shows what would happen if you didn't.

4.4 The workflow file

.github/workflows/ci.yml is the pipeline. GitHub finds any YAML file in that folder and runs it on the events in on:.

name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: yarn

      - name: Install dependencies
        run: yarn install --immutable

      - name: Run ESLint
        run: yarn lint

      - name: Run tests
        run: yarn test

      - name: Generate code coverage
        run: yarn coverage

      - name: Upload coverage HTML report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: code-coverage-report
          path: coverage/
          if-no-files-found: ignore
LineWhat it does
runs-on: ubuntu-latestGitHub starts a fresh Linux virtual machine for every run. Nothing from your laptop is on it.
uses:Runs a ready-made action. checkout copies your repository onto the machine. setup-node installs Node 24 and caches Yarn's downloads between runs.
run:Runs a shell command, the same one you'd type locally.
--immutableFails if yarn.lock would have to change. CI installs exactly what you tested with, or nothing.
if: always()Steps normally stop after the first failure. This one runs anyway, so you still get whatever report exists.
Careful: Yarn 4 on GitHub's machines

GitHub's Ubuntu machines come with the old Yarn 1. The lab's hint "keep the packageManager field and CI activation command aligned" refers to activating the newer Yarn with Corepack, but the workflow as printed has no such step. If your Install or Set up Node step fails with a message about packageManager or Corepack, add this step before "Set up Node.js":

      - name: Enable Corepack
        run: corepack enable

4.5 Run the pipeline

Pick what your commit did, then run it. Each step's log is shortened but follows what GitHub prints.

Try it

Push a commit to main


      

When a step fails, open its log on GitHub, fix the problem locally, run yarn lint, yarn test and yarn coverage yourself, then commit and push again. The run after a fix is just another push.

4.6 CI on a pull request

Step 14 proves the pull request half of on:. Make a branch, add a /health route and its test, and open a PR:

git checkout -b feature/health-route
# edit app.js and test/test.js
yarn lint && yarn test && yarn coverage
git add app.js test/test.js
git commit -m "Add health endpoint and test"
git push -u origin feature/health-route

One way to write it, so that the new test really checks the response:

// app.js
app.get('/health', (req, res) => {
  res.status(200).json({ status: 'ok' });
});

// test/test.js
describe('GET /health', () => {
  it('reports ok', async () => {
    const response = await request(app).get('/health');
    expect(response.status).to.equal(200);
    expect(response.body).to.deep.equal({ status: 'ok' });
  });
});

Open the pull request on GitHub. The CI checks appear on it, and merging it triggers one more run on main.

Careful

Never commit credentials or secrets, even in a lab. A repository can be public, and Git keeps everything in its history even after you delete the file. Use GitHub's encrypted Secrets settings for anything a workflow needs.

Chapter 4 recap

  • Split defining the app from starting it, so tests can import it.
  • Scripts in package.json make local and CI runs identical.
  • ci.yml: when to run (on), where (runs-on), what (steps).
  • --immutable installs exactly the locked versions. Commit yarn.lock.
  • A failed step stops the job, except steps marked if: always().

Check yourself: Chapter 4

Chapter 5

Virtualization and Containers

Wk 4 · s.2–20

5.1 Bare metal

A bare-metal server is a physical computer with the operating system installed straight onto the hardware. Nothing sits in between. Each server usually did one job: one for the database, one for the website, one for email.

Left, a traditional environment: one application on one operating system on physical servers. Right, a virtualized environment: several applications, each on its own operating system, on a virtualization layer over the servers
Wk 4 · s.3Traditional (left) and virtualized (right).
The good: simple

One server, one job. Easy to reason about, and the app gets the hardware's full performance.

The bad: cost

Every new system needs a new machine, with its space and power. Most servers sat mostly idle.

Wk 4 · s.3–4

5.2 Virtual machines

Virtualization adds a layer, the hypervisor, between the hardware and the operating systems. It splits one physical machine into several isolated virtual machines (VMs), each with its own full operating system, and hands out the CPU, memory and storage between them. The technology dates from the 1960s, took off in the 2000s, and is what cloud computing is built on: an EC2 instance is a VM.

Left: apps on guest operating systems on a type 1 hypervisor directly on hardware. Right: apps on guest operating systems on a type 2 hypervisor on top of a host operating system
Wk 4 · s.6Type 1 sits on the hardware. Type 2 sits on an OS.
Type 1 (bare-metal)Type 2 (hosted)
Runs onThe hardware directlyA normal OS like Windows or macOS
OverheadSmaller, more efficientLarger
SetupHarderInstall it like an app
ExamplesVMware ESXi, Microsoft Hyper-V, Citrix XenServerVMware Workstation, Oracle VirtualBox
Used forData centres and cloudsYour laptop

Benefits: cost savings, better use of hardware, VMs you can create, destroy and move between hosts, easier scaling and simpler disaster recovery. Drawbacks: the extra layer costs performance, the up-front investment is high, and there are now two things to manage, the physical hosts and every VM on them.

Wk 4 · s.5–10

5.3 Containers

Definition

Containerization packages the code with only the OS libraries and dependencies it needs into one lightweight executable, a container, that runs the same on any infrastructure.

The key word is only. A VM carries a whole guest operating system. A container carries just your app and its libraries, and shares the host's kernel (the core of the OS). That's why containers start in about a second instead of booting, and are megabytes instead of gigabytes.

Applications 1 to n on top of a container engine, on one operating system, on the infrastructure
Wk 4 · s.11One OS, one container engine, many apps.

The architecture has four layers, bottom to top: the infrastructure (a laptop or a server), the host operating system, the container engine such as Docker Engine, and the containerized applications.

Benefits on the slide:

  • Portability: build once, run anywhere. It ends "it works on my machine", because your machine's setup ships inside the container.
  • Speed: no guest OS to boot.
  • Isolation: each container has its own processes, network and file system view.
  • Simpler operations: you manage standard containers, not hand-configured servers.
  • Fast scaling: a traffic spike? Start more copies in seconds.

Process isolation is decades old. What changed things was Docker in 2013, which gave developers simple tools and one standard package format.

Wk 4 · s.11–14

5.4 Compare the stacks

Try it

Run three apps on one machine

5.5 How a container is made

A Dockerfile, an arrow labelled build to a Docker image, an arrow labelled run to a Docker container
Wk 4 · s.25Dockerfile, build, image, run, container.
  1. Define the environment in a Dockerfile: base image, copy the code, install dependencies, the start command.
  2. Build an image: a read-only, layered package of the app and everything it needs. docker build
  3. Store it in a registry such as Docker Hub, AWS ECR, GCP Artifact Registry or Azure Container Registry. docker push
  4. Run it: the engine creates a container from the image. docker run

Step 4 is where the isolation comes from. The engine asks the host's kernel for:

  • Namespaces: give the container its own process list, network stack and file system view, so it believes it's alone on the machine. Inside, your app is process 1.
  • Resource limits (Linux calls them cgroups): cap its CPU and memory so one container can't starve the rest.

Tool types: a container engine runs containers (Docker Engine), an image builder makes images (docker build), a registry stores them, and an orchestrator runs them at scale.

Wk 4 · s.15–18

5.6 Orchestration

A company can run hundreds or thousands of containers. Nobody starts those by hand. A container orchestration platform does it: it schedules containers onto machines, restarts the ones that crash, balances traffic between copies, and scales the number of copies up and down.

Kubernetes is the standard. Google open-sourced it, based on its internal system Borg. It works with any engine that follows the Open Container Initiative (OCI) standards. Alternatives: Docker Swarm for small clusters, Apache Mesos, Nomad, and managed services such as Amazon ECS/EKS, Google GKE and Azure AKS.

Real world

Google ran containers long before Docker made them popular. In 2014 it said that everything at Google, Search and Gmail included, runs in containers, and that it starts over two billion of them a week. You can't do that by hand, so Borg did the scheduling. Kubernetes, announced the same year, is Google's attempt to give everyone else a Borg.

Wk 4 · s.18–19

Chapter 5 recap

  • Bare metal: OS on the hardware. Simple, but expensive and mostly idle.
  • VM: a hypervisor splits one machine into many, each with a full guest OS.
  • Type 1 hypervisors run on hardware, type 2 on an OS.
  • Container: app plus libraries, sharing the host kernel. Light and fast.
  • Dockerfile, build an image, push to a registry, run a container.
  • Kubernetes orchestrates containers at scale.

Check yourself: Chapter 5

Chapter 6

Docker

Wk 4 · s.21–27

6.1 Architecture

Docker is a platform for building, shipping and running applications in containers. It uses a client-server design.

Client with docker run, docker build and docker pull; the Docker host with the daemon, images and containers; and a registry of images
Wk 4 · s.23Client, host and registry.
PartWhat it does
Client (docker)The command you type. It sends each command to the daemon.
Daemon (dockerd)Does the real work: builds images, runs containers, manages networks and volumes.
RegistryStores images. Docker Hub is the default public one.
ImageA read-only template for a container.
ContainerA running (or stopped) instance of an image.

Client and daemon talk over a REST API, through a UNIX socket on the same machine or over the network, so your docker command can control a daemon on another server. Docker Compose is another client, for apps made of several containers, such as a web app plus its database.

Wk 4 · s.22–24

6.2 Image vs container

Think of it like…

The slide's own comparison: an image is a class, and a container is an object. You write the class once and create as many instances as you like. Each container has its own state, but they all start from the same read-only image.

An image holds everything the app needs: a base OS such as Ubuntu or Alpine, the code, the dependencies, and configuration like environment variables and the start command. A Dockerfile is the text file of instructions that builds it.

6.3 The Dockerfile

This is the Dockerfile on the slide, for a Node app:

FROM node:22-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "./src/index.js"]
InstructionMeaning
FROM node:22-alpineStart from an image that already has Node 22 on Alpine Linux, a very small distribution.
WORKDIR /appWork in /app inside the image, creating it if needed.
COPY . .Copy the project folder from your machine into /app.
RUN yarn install --productionRun a command at build time: install dependencies, without the dev-only ones.
CMD [...]The command to run when a container starts. It isn't run during the build.
docker build -t my-app .            # build an image called my-app from ./Dockerfile
docker run -d -p 3000:3000 my-app    # start a container, map port 3000 to the host
Wk 4 · s.25–26

6.4 Layer caching

Each instruction makes one layer, and Docker reuses a layer from the last build if nothing it depends on has changed. Once one layer changes, every layer after it is rebuilt. So the order of the lines decides how fast your rebuilds are.

Try it

What gets rebuilt?

That's why most real Dockerfiles copy package.json and the lockfile first, install, and only then copy the rest of the code. You change your code all day, and your dependencies rarely.

6.5 Try the commands

The slides end with KodeKloud's free docker run lab. Here's a safe practice terminal with the same commands. Click a command, or type one.

Try it

A practice Docker terminal


      
In the lab

KodeKloud · docker run

The real lab is at kodekloud.com/free-labs/docker/docker-run. It starts by asking how many containers are running on the host: that's docker ps, which only lists running ones. Add -a to see stopped containers too.

Wk 4 · s.27

Chapter 6 recap

  • The docker client sends commands to the dockerd daemon over a REST API.
  • Image: read-only template. Container: a running instance of it.
  • RUN happens at build time, CMD when the container starts.
  • One instruction, one layer. A change rebuilds that layer and everything after it.
  • Copy dependency files and install before copying the code.

Check yourself: Chapter 6

Chapter 7

Microservices

Wk 6 · s.1–28

7.1 What is software architecture?

A system's architecture is its fundamental structure: the parts it's made of and how they interact. In practice it answers five questions:

  1. How are the application's components organized?
  2. How do the components communicate?
  3. How is data stored and managed?
  4. How is the application deployed?
  5. How does it scale?

The last two are why this is a DevOps topic. Chapters 2–6 built the pipeline and the containers. Architecture decides how many things that pipeline has to build, test and deploy, and how they fail. The lecture's question for the cloud era: which model is best for you?

Monolith versus microservices: on the left one big block holding all the parts together; on the right the same parts as separate small circles linked by dotted lines
Wk 6 · s.3One block that holds everything, or many small pieces that talk to each other.
Wk 6 · s.2–3

7.2 The monolith

A monolithic application is the traditional model: one program, with every feature packaged and deployed together. Its components are tightly coupled and share resources and one database. Any change means rebuilding and redeploying the whole thing.

A monolithic application: a browser calls a single deployment unit containing a web layer and components A to D, all reading and writing one shared database
Wk 6 · s.5One deployment unit, one shared database.

A request goes in, the right module handles it, the app talks to the database, and the response comes back. The lecture's example is a user placing an order. All four modules run inside the same deployable application:

Order module creates it→Inventory reduces stock→Payment charges→Notification confirms
AdvantagesDisadvantages
Easy deployment: one executable or directorySlower development as the codebase grows large and complex
Easy development: one codebaseScaling: you can't scale one component on its own
Performance: one in-process call can replace many network APIsReliability: an error in any module can take down the whole app
Simpler testing: end-to-end tests run on one unitTechnology lock-in: changing the framework or language touches everything
Easy debugging: follow one request through one placeDeployment: a one-line change redeploys the whole monolith

Monoliths are convenient early in a project: less code management, less to keep in your head, and everything ships at once.

Wk 6 · s.4–7

7.3 Microservices

A microservices architecture splits the application into a set of independently deployable services. Each one has a specific business goal, its own logic and its own database. Updating, testing, deploying and scaling happen per service, and services talk to each other through simple interfaces.

Left: one Node.js API service containing users, threads and posts. Right: the same three features as a separate users service, threads service and posts service
Wk 6 · s.8The same three features, split into three services.
Microservice architecture: a client browser talks to a UI microservice, which talks to separate payment, shopping cart and inventory services
Wk 6 · s.10A shop as microservices behind a UI service.
Key idea

Microservices don't reduce complexity. They make it visible and more manageable, by splitting the work into smaller processes that run independently. The complexity that was hidden inside one codebase is now out in the open, as network calls between services.

Containers suit microservices well: each service is packaged with its own dependencies, so you can focus on the service and not on what's installed where. Modern cloud-native applications are usually microservices in containers, which is everything from Chapter 5 and Chapter 6 put to work.

AdvantagesDisadvantages
Agility: small independent teams move fasterDevelopment sprawl: more services across more teams
Flexible scaling: scale only the busy serviceHigher infrastructure cost: each service needs its own deployment, testing, hosting and monitoring
Easy deployment: release one service, and roll it back quicklyOrganizational overhead: teams must coordinate on interfaces and updates
Technology freedom: the best tool for each jobHarder debugging: one request leaves logs in many services
Resilience: one failing service doesn't have to crash everythingLess standardization: different languages, log formats and tools

Read the two tables side by side: almost every advantage of one is a disadvantage of the other. Choosing is a trade, not an upgrade.

Wk 6 · s.8–12

7.4 Monolith or microservices?

The lecture asks you to choose for three scenarios, and to say what goes wrong if you choose badly. One reasonable answer:

ScenarioChooseWhyIf you choose wrong
A. A university website, 500 students reading course informationMonolithSmall, steady load and one small team. Nothing needs to scale on its ownMicroservices here add servers, pipelines and network failures for no benefit
B. An online shop with millions of customersMicroservicesSearch, cart and payment have very different loads, and many teams need to ship independentlyA monolith can't scale checkout alone, every release redeploys everything, and one bug takes the whole shop down
C. A startup's first app, three developersMonolith (keep it modular)Speed of change matters most, and the product will change shapeThree people end up running ten services, and the service boundaries are drawn before anyone knows the domain
Real world

Both directions happen. Netflix moved from one large application to hundreds of microservices from about 2009, after a database corruption in 2008 stopped it shipping DVDs for three days. In 2023, Amazon's Prime Video team went the other way: it merged one monitoring tool's distributed parts back into a single process and reported cutting that tool's cost by 90%. Same company group, opposite choices, because the scale and the job were different.

Wk 6 · s.13

7.5 Design principles

PrincipleMeansIgnore it and…
Single responsibilityEach service does one business job, and does it wellYou get small monoliths that all change together
Loose couplingServices talk through APIs and can change independentlyOne change forces releases of five services at once
Decentralized dataEach service owns its data and picks the database that fitsServices read each other's tables, and a schema change breaks strangers
Fault isolationOne failure doesn't crash the app: timeouts, retries, circuit breakersA slow service makes everything that calls it slow, then everything that calls those
API-first designClear, standard APIs, whatever language each service usesEvery integration is a one-off, and nobody can swap a service out
Database per service pattern: a client calls a product microservice, an order microservice and a shipping microservice, each with its own database
Wk 6 · s.14Database per service: each service owns its data.
Careful

"Database per service" means logical ownership, not necessarily a separate database server. Several services' databases can run on the same database infrastructure. The rule is that only the owning service touches its data, and everyone else asks that service.

A circuit breaker works like the one in your house. After enough failed calls to a service, it "trips" and fails the next calls immediately for a while, instead of letting every caller wait for a timeout. That gives the struggling service room to recover.

Wk 6 · s.14

7.6 Synchronous and asynchronous communication

Synchronous

The caller sends a request and waits for the answer. Order Service asks Payment Service to charge, Payment processes it and returns success or failure, and only then does Order Service carry on.

Pros: simple to build, easy to debug and trace, an immediate answer. Cons: tightly coupled. If the receiver is slow or down, the caller is stuck, and it copes worse under heavy load.

Asynchronous

The caller sends a message or event and carries on without waiting. Order Service publishes OrderCreated. A message broker (Kafka, RabbitMQ, AWS SNS/SQS) delivers it to Inventory and every other interested service, which each handle it in their own time.

Pros: loosely coupled, keeps working when some services are down, handles big volumes. Cons: harder to debug, and eventual consistency instead of immediate.

Synchronous versus asynchronous communication: service A calls service B and waits for the response, or puts requests on a queue; below, four protocols: REST over HTTP with JSON, gRPC between services in different languages, AMQP with an exchange and queues, and MQTT with a broker and subscribers
Wk 6 · s.15Sync waits for a reply, async goes through a queue. REST and gRPC are usually sync; AMQP and MQTT are messaging.
Event-driven architecture: the order service, as producer, sends OrderPlaced to a broker with two queues, which delivers it to the inventory, email and shipping services as consumers
Wk 6 · s.17One event, many consumers, all independent.
Key idea

Eventual consistency: right after an order, the stock count may not have dropped yet, because Inventory hasn't processed the event. A moment later, it has. The system is always correct eventually, not at every instant. Most shops accept that for stock and emails. Nobody accepts it for "did the payment go through?", which is why that call usually stays synchronous.

Try it

Place one order, and break something

Break:

    
Wk 6 · s.15–17

7.7 Microservices need DevOps, and the tools that help

Microservices and DevOps enable and require each other. Microservices let teams build and deploy services independently. DevOps supplies the automation to build, test, deploy, monitor and operate them reliably. With 20 or 50 services, doing any of that by hand stops being possible.

A CI/CD flow for containers: a developer submits code to version control; the CI system builds a new Docker container and uploads it to a container registry; CI runs it in a test environment; an ops engineer verifies the tests and deploys the container to production
Wk 6 · s.18The Chapter 2 pipeline, now producing a container per service.
ToolWhat it doesWhy microservices need it
Containers (Docker)Package each service with its code and dependencies. Lighter than VMs because they share the host OS, so they start and stop quicklyRun every service independently, in its own container
API gatewayOne entry point for clients. Routes each request to the right service, and can handle authentication, security, rate limits and combining responsesClients don't have to know about, or call, every service directly
Messaging and event streamingServices publish messages or events to a broker, and others process them when ready (Apache Kafka, RabbitMQ, Amazon SNS and SQS)Fewer direct dependencies between services
ServerlessSmall functions that run when an event or request triggers them, with no servers to manage (AWS Lambda)Small jobs that don't need a service running all the time, like sending an order confirmation email
Docker in microservices architecture: code and libraries are built and packaged into service A, B and C images, which run as isolated containers on a shared network, giving isolation, portability and consistency
Wk 6 · s.19One image per service, one container per running copy.
An API gateway between clients (web browser, application, mobile app) and the user, product and order services, each with its own database
Wk 6 · s.20Clients see one address. The gateway knows the rest.
A message broker such as Kafka or RabbitMQ: three producers send events A, B and C into it, and three consumers receive the events they subscribed to
Wk 6 · s.21Producers and consumers never call each other directly.
A serverless shop on AWS: an API gateway routes to product, basket and ordering microservices, each made of an API, a Lambda function and a DynamoDB table; a checkout event goes through EventBridge to an SQS queue
Wk 6 · s.22Each microservice here is just a function and a table.
Wk 6 · s.18–22

7.8 Best practices

  1. Don't start with microservices. Start with a monolith. Split it when it becomes hard to maintain, update or scale, and only where there's a clear need.
  2. Don't use microservices without DevOps. Many services need CI/CD pipelines for building, testing and deploying, and monitoring and logging to spot problems.
  3. Don't create too many small services. Start with services that group related tasks. Split one only when it gets hard to maintain, deploy or scale, because every extra service adds communication and management work.
Think of it like…

A restaurant kitchen. A food stall with one cook doesn't need a separate grill station, sauce station and dessert station, and passing plates between them would only slow it down. A hotel kitchen serving 2,000 meals a night does need stations, a head chef calling orders, and a pass where dishes are checked. Split the kitchen when the volume demands it, not on day one.

Wk 6 · s.23

7.9 Deployment strategies

Independent deployment is the point of microservices, so how a new version reaches users matters. The lecture covers four strategies:

The most popular deployment strategies: blue/green switches traffic from the blue environment to the green one; canary sends 25% of traffic to the new version and 75% to the old; A/B testing sends users to v1 or v2; a feature flag turns a new feature on for 20% of users; rolling replaces servers stage by stage
Wk 6 · s.24–27The same picture on all four slides. Feature flags (fourth row) are a bonus.
StrategyHow it worksProsConsUse case
Blue/greenTwo identical environments: blue is live, green gets the new version. Test green, then switch all trafficNo downtime, instant rollback (switch back)Double the resources, higher costMission-critical apps that need zero downtime
CanaryRelease to a small group of users first, watch the metrics, then widen step by stepLimits who meets a bug. A safe way to test in productionNeeds more careful monitoringLarge apps with millions of users
A/B testingShow two versions to different user groups at the same time, and compare key metricsPick the winner from real user behaviourOnly some users see the new version, and metrics must be tracked carefullyUI changes, new features, marketing content
RollingReplace old instances with new ones one batch at a timeMinimal downtime, a gradual switchOld and new run side by side, so they must be compatibleApps on many servers or containers
Try it

Roll out version 2

Notice that A/B testing answers a different question. Blue/green, canary and rolling ask "is v2 safe to ship?" A/B testing asks "which version do users prefer?", so it runs for days and needs enough users to make the comparison meaningful.

Wk 6 · s.24–27

Chapter 7 recap

  • Architecture decides how an app is organized, communicates, stores data, deploys and scales.
  • A monolith is one deployable unit with one shared database: simple, but it scales and fails as one.
  • Microservices are independently deployable services, each with its own data. They make complexity visible, not smaller.
  • Principles: single responsibility, loose coupling, decentralized data, fault isolation, API first.
  • Sync calls wait and couple. Async events decouple, at the price of eventual consistency.
  • Containers, an API gateway, a message broker and serverless functions make microservices workable. Without DevOps automation, they aren't.
  • Start with a monolith. Roll out with blue/green, canary or rolling, and use A/B tests to compare features.

Check yourself: Chapter 7

Final

Final Exam

A long paper on its own page, covering every chapter. No feedback until you hand it in, then a score per section and a full review.

49Questions
98Marks
105Minutes

Open the final exam

Reference

Commands and Glossary

CommandWhat it does
git checkout -b feature/xCreate a branch and switch to it
git add . && git commit -m "…"Stage and commit changes
git push -u origin feature/xPush the branch and track it
yarn install --immutableInstall exactly what yarn.lock says, or fail
yarn lint / yarn test / yarn coverageLab 1's three checks
docker build -t name .Build an image from the Dockerfile in this folder
docker run -d -p 8080:80 --name web imgStart a container in the background, host port 8080 to container port 80
docker ps / docker ps -aRunning containers / all containers
docker imagesLocal images
docker stop web / docker rm webStop a container / delete it
docker pull img / docker push imgDownload from / upload to a registry
TermMeaning
CIMerge often, and build and test every change automatically.
Continuous delivery / deploymentAuto-release to a repository and a person deploys / auto-deploy to production.
Pipeline / workflowThe automated sequence of steps. GitHub calls its file a workflow.
Shift left / rightTest earlier in development / keep validating in production.
Regression testChecks that new changes didn't break existing features.
Smoke testQuick check that the basics work before deeper testing.
Stub / driverFake lower module / fake higher module in integration testing.
LinterStatic analyser for mistakes and style.
CoverageHow much of the code ran during the tests.
HypervisorSoftware that runs virtual machines. Type 1 on hardware, type 2 on an OS.
Image / containerRead-only template / running instance of it.
RegistryWhere images are stored, such as Docker Hub.
NamespaceKernel feature that gives a container its own view of processes, network and files.
OrchestrationScheduling, scaling and healing many containers automatically, as Kubernetes does.
MonolithOne application, built and deployed as a single unit with a shared database.
MicroserviceA small, independently deployable service with one business job and its own data.
Loose couplingServices interact only through APIs, so each can change without breaking the others.
Circuit breakerStops calling a failing service for a while and fails fast, so callers don't all wait for timeouts.
Sync / async communicationThe caller waits for a reply / sends a message or event and carries on.
Message brokerMiddleware such as Kafka or RabbitMQ that receives events and delivers them to subscribers.
Eventual consistencyData across services agrees after a short delay, not at every instant.
API gatewayThe single entry point that routes client requests to services, with auth and rate limits.
ServerlessFunctions that run on demand while the cloud provider manages the servers (AWS Lambda).
Blue/greenTwo identical environments; switch all traffic to the new one, switch back to roll back.
CanaryRelease to a small slice of users first, then widen while the metrics stay healthy.
Rolling updateReplace instances batch by batch, so old and new versions run side by side for a while.
A/B testingTwo versions to two user groups at once, to see which performs better on a metric.

Course materials

The original files this page was built from. Tap one to download it.

Lecture decks

Lab

Built from the DevOps decks for Weeks 1, 2, 4 and 6 and the Lab 1 handout (Chandara Chea, CADT). Figures are cropped from those slides, as credited under each one. The demos are simulations: nothing runs on GitHub or Docker. Facts in the "Real world" boxes are from general knowledge, not the slides, so check them before quoting them in an assignment.