跳至正文

🐳 什麼是Docker?

📖 定義

Docker是一個在稱為容器的隔離環境中執行應用程式的平台。容器將應用程式與其所有相依項目打包在一起,確保它在任何地方都能相同執行。它解決了「在我的機器上可以執行...」的問題,使開發、測試和部署變得簡單而一致。

🎯 透過類比理解

運輸貨櫃

將Docker比作物流中的貨櫃:

傳統運輸 (VM)
├─ 每個物品不同的包裝
├─ 尺寸/重量各異
├─ 運輸方式不同
└─ 低效、複雜

貨櫃運輸 (Docker)
├─ 標準化貨櫃
├─ 可容納任何貨物
├─ 船、卡車、火車通用
└─ 高效、簡單

Docker容器 = 標準化軟體套件

公寓 vs 獨棟房屋

虛擬機 (VM) = 獨棟房屋
├─ 每棟都需要完整基礎設施
├─ 土地、建築、水電全部獨立
├─ 成本高
└─ 啟動慢

Docker容器 = 公寓
├─ 共享基礎設施(土地、建築)
├─ 每個單元獨立
├─ 高效
└─ 快速入住

⚙️ 運作原理

1. Docker架構

┌─────────────────────────────────────┐
│ Docker客戶端 (CLI) │
│ docker run, docker build, etc. │
└───────────────┬─────────────────────┘
│ API呼叫
┌───────────────▼─────────────────────┐
│ Docker守護程序 │
│ 容器/映像管理 │
└───────────────┬─────────────────────┘

┌───────────────▼─────────────────────┐
│ 容器(執行中的應用程式) │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │App 1│ │App 2│ │App 3│ │
│ └─────┘ └─────┘ └─────┘ │
└─────────────────────────────────────┘

┌───────────────▼─────────────────────┐
│ 主機作業系統 (Linux核心) │
└─────────────────────────────────────┘

2. 映像 vs 容器

映像 = 藍圖、類別
├─ 唯讀
├─ 分層結構
├─ 可重用
└─ 例如:ubuntu、node、nginx

容器 = 執行實例、物件
├─ 從映像建立
├─ 可執行
├─ 隔離環境
└─ 可建立多個實例

關係:
映像 → 容器1
→ 容器2
→ 容器3

3. Dockerfile → 映像 → 容器

1. 編寫Dockerfile(配方)
├─ FROM node:18
├─ COPY package.json .
├─ RUN npm install
└─ CMD ["npm", "start"]

2. 建置映像(烹飪)
docker build -t my-app .

3. 執行容器(上菜)
docker run -p 3000:3000 my-app

💡 實際範例

編寫Dockerfile

# Node.js應用程式Dockerfile

# 基礎映像
FROM node:18-alpine

# 設定工作目錄
WORKDIR /app

# 複製package.json
COPY package*.json ./

# 安裝相依套件
RUN npm ci --only=production

# 複製原始碼
COPY . .

# 暴露埠號
EXPOSE 3000

# 環境變數
ENV NODE_ENV=production

# 容器啟動時執行的命令
CMD ["node", "server.js"]

建置和執行映像

# 1. 建置映像
docker build -t my-node-app:1.0 .
# -t: 標籤(名稱:版本)
# .: 使用目前目錄的Dockerfile

# 2. 檢查映像
docker images
# REPOSITORY TAG IMAGE ID CREATED
# my-node-app 1.0 abc123 2分鐘前

# 3. 執行容器
docker run -d \
--name my-app \
-p 3000:3000 \
-e NODE_ENV=production \
my-node-app:1.0

# -d: 背景執行
# --name: 容器名稱
# -p: 埠號對映(主機:容器)
# -e: 環境變數

# 4. 檢查執行中的容器
docker ps

# 5. 查看日誌
docker logs my-app

# 6. 存取容器內部
docker exec -it my-app sh

# 7. 停止容器
docker stop my-app

# 8. 刪除容器
docker rm my-app

# 9. 刪除映像
docker rmi my-node-app:1.0

Docker Compose(管理多個容器)

# docker-compose.yml

