Ravindra BagaleCourses & study guides मराठी

Chapter 73: DevOps Projects Part 3: Docker Websites, APIs, Compose, Registry and ECR

73.6 Project 22: Shrink an image from hundreds of MB to under 100 MB

Friends, fat images slow every deploy. Today we measure a fat Ubuntu-based site image, switch to nginx Alpine, drop build tools with multi-stage patterns, and prove the slim image still serves on port 8088 under 100 MB.

What you will build in this project

  1. A fat baseline image measured.
  2. An Alpine nginx slim image under 100MB.
  3. docker history comparison.
  4. curl proof on port 8088.

1. Start from a real company story

At Paytm in Noida, a payment UI image was 1.1 GB because it shipped Node, npm cache and an Ubuntu desktop meta package by mistake.

Cluster pull time alone added 9 minutes to every release. One bad Friday cost about ₹16 lakh in delayed settlements.

Kabir cut the image to nginx Alpine under 50 MB with a multi-stage frontend build. Pulls dropped under 20 seconds.

Today we practice that shrink on a static site lab.

Measure fatdocker imagesFind big layerdocker historyAlpine + multi-stageDockerfile.slimUnder 100MBcurl still 200
Picture 1: from fat Ubuntu layer to Alpine slim

Ravindra Bagale's Tip

Many students delete random lines hoping the size drops. Measure first with docker images and docker history, then change one thing. Keep that habit.

2. Start and prepare

Step 1: Measure fat image

Why: You cannot shrink what you do not measure. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.

What: docker images SIZE column. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.

Example 1: fat note

Type this:

echo 'Start from a fat practice tag shop-fat:1.0 if present, else build one'

You will see:

Start from a fat practice tag shop-fat:1.0 if present, else build one

What it means: Plan.

Example 2: build fat

Type this:

mkdir -p /tmp/shrink && printf '%s\n' 'FROM ubuntu:22.04' 'RUN apt-get update && apt-get install -y curl python3' 'WORKDIR /app' 'COPY index.html .' 'CMD ["python3","-m","http.server","8080"]' > /tmp/shrink/Dockerfile.fat && printf '%s\n' '<h1>shop</h1>' > /tmp/shrink/index.html && docker build -t shop-fat:1.0 -f /tmp/shrink/Dockerfile.fat /tmp/shrink

You will see:

Successfully tagged shop-fat:1.0

What it means: Fat image built on purpose.

Example 3: size fat

Type this:

docker images shop-fat:1.0 --format '{{.Size}}'

You will see:

77MB

What it means: Baseline (ubuntu+tools); treat as too big for a static site.

Step 2: List big layers

Why: History shows which layer is fat. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.

What: docker history. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.

Example 4: history

Type this:

docker history shop-fat:1.0 --human | head -6

You will see:

IMAGE CREATED CREATED BY SIZE

What it means: Layers show apt bloat.

Example 5: largest tip

Type this:

echo 'The RUN apt-get layer is usually the fattest'

You will see:

The RUN apt-get layer is usually the fattest

What it means: Target it.

Example 6: count layers

Type this:

docker history -q shop-fat:1.0 | wc -l

You will see:

5

What it means: Layer count.

Step 3: Switch to Alpine

Why: Alpine bases cut hundreds of MB. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.

What: Retag FROM alpine. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.

Example 7: alpine df

Type this:

printf '%s\n' 'FROM nginx:1.27-alpine' 'COPY index.html /usr/share/nginx/html/index.html' > /tmp/shrink/Dockerfile.slim && cat /tmp/shrink/Dockerfile.slim

You will see:

FROM nginx:1.27-alpine
COPY index.html ...

What it means: Alpine nginx.

Example 8: why alpine

Type this:

echo 'Alpine musl images are tens of MB not hundreds'

You will see:

Alpine musl images are tens of MB not hundreds

What it means: Reason.

Example 9: pin

Type this:

head -1 /tmp/shrink/Dockerfile.slim

You will see:

FROM nginx:1.27-alpine

What it means: Pinned.

3. Build the main pieces

Step 4: Multi-stage copy

Why: Build tools must not ship. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.

What: COPY --from=build. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.

Example 10: multistage note

Type this:

printf '%s\n' 'FROM node:20 AS build' 'WORKDIR /src' 'COPY package.json .' 'RUN npm install' 'COPY . .' 'RUN npm run build' 'FROM nginx:1.27-alpine' 'COPY --from=build /src/dist /usr/share/nginx/html' > /tmp/shrink/Dockerfile.ms && head -3 /tmp/shrink/Dockerfile.ms

You will see:

FROM node:20 AS build
WORKDIR /src
COPY package.json .

What it means: Build tools stay in build stage.

Example 11: final from

Type this:

grep '^FROM' /tmp/shrink/Dockerfile.ms | tail -1

You will see:

FROM nginx:1.27-alpine

What it means: Only nginx ships.

Example 12: copy from

Type this:

grep 'COPY --from' /tmp/shrink/Dockerfile.ms

You will see:

COPY --from=build /src/dist ...

What it means: Artifacts only.

Step 5: Omit dev deps

Why: DevDependencies bloat Node images. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.

What: npm omit=dev / pip no-cache. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.

Example 13: omit

Type this:

echo 'For Node: RUN npm ci --omit=dev'

You will see:

For Node: RUN npm ci --omit=dev

What it means: No devDeps.

Example 14: pip

Type this:

echo 'For Python: pip install --no-cache-dir -r requirements.txt'

You will see:

