การ Optimize & Best Practices — ภาพรวมโมดูล
ทำไมต้อง Optimize Docker image?
หัวข้อที่มีชื่อว่า “ทำไมต้อง Optimize Docker image?”เมื่อ build Docker image ครั้งแรก เราอาจได้ image ที่มีขนาดถึง 1–2 GB ได้ง่ายๆ ขนาดนั้นส่งผลกระทบในทุกมิติ: ทุกครั้งที่ docker pull บน CI จะเสียเวลาเป็นวินาที (หรือนาที) ทุกการ push ไปยัง registry เสียแบนด์วิดท์ และทุก megabyte ใน image คือพื้นที่ที่ผู้โจมตีอาจใช้ประโยชน์ได้
การ Optimize ไม่ได้หมายถึงการปรับปรุงเล็กน้อย แต่คือการใช้เทคนิคที่ผ่านการพิสูจน์แล้วไม่กี่อย่าง ได้แก่ multi-stage builds, การเรียงลำดับ layer อย่างชาญฉลาด, base image ที่กระทัดรัด, และการตั้งค่าความปลอดภัยที่เหมาะสม ซึ่งสามารถลดขนาด image ได้ถึง 70–90% และทำให้ deploy ได้เร็วขึ้นและปลอดภัยขึ้น
สามมิติของการ Optimize
หัวข้อที่มีชื่อว่า “สามมิติของการ Optimize”| มิติ | เป้าหมาย | เทคนิคที่ครอบคลุม |
|---|---|---|
| ขนาด | Image เล็ก = pull เร็ว, ใช้พื้นที่น้อย | Multi-stage builds, base image เล็ก, .dockerignore |
| ความเร็วในการ build | Build ซ้ำๆ ได้เร็วขึ้น | การเรียงลำดับ layer cache, --mount=type=cache |
| ความปลอดภัย | ลด attack surface | User ที่ไม่ใช่ root, ไม่ฝัง secret, HEALTHCHECK, การ scan |
ก่อนและหลัง
หัวข้อที่มีชื่อว่า “ก่อนและหลัง”นี่คือการเปรียบเทียบที่สมจริง ฝั่งซ้ายคือ Node.js image แบบ single-stage ทั่วไป ฝั่งขวาคือแอปเดียวกันที่ build ด้วยเทคนิคในโมดูลนี้:
REPOSITORY TAG SIZEmyapp naive 1.21GBmyapp optimized 112MBลดลงถึง 10 เท่า โดยไม่มีการเปลี่ยนแปลงพฤติกรรมของ application — เปลี่ยนแค่ Dockerfile เท่านั้น
โมดูลนี้ครอบคลุมอะไรบ้าง
หัวข้อที่มีชื่อว่า “โมดูลนี้ครอบคลุมอะไรบ้าง”| บทเรียน | สิ่งที่จะได้เรียนรู้ |
|---|---|
| หน้านี้ | ทำไมต้อง Optimize, สามมิติ, ตัวอย่างขนาดก่อน/หลัง |
| Multi-stage builds | แยก build stage กับ runtime stage; COPY --from |
| การเรียงลำดับ cache | จัดลำดับ instruction เพื่อใช้ cache ได้สูงสุด; pin versions |
Base image เล็กและ .dockerignore | Alpine/distroless, ตัด noise ออกจาก build context |
| Security และ HEALTHCHECK | Non-root user, ไม่ฝัง secret, HEALTHCHECK, docker scout |
ลงมือทำ: ดูขนาด image
หัวข้อที่มีชื่อว่า “ลงมือทำ: ดูขนาด image”snippet ด้านล่าง build image สองอัน — อันหนึ่งใหญ่โต อีกอันกระทัดรัด — เพื่อให้เห็น output ของ docker images แบบเปรียบเทียบ
# Build a "before" image with unnecessary dev tools
# syntax=docker/dockerfile:1
FROM node:22 AS before
WORKDIR /app
RUN echo '{"name":"demo","version":"1.0.0"}' > package.json
RUN npm install --save-dev typescript eslint
RUN echo "console.log('hello');" > index.js
CMD ["node","index.js"]
# Build a "after" image with only what is needed
FROM node:22-alpine AS after
WORKDIR /app
RUN echo '{"name":"demo","version":"1.0.0"}' > package.json
RUN echo "console.log('hello');" > index.js
CMD ["node","index.js"]
# --- build both and compare sizes ---
# docker build --target before -t myapp:before .
# docker build --target after -t myapp:after .
# docker images myapp