Docker 多容器應用部署實戰
在現代軟體開發與運維領域中,單一容器往往無法滿足複雜的應用需求。我們常會遇到需要資料庫、快取伺服器、後端 API 以及前端 Web 伺服器協同工作的場景。雖然 Docker Compose 是處理多容器應用的標準工具,但許多開發者仍習慣直接使用 docker run 命令來理解底層邏輯。本文將帶領中階 Linux 使用者,透過純手動方式,逐步建立一個包含 Nginx(前端)、Redis(快取)與 Node.js Express(後端)的基礎微服務架構,深入理解容器間的網路連結與資料持久化概念。
環境準備與網路建立
在啟動任何容器之前,首要任務是建立一個自定義的 Docker 網路。這是多容器通訊的關鍵。若不指定網路,Docker 預設會使用 bridge 網路,但容器間的 DNS 解析在預設網橋上並不總是穩定或可預測的。
請在終端機中執行以下指令,建立一個名為 app-network 的橋接網路:
docker network create app-network
這條指令執行後,Docker 會在背景建立一個虛擬網段。接下來啟動的容器只要加入這個網路,就能透過容器名稱互相通訊,而不需要記住複雜的 IP 位址。
部署 Redis 快取服務
首先,我們部署 Redis 作為我們的快取層。為了確保資料不隨容器刪除而消失,我們需要掛載一個 Volume。
docker run -d \
--name redis-cache \
--network app-network \
-v redis-data:/data \
redis:7-alpine
這裡有幾個關鍵參數值得注意:
-d:以後台模式運行。--name redis-cache:為容器賦予易於記憶的名稱。在app-network中,其他容器將直接使用redis-cache作為主機名來連線。-v redis-data:/data:將 Docker 管理的 Volumeredis-data掛載到容器內的/data目錄,確保 Redis 資料持久化。redis:7-alpine:使用輕量級的 Alpine Linux 基礎映像,減少佔用空間。
您可以透過 docker ps 確認 Redis 容器是否正在運行,並使用 docker logs redis-cache 查看啟動日誌。
啟動 Node.js 後端 API
接下來,我們建立後端服務。為了方便演示,我們使用 Docker Hub 上現成的 node 映像,並透過 -v 參數將本機目錄掛載至容器內,這樣修改程式碼後無需重新建構映像即可生效。
首先,在本機建立一個簡單的 Express 應用目錄結構:
mkdir -p ~/myapp/backend
cd ~/myapp/backend
npm init -y
npm install express
cat > index.js << 'EOF'
const express = require('express');
const app = express();
const port = 3000;
app.get('/health', (req, res) => {
res.json({ status: 'healthy', message: 'Backend is running' });
});
app.listen(port, '0.0.0.0', () => {
console.log(`Backend app listening on port ${port}`);
});
EOF
接著啟動後端容器,並將其連接到 app-network:
docker run -d \
--name backend-api \
--network app-network \
-v $(pwd):/usr/src/app \
-w /usr/src/app \
-p 3000:3000 \
node:18-alpine \
sh -c "npm install && node index.js"
注意 -w /usr/src/app 參數,它設定工作目錄。這裡我們直接使用 sh -c 執行安裝依賴並啟動腳本的命令。若您的環境需要連線 Redis,可在程式碼中使用 redis-cache 作為主機名,例如 redis.createClient({ socket: { host: 'redis-cache' } })。
部署 Nginx 前端反向代理
最後,我們部署 Nginx 作為反向代理,將外部請求轉發至後端 API。我們將使用 Docker Hub 的官方 Nginx 映像,並掛載一個自定義的配置文件。
建立 Nginx 配置文件 nginx.conf:
server {
listen 80;
location / {
proxy_pass http://backend-api:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
啟動 Nginx 容器:
docker run -d \
--name frontend-web \
--network app-network \
-v $(pwd)/nginx.conf:/etc/nginx/conf.d/default.conf \
-p 8080:80 \
nginx:alpine
此時,您可以開啟瀏覽器訪問 http://localhost:8080/health,流量將經過 Nginx 容器,透過 app-network 解析到 backend-api 容器,最終返回 JSON 回應。
常見問題與排除
1. 容器間無法互相 Ping 通或連線拒絕?
這通常是因為容器未加入同一個自定義網路。請確認所有容器在啟動時都加上了 --network app-network。此外,請檢查容器名稱是否拼寫正確,因為 DNS 解析是基於容器名稱的。
2. 資料掛載後權限錯誤?
在 Linux 主機上,Docker 容器內的預設使用者通常為 root (UID 0)。若掛載的本機目錄權限非 root 所有,可能會導致寫入失敗。解決方案包括:在 docker run 時使用 --user 參數指定 UID/GID,或在本機目錄執行 sudo chown -R 1000:1000(視容器內預設使用者 ID 而定)。
小結
透過手動啟動多個容器,我們不僅建立了完整的微服務架構,更深刻理解了 Docker 網路隔離、Volume 持久化以及反向代理的運作原理。雖然在生產環境中,我們最終會轉向使用 docker-compose.yml 或 Kubernetes 來簡化管理,但掌握底層的 docker run 指令與網路概念,是解決複雜除錯問題的必要基礎。建議讀者嘗試在後端加入 Redis 連線邏輯,進一步體驗多容器間的資料交換。