version: '3.8'

services:
# Web應用程式
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=database
depends_on:
- database
- redis
volumes:
- ./logs:/app/logs

# 資料庫
database:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=myapp
volumes:
- db-data:/var/lib/mysql
ports:
- "3306:3306"

# 快取
redis:
image: redis:7-alpine
ports:
- "6379:6379"

# Nginx(反向代理)
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web

volumes:
db-data:
# Docker Compose命令

# 啟動所有服務
docker-compose up -d

# 查看日誌
docker-compose logs -f web

# 重啟特定服務
docker-compose restart web

# 擴展服務
docker-compose up -d --scale web=3

# 停止並刪除所有服務
docker-compose down

# 刪除包含資料卷
docker-compose down -v

實戰範例:React + Node.js + MongoDB

# frontend/Dockerfile
FROM node:18-alpine AS build

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 使用Nginx提供靜態檔案
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
# backend/Dockerfile
FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

EXPOSE 5000
CMD ["node", "server.js"]
# docker-compose.yml
version: '3.8'

services:
frontend:
build:
context: ./frontend
ports:
- "80:80"
depends_on:
- backend

backend:
build:
context: ./backend
ports:
- "5000:5000"
environment:
- MONGO_URL=mongodb://mongodb:27017/myapp
depends_on:
- mongodb

mongodb:
image: mongo:6
volumes:
- mongo-data:/data/db
ports:
- "27017:27017"

volumes:
mongo-data:

管理資料卷

# 建立資料卷
docker volume create my-data

# 列出資料卷
docker volume ls

# 將資料卷掛載到容器
docker run -v my-data:/app/data my-app

# 掛載主機目錄(開發時有用)
docker run -v $(pwd):/app my-app

# 唯讀掛載
docker run -v $(pwd):/app:ro my-app

# 刪除資料卷
docker volume rm my-data

# 刪除所有未使用的資料卷
docker volume prune

.dockerignore檔案

# .dockerignore

# Git
.git
.gitignore

# Node
node_modules
npm-debug.log

# 環境設定
.env
.env.local

# 建置輸出
dist
build

# IDE
.vscode
.idea

# 測試
coverage
*.test.js

# 文件
README.md
docs/

🤔 常見問題

Q1. Docker vs 虛擬機?

A:

虛擬機 (VM)
┌─────────────────┐ ┌─────────────────┐
│ App A │ │ App B │
│ Libs │ │ Libs │
│ 客戶作業系統 │ │ 客戶作業系統 │
│ (Linux) │ │ (Ubuntu) │
└─────────────────┘ └─────────────────┘
┌─────────────────────────────────────┐
│ 虛擬機監控程式 (VMware等) │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 主機作業系統 │
└─────────────────────────────────────┘

Docker容器
┌───────┐ ┌───────┐ ┌───────┐
│ App A │ │ App B │ │ App C │
│ Libs │ │ Libs │ │ Libs │
└───────┘ └───────┘ └───────┘
┌─────────────────────────────┐
│ Docker引擎 │
└─────────────────────────────┘
┌─────────────────────────────┐
│ 主機作業系統 (Linux核心) │
└─────────────────────────────┘

比較:
VM Docker
大小 GB MB
啟動 分鐘 秒
隔離 完全 行程層級
效能 開銷大 接近原生
可攜性 低 高

Q2. 什麼是Docker映像層?

A:

FROM node:18          # 層1:基礎映像
WORKDIR /app # 層2:工作目錄
COPY package.json . # 層3:package.json
RUN npm install # 層4:安裝相依套件
COPY . . # 層5:原始碼
CMD ["node", "app.js"]# 層6:啟動命令

# 層快取
# - 未更改的層會被重用
# - 加快建置速度

# ✅ 好的做法:頻繁更改的內容放在後面
COPY package.json .
RUN npm install # 快取(如果package.json未更改)
COPY . . # 只有程式碼更改

# ❌ 不好的做法:頻繁更改的內容放在前面
COPY . . # 每次程式碼更改
RUN npm install # npm install會重新執行!

Q3. 什麼是容器編排?

