昨天我們看到 Kubernetes 如果將資料存儲脫離 Pod 的生命週期,從 Volume 到 PersistentVolume (PV)、PersistentVolumeClaim (PVC),最後再到 StorageClass。透過這些機制,我們看到了如何讓資料不再隨著 Pod 的生命週期而遺失,並且能夠根據需求彈性調度,為應用程式提供可靠的儲存基礎。
今天要實戰把這些概念真正落地,嘗試部署一個更完整的應用組合,WordPress 搭配 MariaDB。前端的 WordPress 負責網站服務,後端的 MariaDB 提供資料庫支持,而 PVC 則確保資料庫的內容能安全保存,不會因為 Pod 的重建而遺失。這將會一次把 Deployment、Service、Volumes 串起來的實戰,也算是鐵人賽進度過半後的一次驗收。
在開始之前,我們要將資料掛載 Volumes 才不會讓資料隨著 Pod 生命週期而消失。但我的環境是 KinD,它本身就把 Node 之間的資料夾共享處理好了,每個 Node 底下的 data 資料夾是共享的。所以不需要另外去設定 NFS 或雲端磁碟,直接用 hostPath 的方式掛載到 /data 資料夾即可。
⚠️ 在實際的雲端或實體機環境,hostPath 並不會自動跨 Node 同步,除非有設定 NFS,但這種作法不適合生產環境。最佳實踐應該是要使用 PV/PVC + StorageClass 搭配雲端磁碟 (像 GCP PD、AWS EBS)。
docker exec -it kind-worker bash
我們從建立資料庫開始,首先先建立資料庫需要的密碼和時區,那當然 ConfigMap 儲存時區,而 Secrets 儲存密碼:
# ConfigMap
kubectl create configmap mydb-env --from-literal=TZ="Asia/Taipei" --dry-run=client -o yaml > db-configmap.yaml
# Secrets
kubectl create secret generic mydb-pwd --from-literal=MYSQL_ROOT_PASSWORD=IThome2025! --dry-run=client -o yaml > db-secretes.yaml
apiVersion: v1
kind: ConfigMap
metadata:
creationTimestamp: null
name: mydb-env
data:
TZ: '"Asia/Taipei"'
---
apiVersion: v1
kind: Secret
metadata:
creationTimestamp: null
name: mydb-pwd
data:
MYSQL_ROOT_PASSWORD: SVRob21lMjAyNSE=
接下來建立 MariaDB Deployment,並將 ConfigMap / Secret 掛載成環境變數,資料目錄掛到 Node 的 /data/db
:
kubectl create deployment mydb --image=mariadb:10.5 --dry-run=client -o yaml > db-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: mydb
name: mydb
spec:
replicas: 1
selector:
matchLabels:
app: mydb
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: mydb
spec:
containers:
- image: mariadb:10.5
name: mariadb
resources: {}
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
key: MYSQL_ROOT_PASSWORD
name: mydb-pwd
- name: TZ
valueFrom:
configMapKeyRef:
key: TZ
name: mydb-env
volumeMounts:
- mountPath: /var/lib/mysql
name: mydb-data
volumes:
- name: mydb-data
hostPath:
path: /data/db
type: Directory
status: {}
Pod 狀態變成 Running 之後,可以用 describe
看一下環境變數有沒有成功掛載:
也可以進入 Node 查看掛載的資料:
建立 MariaDB Table,進入 Pod 並登入 MariaDB:
# 進入 Pod 裡面的 Container
kubectl exec -it mydb-974568569-lpk6d -- bash
# 登入 MariaDB(輸入前面在 Secrets 設定的密碼)
mysql -uroot -p
建立 WordPress 專用的資料庫 wp:
create database wp;
接下來為了要讓我們後面建立的 Wordpress 連接到 MariaDB,因此要幫 DB 開一個 ClusterIP 的 Service,MariaDB 對外只需要被 WordPress 存取,所以用 ClusterIP 就足夠:
kubectl expose deployment mydb --port=3306 --dry-run=client -o yaml > db-svc.yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: mydb
name: mydb
spec:
type: ClusterIP
ports:
- port: 3306
protocol: TCP
targetPort: 3306
selector:
app: mydb
status:
loadBalancer: {}
建立一個 ConfigMap,存放 WordPress 需要的 DB 設定:
apiVersion: v1
kind: ConfigMap
metadata:
creationTimestamp: null
name: wordpress-env
data:
ServerName: localhost
WORDPRESS_DB_HOST: mydb
WORDPRESS_DB_NAME: wp
WORDPRESS_DB_PASSWORD: IThome2025!
WORDPRESS_DB_USER: root
接下來建立 WordPress Deployment,將 /var/www/html
掛載到 /data/wordpress
:
kubectl create deployment myweb --image=wordpress --dry-run=client -o yaml > wp-dep.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: myweb
name: myweb
spec:
replicas: 1
selector:
matchLabels:
app: myweb
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: myweb
spec:
containers:
- image: wordpress
name: wordpress
resources: {}
env:
- name: WORDPRESS_DB_NAME
valueFrom:
configMapKeyRef:
key: WORDPRESS_DB_NAME
name: wordpress-env
- name: WORDPRESS_DB_USER
valueFrom:
configMapKeyRef:
key: WORDPRESS_DB_USER
name: wordpress-env
- name: WORDPRESS_DB_PASSWORD
valueFrom:
configMapKeyRef:
key: WORDPRESS_DB_PASSWORD
name: wordpress-env
- name: ServerName
valueFrom:
configMapKeyRef:
key: ServerName
name: wordpress-env
- name: WORDPRESS_DB_HOST
valueFrom:
configMapKeyRef:
key: WORDPRESS_DB_HOST
name: wordpress-env
volumeMounts:
- mountPath: /var/www/html
name: myweb-data
volumes:
- name: myweb-data
hostPath:
path: /data/wordpress
type: Directory
status: {}
建立 Service,但因為我是 KinD,NodePort 還是 ClusterIP 對我來說不是很重要,但我一樣用 NodePort,因為實戰要給使用者存取是要用 NodePort:
kubectl expose deployment myweb --port=80 --type=NodePort --dry-run=client -o yaml > wp-svc.yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: myweb
name: myweb
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: myweb
type: NodePort
status:
loadBalancer: {}
透過 port-forward 將 Service 開放到本機 30123,30123 是隨便訂的,要別的 Port 也行:
kubectl port-forward --address 0.0.0.0 services/myweb 30123:80
發佈第一篇文章:
登入 MariaDB 查看 wp_posts,確認文章被寫進資料庫:
SELECT ID, post_title, post_date FROM wp_posts;
查看登入用戶的資訊:
SELECT ID, user_login, user_url FROM wp_users;
👉 這證明 WordPress 成功連接 MariaDB,文章與使用者資訊都能正確保存到資料庫中!
今天把 WordPress 和 MariaDB 跑起來之後,感覺算是把前面學過的東西串成一個完整的應用。雖然只是測試環境,用 hostPath 搭配 KinD 的共享資料夾就能讓資料保留下來,但這也讓我更清楚地了解 Volumes、ConfigMap、Secrets、Deployment、Service 這些概念在真實應用裡是怎麼互相搭配的。
在過程中有種「拼圖」的感覺:前端 WordPress、後端資料庫 MariaDB,透過 Service 打通彼此的連線,再加上 Volume 把資料固定下來。最後能在瀏覽器裡打開 WordPress,新增文章後到資料庫查到結果,這種驗證讓學習更具體,也更有成就感。
不過目前的服務對外方式還是靠 NodePort 或 port-forward,這並不是一個正式的網站的 Best Practice。明天要來看看 Ingress,把 HTTP/HTTPS 路由和網域管理帶進來,這樣才能更貼近實際的網站部署情境。