DevOps

Docker 容器化部署完整指南

從基礎概念到實戰應用,學習Docker容器化技術,包含映像檔建立、容器管理、Docker Compose和生產環境部署。

黃工程師
20分鐘閱讀
#Docker#容器化#DevOps#部署

# Docker 容器化部署完整指南

Docker已經成為現代軟體開發和部署的標準工具。本文將帶你從零開始掌握Docker的核心概念和實戰應用。

## 1. Docker 基礎概念

### 什麼是Docker?

Docker是一個開源的容器化平台,允許開發者將應用程式和其依賴項打包到輕量級的容器中,確保在任何環境中都能一致運行。

### 核心概念

- **映像檔(Image)**:包含應用程式和運行環境的唯讀模板
- **容器(Container)**:映像檔的運行實例
- **Dockerfile**:定義映像檔內容的腳本
- **Registry**:儲存和分發映像檔的服務

## 2. Docker 安裝與設置

### 安裝Docker

bash
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install docker.io
sudo systemctl start docker
sudo systemctl enable docker

# macOS
brew install --cask docker

# Windows
# 下載並安裝 Docker Desktop


### 驗證安裝

bash
docker --version
docker run hello-world


## 3. 基本Docker命令

### 映像檔管理

bash
# 搜尋映像檔
docker search nginx

# 拉取映像檔
docker pull nginx:latest
docker pull ubuntu:20.04

# 列出本地映像檔
docker images
docker image ls

# 刪除映像檔
docker rmi nginx:latest
docker image rm ubuntu:20.04

# 強制刪除映像檔
docker rmi -f nginx:latest


### 容器管理

bash
# 運行容器
docker run nginx
docker run -d nginx # 背景運行
docker run -p 8080:80 nginx # 端口映射
docker run -v /host/path:/container/path nginx # 卷掛載
docker run --name my-nginx nginx # 指定容器名稱

# 列出運行中的容器
docker ps

# 列出所有容器
docker ps -a

# 停止容器
docker stop container_id
docker stop container_name

# 啟動容器
docker start container_id

# 重啟容器
docker restart container_id

# 刪除容器
docker rm container_id
docker rm -f container_id # 強制刪除運行中的容器

# 查看容器日誌
docker logs container_id
docker logs -f container_id # 即時查看日誌

# 進入容器
docker exec -it container_id /bin/bash
docker exec -it container_id /bin/sh


## 4. Dockerfile 實戰

### 基本Dockerfile

dockerfile
# 使用官方Node.js映像檔作為基礎
FROM node:16-alpine

# 設置工作目錄
WORKDIR /app

# 複製package.json和package-lock.json
COPY package*.json ./

# 安裝依賴
RUN npm ci --only=production

# 複製應用程式程式碼
COPY . .

# 暴露端口
EXPOSE 3000

# 定義環境變數
ENV NODE_ENV=production

# 啟動應用程式
CMD ["npm", "start"]


### 多階段建置

dockerfile
# 建置階段
FROM node:16-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# 生產階段
FROM node:16-alpine

WORKDIR /app

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

COPY --from=builder /app/dist ./dist

EXPOSE 3000

CMD ["npm", "start"]


### 最佳實踐

dockerfile
# 使用特定版本標籤
FROM node:16.20-alpine

# 創建非root用戶
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001

# 設置工作目錄
WORKDIR /app

# 複製依賴檔案
COPY package*.json ./

# 安裝依賴
RUN npm ci --only=production && npm cache clean --force

# 切換到非root用戶
USER nodejs

# 複製應用程式
COPY --chown=nodejs:nodejs . .

# 健康檢查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1

EXPOSE 3000

CMD ["npm", "start"]


## 5. 建置和運行自定義映像檔

### 建置映像檔

bash
# 基本建置
docker build -t my-app .

# 指定標籤
docker build -t my-app:v1.0.0 .

# 指定Dockerfile路徑
docker build -f Dockerfile.prod -t my-app:prod .

# 建置時傳遞參數
docker build --build-arg NODE_ENV=production -t my-app:prod .


### 運行自定義映像檔

bash
# 基本運行
docker run -d -p 3000:3000 my-app

# 指定環境變數
docker run -d -p 3000:3000 -e NODE_ENV=production my-app

# 掛載卷
docker run -d -p 3000:3000 -v /app/logs:/app/logs my-app

# 指定網路
docker run -d -p 3000:3000 --network my-network my-app


## 6. Docker Compose

### 基本docker-compose.yml

yaml
version: '3.8'

services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/mydb
depends_on:
- db
volumes:
- ./logs:/app/logs
networks:
- app-network