A:

單一伺服器
┌─────────────────────┐
│ ┌───┐ ┌───┐ ┌───┐ │
│ │C1 │ │C2 │ │C3 │ │
│ └───┘ └───┘ └───┘ │
└─────────────────────┘

編排 (Kubernetes)
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Server1 │ │ Server2 │ │ Server3 │
│ ┌───┐ │ │ ┌───┐ │ │ ┌───┐ │
│ │C1 │ │ │ │C2 │ │ │ │C3 │ │
│ └───┘ │ │ └───┘ │ │ └───┘ │
└─────────┘ └─────────┘ └─────────┘

功能:
- 自動部署
- 擴展(自動擴容/縮容)
- 負載平衡
- 自我修復(容器死亡時重啟)
- 滾動更新

工具:
- Kubernetes(最流行)
- Docker Swarm
- Amazon ECS

Q4. 在開發環境中使用Docker?

A:

# 情境:團隊成員環境各不相同

# 成員A:macOS、Node 16
# 成員B:Windows、Node 18
# 成員C:Linux、Node 14

# 問題:「在我的機器上可以執行...」

# 解決方案:使用Docker統一環境

# docker-compose.yml
version: '3.8'
services:
app:
image: node:18
volumes:
- .:/app
working_dir: /app
command: npm run dev
ports:
- "3000:3000"

# 所有團隊成員使用相同環境
docker-compose up

# 優勢:
# 1. 無需安裝Node.js
# 2. 版本統一
# 3. 相依套件隔離
# 4. 環境設定共享

Q5. Docker安全性?

A:

# ✅ 安全最佳實務

# 1. 最小權限基礎映像
FROM node:18-alpine # ✅ 小而安全
FROM node:18 # ❌ 不必要的工具

# 2. 以非root使用者執行
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs # ✅ 最小化權限

# 3. 排除敏感資訊
# 新增到.dockerignore
.env
*.key
secrets/

# 4. 多階段建置(刪除不必要的工具)
FROM node:18 AS builder
WORKDIR /app
COPY . .
RUN npm run build

FROM node:18-alpine # 更小的映像
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/main.js"]

# 5. 掃描映像
docker scan my-image

# 6. 唯讀根檔案系統
docker run --read-only my-app

# 7. 資源限制
docker run --memory="512m" --cpus="1" my-app

🎓 下一步

理解Docker後,探索:

  1. 什麼是Git?(即將推出)- 版本控制和部署
  2. 什麼是Node.js?(即將推出)- 使用Docker部署Node應用程式
  3. 什麼是TDD? - 在Docker中執行測試

親自嘗試

# 1. 檢查Docker安裝
docker --version

# 2. Hello World
docker run hello-world

# 3. 執行Nginx
docker run -d -p 8080:80 nginx
# 造訪 http://localhost:8080

# 4. 檢查容器
docker ps

# 5. 停止並刪除
docker stop <container-id>
docker rm <container-id>

# 6. 容器化你的應用程式
# - 編寫Dockerfile
# - docker build
# - docker run

有用的命令

# 映像管理
docker pull nginx # 下載映像
docker images # 列出映像
docker rmi nginx # 刪除映像

# 容器管理
docker run # 執行容器
docker ps # 執行中的容器
docker ps -a # 所有容器
docker stop <id> # 停止容器
docker start <id> # 啟動容器
docker restart <id> # 重啟容器
docker rm <id> # 刪除容器

# 日誌和除錯
docker logs <id> # 查看日誌
docker logs -f <id> # 追蹤日誌
docker exec -it <id> sh # 存取容器shell
docker inspect <id> # 詳細資訊

# 清理
docker system prune # 刪除未使用的資源
docker system prune -a # 刪除所有未使用的資源
docker volume prune # 刪除未使用的資料卷

🎬 總結

Docker是現代開發的必備工具:

  • 容器:隔離的執行環境
  • 映像:應用程式套件
  • 可攜性:在任何地方都能相同執行
  • 高效性:輕量且快速

使用Docker解決「在我的機器上可以執行」的問題! 🐳✨