Docker/How I Cut Docker Image Size by 90 per cent - Best Practices for Lean Containers.md
... ...
@@ -0,0 +1,240 @@
1
+https://medium.com/@ksaquib/how-i-cut-docker-image-size-by-90-best-practices-for-lean-containers-1f705cead02b
2
+
3
+# How I Cut Docker Image Size by 90%: Best Practices for Lean Containers | by Saquib Khan | Medium
4
+
5
+![Saquib Khan](https://miro.medium.com/v2/resize:fill:88:88/1*Lb8-Ok-xsM9LXqzEsE6A3w.jpeg)
6
+[Saquib Khan](https://medium.com/@ksaquib)
7
+
8
+Reducing Docker image sizes is crucial for streamlining development workflows, speeding up builds, and minimizing deployment times, all while saving valuable storage space. Drawing from my own experience, I’ve discovered several effective strategies that not only optimize Docker images but also improve overall performance and efficiency. Here’s a guide to the best practices I’ve used and highly recommend for maintaining lean, efficient Docker images.
9
+
10
+1\. Use a Minimal Base Image
11
+----------------------------
12
+
13
+Selecting a minimal base image is one of the most effective ways to reduce Docker image size. Minimal base images, such as `alpine`, `scratch`, or `debian-slim`, are significantly smaller than larger base images like `ubuntu` or `debian`, as they come with only the essentials.
14
+
15
+Example with Python
16
+-------------------
17
+
18
+Consider the difference in size between a typical `ubuntu`\-based Python image and an `alpine`\-based Python image:
19
+
20
+**Using Ubuntu as Base Image:**
21
+
22
+```
23
+FROM python:3.11-slim
24
+```
25
+
26
+
27
+* **Image Size**: Approximately **60 MB** (Python 3.11 with Ubuntu base image)
28
+
29
+**Using Alpine as Base Image:**
30
+
31
+```
32
+FROM python:3.11-alpine
33
+```
34
+
35
+
36
+* **Image Size**: Approximately **23 MB** (Python 3.11 with Alpine base image)
37
+
38
+The Alpine-based image is around 3 times smaller than the Ubuntu-based image. This significant reduction in size is due to Alpine Linux being a minimal distribution specifically designed for Docker environments. Using such minimal base images not only reduces the image size but also decreases the attack surface, enhancing security.
39
+
40
+2\. Multistage Builds
41
+---------------------
42
+
43
+Multistage builds allow you to separate the build environment from the runtime environment, ensuring that only the essential files make it into the final image. This approach helps in reducing the size of the final Docker image by excluding build tools and dependencies that are not needed at runtime.
44
+
45
+Example with Python
46
+-------------------
47
+
48
+Consider a Python application where you want to use multistage builds to keep the final image lean:
49
+
50
+**Multistage Build Dockerfile:**
51
+
52
+```
53
+# Build stage
54
+FROM python:3.11-slim AS builder
55
+WORKDIR /app
56
+# Install build dependencies
57
+COPY requirements.txt .
58
+RUN pip install --user -r requirements.txt
59
+# Copy application code
60
+COPY . .
61
+# Final stage
62
+FROM python:3.11-slim
63
+WORKDIR /app
64
+# Install only runtime dependencies
65
+COPY --from=builder /root/.local /root/.local
66
+COPY . .
67
+# Set the path to include user-installed packages
68
+ENV PATH=/root/.local/bin:$PATH
69
+CMD ["python", "app.py"]
70
+```
71
+
72
+
73
+Size Comparison
74
+---------------
75
+
76
+* **Without Multistage Builds**: If you use a single stage Dockerfile, the final image would include both the build dependencies and the application code. For example:
77
+
78
+```
79
+FROM python:3.11-slim
80
+WORKDIR /app
81
+COPY requirements.txt .
82
+RUN pip install -r requirements.txt
83
+COPY . .
84
+CMD ["python", "app.py"]
85
+```
86
+
87
+
88
+* **Image Size**: Approximately **150 MB** (includes both build and runtime dependencies).
89
+
90
+**With Multistage Builds**: Using the multistage build example provided, the final image is significantly smaller:
91
+
92
+* **Image Size**: Approximately **60 MB** (contains only runtime dependencies and application code).
93
+
94
+3\. Remove Unnecessary Files
95
+----------------------------
96
+
97
+Cleaning up unnecessary files such as cache, temporary files, and build dependencies is a crucial step in reducing Docker image size. This practice ensures that your image contains only the essential components required for running your application, while minimizing the size and potential attack surface.
98
+
99
+Example with Python
100
+-------------------
101
+
102
+Here’s an example of how to remove unnecessary files in a Dockerfile for a Python application:
103
+
104
+**Before Cleanup:**
105
+
106
+```
107
+FROM python:3.11-slim
108
+WORKDIR /app
109
+# Install build dependencies
110
+COPY requirements.txt .
111
+RUN pip install -r requirements.txt
112
+# Copy application code
113
+COPY . .
114
+CMD ["python", "app.py"]
115
+```
116
+
117
+
118
+**With Cleanup:**
119
+
120
+```
121
+FROM python:3.11-slim
122
+WORKDIR /app
123
+# Install build dependencies
124
+COPY requirements.txt .
125
+RUN pip install -r requirements.txt \
126
+ # Clean up temporary files and caches
127
+ && rm -rf /root/.cache/pip
128
+# Copy application code
129
+COPY . .
130
+CMD ["python", "app.py"]
131
+```
132
+
133
+
134
+**Without Cleanup**: In a Dockerfile where unnecessary files are not removed, the size of the image can be larger due to leftover cache and temporary files:
135
+
136
+* **Image Size**: Approximately **150 MB** (includes build caches and unnecessary files).
137
+
138
+**With Cleanup**: Using cleanup commands such as `rm -rf /root/.cache/pip` to remove caches and temporary files can reduce the final image size:
139
+
140
+* **Image Size**: Approximately **120 MB** (after cleaning up caches and temporary files).
141
+
142
+4\. Use `.dockerignore` File
143
+----------------------------
144
+
145
+The `.dockerignore` file functions similarly to a `.gitignore` file but for Docker builds. It specifies which files and directories should be excluded from the Docker build context. This helps in reducing the size of the build context, leading to faster builds and smaller Docker images.
146
+
147
+Benefits of Using `.dockerignore`
148
+---------------------------------
149
+
150
+1. **Reduced Build Context Size**: By excluding unnecessary files, you minimize the amount of data sent to the Docker daemon, speeding up the build process.
151
+2. **Smaller Docker Images**: Excluding files that are not needed in the final image prevents them from being included, which helps in keeping the image size down.
152
+3. **Improved Build Efficiency**: Smaller build contexts mean Docker can cache layers more effectively, leading to faster rebuilds.
153
+
154
+Example `.dockerignore` File
155
+----------------------------
156
+
157
+Here’s a simple example of a `.dockerignore` file:
158
+
159
+```
160
+.git
161
+node_modules
162
+*.log
163
+.DS_Store
164
+```
165
+
166
+
167
+**Without** `**.dockerignore**`:
168
+
169
+* When unnecessary files are included in the Docker build context, they are sent to the Docker daemon and become part of the Docker image, even if they are not used in the final image.
170
+* For example, including a `.git` directory or `node_modules` folder can significantly increase the size of the build context. The `.git` directory can contain several hundred megabytes of version history, while `node_modules` might add another several hundred megabytes of dependencies that are not needed in the production image.
171
+* **Impact on Build Context Size**: Excluding files and directories that are not needed in the final image helps reduce the build context size, which can otherwise add up to several gigabytes, depending on the size and number of excluded files. Approximately t he build context might be around **1 GB** if it includes a large `.git` directory, `node_modules`, and other files not needed in the image.
172
+
173
+**With** `**.dockerignore**`:
174
+
175
+* By using a `.dockerignore` file to exclude unnecessary files, you limit the build context to only those files that are essential for the application.
176
+* This exclusion can lead to a significantly smaller build context. For example, excluding `.git`, `node_modules`, and other large directories can reduce the context size from several gigabytes to just a few megabytes.
177
+* **Impact on Image Size**: Although the `.dockerignore` file itself doesn’t directly reduce the size of the final Docker image, it does prevent unnecessary files from being added to the build context. This results in a more efficient build process and helps in creating a leaner final image by ensuring that only relevant files are included. After excluding these unnecessary files, the build context might be reduced to **50 MB**, which can significantly decrease build times and make the final Docker image more efficient.
178
+
179
+5\. Minimize Layers
180
+-------------------
181
+
182
+In Docker, each `RUN`, `COPY`, and `ADD` instruction in your Dockerfile creates a new layer in the resulting image. These layers can increase the overall size of the Docker image and impact build performance. Combining commands into a single `RUN` instruction helps in minimizing the number of layers, resulting in a more efficient and compact Docker image.
183
+
184
+![](https://miro.medium.com/v2/resize:fit:720/format:webp/1*JgmN_VEl5Nsx5RFw4ABtmA.png)
185
+
186
+**Without Layer Minimization**:
187
+
188
+* Each individual instruction (`RUN`, `COPY`, etc.) creates a new layer in the Docker image. These layers accumulate and can lead to a larger image size due to intermediate files, temporary data, and additional metadata.
189
+* For example, using separate `RUN` instructions can result in multiple layers, each adding its own metadata and overhead, which can bloat the final image size.
190
+* **Impact on Image Size**: If you use multiple `RUN` instructions like:
191
+
192
+```
193
+RUN apt-get update
194
+RUN apt-get install -y curl
195
+RUN apt-get clean
196
+```
197
+
198
+
199
+Using multiple `RUN` instructions can lead to an image size of around **150 MB**, with each layer adding overhead.
200
+
201
+**With Layer Minimization**:
202
+
203
+* Combining commands into a single `RUN` instruction reduces the number of layers and helps in consolidating changes into fewer, more optimized layers.
204
+* For example, combining the commands into one `RUN` instruction:
205
+
206
+```
207
+RUN apt-get update && apt-get install -y curl && apt-get clean
208
+```
209
+
210
+
211
+Combining commands into a single `RUN` instruction can reduce the image size to approximately **130 MB**. This reduction is achieved by consolidating changes into fewer layers and minimizing unnecessary intermediate data.
212
+
213
+**6\. Using Specific COPY Commands**:
214
+-------------------------------------
215
+
216
+Instead of copying entire directories into your Docker image, use specific `COPY` commands to include only the files you need. This approach avoids transferring unnecessary files, reducing the image size.
217
+
218
+**Example**:
219
+
220
+```
221
+COPY package.json .
222
+COPY src/ src/
223
+```
224
+
225
+
226
+* **Size Impact**: By copying only specific files and directories, you avoid including unwanted files that can bloat your image. For example, excluding development files or build artifacts can reduce the image size by several megabytes, depending on the size of the excluded data.
227
+
228
+7\. Use Multi-Arch Images
229
+-------------------------
230
+
231
+Creating multi-architecture Docker images ensures compatibility with various environments (e.g., ARM, x86). This approach optimizes images for different hardware platforms.
232
+
233
+**Example**:
234
+
235
+* **Size Impact**: Multi-arch images are optimized for specific architectures, potentially reducing the size of images used on different platforms. This helps in avoiding bloated images that include support for multiple architectures when only one is needed.
236
+
237
+Conclusion
238
+----------
239
+
240
+These best practices lead to more efficient, secure, and faster Docker images, enhancing your overall container management and deployment processes.
... ...
\ No newline at end of file