อย่าใช้เมื่อ: ปัญหาอยู่ที่ runtime orchestration (k8s manifests, compose networking) ไม่ใช่ตัว image เอง
-
อ่านบริบทก่อนแก้ — cat Dockerfile .dockerignore + ดู lockfile (package-lock.json / go.mod / requirements.txt / pom.xml) เพื่อรู้ภาษา/runtime. รัน docker images <name> เช็ค size ปัจจุบันเป็น baseline. ถ้ามี image อยู่แล้ว: docker history <image> --no-trunc | sort -k1 หา layer ที่ใหญ่ที่สุดก่อน
-
Multi-stage split — แยก builder (มี compiler/dev deps/SDK) ออกจาก runtime (มีแค่ binary/artifact + runtime deps). final stage ต้อง COPY --from=builder เฉพาะ output ที่จำเป็น เช่น:
- Go: copy แค่ binary → runtime ใช้
scratch หรือ distroless/static
- Node: build ใน
node:20 → copy dist/ + node_modules (prod เท่านั้น) → runtime node:20-slim หรือ distroless/nodejs20
- Python: build wheels ใน builder →
pip install --no-index ใน runtime จาก wheels
- ตั้งชื่อ stage ชัด (
AS builder, AS runtime) — ห้ามมี stage ลอยที่ไม่ถูก copy
-
เลือก + pin base image — เลือกตามลำดับ: distroless (no shell, เล็กสุด, ปลอดภัยสุด) > alpine (มี shell, glibc-incompat เสี่ยง) > -slim (debian, glibc ครบ). pin ด้วย digest ไม่ใช่ tag ลอย:
FROM node:20-slim@sha256:<digest>
หา digest: docker buildx imagetools inspect node:20-slim. tag เปลี่ยนเงียบได้ digest ไม่เปลี่ยน → reproducible build
-
Layer ordering + cache mounts — เรียงจาก "เปลี่ยนน้อย → เปลี่ยนบ่อย": copy manifest+lockfile ก่อน install, แล้วค่อย COPY . .:
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY . .
ใช้ --mount=type=cache (BuildKit) สำหรับ package cache: npm /root/.npm, pip /root/.cache/pip, go /root/.cache/go-build+/go/pkg/mod, apt /var/cache/apt. รวม RUN apt-get update && apt-get install -y --no-install-recommends ... && rm -rf /var/lib/apt/lists/* ใน RUN เดียว
-
Non-root + harden — สร้าง user แล้วสลับก่อน CMD:
RUN groupadd -r app && useradd -r -g app -u 10001 app
USER 10001
distroless ใช้ USER nonroot หรือ :nonroot tag. เพิ่ม HEALTHCHECK, ใช้ exec-form CMD ["app"] (ไม่ใช่ shell-form), ตั้ง WORKDIR ที่ user เขียนได้. caps/read-only fs บังคับตอน runtime: --read-only --cap-drop=ALL --security-opt=no-new-privileges (เขียนไว้ใน compose/k8s + comment ใน Dockerfile)
-
.dockerignore + ลบ build artifacts — สร้าง/แก้ .dockerignore ตัด .git, node_modules, *.md, .env*, dist, __pycache__, target, coverage, *.log, .DS_Store, secrets. ตรวจ final stage ห้ามมี: compiler, package-manager cache, .env, private key, .git. secret ที่ใช้ตอน build → ใช้ RUN --mount=type=secret,id=... ไม่ใช่ ARG/COPY (ARG ติดใน history)
-
Scan + วัด diff — build แล้ว scan:
docker build -t app:new .
trivy image --severity HIGH,CRITICAL app:new
docker images app:new --format '{{.Size}}'
รายงาน before/after: size (MB), build time (cold + warm cache), CVE count. แก้ HIGH/CRITICAL ที่มาจาก base image โดยขยับ base version ก่อน