Docker Multi-Stage Build से container image का आकार कम करना
(labs.iximiuz.com)- Docker container image को build करते समय, अगर Dockerfile की संरचना Multi-Stage नहीं है, तो अनावश्यक files शामिल होने की संभावना बहुत अधिक होती है
- इससे image size बढ़ता है और security vulnerabilities भी बढ़ती हैं
- container image में पैदा होने वाली “अनावश्यक files” के मुख्य कारणों का विश्लेषण किया गया है, और Multi-Stage Build के ज़रिए उन्हें कैसे हल किया जाए यह समझाया गया है
image size बढ़ने के कारण
- application के पास build-time और runtime dependencies होती हैं.
- build-time dependencies, runtime की तुलना में ज़्यादा होती हैं और उनमें security vulnerabilities (CVEs) भी अधिक होते हैं.
- अगर एक ही image को build और run दोनों के लिए इस्तेमाल किया जाए, तो उसमें अनावश्यक build-time dependencies (compiler, linter आदि) शामिल हो जाती हैं.
- build और runtime images को अलग होना चाहिए, लेकिन अक्सर इस बात को नज़रअंदाज़ कर दिया जाता है.
गलत Dockerfile संरचना के उदाहरण
Go application के लिए गलत उदाहरण
FROM golang:1.23
WORKDIR /app
COPY . .
RUN go build -o binary
CMD ["/app/binary"]
golang:1.23image compilation के लिए है, लेकिन अगर इसे सीधे production environment में इस्तेमाल किया जाए, तो पूरा Go compiler और उसकी dependencies भी शामिल हो जाती हैं.- image size: 800MB से अधिक, और 800 से ज़्यादा security vulnerabilities मौजूद.
Node.js application का गलत उदाहरण
FROM node:lts-slim
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "/app/.output/index.mjs"]
node_modulesfolder में runtime के लिए ज़रूरी न होने वाली development dependencies भी शामिल हो जाती हैं.- इसे
npm ci --omit=devसे ठीक नहीं किया जा सकता, क्योंकि build process के लिए ज़रूरी development dependencies हट सकती हैं.
Multi-Stage Build से पहले lean image बनाने का तरीका
Builder pattern
Dockerfile.buildमें application को build किया जाता है:
FROM node:lts-slim
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build
- built artifacts को host पर copy किया जाता है:
docker cp $(docker create build:v1):/app/.output .
Dockerfile.runमें runtime image बनाई जाती है:
FROM node:lts-slim
WORKDIR /app
COPY .output .
CMD ["node", "/app/.output/index.mjs"]
• समस्याएँ: कई Dockerfile लिखने पड़ते हैं, build order manage करना पड़ता है, और अतिरिक्त scripts की ज़रूरत होती है.
Multi-Stage Build को समझना
- Multi-Stage Build, Docker के अंदर Builder pattern को लागू करने वाली feature है.
- कई
FROMcommands का उपयोग करके एक ही Dockerfile में build और runtime stages को define किया जा सकता है. COPY --from=<stage>command का उपयोग करके पिछले stage में built files को लाया जाता है.
- कई
Multi-Stage Dockerfile उदाहरण (Node.js)
# Build stage
FROM node:lts-slim AS build
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build
# Runtime stage
FROM node:lts-slim AS runtime
WORKDIR /app
COPY --from=build /app/.output .
ENV NODE_ENV=production
CMD ["node", "/app/.output/index.mjs"]
COPY --from=buildसे built artifacts को सीधे copy किया जाता है, जिससे host के बिना files को move किया जा सकता है.
Multi-Stage Build के व्यावहारिक उदाहरण
React application
# Build stage
FROM node:lts-slim AS build
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build
# Runtime stage
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
ENTRYPOINT ["nginx", "-g", "daemon off;"]
- React application build होने के बाद static files बन जाती है, और उसे Nginx से serve किया जा सकता है.
Go application
# Build stage
FROM golang:1.23 AS build
WORKDIR /app
COPY . .
RUN go build -o binary
# Runtime stage
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /app/binary /app/binary
ENTRYPOINT ["/app/binary"]
distrolessimage का उपयोग करके minimal runtime environment दिया जाता है.
Java application
# Build stage
FROM eclipse-temurin:21-jdk-jammy AS build
WORKDIR /build
COPY . .
RUN ./mvnw package -DskipTests
# Runtime stage
FROM eclipse-temurin:21-jre-jammy
COPY --from=build /build/target/app.jar /app.jar
CMD ["java", "-jar", "/app.jar"]
- build के लिए JDK और runtime के लिए हल्का JRE इस्तेमाल किया जाता है.
निष्कर्ष
- Multi-Stage Build, build और runtime environments को अलग करके अनावश्यक development dependencies के कारण image size बढ़ने से रोकता है
- इससे image size कम किया जा सकता है, security बेहतर होती है, और build process सरल बनता है
- Multi-Stage Build, efficient container images बनाने का एक standard तरीका है, और advanced features (जैसे branching conditions, build के दौरान unit tests) को भी support करता है
6 टिप्पणियां
Java के मामले में
jlink9 वर्ज़न से पेश किया गया था, लेकिनjdepsसे dependency modules ढूंढकर उन्हें अलग से specify करना पड़ता है, इसलिए इसकी usability अच्छी नहीं है। लोगों को ऐसे तरीके पता नहीं होते या वे JRE खोजते हैं, यह देखकर लगता है कि Java tools का प्रचार-प्रसार कम रहा है, और इसे इस तरह बेहतर बनाने की ज़रूरत है कि एक ही command से JRE तैयार हो जाए।मैं भी इसे ऐसे ही इस्तेमाल कर रहा हूँ, लेकिन build time ज़्यादा लगना शायद इसकी एक कमी है।
बिल्ड समय में कोई फ़र्क नहीं होना चाहिए। अगर फ़र्क है, तो सेटिंग गलत की गई है!
आह, ऐसा है!
स्ट्रैटेजी के हिसाब से कभी-कभी पूरे stage को cache किया जा सकता है, इसलिए मेरे मामले में तो उल्टा build time कम हो गया था!
लगता है Docker के बारे में मुझे थोड़ा और जानना पड़ेगा!