# Day 16 - Multi-Stage Docker Builds & Distroless Images

### 🚀 Reduce Image Size by 800% and Improve Security

---

## 🎯 **1\. Objective**

In this session, we’ll learn:

1. The concept of **Multi-Stage Docker Builds**
    
2. The concept of **Distroless (Destroyless) Images**
    

Both concepts are **closely related** — using distroless images enhances the efficiency and security of multi-stage builds.

---

## 🧱 **2\. The Problem with Traditional Docker Builds**

Let’s take a simple example:  
You want to containerize a **Python calculator application**.

### Typical Steps

1. Start from a base image (e.g., `ubuntu:latest`)
    
2. Set a working directory (optional)
    
3. Install dependencies:
    
    * Python
        
    * pip
        
    * Required Python modules/packages
        
4. Copy source code into the image
    
5. Build and run the application (via `CMD` or `ENTRYPOINT`)
    

### ⚠️ Problem

Although this Dockerfile works, it’s **inefficient**:

* The image includes the **entire Ubuntu OS** plus unnecessary packages (`apt`, `curl`, etc.)
    
* These packages are **only needed during build**, not runtime
    
* The final image becomes **huge** and **slow to pull/run**
    

---

## ⚙️ **3\. Build vs. Run Phases**

| Stage | Purpose | Example |
| --- | --- | --- |
| **Build Stage** | Compiles or prepares the app | Installs compilers, dependencies |
| **Run Stage** | Executes the final app | Needs only runtime environment |

For instance:

* A **Java** app needs **JDK** to build, but only **JRE** to run.
    
* A **Python** app needs **pip** and libraries to build, but only **Python runtime** to run.
    

So, it’s wasteful to keep all build-time tools in the final image.

---

## 🧩 **4\. Docker’s Solution — Multi-Stage Builds**

To solve this problem, Docker introduced **multi-stage builds**.

### 💡 Concept

You can **split your Dockerfile into multiple stages**, using **multiple** `FROM` statements in one file.

Each stage:

* Builds a specific part of your application
    
* Can copy artifacts (like binaries) to the next stage
    
* Keeps the **final image minimal**
    

---

## 🏗️ **5\. Example: Two-Stage Dockerfile**

### Stage 1 – Build Stage

```plaintext
FROM ubuntu AS build
RUN apt-get update && apt-get install -y python3 pip
COPY . /app
WORKDIR /app
RUN python3 setup.py build
```

### Stage 2 – Final Stage

```plaintext
FROM python:3.10-slim
COPY --from=build /app/dist /app
CMD ["python3", "/app/main.py"]
```

✅ **Result:**

* The build tools (like `apt`, compilers, pip caches) are **excluded**
    
* Only the necessary runtime (Python + your app) remains
    
* Image size drastically **reduces**
    

---

## 🧮 **6\. Example: Multi-Stage Build for Complex App**

Imagine a **3-tier application**:

* Frontend (React)
    
* Backend (Java Spring Boot)
    
* Database (MySQL)
    

### Traditional Method:

* All dependencies (Node.js, JDK, MySQL client) installed in one image → ~1 GB+.
    

### With Multi-Stage Build:

* Each part built in separate stages:
    
    * Stage 1 → Frontend build (React)
        
    * Stage 2 → Backend build (Java)
        
    * Stage 3 → Final stage (only Java runtime + built artifacts)
        
* Final image = ~150 MB
    

**Result:**  
Image size reduced by ~85–90% with cleaner, modular build process.

---

## 📉 **7\. Image Size Comparison Example**

| Type | Description | Approx. Size |
| --- | --- | --- |
| Traditional single-stage | `ubuntu + go + source + runtime` | 861 MB |
| Multi-stage + Distroless | Only runtime + binary | **1.83 MB** |
| Reduction | – | ~800× smaller 🚀 |

---

## ⚙️ **8\. Multi-Stage Docker Syntax Example**

