ข้ามไปยังเนื้อหา

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 อ่าน Dockerfile และสร้าง image ไวยากรณ์พื้นฐานคือ:

Terminal window
docker build [OPTIONS] PATH

อาร์กิวเมนต์ PATH (มักเป็น .) เรียกว่า build context — คือไดเรกทอรีที่ Docker ส่งให้ builder ไฟล์ใน build context ใช้กับ COPY ใน Dockerfile ได้ ไฟล์นอก build context เข้าถึงไม่ได้

Terminal window
docker build .

คำสั่งนี้ส่งไดเรกทอรีปัจจุบันเป็น build context หากไม่มี -t image ที่ได้จะไม่มีชื่อ พิสูจน์แล้ว: docker build -q . พิมพ์เพียง sha256:a225065a… และ image ปรากฏโดยมี <none> เป็น repository และ tag (image แบบ dangling ซึ่งแสดงด้วย docker image ls --filter dangling=true) คุณเกือบทุกครั้งจะเพิ่ม -t

flag -t กำหนด name:tag ที่มนุษย์อ่านได้ให้ image:

Terminal window
docker build -t myapp:1.0 .

รูปแบบเต็มของ reference คือ:

[registry/][owner/]name[:tag][@digest]

ตัวอย่าง:

myapp:1.0
myapp:latest
ghcr.io/acme/myapp:1.0
docker.io/library/node:24-alpine
alpine@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6

หากไม่ระบุส่วน tag Docker ใช้ :latest เป็นค่าเริ่มต้น

Terminal window
docker images

output จริงของ image ที่ build ข้างบนหลัง tag สองครั้ง (ตารางของ Engine 29 เพิ่มคอลัมน์ DISK USAGE และ CONTENT SIZE; docker image ls --format ให้ output ที่คงที่สำหรับสคริปต์):

IMAGE ID DISK USAGE CONTENT SIZE EXTRA
docker-b1-bt:1.0 a225065a711d 28.7MB 0B
docker-b1-bt:latest a225065a711d 28.7MB 0B

ทั้งสองชื่อแสดง image ID เดียวกัน DISK USAGE รวม node_modules 20 MB ที่ demo นี้คัดลอกเข้า image (ดูหัวข้อ .dockerignore)

docker tag เพิ่มชื่อหรือ tag ใหม่ให้ image ที่มีอยู่โดยไม่คัดลอก ทั้งสองชื่อชี้ไปที่ image ID เดียวกัน

Terminal window
# Promote a build image to a release name
docker tag myapp:1.0 myapp:stable
docker 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 ไม่ใช่ 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 3cd9c70ed14e
2.0 9adad49089a1
1.0 a225065a711d
latest a225065a711d # still the first build

latest ยังชี้ไปที่ image ที่เก่าที่สุด ถ้าคุณ push myapp:1.0 และ myapp:latest แยกกัน มันชี้ไปที่ layer ที่ต่างกันโดยสิ้นเชิงได้ ใน pipeline ของ production ให้ใช้ tag ที่ชัดเจนและมีความหมาย (เลขเวอร์ชัน, git SHA, วันที่) เสมอ เพื่อรู้ว่ากำลัง deploy อะไรอยู่ ถ้าต้องการ pin ระดับไบต์ให้ใช้ digest

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-digest
by-digest

image ที่ build ในเครื่องไม่มี digest จนกว่าจะ push ไปยัง registry

Dockerfile.multi
# @build
FROM alpine:3.24 AS base
ARG GREETING=hi
RUN echo "$GREETING from base" > /msg
FROM base AS test
RUN echo tested >> /msg
FROM base AS release
CMD ["cat", "/msg"]
$ docker build -f Dockerfile.multi --target release --build-arg GREETING=hello -t multi:rel .
$ docker run --rm multi:rel
hello from base # --build-arg overrode the ARG default
$ docker build -f Dockerfile.multi --target test -t multi:test .
$ docker run --rm multi:test cat /msg
hi from base
tested # 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 นี้

โดยค่าเริ่มต้น 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 done
.dockerignore
node_modules/
.git/
.env
*.log
dist/
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

Terminal window
# Build with a version tag
docker build -t myapp:1.0 .
# Also tag as latest
docker tag myapp:1.0 myapp:latest
# Inspect the result
docker images myapp

output ที่คาดหวัง: สองแถวที่มี 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.0

.dockerignore ที่ AI เขียน “เพื่อให้ context เล็ก” สำหรับ Dockerfile ที่ทำ COPY src/app.js ./app.js:

🐛 หา bug ในโค้ดนี้

.dockerignore
**/*.js
node_modules/
เฉลย

pattern นี้ตัดไฟล์ JavaScript ทั้งหมด ออกจาก context รวมถึงไฟล์ที่ Dockerfile ต้องใช้ การ build ล้มที่ COPY ด้วย error ที่พูดถึง ไฟล์ ไม่ใช่ไฟล์ ignore:

#7 ERROR: failed to calculate checksum of ref …: "/src/app.js": not found
ERROR: 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 ต้องใช้:

.dockerignore
node_modules/
.git/
.env
**/*.test.js
dist/

ความผิดพลาดตรงข้ามแย่กว่าเพราะเงียบ: ไม่มี .dockerignore บวก COPY . . คัดลอกไฟล์ .env (SECRET=<your-api-key>) เข้า image และ docker run … cat /app/.env พิมพ์ออกมา ให้ ignore ไฟล์ .env; ส่งความลับตอนรัน

docker build คือการจับมือสองขั้น: CLI แพ็ก context (ลบด้วย .dockerignore) แล้วส่งให้ builder; จากนั้น builder รัน Dockerfile กับ context นั้น เท่านั้น tag คือ ป้ายที่ย้ายได้บน image ID — หลายป้าย image เดียว; digest คือ content hash ของ image และย้ายไม่ได้ latest เป็นแค่อีกป้ายหนึ่งที่คุณต้องย้ายเอง

ทางเลือกข้อดีข้อเสีย
base image alpineimage เล็ก 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 พัง
  • ลืม 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 ตัวที่เหมาะ

  • “docker tag คัดลอก image” มันเพิ่มชื่อ ทั้งสองแถวแสดง ID a225065a711d และใช้พื้นที่ดิสก์เดียวกัน
  • “latest คือ image ใหม่ที่สุด” มันค้างที่ build แรกจนกว่าจะ tag ใหม่
  • “.dockerignore แค่ทำให้เร็วขึ้น” มันเปลี่ยนสิ่งที่ COPY . . ใส่ใน image ด้วย (.env และ node_modules หายจาก /app)
  • “Build context = ไดเรกทอรีของ Dockerfile” context คืออาร์กิวเมนต์ PATH; -f ชี้ที่อื่นได้
build context ใน docker build . คืออะไร?
docker tag myapp:1.0 myapp:stable ทำอะไร?
จุดประสงค์หลักของไฟล์ .dockerignore คืออะไร?
ข้อใดเกี่ยวกับ tag latest เป็นจริง?
docker build . โดยไม่มี -t เสร็จสำเร็จ คุณได้อะไร?
Dockerfile มี COPY src/app.js ./app.js ไฟล์มีอยู่จริงแต่ build บอก "/src/app.js": not found คุณตรวจอะไรก่อน?