18 पॉइंट द्वारा xguru 2024-11-17 | 6 टिप्पणियां | WhatsApp पर शेयर करें
  • 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.23 image 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_modules folder में runtime के लिए ज़रूरी न होने वाली development dependencies भी शामिल हो जाती हैं.
  • इसे npm ci --omit=dev से ठीक नहीं किया जा सकता, क्योंकि build process के लिए ज़रूरी development dependencies हट सकती हैं.

Multi-Stage Build से पहले lean image बनाने का तरीका

Builder pattern

  1. Dockerfile.build में application को build किया जाता है:
FROM node:lts-slim  
WORKDIR /app  
COPY . .  
RUN npm ci  
RUN npm run build  
  1. built artifacts को host पर copy किया जाता है:
docker cp $(docker create build:v1):/app/.output .  
  1. 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 है.
    • कई FROM commands का उपयोग करके एक ही 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"]  
  • distroless image का उपयोग करके 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 टिप्पणियां

 
savvykang 2024-11-18

Java के मामले में jlink 9 वर्ज़न से पेश किया गया था, लेकिन jdeps से dependency modules ढूंढकर उन्हें अलग से specify करना पड़ता है, इसलिए इसकी usability अच्छी नहीं है। लोगों को ऐसे तरीके पता नहीं होते या वे JRE खोजते हैं, यह देखकर लगता है कि Java tools का प्रचार-प्रसार कम रहा है, और इसे इस तरह बेहतर बनाने की ज़रूरत है कि एक ही command से JRE तैयार हो जाए।

 
brainer 2024-11-17

मैं भी इसे ऐसे ही इस्तेमाल कर रहा हूँ, लेकिन build time ज़्यादा लगना शायद इसकी एक कमी है।

 
kandk 2024-11-18

बिल्ड समय में कोई फ़र्क नहीं होना चाहिए। अगर फ़र्क है, तो सेटिंग गलत की गई है!

 
brainer 2024-11-18

आह, ऐसा है!

 
qurare 2024-11-18

स्ट्रैटेजी के हिसाब से कभी-कभी पूरे stage को cache किया जा सकता है, इसलिए मेरे मामले में तो उल्टा build time कम हो गया था!

 
brainer 2024-11-18

लगता है Docker के बारे में मुझे थोड़ा और जानना पड़ेगा!