```plaintext
# Stage 1: Build
FROM ubuntu AS build
RUN apt-get install -y golang
COPY . /src
WORKDIR /src
RUN go build -o calculator calculator.go

# Stage 2: Final (Distroless)
FROM scratch
COPY --from=build /src/calculator /
ENTRYPOINT ["/calculator"]
```

### Explanation:

* `AS build` → Creates a named stage
    
* `COPY --from=build` → Copies artifact from build stage
    
* `FROM scratch` → Uses an empty minimal image (distroless base)
    

---

## 🪶 **9\. What Are Distroless Images?**

### Definition

A **Distroless Image** is a **very minimalistic base image** that includes:

* Only **runtime binaries** (e.g., Python runtime, Java runtime)
    
* **No package manager**, **no shell**, **no OS utilities**
    

### 🔍 Examples

| Language | Distroless Image Example |
| --- | --- |
| Java | `gcr.io/distroless/java17` |
| Python | `gcr.io/distroless/python3` |
| Node.js | `gcr.io/distroless/nodejs` |
| Go | `scratch` (empty base, needs no runtime) |

---

## 🧰 **10\. Why Distroless?**

### ✅ Advantages

| Benefit | Description |
| --- | --- |
| **Smaller Image Size** | Removes all unnecessary OS layers |
| **Higher Security** | No package manager, shell, or vulnerable binaries |
| **Faster Deployment** | Lightweight → faster pull/run |
| **Best with Multi-Stage Builds** | Build heavy → final image minimal |

---

## 🔒 **11\. Security Benefits**

* Traditional base images (like Ubuntu, CentOS) come with many **system packages** → higher **attack surface**.
    
* Distroless images have **no shell**, **no apt**, **no curl**, etc.
    
* Hackers can’t exploit missing tools.
    
* Greatly reduces **CVE (Common Vulnerability Exposure)** count.
    

Example:

> In interviews, you can say:  
> “We moved from Ubuntu-based containers to Python Distroless images, eliminating unnecessary system binaries and greatly reducing vulnerability exposure.”

---

## 🦴 **12\. Special Case: Go (Golang) Applications**

* Go produces **statically compiled binaries**.
    
* Doesn’t even need a runtime to execute.
    
* Works perfectly with the `scratch` base image.
    
* Final image size can be as small as **1–2 MB**.
    

Hence, Go + Multi-Stage + Distroless = 💯 perfect combination.

---

## 🔍 **13\. Finding Distroless Images**

Visit the official **Google Distroless GitHub repository**:  
👉 [https://github.com/GoogleContainerTools/distroless](https://github.com/GoogleContainerTools/distroless)

There you’ll find folders for:

* `base`, `cc`, `java`, `python3`, `nodejs`, etc.  
    Each folder’s **README.md** lists the image name (e.g., `gcr.io/distroless/java17`).
    

---

## 🧾 **14\. Key Interview Points**

| Question | Short Answer |
| --- | --- |
| What is a Multi-Stage Docker Build? | A way to split a Dockerfile into multiple build stages and keep only the final runtime stage. |
| What are Distroless Images? | Minimal base images without OS or shell, containing only runtime dependencies. |
| Benefits of Multi-Stage Builds | Smaller, faster, modular images. |
| Benefits of Distroless Images | Security, minimalism, reduced vulnerabilities. |
| How many stages can a multi-stage build have? | Unlimited, but only one final stage is used to run the container. |
| Which base image is the most minimal? | `scratch` (completely empty). |

---

## 🧠 **15\. Summary**

| Concept | Key Idea |
| --- | --- |
| **Traditional Dockerfile** | Installs build + runtime tools in one image → large & insecure |
| **Multi-Stage Docker Build** | Separates build & runtime stages → smaller & efficient |
| **Distroless Image** | Removes OS layer entirely → minimal, secure runtime |
| **Result** | Up to **800× smaller**, **highly secure**, **production-ready** images |

---

✅ **Final Takeaway:**

> Using **Multi-Stage Docker Builds** + **Distroless Images** gives you:
> 
> * Massive reduction in image size
>     
> * Improved container startup speed
>     
> * Drastically fewer security vulnerabilities
>     
> * Best practice for all modern production-grade container builds
>
