Docker & CI — ส่ง Rust ขึ้น Production
ทำไม multi-stage Docker จึงสำคัญสำหรับ Rust
หัวข้อที่มีชื่อว่า “ทำไม multi-stage Docker จึงสำคัญสำหรับ Rust”Docker image ของ Node.js ต้องมี Node runtime ~180 MB (Alpine) เพียงเพื่อรัน Rust binary ไม่มี runtime dependency จึงสามารถ copy ไปไว้ใน image แบบ distroless หรือ scratch ได้เลย ผลคือ production image มักมีขนาดเพียง 5–30 MB
เคล็ดลับคือ multi-stage build: Rust toolchain เต็มรูปแบบใน builder stage และเฉพาะ compiled binary ใน final stage
เปรียบเทียบ Dockerfile
หัวข้อที่มีชื่อว่า “เปรียบเทียบ Dockerfile”# Node / TypeScript Dockerfile (multi-stage)FROM node:20-alpine AS builderWORKDIR /appCOPY package*.json ./RUN npm ciCOPY . .RUN npm run build
FROM node:20-alpine AS runtimeWORKDIR /appCOPY --from=builder /app/dist ./distCOPY --from=builder /app/node_modules ./node_modulesEXPOSE 3000CMD ["node", "dist/index.js"]# Final image: ~180 MB (Node runtime required)# Rust Dockerfile (multi-stage → distroless)FROM rust:1.80-slim AS builderWORKDIR /app# Cache dependency แยกจาก source codeCOPY Cargo.toml Cargo.lock ./RUN mkdir src && echo "fn main() {}" > src/main.rsRUN cargo build --release --lockedRUN rm -rf src
# Build source จริงCOPY src ./srcRUN touch src/main.rs && cargo build --release --locked
FROM gcr.io/distroless/cc-debian12 AS runtimeCOPY --from=builder /app/target/release/my-app /usr/local/bin/my-appEXPOSE 8080CMD ["/usr/local/bin/my-app"]# Final image: ~20 MB (ไม่ต้องมี runtime)อธิบาย trick การ cache dependency
หัวข้อที่มีชื่อว่า “อธิบาย trick การ cache dependency”pattern COPY สองขั้นตอนข้างต้นสำคัญมากสำหรับ rebuild ที่เร็ว:
# ขั้นตอน 1: copy เฉพาะ manifest และ build stub main.rs# Docker layer-cache สิ่งนี้ — invalidate เฉพาะเมื่อ Cargo.toml/Cargo.lock เปลี่ยนCOPY Cargo.toml Cargo.lock ./RUN mkdir src && echo "fn main() {}" > src/main.rsRUN cargo build --release --locked
# ขั้นตอน 2: copy source จริง — layer นี้ invalidate ทุกครั้งที่ code เปลี่ยน# แต่การ compile dependency ถูก cache ไว้แล้วCOPY src ./srcRUN touch src/main.rs && cargo build --release --lockedนี่เทียบเท่ากับ pattern ของ Node ที่ COPY package*.json ./ แล้ว RUN npm ci ก่อน COPY . .
GitHub Actions workflow
หัวข้อที่มีชื่อว่า “GitHub Actions workflow”name: CI
on: push: branches: [main, dev] pull_request: branches: [main]
env: CARGO_TERM_COLOR: always
jobs: check: name: Check, Lint & Test runs-on: ubuntu-latest
steps: - uses: actions/checkout@v4
- name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy
- name: Cache Cargo registry and build artifacts uses: Swatinem/rust-cache@v2
- name: Check formatting run: cargo fmt --check
- name: Clippy (lint) run: cargo clippy -- -D warnings
- name: Run tests run: cargo test --locked
- name: Build release binary run: cargo build --release --lockedเปรียบเทียบ Node CI
หัวข้อที่มีชื่อว่า “เปรียบเทียบ Node CI”# .github/workflows/ci.yml (Node / TypeScript)name: CIon: [push, pull_request]jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - run: npm ci - run: npx eslint src/ - run: npx prettier --check "src/**/*.ts" - run: npm test - run: npm run buildRust workflow ใช้ dtolnay/rust-toolchain (action ทางการ) และ Swatinem/rust-cache เพื่อ cache ~/.cargo registry และ target/ ระหว่าง run — เทียบเท่ากับ actions/setup-node กับ cache: 'npm'
เหล่านี้คือ Dockerfile และ YAML configuration ไม่มี browser Playground สำหรับ Docker build — ใช้ไฟล์เหล่านี้ใน project ของคุณและ push ไปยัง GitHub เพื่อ trigger Actions workflow