Build, Tag และ Build Context
วัดบน Docker Desktop 4.81.0 / Engine 29.6.1, Buildx v0.35.0-desktop.2, BuildKit v0.31.1 (macOS, linux/arm64), 2026-09-30 image ID และขนาดมาจากการรันนั้นและจะต่างบนเครื่องของคุณ
คำสั่ง docker build
หัวข้อที่มีชื่อว่า “คำสั่ง docker build”docker build อ่าน Dockerfile และสร้าง image ไวยากรณ์พื้นฐานคือ:
docker build [OPTIONS] PATHอาร์กิวเมนต์ PATH (มักเป็น .) เรียกว่า build context — คือไดเรกทอรีที่ Docker ส่งให้ builder ไฟล์ใน build context ใช้กับ COPY ใน Dockerfile ได้ ไฟล์นอก build context เข้าถึงไม่ได้
docker build .คำสั่งนี้ส่งไดเรกทอรีปัจจุบันเป็น build context หากไม่มี -t image ที่ได้จะไม่มีชื่อ พิสูจน์แล้ว: docker build -q . พิมพ์เพียง sha256:a225065a… และ image ปรากฏโดยมี <none> เป็น repository และ tag (image แบบ dangling ซึ่งแสดงด้วย docker image ls --filter dangling=true) คุณเกือบทุกครั้งจะเพิ่ม -t
ตั้ง tag ด้วย -t
หัวข้อที่มีชื่อว่า “ตั้ง tag ด้วย -t”flag -t กำหนด name:tag ที่มนุษย์อ่านได้ให้ image:
docker build -t myapp:1.0 .รูปแบบเต็มของ reference คือ:
[registry/][owner/]name[:tag][@digest]ตัวอย่าง:
myapp:1.0myapp:latestghcr.io/acme/myapp:1.0docker.io/library/node:24-alpinealpine@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6หากไม่ระบุส่วน tag Docker ใช้ :latest เป็นค่าเริ่มต้น
แสดงรายการ image ด้วย docker images
หัวข้อที่มีชื่อว่า “แสดงรายการ image ด้วย docker images”docker imagesoutput จริงของ image ที่ build ข้างบนหลัง tag สองครั้ง (ตารางของ Engine 29 เพิ่มคอลัมน์ DISK USAGE และ CONTENT SIZE; docker image ls --format ให้ output ที่คงที่สำหรับสคริปต์):
IMAGE ID DISK USAGE CONTENT SIZE EXTRAdocker-b1-bt:1.0 a225065a711d 28.7MB 0Bdocker-b1-bt:latest a225065a711d 28.7MB 0Bทั้งสองชื่อแสดง image ID เดียวกัน DISK USAGE รวม node_modules 20 MB ที่ demo นี้คัดลอกเข้า image (ดูหัวข้อ .dockerignore)
Tag ใหม่ด้วย docker tag
หัวข้อที่มีชื่อว่า “Tag ใหม่ด้วย docker tag”docker tag เพิ่มชื่อหรือ tag ใหม่ให้ image ที่มีอยู่โดยไม่คัดลอก ทั้งสองชื่อชี้ไปที่ image ID เดียวกัน
# Promote a build image to a release namedocker tag myapp:1.0 myapp:stabledocker tag myapp:1.0 ghcr.io/acme/myapp:1.0พิสูจน์แล้ว: docker tag $ID docker-b1-bt:1.0 และ docker tag $ID docker-b1-bt:latest ให้สองแถวที่มี ID เหมือนกัน a225065a711d และไม่ใช้ดิสก์เพิ่ม
ความหมายของ latest
หัวข้อที่มีชื่อว่า “ความหมายของ latest”latest ไม่ใช่ tag วิเศษที่แปลว่า “ใหม่ที่สุด” มันเป็นแค่สตริงเริ่มต้นที่ Docker ใช้เมื่อคุณไม่ระบุ tag ไม่มีอะไรย้ายมันให้คุณ พิสูจน์แล้ว: หลัง build 1.0 (tag latest ด้วย) แล้ว build 2.0 และ 3.0 ที่เปลี่ยนแปลง โดยใช้แค่ -t …:2.0/:3.0:
$ docker image ls docker-b1-bt --format '{{.Tag}} {{.ID}}'3.0 3cd9c70ed14e2.0 9adad49089a11.0 a225065a711dlatest a225065a711d # still the first buildlatest ยังชี้ไปที่ image ที่เก่าที่สุด ถ้าคุณ push myapp:1.0 และ myapp:latest แยกกัน มันชี้ไปที่ layer ที่ต่างกันโดยสิ้นเชิงได้ ใน pipeline ของ production ให้ใช้ tag ที่ชัดเจนและมีความหมาย (เลขเวอร์ชัน, git SHA, วันที่) เสมอ เพื่อรู้ว่ากำลัง deploy อะไรอยู่ ถ้าต้องการ pin ระดับไบต์ให้ใช้ digest
Digest เทียบกับ tag
หัวข้อที่มีชื่อว่า “Digest เทียบกับ tag”tag ย้ายได้; digest (@sha256:…) ระบุเนื้อหา docker buildx imagetools inspect alpine:3.24 พิมพ์ Digest: sha256:294b683c… และ docker image ls --digests alpine แสดงค่าเดียวกันสำหรับ tag 3 และ 3.24 (สองชื่อ image เดียว) รันด้วย digest ได้และไม่มีทางเปลี่ยน:
$ docker run --rm alpine@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 echo by-digestby-digestimage ที่ build ในเครื่องไม่มี digest จนกว่าจะ push ไปยัง registry
ตัวเลือก docker build เพิ่มเติม
หัวข้อที่มีชื่อว่า “ตัวเลือก docker build เพิ่มเติม”# @buildFROM alpine:3.24 AS baseARG GREETING=hiRUN echo "$GREETING from base" > /msg
FROM base AS testRUN echo tested >> /msg
FROM base AS releaseCMD ["cat", "/msg"]$ docker build -f Dockerfile.multi --target release --build-arg GREETING=hello -t multi:rel .$ docker run --rm multi:relhello from base # --build-arg overrode the ARG default$ docker build -f Dockerfile.multi --target test -t multi:test .$ docker run --rm multi:test cat /msghi from basetested # the `test` stage has the extra line; release does not-f <file>เลือก Dockerfile อื่นที่ไม่ใช่./Dockerfile(context ยังเป็นPATH)--target <stage>หยุดที่ stage นั้นของ Dockerfile แบบ multi-stage--build-arg NAME=valueตั้งARGสำหรับการ build นี้
เร่ง context ด้วย .dockerignore
หัวข้อที่มีชื่อว่า “เร่ง context ด้วย .dockerignore”โดยค่าเริ่มต้น docker build . ส่งทั้งไดเรกทอรีปัจจุบันให้ builder — รวมถึง node_modules/, .git/, fixture ของการทดสอบ และความลับในเครื่อง ไฟล์ .dockerignore ตัด path ออกจาก build context ซึ่ง:
- เร่ง build (ข้อมูลที่ต้องส่งน้อยลง)
- ป้องกันการ
COPYความลับเข้า image โดยไม่ตั้งใจ
วัดจากไดเรกทอรีที่มี node_modules/ 20 MB, .env และ src/app.js ตัวเล็ก build ด้วย COPY . .:
# no .dockerignore#6 [internal] load build context#6 transferring context: 20.01MB 0.2s done# with the .dockerignore below#5 transferring context: 152B donenode_modules/.git/.env*.logdist/coverage/context ลดจาก 20.01 MB เหลือ 152 ไบต์ (บรรทัด #3 transferring context: 71B เพิ่มเติมคือ BuildKit อ่านไฟล์ .dockerignore เอง) และเนื้อหา image เปลี่ยนด้วย: ls -a /app ใน image ที่ build แล้วแสดง .env, Dockerfile, node_modules, src เมื่อไม่มีไฟล์ ignore และมีเพียง .dockerignore, Dockerfile, src เมื่อมี
ไวยากรณ์ใกล้เคียง .gitignore แต่ไม่เหมือนกันทุกประการ: pattern จับคู่เทียบกับราก context, *.js ตรงเฉพาะไฟล์ระดับราก (ดู bug ด้านล่าง) และ **/*.js ตรงทุกความลึก เอกสารอ้างอิงคือ .dockerignore file
รวมทุกอย่างเข้าด้วยกัน
หัวข้อที่มีชื่อว่า “รวมทุกอย่างเข้าด้วยกัน”# Build with a version tagdocker build -t myapp:1.0 .
# Also tag as latestdocker tag myapp:1.0 myapp:latest
# Inspect the resultdocker images myappoutput ที่คาดหวัง: สองแถวที่มี image ID เดียวกัน ตามที่แสดงข้างบน
ลงมือฝึก
หัวข้อที่มีชื่อว่า “ลงมือฝึก”# syntax=docker/dockerfile:1
FROM alpine:3.24
LABEL maintainer="[email protected]"
WORKDIR /app
RUN echo "v1.0" > version.txt
CMD ["cat", "version.txt"]
# --- build, tag, and inspect ---
# docker build -t myapp:1.0 .
# docker tag myapp:1.0 myapp:latest
# docker images myapp
# docker run --rm myapp:1.0Reading AI-generated code
หัวข้อที่มีชื่อว่า “Reading AI-generated code”.dockerignore ที่ AI เขียน “เพื่อให้ context เล็ก” สำหรับ Dockerfile ที่ทำ COPY src/app.js ./app.js:
🐛 หา bug ในโค้ดนี้
**/*.jsnode_modules/เฉลย
pattern นี้ตัดไฟล์ JavaScript ทั้งหมด ออกจาก context รวมถึงไฟล์ที่ Dockerfile ต้องใช้ การ build ล้มที่ COPY ด้วย error ที่พูดถึง ไฟล์ ไม่ใช่ไฟล์ ignore:
#7 ERROR: failed to calculate checksum of ref …: "/src/app.js": not foundERROR: failed to build: failed to solve: failed to compute cache key: … "/src/app.js": not foundสองบทเรียน ข้อแรก เมื่อ COPY บอก “not found” สำหรับไฟล์ที่มีอยู่บนดิสก์ ให้อ่าน .dockerignore ก่อนอย่างอื่น ข้อสอง *.js จะไม่ ทำให้เกิดเรื่องนี้: ด้วย pattern ที่สั้นกว่า build เดียวกันสำเร็จ เพราะ * ไม่ข้ามไดเรกทอรี (มันตรงแค่ไฟล์แบบ ./app.js ที่ราก) **/*.js ของ AI ต่างหากที่ตรง src/app.js ให้ ignore สิ่งที่ไม่ควรถูกส่งออกไป (test, build output) ไม่ใช่ซอร์สที่ image ต้องใช้:
node_modules/.git/.env**/*.test.jsdist/ความผิดพลาดตรงข้ามแย่กว่าเพราะเงียบ: ไม่มี .dockerignore บวก COPY . . คัดลอกไฟล์ .env (SECRET=<your-api-key>) เข้า image และ docker run … cat /app/.env พิมพ์ออกมา ให้ ignore ไฟล์ .env; ส่งความลับตอนรัน
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”docker build คือการจับมือสองขั้น: CLI แพ็ก context (ลบด้วย .dockerignore) แล้วส่งให้ builder; จากนั้น builder รัน Dockerfile กับ context นั้น เท่านั้น tag คือ ป้ายที่ย้ายได้บน image ID — หลายป้าย image เดียว; digest คือ content hash ของ image และย้ายไม่ได้ latest เป็นแค่อีกป้ายหนึ่งที่คุณต้องย้ายเอง
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”| ทางเลือก | ข้อดี | ข้อเสีย |
|---|---|---|
base image alpine | image เล็ก pull เร็ว | ใช้ musl libc ทำให้ native binary หรือ npm package บางตัว compile ไม่ผ่านหรือพังตอนรัน |
base image debian/เต็ม | มีเครื่องมือครบ เข้ากันกับ glibc สูง | image ใหญ่กว่ามาก push/pull ช้ากว่า |
tag ด้วยเวอร์ชัน (1.4.2) | อ่านง่าย rollback ง่าย | tag ถูก push ซ้ำและเปลี่ยนได้ |
pin ด้วย digest (@sha256:…) | ไม่เปลี่ยน reproducible | อ่านยาก ต้องอัปเดตด้วยความตั้งใจ |
.dockerignore กว้าง | context เล็ก (20.01 MB → 152 B) ไม่มีไฟล์รั่ว | pattern กว้างเกินทำให้ COPY พัง |
Common mistakes
หัวข้อที่มีชื่อว่า “Common mistakes”- ลืม pin tag ของ image (ปล่อยเป็น
latestหรือไม่ระบุ tag เลย) ทำให้ build วันนี้กับพรุ่งนี้ได้ image ต่างกัน - ไม่มี
.dockerignoreทำให้ build context บวม build ช้าลง และเสี่ยงความลับรั่วเข้า image - รัน
docker buildโดยไม่ใช้-tเลย จบด้วย image ไม่มีชื่อที่ต้องไล่หาด้วย ID ภายหลัง - คิดว่า
latestตาม build ใหม่ล่าสุดของคุณ - เขียน
*.extใน.dockerignoreแล้วคาดว่าจะตรงในไดเรกทอรีย่อย
image ทางการอย่าง node และ postgres เผยแพร่หลาย tag ต่อหนึ่ง release (เวอร์ชัน, -alpine, -slim) เป็น image แบบหลาย platform ดูรายการ tag บน Docker Hub แล้ว pin ตัวที่เหมาะ
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “
docker tagคัดลอก image” มันเพิ่มชื่อ ทั้งสองแถวแสดง IDa225065a711dและใช้พื้นที่ดิสก์เดียวกัน - “
latestคือ image ใหม่ที่สุด” มันค้างที่ build แรกจนกว่าจะ tag ใหม่ - “
.dockerignoreแค่ทำให้เร็วขึ้น” มันเปลี่ยนสิ่งที่COPY . .ใส่ใน image ด้วย (.envและnode_modulesหายจาก/app) - “Build context = ไดเรกทอรีของ Dockerfile” context คืออาร์กิวเมนต์
PATH;-fชี้ที่อื่นได้