db:
image: postgres:13
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network

nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app
networks:
- app-network

volumes:
postgres_data:

networks:
app-network:
driver: bridge


### 進階配置

yaml
version: '3.8'

services:
app:
build:
context: .
dockerfile: Dockerfile
args:
NODE_ENV: production
image: my-app:latest
container_name: my-app
restart: unless-stopped
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/mydb
- REDIS_URL=redis://redis:6379
env_file:
- .env
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
volumes:
- ./logs:/app/logs
- ./uploads:/app/uploads
networks:
- app-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s

db:
image: postgres:13-alpine
container_name: postgres-db
restart: unless-stopped
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 10s
timeout: 5s
retries: 5

redis:
image: redis:6-alpine
container_name: redis-cache
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis_data:/data
ports:
- "6379:6379"
networks:
- app-network

nginx:
image: nginx:alpine
container_name: nginx-proxy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- app
networks:
- app-network

volumes:
postgres_data:
driver: local
redis_data:
driver: local

networks:
app-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16


### Compose命令

bash
# 啟動服務
docker-compose up
docker-compose up -d # 背景運行

# 停止服務
docker-compose down

# 重新建置並啟動
docker-compose up --build

# 查看服務狀態
docker-compose ps

# 查看服務日誌
docker-compose logs
docker-compose logs app
docker-compose logs -f app

# 執行命令
docker-compose exec app npm run test
docker-compose exec db psql -U user -d mydb

# 清理
docker-compose down -v # 刪除卷
docker-compose down --rmi all # 刪除映像檔


## 7. 網路和儲存

### Docker網路

bash
# 列出網路
docker network ls

# 創建網路
docker network create my-network

# 連接容器到網路
docker run --network my-network nginx

# 查看網路詳情
docker network inspect my-network

# 刪除網路
docker network rm my-network


### Docker卷

bash
# 創建卷
docker volume create my-volume

# 列出卷
docker volume ls

# 查看卷詳情
docker volume inspect my-volume

# 使用卷
docker run -v my-volume:/app/data nginx

# 刪除卷
docker volume rm my-volume


## 8. 生產環境部署

### 安全最佳實踐

dockerfile
# 使用最小基礎映像檔
FROM node:16-alpine

# 創建非root用戶
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001

# 設置工作目錄
WORKDIR /app

# 複製依賴檔案
COPY package*.json ./

# 安裝依賴並清理快取
RUN npm ci --only=production && \
npm cache clean --force && \
rm -rf /tmp/*

# 切換到非root用戶
USER nodejs

# 複製應用程式
COPY --chown=nodejs:nodejs . .

# 設置檔案權限
RUN chmod -R 755 /app

# 健康檢查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1

EXPOSE 3000

CMD ["npm", "start"]


### 多環境配置

yaml
# docker-compose.yml
version: '3.8'

services:
app:
build: .
environment:
- NODE_ENV=${NODE_ENV:-development}
env_file:
- .env.${NODE_ENV:-development}



# docker-compose.prod.yml
version: '3.8'

services:
app:
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M


### 監控和日誌

yaml
# 添加監控服務
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
networks:
- app-network

grafana:
image: grafana/grafana
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
networks:
- app-network

volumes:
grafana_data:


## 9. CI/CD整合

### GitHub Actions範例

yaml
# .github/workflows/docker.yml
name: Docker Build and Deploy

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
my-app:latest
my-app:${{ github.sha }}
cache-from: type=registry,ref=my-app:buildcache
cache-to: type=registry,ref=my-app:buildcache,mode=max


## 10. 故障排除

### 常見問題

bash
# 查看容器資源使用情況
docker stats

# 查看容器詳細資訊
docker inspect container_id

# 查看映像檔歷史
docker history image_name

# 清理未使用的資源
docker system prune -a

# 查看Docker守護程序日誌
sudo journalctl -u docker.service

# 重啟Docker服務
sudo systemctl restart docker


### 效能優化

dockerfile
# 使用多階段建置減少映像檔大小
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["npm", "start"]

# 使用.dockerignore減少建置上下文

node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage


## 總結

Docker容器化部署提供了:

1. **一致性**:確保開發、測試和生產環境的一致性
2. **可移植性**:在任何支援Docker的環境中運行
3. **隔離性**:應用程式和依賴項的完全隔離
4. **可擴展性**:輕鬆的水平擴展
5. **版本控制**:映像檔版本管理和回滾
6. **資源效率**:比虛擬機器更輕量級

掌握Docker技術,將大大提升應用程式的部署效率和可維護性。
Docker 容器化部署完整指南 | 香港大專CS功課代做