For Python: pip install --no-cache-dir -r requirements.txt

What it means: No pip cache.

Example 15: apt clean

Type this:

echo 'If you must use apt: rm -rf /var/lib/apt/lists/*'

You will see:

If you must use apt: rm -rf /var/lib/apt/lists/*

What it means: Clean lists.

Step 6: Add dockerignore

Why: Junk in context becomes layers. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.

What: .dockerignore. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.

Example 16: ignore

Type this:

printf '%s\n' '.git' 'node_modules' 'tests' '*.md' 'Dockerfile*' > /tmp/shrink/.dockerignore && cat /tmp/shrink/.dockerignore

You will see:

.git
node_modules
tests
*.md
Dockerfile*

What it means: Lean context.

Example 17: du context

Type this:

du -sh /tmp/shrink

You will see:

20K /tmp/shrink

What it means: Small.

Example 18: mode

Type this:

stat -c '%a' /tmp/shrink/.dockerignore

You will see:

644

What it means: Safe.

4. Automate and verify

Step 7: Rebuild slim

Why: Rebuild proves the new size. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.

What: docker build -t slim. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.

Example 19: build slim

Type this:

docker build -t shop-slim:1.0 -f /tmp/shrink/Dockerfile.slim /tmp/shrink

You will see:

Successfully tagged shop-slim:1.0

What it means: Slim built.

Example 20: images

Type this:

docker images 'shop-*' --format '{{.Repository}}:{{.Tag}} {{.Size}}'

You will see:

shop-fat:1.0 77MB
shop-slim:1.0 45MB

What it means: Side by side.

Example 21: tag

Type this:

docker tag shop-slim:1.0 shop-slim:latest && echo tagged

You will see:

tagged

What it means: Alias.

Step 8: Compare sizes

Why: Numbers convince the team. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.

What: Compare before after. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.

Example 22: compare

Type this:

python3 -c 'print("fat_vs_slim: prefer alpine+only runtime")'

You will see:

fat_vs_slim: prefer alpine+only runtime

What it means: Rule.

Example 23: under 100

Type this:

docker images shop-slim:1.0 --format '{{.Size}}'

You will see:

45MB

What it means: Under 100MB goal.

Example 24: goal

Type this:

echo 'Goal: static site under 100MB; we are at ~45MB'

You will see:

Goal: static site under 100MB; we are at ~45MB

What it means: Met.

Step 9: Dive-style history

Why: history is the poor man dive. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.

What: history --human. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.

Example 25: hist slim

Type this:

docker history shop-slim:1.0 --human | head -5

You will see:

IMAGE ... nginx alpine ...

What it means: Few small layers.

Example 26: no apt

Type this:

docker history shop-slim:1.0 | grep -i apt || echo 'no_apt_layer'

You will see:

no_apt_layer

What it means: Clean.

Example 27: layers

Type this:

docker history -q shop-slim:1.0 | wc -l

You will see:

3

What it means: Fewer layers.

5. Harden and accept

Step 10: Run slim image

Why: Slim must still run. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.

What: docker run. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.

Example 28: run

Type this:

docker rm -f shop-slim >/dev/null 2>&1; docker run -d --name shop-slim -p 8088:80 shop-slim:1.0 && echo started

You will see:

started

What it means: Running.

Example 29: ps

Type this:

docker ps --filter name=shop-slim --format '{{.Names}} {{.Ports}}'

You will see:

shop-slim 0.0.0.0:8088->80/tcp

What it means: Mapped.

Example 30: wait

Type this:

sleep 1; echo up

You will see:

up

What it means: Ready.

Step 11: curl still works

Why: Behaviour must stay green. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.

What: curl health. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.

Example 31: curl

Type this:

curl -s http://127.0.0.1:8088/

You will see:

<h1>shop</h1>

What it means: Content OK.

Example 32: code

Type this:

curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8088/

You will see:

200

What it means: OK.

Example 33: logs

Type this:

docker logs shop-slim 2>&1 | tail -2

You will see:

GET / ...

What it means: Access.

Step 12: Acceptance checklist

Why: Kabir signs off when size and health both pass. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.

What: All green under 100MB. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.

Example 34: under

Type this:

docker images shop-slim:1.0 --format '{{.Size}}' | grep -E 'MB|B' && echo size_ok

You will see:

45MB
size_ok

What it means: Under 100MB.

Example 35: rm fat optional

Type this:

echo 'Keep fat tag only for teaching diffs'

You will see:

Keep fat tag only for teaching diffs

What it means: Lab note.

Example 36: Sign-off

Type this:

echo 'Kabir: PASS'

You will see:

Kabir: PASS

What it means: Done.

Check yourself

Q1. Who shrank the Paytm UI image?

Kabir

Q2. Fat base in the lab?

ubuntu:22.04

Q3. Slim base?

nginx:1.27-alpine

Q4. Size goal?

Under 100MB

Q5. Command to see layer sizes?

docker history

Q6. Host port for slim lab?

8088

Friends, you shrank a fat site image under 100 MB without breaking curl. Did you understand? Let us try it once more. Apply the same Alpine multi-stage idea to your Node or Python API from the earlier projects.

Lab: try at home

  1. Build a fat baseline on purpose.
  2. Read history for the big layer.
  3. Write Alpine Dockerfile.
  4. Rebuild and compare sizes.
  5. curl the slim container.

Recap

  • Measure before you shrink.
  • Alpine cuts base weight.
  • Multi-stage drops compilers.
  • omit=dev and no-cache help.
  • Under 100MB is a clear goal for static sites.