The goal is to reproduce this entire architecture independently:
Developer
↓
Feature Branch
↓
Pull Request
↓
┌──────── Lint ────────┐
│ │
├──────── SonarQube ────┼──→ ALL PASS
│ │ ↓
└──────── Trivy ────────┘ needs
↓
Build
↓
dist/
↓
Dockerfile
↓
Docker Image
↓
Container
↓
Host 8080 → 80
↓
Nginx
↓
Restaurant Company
Part 1 — Git preparation
Start from the repository:
cd ~/restaurant-company
git status
git branch
git switch main
git pull origin main
git switch -c homework-needs-docker
Verify:
git branch
git status
Students must explain: What is main? Why do we create a feature branch? Why do we run git pull before creating the branch? What does git status tell us?
Part 2 — Review the three parallel CI jobs
Open:
cat .github/workflows/ci.yml
Students must identify:
lint
sonarqube
trivy
They should write answers to these questions:
What is a Job? What is a Runner? What is a Step? Why can Lint, SonarQube and Trivy run in parallel? What is the difference between parallel jobs and sequential steps? What does runs-on: ubuntu-latest mean?
They should draw:
┌── Lint
│
Pull Request ──┼── SonarQube
│
└── Trivy
Part 3 — Yesterday's main topic: needs
Students must add/review:
build:
name: Build
runs-on: ubuntu-latest
needs:
- lint
- sonarqube
- trivy
They must explain every line.
Especially:
needs:
- lint
- sonarqube
- trivy
Written questions:
What does needs mean? What is a dependent job? What is an independent job? Are lint, sonarqube, and trivy job IDs or display names? Why shouldn't Build start immediately?
Then explain:
Lint ✅
SonarQube ✅
Trivy ✅
↓
Build ▶ RUN
versus:
Lint ✅
SonarQube ✅
Trivy ❌
↓
Build ⏭ SKIPPED
Students must answer:
What is the difference between FAILED and SKIPPED?
Part 4 — Build Job
Complete/review:
build:
name: Build
runs-on: ubuntu-latest
needs:
- lint
- sonarqube
- trivy
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Verify build
run: ls -la dist
Students explain:
Checkout
↓
Setup Node
↓
npm ci
↓
npm run build
↓
dist/
↓
Verify
Written questions: What does npm ci do? Which file locks exact dependencies? Where does npm run build come from? What does Vite create? What is dist/? Why do we run ls -la dist?
Part 5 — Commit, Push and Pull Request
Before committing:
git status
git diff
Then:
git add .github/workflows/ci.yml
git status
git commit -m "Add dependent build job"
git push -u origin homework-needs-docker
Create a Pull Request:
homework-needs-docker
↓
Pull Request
↓
main
Open GitHub Actions.
Take a screenshot showing the jobs.
Students should be able to explain:
┌── Lint ────────┐
│ │
PR ─────┼── SonarQube ────┼──→ Build
│ │
└── Trivy ────────┘
Part 6 — Docker Fundamentals
Now today's material.
Before touching commands, students write definitions for:
Docker, Dockerfile, Docker Image, Container, Container Runtime.
Then explain:
Dockerfile
↓
docker build
↓
Image
↓
docker run
↓
Container
They must clearly answer:
What is the difference between an Image and a Container?
Part 7 — Study the Restaurant Company Dockerfile
Run:
cat Dockerfile
Study:
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Students must explain every instruction:
FROM
AS builder
WORKDIR
COPY
RUN
COPY --from
EXPOSE
CMD
Part 8 — Multi-stage Build
Students draw:
STAGE 1 — BUILDER
────────────────────
node:22-alpine
↓
package.json
package-lock.json
↓
npm ci
↓
Source Code
↓
npm run build
↓
/app/dist
↓ COPY
STAGE 2 — RUNTIME
────────────────────
nginx:alpine
↓
/usr/share/nginx/html
↓
Nginx
↓
Port 80
Questions:
Why do we have two FROM instructions? What is a multi-stage build? Why do we need Node.js during Build? Why can Nginx serve the final frontend? What does COPY --from=builder mean?
Part 9 — Build the Docker Image
Run:
docker --version
docker build -t restaurant-company:homework .
Then:
docker images
Take a screenshot.
Students explain:
docker build
-t
restaurant-company
:homework
.
Especially:
What does
.mean?
Answer: current directory as the build context.
Part 10 — Run the Container
Run:
docker run -d \
--name restaurant-homework \
-p 8080:80 \
restaurant-company:homework
Verify:
docker ps
Students explain:
docker run
-d
--name
-p
8080:80
restaurant-company:homework
Draw:
EC2 HOST
Port 8080
↓
Docker
↓
CONTAINER
Port 80
↓
Nginx
They must explain:
In
8080:80, which is Host and which is Container?
Part 11 — Go Inside the Container
Run:
docker exec -it restaurant-homework sh
Now inside:
cat /etc/os-release
hostname
pwd
Students answer:
My EC2 is Ubuntu. Why does the container show Alpine?
Then:
cd /usr/share/nginx/html
pwd
ls -la
Inspect:
cat index.html
ls assets
Then:
cat /etc/nginx/conf.d/default.conf
Students must connect this to Dockerfile:
COPY --from=builder /app/dist /usr/share/nginx/html
They should explain:
/app/dist
↓
COPY
↓
/usr/share/nginx/html
Part 12 — Exit Without Stopping the Container
Run:
exit
Then:
docker ps
Students answer:
Why is the container still running after I typed
exit?
They need to understand that they exited their interactive shell; the container's main Nginx process is still running.
Part 13 — Test with curl
From EC2:
curl -I http://localhost:8080
Then:
curl http://localhost:8080
Students should identify an HTTP response such as:
HTTP/1.1 200 OK
Server: nginx
Explain the request:
curl
↓
localhost:8080
↓
Docker Port Mapping
↓
Container :80
↓
Nginx
↓
Website
Take a screenshot.
Part 14 — Open Restaurant Company in Browser
Check AWS Security Group.
For the lab, allow the needed TCP 8080 access, preferably from the student's own IP when practical.
Then:
http://EC2-PUBLIC-IP:8080
Take a screenshot showing the Restaurant Company website.
Students must draw the complete architecture:
Laptop
↓
Browser
↓
Internet
↓
EC2 Public IP :8080
↓
AWS Security Group
↓
EC2 Host Port 8080
↓
Docker Port Mapping
↓
Container Port 80
↓
Nginx
↓
/usr/share/nginx/html
↓
Restaurant Company
Part 15 — Troubleshooting Incident
Now deliberately break the application:
docker stop restaurant-homework
Try:
curl -I http://localhost:8080
Then:
docker ps
Students should investigate:
docker ps -a
Find the problem.
Fix:
docker start restaurant-homework
Verify:
docker ps
curl -I http://localhost:8080
Refresh the browser.
Students write:
CHECK
↓
docker ps
FIND
↓
Container stopped
FIX
↓
docker start restaurant-homework
VERIFY
↓
docker ps
curl
browser
This should connect to your troubleshooting model:
CHECK → FIND → FIX → VERIFY
Part 16 — Command Challenge Without Notes
Students should be able to explain these commands without copying definitions:
docker build -t restaurant-company:homework .
docker images
docker run -d --name restaurant-homework -p 8080:80 restaurant-company:homework
docker ps
docker ps -a
docker exec -it restaurant-homework sh
docker stop restaurant-homework
docker start restaurant-homework
curl -I localhost:8080
Part 17 — Interview Questions
Students should write answers in their own words to these questions:
What is Docker? What problem does Docker solve? What is a Dockerfile? What is a Docker Image? What is a Container? Image vs Container? What does docker build do? What does docker run do? What does docker exec do? docker ps vs docker ps -a? What does -p 8080:80 mean? What is a multi-stage Docker build? Why does our first stage use Node.js? Why does our second stage use Nginx? What is dist/? What does COPY --from=builder do? Why is the EC2 Ubuntu but the container Alpine? What is needs in GitHub Actions? Why do Lint/SonarQube/Trivy run in parallel? Why does Build wait? What happens to Build when a required job fails?
Interview answer to practice
“In our CI pipeline, Lint, SonarQube and Trivy run as independent jobs in parallel. Our Build job depends on those required checks using
needs. After the checks pass, we build the Vite application and produce thedistdirectory. We then use a multi-stage Dockerfile: Node.js builds the application, and the final Nginx stage serves the built files. We build a Docker image and run it as a container with host port 8080 mapped to container port 80.”
Top comments (0)