把零件組成一個網站
前面 25 篇我們學了 K8s 的所有基本零件:
- Pod / Deployment — 跑容器
- Service — 固定入口
- Ingress — 對外網域
- ConfigMap — 一般設定
- Secret — 密碼
瀏覽器 myshop.local
↓
Ingress
↓
frontend Service ──┬─→ frontend Pod (用 ConfigMap 注入 MESSAGE / USERNAME)
│ └─ envFrom Secret 拿 PASSWORD
│
└─→ MySQL Pod (用 Secret 注入帳密)
我們要部署什麼?
兩個 Deployment:
yanchen184/k8s-demo-app(簡單的 PHP,回應四行:Server / Message / Username / Password)mysql:8.0 用密碼啟動兩個 Service(ClusterIP)+ 一個 Ingress。
Step 1:建 Namespace
$ kubectl create namespace demo
$ kubectl config set-context --current --namespace=demo # 預設都用 demo
Step 2:ConfigMap(公開設定)
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: demo
data:
MESSAGE: "Hello from Production"
USERNAME: "admin"
LOG_LEVEL: "info"
Step 3:Secret(敏感資料)
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: app-secret
namespace: demo
type: Opaque
stringData:
PASSWORD: "user-password-123"
---
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: demo
type: Opaque
stringData:
MYSQL_ROOT_PASSWORD: "root-pw-456"
MYSQL_DATABASE: "shopdb"
MYSQL_USER: "shopuser"
MYSQL_PASSWORD: "shop-pw-789"
💡 一份 YAML 用 --- 分隔可以放多個資源。
Step 4:Frontend Deployment + Service
# frontend.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-deploy
namespace: demo
spec:
replicas: 3
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: yanchen184/k8s-demo-app:latest
ports:
- containerPort: 80
envFrom:
- configMapRef:
name: app-config # ← 一次注入 ConfigMap
- secretRef:
name: app-secret # ← 一次注入 Secret
---
apiVersion: v1
kind: Service
metadata:
name: frontend-svc
namespace: demo
spec:
type: ClusterIP
selector:
app: frontend
ports:
- port: 80
targetPort: 80
Step 5:MySQL Deployment + Service
# mysql.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-deploy
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
envFrom:
- secretRef:
name: mysql-secret # ← 帳密整包進來
---
apiVersion: v1
kind: Service
metadata:
name: mysql-svc
namespace: demo
spec:
type: ClusterIP
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
⚠️ MySQL 沒有持久化儲存(Pod 重啟資料會掉)。下一篇 PV/PVC 補上。
Step 6:Ingress
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress
namespace: demo
spec:
ingressClassName: traefik
rules:
- host: myshop.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-svc
port:
number: 80
一次 apply
$ kubectl apply -f .
configmap/app-config created
secret/app-secret created
secret/mysql-secret created
deployment.apps/frontend-deploy created
service/frontend-svc created
deployment.apps/mysql-deploy created
service/mysql-svc created
ingress.networking.k8s.io/shop-ingress created
$ kubectl get all -n demo
NAME STATUS READY
pod/frontend-deploy-6b8c-abc12 Running 1/1
pod/frontend-deploy-6b8c-def34 Running 1/1
pod/frontend-deploy-6b8c-ghi56 Running 1/1
pod/mysql-deploy-7f9d-xyz12 Running 1/1
NAME TYPE CLUSTER-IP PORT(S)
service/frontend-svc ClusterIP 10.43.0.150 80/TCP
service/mysql-svc ClusterIP 10.43.0.200 3306/TCP
驗證鏈路
# 1. /etc/hosts 加上
$ echo "192.168.97.2 myshop.local" | sudo tee -a /etc/hosts
# 2. 對外 curl
$ curl http://myshop.local/
Server: 10.42.0.5:80 (frontend-deploy-6b8c-abc12)
Message: Hello from Production # ← ConfigMap
Username: admin # ← ConfigMap
Password: user-password-123 # ← Secret
# 3. 從 frontend 連 MySQL
$ kubectl exec -it deploy/frontend-deploy -n demo -- sh
/ # apk add mysql-client
/ # mysql -h mysql-svc -u shopuser -p$MYSQL_PASSWORD -e "SHOW DATABASES;"
+--------------------+
| Database |
+--------------------+
| information_schema |
| shopdb | # ← Secret 設定的 MYSQL_DATABASE
+--------------------+
完整鏈路通了:
- 外面
myshop.local→ Ingress → frontend-svc → frontend Pod - frontend Pod 用
mysql-svc:3306連 MySQL - 帳密來自 Secret,沒有任何寫死
改設定不用重 build
工作上最常見的需求:把 MESSAGE 改一下。
$ kubectl edit configmap app-config -n demo
# MESSAGE: "Hello from Staging"
# envFrom 注入 → 要重啟 Pod
$ kubectl rollout restart deployment/frontend-deploy -n demo
$ curl http://myshop.local/
Message: Hello from Staging # ← 改了
沒有重 build Image,沒有重 push,沒有改 Dockerfile。這就是 ConfigMap 的價值。
排錯:每層拆開驗證
| 症狀 | 檢查 |
|---|---|
| 外面 curl 不通 | /etc/hosts 設好了?kubectl get ingress ADDRESS 對嗎? |
| Ingress 通但 503 | kubectl get endpoints frontend-svc 看 Pod 接上沒 |
| Pod 起不來 | kubectl describe pod,看 ConfigMap / Secret 是否找不到 |
| 環境變數空的 | kubectl exec ... -- env 確認注入了 |
| MySQL 連不上 | 在 frontend Pod 內 nslookup mysql-svc 看 DNS |
get / describe 看一眼。
工作上的下一步
到這個架構你已經能上線一個簡單的網站。但實際工作環境會再加:
- TLS — Ingress + cert-manager(已講過)
- 持久化 — MySQL 不能掉資料,要 PV / PVC
- 多副本資料庫 — StatefulSet
- 健康檢查 — Liveness / Readiness Probe
- 資源限制 — HPA + Resource Limits
重點整理
- 一個完整網站 = Namespace + ConfigMap + Secret + Deployment×N + Service×N + Ingress
- ConfigMap 放公開設定、Secret 放密碼,兩者用
envFrom同時注入 - MySQL 用 Service name 就能被 frontend 連到(CoreDNS)
- 改設定 →
editConfigMap →rollout restart,不用重 build - 排錯:從外往內每層
get/describe
下一步
到這裡 MySQL Pod 重啟,資料會全部不見(容器是無狀態的)。要在 K8s 上跑資料庫,必須學持久化儲存。
📅 下一篇:PV / PVC:K8s 怎麼讓 Pod 用磁碟
Pod 重啟資料就不見?把 K8s 的「持久化儲存」三層架構講清楚——Pod 不死、資料不丟。
📚 完整系列總覽:K8s 系列教學首頁(共 40 課,按學習路徑順序排)