Docker Image Concept
ถ้าคุณเข้าใจความต่างระหว่าง image กับ container และเข้าใจเรื่อง layer ได้ชัด คุณจะอ่าน Dockerfile เป็น วางแผน build ได้ดี และแก้ปัญหา runtime ได้ง่ายขึ้นมาก
ภาพจำ: image คือชุด layer ที่ไม่แก้ทับ ส่วน container คือ runtime instance ที่มี writable layer เพิ่มด้านบน
ภาพจำสำคัญบทนี้
จำให้แม่นว่า image คือแม่พิมพ์ที่ไม่แก้ทับ ส่วน container คือของที่ถูกใช้งานจริงและมีชั้นเขียนเฉพาะตัว
ส่วนที่ 1
เริ่มจากภาพใหญ่: image คือแบบ, container คือของที่ถูกใช้งานจริง
ให้นึกถึงงานสร้างบ้าน Image คือแบบบ้านที่ล็อกสเปกทุกอย่างไว้แล้ว เช่น โครงสร้าง วัสดุ และตำแหน่งห้อง ส่วน Container คือบ้านที่สร้างจากแบบนั้นแล้วมีคนเข้าไปอยู่จริง มีไฟเปิดปิด มีของวางเพิ่ม และมีการเปลี่ยนแปลงระหว่างใช้งาน จุดสำคัญคือเราสร้างบ้านได้หลายหลังจากแบบเดียวกัน เช่นเดียวกับการรันหลาย container จาก image เดียวกัน
ประเด็นที่ 1
Image = ต้นฉบับที่ใช้ซ้ำได้และแจกจ่ายได้
ประเด็นที่ 2
Container = instance ที่กำลังรันจริงและมี state ระหว่างใช้งาน
ประเด็นที่ 3
หนึ่ง image สามารถสร้างได้หลาย container พร้อมกัน
ส่วนที่ 2
Dockerfile สร้าง NestJS image ได้อย่างไร
ถ้าคุณไม่เคยใช้ NestJS หรือ Docker มาก่อน ให้มอง section นี้เป็นคู่มือเริ่มจากศูนย์: เตรียมเครื่องมือให้ครบก่อน, เช็กว่าเครื่องพร้อม, สร้างโปรเจกต์ NestJS ครั้งแรก, แล้วค่อยเขียน Dockerfile เพื่อเปลี่ยนโปรเจกต์นั้นให้เป็น image ที่รันได้ทุกเครื่อง แนวคิดสำคัญคือ Dockerfile คือสูตรที่บอกชัดว่าแอปต้องใช้อะไรบ้างและเริ่มต้นยังไง จึงลดปัญหา "เครื่องฉันรันได้ แต่เครื่องอื่นรันไม่ได้"
ตัวอย่างนี้ทำครบตั้งแต่ติดตั้ง dependency, build TypeScript ไปเป็น dist, และสั่งรัน NestJS app ผ่าน dist/main.js
หลังสร้างและบันทึก Dockerfile แล้ว ให้รันคำสั่งนี้ที่ root ของโปรเจกต์เพื่อสร้าง image ชื่อ nest-image-lab:1.0
- เตรียมเครื่องมือก่อนเริ่ม: ต้องมี Node.js LTS, pnpm, Nest CLI, และ Docker Desktop (macOS/Windows) หรือ Docker Engine (Linux)
- แนวทางติดตั้งแบบสั้น: macOS แนะนำใช้ Homebrew, Windows แนะนำใช้ winget/choco + Docker Desktop, Linux แนะนำใช้ apt/yum ตามดิสโทรและเปิดบริการ Docker
- เช็กว่าเครื่องพร้อม: รัน node -v, pnpm -v, nest -v, docker --version และ docker info ให้ผ่านก่อนเริ่มทำโปรเจกต์
- สร้างโปรเจกต์ครั้งแรก: nest new nest-image-lab --package-manager pnpm แล้วเลือกค่า default ของ Nest CLI ได้เลย
- ทดสอบก่อน dockerize: cd nest-image-lab แล้วรัน pnpm start:dev จากนั้นเปิด http://localhost:3000 ให้เห็น Hello World! ก่อน
- เขียน Dockerfile ทีละบรรทัด: FROM=เลือกรันไทม์, WORKDIR=โฟลเดอร์ทำงาน, COPY=คัดลอกไฟล์, RUN=ติดตั้ง/บิลด์, EXPOSE=พอร์ตแอป, CMD=คำสั่งเริ่มต้นของ container
ส่วนที่ 3
Image เป็น immutable หมายถึงอะไร
Immutable แปลว่าเมื่อ image ถูกสร้างแล้ว ชั้นข้อมูลของมันจะไม่ถูกแก้ทับตรง ๆ ถ้าคุณอยากเปลี่ยน dependency หรือไฟล์แอป คุณต้อง build image ใหม่เป็นเวอร์ชันใหม่แทน แนวคิดนี้ทำให้สภาพแวดล้อมคาดเดาได้และทำงานซ้ำได้เหมือนเดิมทั้งทีม
- ลดปัญหาเครื่องฉันรันได้แต่เครื่องอื่นรันไม่ได้
- เวอร์ชันชัดเจน ย้อนกลับ image เดิมได้ง่าย
- ปลอดภัยต่อการ deploy เพราะต้นฉบับไม่ถูกแก้กลางทาง
ส่วนที่ 4
Layer-based filesystem: ชั้นอ่านอย่างเดียว + ชั้นเขียนของ container
Image ถูกประกอบจากหลาย layer ที่เป็น read-only ซ้อนกันเป็นชั้น ๆ ตอนรัน Docker จะเพิ่ม writable layer ใหม่ไว้ด้านบนสำหรับ container นั้นโดยเฉพาะ เมื่อมีการแก้ไฟล์ Docker ใช้แนวคิด copy-on-write คือคัดลอกไฟล์ที่ต้องแก้ขึ้นมาชั้นบนก่อนแล้วค่อยแก้ โดยไม่แตะ image layer เดิม
ประเด็นที่ 1
Image layers = read-only และแชร์ร่วมกันได้
ประเด็นที่ 2
Container writable layer = state ชั่วคราวของ container นั้น
ประเด็นที่ 3
copy-on-write = แก้เฉพาะไฟล์ที่ถูกแตะจริง ลดการคัดลอกเกินจำเป็น
ส่วนที่ 5
ภายใน docker run เกิดอะไรขึ้นทีละขั้น
เมื่อสั่ง run Docker จะหา image ตามชื่อและแท็ก จากนั้นประกอบ root filesystem โดยรวม image layers เข้าด้วยกัน แล้ววาง writable layer ของ container ไว้ชั้นบนสุด ต่อด้วยตั้งค่า network/namespace/cgroup และสุดท้ายเริ่ม process หลักตาม CMD หรือคำสั่งที่คุณ override
รัน NestJS app บนพอร์ต 3000 ภายใน container แล้ว publish ออกมาที่พอร์ต 3000 ของเครื่องเรา
- ระบุและค้นหา image ตามชื่อและแท็ก
- ประกอบ union filesystem จาก image layers
- เชื่อม writable layer ของ container
- เริ่ม process หลักของแอป
ส่วนที่ 6
Mini Lab: สร้าง NestJS project แล้ว build image ตั้งแต่ศูนย์
แล็บนี้ออกแบบสำหรับมือใหม่ที่ยังไม่เคยใช้เลย คุณจะทำครบทั้งวงจร: เริ่มจากสร้างโปรเจกต์ด้วย Nest CLI, เพิ่ม Dockerfile, build image, run container, ทดสอบผลลัพธ์ และรู้วิธีแก้ปัญหาพื้นฐานที่เจอบ่อยที่สุด
sequence เดียวที่ copy-run ได้จริงสำหรับมือใหม่: ตั้งแต่ nest new จนทดสอบ container สำเร็จ
- Step 1: สร้างโปรเจกต์ด้วย nest new nest-image-lab --package-manager pnpm และเลือกค่า default ที่ CLI แนะนำ
- Step 2: เข้าโฟลเดอร์ด้วย cd nest-image-lab แล้วทดสอบ local ด้วย pnpm start:dev
- Step 3: สร้างไฟล์ Dockerfile ใน root ของโปรเจกต์ แล้ววางเนื้อหาตามตัวอย่างใน section ก่อนหน้า
- Step 4: build image ด้วย docker build -t nest-image-lab:1.0 .
- Step 5: run container ด้วย docker run --rm --name nest-image-demo -p 3000:3000 nest-image-lab:1.0
- Step 6: เปิด browser ที่ http://localhost:3000 หรือใช้ curl เพื่อตรวจว่าได้ Hello World!
- ปัญหาที่เจอบ่อย: port 3000 ชน ให้เปลี่ยนเป็น -p 3001:3000 หรือปิดโปรเซสเดิมก่อน
- ปัญหาที่เจอบ่อย: nest command not found ให้ติดตั้ง Nest CLI ใหม่ด้วย pnpm add -g @nestjs/cli
- ปัญหาที่เจอบ่อย: Docker daemon not running ให้เปิด Docker Desktop หรือ start service ของ Docker ใน Linux
- ปัญหาที่เจอบ่อย: pnpm-lock.yaml ไม่ตรงกับ package.json ให้รัน pnpm install แล้ว build image ใหม่