Docker 教學 第 3 堂:MySQL、Docker Compose 與多容器管理
本系列為 18 小時 Docker 基礎教學講義,適合初學者,使用 Windows 電腦。 本篇為第 3 堂課,約 3 小時。
3-1 啟動 MySQL 容器與基本設定(約 1 小時)
為什麼用 Docker 跑資料庫?
傳統安裝 MySQL:
- 下載安裝程式
- 設定安裝路徑
- 設定使用者密碼
- 設定服務
- 如果要移除,還要清掉一堆殘留檔案
用 Docker:
- 一行指令搞定
- 不想要了?直接刪容器,乾乾淨淨
開發環境 vs 生產環境:資料庫該不該放在容器裡?
開發環境(你的電腦) 生產環境(正式上線) 用途 工程師寫程式、測試 真正的使用者在用 資料 測試資料,丟了沒關係 真實資料,不能丟 用 Docker 跑 DB? ✅ 非常適合 ❌ 不建議 開發環境用 Docker 跑資料庫是最佳實踐——一行指令裝好、不用的時候刪掉、還能同時跑不同版本。
生產環境不建議,因為資料庫需要高效能磁碟 I/O、資料不能丟、備份和主從複製在容器裡更複雜。正式環境通常使用雲端託管服務(如 AWS RDS、GCP Cloud SQL)。
實務上的流程:開發環境(Docker 跑 MySQL)→ 測試沒問題 → 部署到生產環境(用託管服務)。
如何看 Docker Hub 官方文件找參數
在直接教指令之前,先學一個更重要的技能——怎麼自己去 Docker Hub 查參數。因為不同的映像檔支援的環境變數和設定都不一樣,不可能全部背下來。
步驟 1:進入 Docker Hub 搜尋映像檔
前往 hub.docker.com,搜尋 mysql,點進官方映像檔頁面。
步驟 2:看 Overview 說明
往下滑,官方映像檔都會有詳細的說明文件,重點看這幾個區塊:
- How to use this image:教你怎麼啟動容器,通常會有範例指令
- Environment Variables:列出所有支援的環境變數(
-e參數) - Where to Store Data:告訴你資料存在容器的哪個路徑(
-v要掛載的路徑)
步驟 3:找到你需要的參數
以 MySQL 為例,官方文件會列出這些環境變數:
| 環境變數 | 說明 | 是否必填 |
|---|---|---|
MYSQL_ROOT_PASSWORD | root 密碼 | 必填 |
MYSQL_DATABASE | 自動建立一個資料庫 | 選填 |
MYSQL_USER | 自動建立一個使用者 | 選填 |
MYSQL_PASSWORD | 該使用者的密碼 | 選填 |
MYSQL_ALLOW_EMPTY_PASSWORD | 允許空密碼 | 選填 |
MYSQL_RANDOM_ROOT_PASSWORD | 自動產生隨機 root 密碼 | 選填 |
文件也會告訴你資料存放路徑是 /var/lib/mysql,所以你就知道 -v 要掛載這個路徑。
養成好習慣:每次使用新的映像檔之前,先去 Docker Hub 看一下官方文件。這比 Google 搜尋「怎麼用 Docker 跑 XXX」更準確,因為官方文件永遠是最新的。
實作:啟動 MySQL 容器
docker run -d ^
--name my-mysql ^
-e MYSQL_ROOT_PASSWORD=my-secret-pw ^
-e MYSQL_DATABASE=testdb ^
-e MYSQL_USER=testuser ^
-e MYSQL_PASSWORD=testpass ^
-p 3306:3306 ^
mysql:8.0參數解釋:
-d:背景執行--name my-mysql:容器名稱-e:設定環境變數(Environment Variable)MYSQL_ROOT_PASSWORD:root 密碼(必填)MYSQL_DATABASE:自動建立一個資料庫MYSQL_USER/MYSQL_PASSWORD:自動建立一個使用者
-p 3306:3306:映射 MySQL 預設端口mysql:8.0:使用 MySQL 8.0 映像檔
環境變數就像是啟動容器時傳入的「設定值」。不同的映像檔支援不同的環境變數,可以在 Docker Hub 的說明頁面找到。
確認 MySQL 已啟動:
docker ps
docker logs my-mysql等看到 ready for connections 就代表啟動成功了。
連線到 MySQL
方法 1:從容器內部連線
docker exec -it my-mysql mysql -u root -p輸入密碼 my-secret-pw,然後來實際操作資料庫,確認它是真的能用的:
-- 顯示所有資料庫
SHOW DATABASES;
-- 使用 testdb
USE testdb;建立產品資料表:
CREATE TABLE Product (
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '產品編號',
Name VARCHAR(100) NOT NULL COMMENT '產品名稱',
Price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '售價',
Stock INT NOT NULL DEFAULT 0 COMMENT '庫存數量',
CreatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='產品資料表';插入測試資料:
INSERT INTO Product (Name, Price, Stock) VALUES
('藍牙耳機', 1290.00, 50),
('USB-C 線', 299.00, 150),
('27吋螢幕', 5990.00, 20),
('無線滑鼠', 590.00, 80),
('機械鍵盤', 2490.00, 35);查詢資料,驗證資料庫正常運作:
-- 查看所有產品
SELECT * FROM Product;
-- 查詢庫存低於 50 的產品
SELECT Name, Stock FROM Product WHERE Stock < 50;
-- 查詢總庫存價值
SELECT SUM(Price * Stock) AS '總庫存價值' FROM Product;
-- 更新價格
UPDATE Product SET Price = 1190.00 WHERE Name = '藍牙耳機';
-- 確認更新
SELECT * FROM Product WHERE Name = '藍牙耳機';
-- 刪除一筆資料
DELETE FROM Product WHERE Name = 'USB-C 線';
-- 確認刪除
SELECT * FROM Product;
-- 離開 MySQL
EXIT;透過以上操作,你可以確認 Docker 容器裡的 MySQL 是一個完整可用的資料庫,支援 CREATE、INSERT、SELECT、UPDATE、DELETE 所有基本操作。
方法 2:從主機用工具連線
因為我們有做端口映射(-p 3306:3306),你也可以用外部工具連線:
- 主機:
localhost - 端口:
3306 - 帳號:
root/ 密碼:my-secret-pw - 或帳號:
testuser/ 密碼:testpass
推薦工具:DBeaver、MySQL Workbench、HeidiSQL
資料持久化
問題演示:
先在 MySQL 裡新增一些資料,然後:
# 刪除容器
docker rm -f my-mysql
# 重新建立一個
docker run -d --name my-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 mysql:8.0
# 進去看看
docker exec -it my-mysql mysql -u root -pSHOW DATABASES;
-- testdb 不見了!之前的資料全部消失!解決方案:使用 Volume
docker rm -f my-mysql
docker run -d ^
--name my-mysql ^
-e MYSQL_ROOT_PASSWORD=my-secret-pw ^
-e MYSQL_DATABASE=testdb ^
-p 3306:3306 ^
-v mysql-data:/var/lib/mysql ^
mysql:8.0-v mysql-data:/var/lib/mysql:建立一個名為mysql-data的 Volume,掛載到容器內 MySQL 存放資料的目錄
Volume 管理指令:
# 查看所有 Volume
docker volume ls
# 查看 Volume 詳情
docker volume inspect mysql-data
# 刪除 Volume
docker volume rm mysql-data現在即使刪掉容器再重建,只要掛載同一個 Volume,資料就不會消失。
練習:加入資料 → 刪除容器 → 重建容器(掛同一個 Volume) → 確認資料還在。
3-2 Docker Compose:一次管理多個容器(約 1 小時)
為什麼需要 Docker Compose?
回想一下,前面我們啟動 MySQL 的指令有多長:
docker run -d --name my-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -e MYSQL_DATABASE=testdb -e MYSQL_USER=testuser -e MYSQL_PASSWORD=testpass -p 3306:3306 -v mysql-data:/var/lib/mysql mysql:8.0如果同時要啟動 Web + 資料庫 + 快取,每個都要打一長串指令,而且還要記得建立網路、設定連線。這很麻煩,也很容易打錯。
Docker Compose 就是用來解決這個問題的——把所有容器的設定寫在一個 docker-compose.yml 檔案裡,一個指令就能全部啟動。
什麼是 YAML?
docker-compose.yml 使用 YAML 格式。YAML 是一種簡潔的設定檔格式,用來告訴電腦「這個服務要怎麼啟動」、「資料在哪裡」、「名字是什麼」等等。
YAML 的基本規則:
- 用縮排(空格)表示層級關係,不能用 Tab
- 用
:分隔 key 和 value - 用
-表示清單項目 #開頭是註解
# 這是一個 YAML 範例
name: my-app # key: value
ports: # 底下是清單
- "8080:80" # 清單項目用 - 開頭
- "443:443"
environment: # 底下也是 key: value
DB_HOST: mysql
DB_PORT: 3306注意:YAML 對縮排非常敏感,多一個或少一個空格都會出錯。建議用 VS Code 等編輯器,它會幫你自動對齊。
docker-compose.yml 基礎語法
在 C:\docker-lab\flask-app\ 目錄下建立 docker-compose.yml:
services:
web:
image: python:3.11-slim
container_name: flask-web # 指定容器名稱
restart: unless-stopped # 重啟策略
ports:
- "5000:5000"
volumes:
- .:/app
working_dir: /app
command: bash -c "pip install flask && python app.py"
depends_on:
- db
db:
image: mysql:8.0
container_name: mysql-db # 指定容器名稱
restart: unless-stopped # 重啟策略
environment:
MYSQL_ROOT_PASSWORD: my-secret-pw
MYSQL_DATABASE: testdb
MYSQL_USER: testuser
MYSQL_PASSWORD: testpass
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
volumes:
mysql-data:對照一下,每個設定對應哪個 docker run 參數:
| docker-compose.yml | docker run 參數 | 說明 |
|---|---|---|
image: | 映像檔名稱 | 要用哪個映像檔 |
container_name: | --name | 容器名稱 |
restart: | --restart | 重啟策略 |
ports: | -p | 端口映射 |
volumes: | -v | 掛載目錄 |
environment: | -e | 環境變數 |
working_dir: | -w | 工作目錄 |
command: | 最後面的指令 | 啟動指令 |
depends_on: | (手動控制啟動順序) | 先啟動哪個服務 |
restart 重啟策略:
| 值 | 說明 |
|---|---|
no | 預設值,不自動重啟 |
always | 不管什麼原因停止,都自動重啟 |
unless-stopped | 自動重啟,除非你手動 stop 它 |
on-failure | 只有程式出錯(非正常結束)才重啟 |
開發環境推薦用
unless-stopped,這樣電腦重開機後 Docker Desktop 啟動時,容器也會自動跟著啟動。
執行 Docker Compose
# 進入專案目錄
cd C:\docker-lab\flask-app
# 啟動所有容器(背景執行)
docker compose up -d
# 查看狀態
docker compose ps
# 查看所有容器的 log
docker compose logs
# 即時追蹤 log
docker compose logs -f
# 停止並刪除所有容器
docker compose down
# 停止並刪除所有容器,連 Volume 也刪掉
docker compose down -v注意:舊版 Docker 使用
docker-compose(有連字號),新版已整合為docker compose(空格)。如果你的 Docker Desktop 是近期安裝的,直接用docker compose就對了。
Docker Compose 的好處
- 一個檔案管理所有容器:不用記一堆長指令
- 可重複使用:
docker-compose.yml可以 commit 進 git,團隊成員 clone 下來直接docker compose up就能跑 - 自動建立網路:同一個 compose 檔案裡的容器會自動放在同一個網路,可以用服務名稱(如
db)互相連線 - 一鍵啟動/關閉:不用一個一個 start/stop
Docker Compose 適合開發環境和小型部署。大規模生產環境通常會使用 Kubernetes(K8s)來管理,但那是進階主題,不在本課程範圍內。
實作:用 Docker Compose 建立 Nginx + MySQL 環境
現在來動手做!我們要用一個 docker-compose.yml 同時啟動一個 Nginx 網站和一個 MySQL 資料庫。
步驟 1:建立專案資料夾
mkdir C:\docker-lab\compose-demo
cd C:\docker-lab\compose-demo步驟 2:建立網頁檔案
在 C:\docker-lab\compose-demo\ 下建立 html 資料夾,然後建立 html\index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Docker Compose Demo</title>
<style>
body { font-family: Arial; max-width: 600px; margin: 50px auto; text-align: center; }
h1 { color: #0db7ed; }
.info { background: #f0f0f0; padding: 20px; border-radius: 10px; margin: 20px 0; }
</style>
</head>
<body>
<h1>Docker Compose 實作成功!</h1>
<div class="info">
<p>這個網頁由 Nginx 容器提供服務</p>
<p>同時還有一個 MySQL 容器在背景運行</p>
<p>兩個容器都是用一個 docker-compose.yml 一起啟動的</p>
</div>
</body>
</html>步驟 3:建立 docker-compose.yml
在 C:\docker-lab\compose-demo\ 下建立 docker-compose.yml:
services:
# Nginx 網頁伺服器
web:
image: nginx
container_name: demo-web
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
# MySQL 資料庫
db:
image: mysql:8.0
container_name: demo-mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: root1234
MYSQL_DATABASE: demo_db
MYSQL_USER: demo_user
MYSQL_PASSWORD: demo_pass
ports:
- "3306:3306"
volumes:
- db-data:/var/lib/mysql
volumes:
db-data:步驟 4:啟動所有服務
cd C:\docker-lab\compose-demo
docker compose up -d你會看到 Docker 同時拉取 Nginx 和 MySQL 的映像檔,然後啟動兩個容器。
步驟 5:驗證服務
# 查看容器狀態,應該看到兩個都是 Up
docker compose ps
# 查看 log,確認都啟動成功
docker compose logs打開瀏覽器:
http://localhost:8080→ 看到我們寫的網頁- MySQL 也在跑了,可以用工具連線(localhost:3306,帳號 demo_user,密碼 demo_pass)
步驟 6:試試看修改網頁
因為我們用了 -v 把本機的 html 資料夾掛載進去,所以直接修改 html\index.html 的內容,重新整理瀏覽器就會看到更新。不用重啟容器!
步驟 7:連線到 MySQL 確認資料庫可用
docker exec -it demo-mysql mysql -u demo_user -p輸入密碼 demo_pass:
SHOW DATABASES;
USE demo_db;
CREATE TABLE test (id INT PRIMARY KEY, message VARCHAR(100));
INSERT INTO test VALUES (1, 'Docker Compose works!');
SELECT * FROM test;
EXIT;步驟 8:體驗一鍵關閉
# 停止並刪除所有容器
docker compose down
# 確認都清乾淨了
docker ps -a如果要連 Volume 也刪掉(資料庫資料也不要了):
docker compose down -v回顧一下:剛才我們用一個
docker-compose.yml就同時搞定了 Nginx + MySQL,不用分別打兩段長長的docker run指令。這就是 Docker Compose 的威力。
3-3 實戰練習與清理(約 1 小時)
實務情境題:同時跑兩個版本的 MySQL
情境: 你現在主機已經有 mysql:8.0 的容器在跑舊專案。主管說有新客戶,他們公司用的是 mysql:9.0。但你的開發機因為要維護舊專案還是需要 8.0,這時候請再建一個 mysql:9.0 的容器出來。
為什麼這個情境很重要? 傳統安裝 MySQL,同一台電腦要跑兩個版本幾乎不可能——port 衝突、路徑衝突、服務名稱衝突,搞半天搞不定。但 Docker 的隔離性讓這件事變得超簡單。
方法 A:使用 docker run
# 舊專案的 MySQL 8.0(假設已經在跑了,用 3306 port)
docker run -d --name mysql8 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root123 mysql:8.0
# 新客戶的 MySQL 9.0(port 改成 3307,避免衝突)
docker run -d --name mysql9 -p 3307:3306 -e MYSQL_ROOT_PASSWORD=root123 mysql:9.0重點:兩個容器內部都是 3306,但對外的 port 要錯開(3306 和 3307)。
方法 B:使用 docker-compose
services:
mysql8:
image: mysql:8.0
container_name: mysql8
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: old_project
ports:
- "3306:3306"
volumes:
- mysql8-data:/var/lib/mysql
mysql9:
image: mysql:9.0
container_name: mysql9
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: new_project
ports:
- "3307:3306"
volumes:
- mysql9-data:/var/lib/mysql
volumes:
mysql8-data:
mysql9-data:驗證兩個都能用:
# 連線到 MySQL 8.0
docker exec -it mysql8 mysql -u root -p -e "SELECT VERSION();"
# 連線到 MySQL 9.0
docker exec -it mysql9 mysql -u root -p -e "SELECT VERSION();"這就是 Docker 隔離性的威力——同一台電腦輕鬆跑多個版本,互不干擾。在開發環境中,這是非常常見的需求。
下載 DOS Game
用 Docker 玩復古遊戲
這一節是一個有趣的實作,讓大家體驗 Docker 的另一面——它不只能跑正經的伺服器程式,還能跑各種有趣的東西。
實作 1:DOS 遊戲映像檔
Docker Hub 上有人把經典的 DOS 遊戲打包成映像檔,可以在瀏覽器裡直接玩!
# 下載 DOS 遊戲映像檔
docker pull oldiy/dosgame-web-docker
# 啟動容器
docker run -d --name dos-game -p 8080:262 oldiy/dosgame-web-docker打開瀏覽器前往 http://localhost:8080,你會看到一個懷舊的 DOS 遊戲選單,裡面有上百款經典遊戲可以玩!
同學可以自行到 Docker Hub 搜尋
dosgame-web-docker查看這個映像檔的說明。這也是一個練習「看官方文件找參數」的好機會。
實作 2:2048 網頁版
docker run -d --name game-2048 -p 9090:80 alexwhen/docker-2048打開瀏覽器前往 http://localhost:9090 就能玩 2048!
實作 3:命令列小遊戲
# 用 Alpine Linux 容器體驗命令列遊戲
docker run -it --name game-box alpine sh在容器裡安裝文字遊戲:
# 安裝遊戲套件
apk add --no-cache bsd-games
# 玩貪食蛇
snake
# 或玩其他遊戲
tetris
worm透過這些有趣的例子:
- 練習從 Docker Hub 找映像檔並查看說明
- 練習
-p端口映射(注意每個映像檔的容器 port 不一樣)- 練習
docker run、docker stop、docker rm等基本操作- 體驗 Docker 可以運行各種類型的應用,不只是正經的伺服器程式
鼓勵同學自己去 Docker Hub 探索其他有趣的映像檔!
課後練習:自己跑起來
以下兩個映像檔請同學自行練習——去 Docker Hub 查看說明,找出需要的參數,把它跑起來:
| 映像檔 | 說明 | Docker Hub 頁面 |
|---|---|---|
ramuses/photopea | 線上版 Photoshop 替代品,跑在瀏覽器裡 | hub.docker.com/r/ramuses/photopea |
xinsodev/drawdb | 資料庫 ER 圖設計工具,視覺化設計資料表關聯 | hub.docker.com/r/xinsodev/drawdb |
練習重點:
- 到 Docker Hub 頁面閱讀說明
- 找出容器使用的 port 是什麼
- 用
docker run跑起來並在瀏覽器開啟 - 玩完後用
docker rm -f清理
這個練習的目的不是學這兩個工具,而是練習「看 Docker Hub 文件 → 自己把容器跑起來」的能力。以後工作上遇到任何新的映像檔,都是用同樣的流程。
清理練習
趁這個機會練習容器和映像檔的清理:
# 停止所有執行中的容器
docker stop $(docker ps -q)
# 刪除所有已停止的容器
docker container prune
# 刪除不再使用的映像檔
docker image prune
# 核彈級清理(小心使用!)
docker system prune -a