กลับหน้าหลัก
ภาพปกบทความ คู่มือการ Deploy Astro ด้วย Docker & Docker Compose
DevOps#Docker#Nginx#Deployment#CI/CD

คู่มือการ Deploy Astro ด้วย Docker & Docker Compose

เมื่อจะนำเว็บที่สร้างด้วย Astro ขึ้นใช้งานจริง (Production) วิธีที่นิยมและคุ้มค่าที่สุดคือ build เว็บให้เป็นไฟล์ Static (HTML, CSS, JS) แล้วเสิร์ฟผ่านเว็บเซิร์ฟเวอร์น้ำหนักเบาอย่าง Nginx ภายใน Docker Container ครับ — บทความนี้จะอธิบายตั้งแต่แนวคิดไปจนถึงไฟล์ตั้งค่าจริงที่ใช้ในโปรเจกต์นี้

NOTE

ทำไมต้อง Static? เพราะ Astro เป็น Zero-JS by default เว็บที่ build ออกมาจึงเป็นไฟล์ HTML ธรรมดาที่ Nginx เสิร์ฟได้เร็วมาก ไม่ต้องรัน Node.js เบื้องหลังตลอดเวลา ทำให้ปลอดภัยกว่าและกิน RAM น้อยกว่า


1. แนวคิด: Multi-stage Build

หัวใจของการ deploy นี้คือเทคนิค Multi-stage Build ที่แยกขั้นตอน “build” ออกจาก “การเสิร์ฟจริง” เพื่อให้ Image สุดท้ายมีขนาดเล็กที่สุด

StageBase Imageหน้าที่อยู่ใน Image สุดท้าย?
Buildnode:22-alpineติดตั้ง dependencies + รัน npm run build ได้ไฟล์ static ที่ /app/dist❌ ถูกทิ้ง
Productionnginx:alpineดึงเฉพาะโฟลเดอร์ dist มาเสิร์ฟ

ผลลัพธ์: Node.js, node_modules และ source code ไม่ติดไปกับ Image จริง เหลือแค่ Nginx + ไฟล์ static ขนาดไม่กี่ MB เท่านั้น


2. Dockerfile

# Stage 1: Build the Astro static site
FROM node:22-alpine AS builder
WORKDIR /app

# คัดลอกไฟล์ package ก่อน เพื่อให้ Docker cache ชั้น dependencies
COPY package*.json ./
RUN npm ci

# คัดลอกโค้ดที่เหลือแล้ว build
COPY . .
RUN npm run build

# Stage 2: Serve using Nginx
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

TIP

การ COPY package*.json ก่อน แล้วค่อย COPY . . เป็นเทคนิคใช้ Layer Cache — ถ้าแก้แค่โค้ด (ไม่แตะ dependencies) Docker จะข้ามขั้นตอน npm ci ที่ช้า ทำให้ rebuild เร็วขึ้นมาก

ทำไมใช้ npm ci แทน npm install? เพราะ npm ci ติดตั้งตาม package-lock.json แบบเป๊ะ ๆ ทำให้ build ได้ผลเหมือนกันทุกครั้ง (Reproducible) เหมาะกับ CI/CD


3. docker-compose.yml

services:
  kb-blog:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: pcpkb-blog
    ports:
      - "8080:80"
    restart: unless-stopped

อธิบายแต่ละบรรทัด:

  • build.context: . — สั่ง build จาก Dockerfile ในโฟลเดอร์ปัจจุบัน
  • ports: "8080:80" — map พอร์ต 8080 ของเครื่อง → พอร์ต 80 ใน container (เข้าผ่าน http://localhost:8080)
  • restart: unless-stopped — ให้ container กลับมารันเองอัตโนมัติเมื่อเครื่อง/Docker รีสตาร์ต (เว้นแต่เราสั่งหยุดเอง)

4. nginx.conf (จุดที่มักลืม)

สำหรับ Single Page ทั่วไปอาจไม่ต้องตั้งค่าอะไรมาก แต่ของเรามีจุดสำคัญที่ทำให้ทำงานถูกต้อง:

server {
    listen 80;
    root /usr/share/nginx/html;
    index index.html;

    # เปิด Gzip ลดขนาดไฟล์ที่ส่งให้ผู้ใช้
    gzip on;
    gzip_types text/css application/javascript application/xml;

    location / {
        try_files $uri $uri/ =404;
    }

    # Cache ไฟล์ static ยาว 1 ปี
    location ~* \.(?:css|js|png|svg|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, no-transform";
    }

    # หน้า 404 ที่ Astro สร้างไว้
    error_page 404 /404.html;
}

IMPORTANT

try_files $uri $uri/ =404; สำคัญมากกับ Astro เพราะ build ออกมาเป็นโฟลเดอร์ (เช่น /kb/welcome/index.html) บรรทัดนี้ช่วยให้เข้า URL แบบ /kb/welcome/ แล้วเจอ index.html ข้างในได้ถูกต้อง


5. คำสั่งที่ใช้บ่อย

NOTE

Docker เวอร์ชันใหม่ใช้ docker compose (เว้นวรรค) ส่วน docker-compose (มีขีด) คือเวอร์ชันเก่า v1 ใช้แทนกันได้

# build แล้วรันขึ้นระบบแบบ background (-d)
docker compose up -d --build

# ดูสถานะ container
docker compose ps

# ดู log แบบ real-time
docker compose logs -f

# หยุดและลบ container
docker compose down
สถานการณ์คำสั่ง
แก้โค้ดแล้วอยาก deploy ใหม่docker compose up -d --build
เว็บล่ม อยากดูสาเหตุdocker compose logs -f
ปิดเว็บชั่วคราวdocker compose stop
ลบทิ้งทั้งหมดdocker compose down

💡 สรุปจำง่าย

  1. Build → Serve แยกกัน: ใช้ Node build เสร็จแล้วโยน static ให้ Nginx เสิร์ฟ
  2. Multi-stage = Image เล็ก: ทิ้ง Node.js/source ออก เหลือแค่ Nginx + dist
  3. npm ci + cache layer: build ซ้ำได้ผลเดิม และเร็วขึ้น
  4. try_files + 404.html: ทำให้ routing ของ Astro ทำงานถูกบน Nginx
  5. คำสั่งหลักจำแค่ตัวเดียว: docker compose up -d --build

ไฟล์ Dockerfile, docker-compose.yml และ nginx.conf ตัวจริงทั้งหมดอยู่ที่รากของโปรเจกต์นี้ครับ — แก้ตามต้องการแล้วสั่ง docker compose up -d --build ได้เลย!