學完八大概念,但要怎麼串起來?
到目前為止,你學了:
- Pod / Deployment / Service / Ingress
- ConfigMap / Secret / PV/PVC / StatefulSet / Helm
- Probe / Resource limits / HPA / RBAC / NetworkPolicy
這 2 篇用 12 個步驟從空 Namespace 開始,部署一套真正可上線的應用。
目標架構
使用者 → Ingress(myapp.local)
├── / → frontend-svc → frontend Pod x2
└── /api → api-svc → api Pod x3(HPA)
↓
mysql-headless → mysql Pod x1
(StatefulSet + PVC)
NetworkPolicy:前端 → API → DB,逐層隔離
所有 Pod:Probe + Resource limits
12 步部署總覽
| 步驟 | 做什麼 | 對應第幾堂 |
|---|---|---|
| 1 | 建 Namespace | 第 5 |
| 2 | 建 Secret(DB 密碼) | 第 6 |
| 3 | 建 ConfigMap(API 設定) | 第 6 |
| 4 | 部署 MySQL(StatefulSet + PVC) | 第 6 |
| 5 | 部署 API(Deployment + Probe + Resource) | 第 5+7 |
| 6 | 部署前端 | 第 5 |
| 7 | 建 Service | 第 5 |
| 8 | 建 Ingress | 第 6 |
| 9 | 設 NetworkPolicy | 第 7 |
| 10 | 設 HPA | 第 7 |
| 11 | 完整驗證 | 全部 |
| 12 | 壓測 HPA | 第 7 |
---
Step 1:建 Namespace — 邏輯隔離
\\\yaml\
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: prod
\\
\\\bash\
kubectl apply -f namespace.yaml
kubectl get ns prod
# NAME STATUS AGE
# prod Active 3s
\\
之後所有資源都加 \-n prod\——避免跟 default namespace 混。
---
Step 2:建 Secret — DB 密碼
\\\yaml\
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: prod
type: Opaque
stringData:
MYSQL_ROOT_PASSWORD: "rootpw"
MYSQL_PASSWORD: "apppw"
MYSQL_DATABASE: "appdb"
MYSQL_USER: "appuser"
\\
\\\bash\
kubectl apply -f secret.yaml
kubectl get secret -n prod
\\
注意:這裡用 \stringData\,K8s 會自動幫你 Base64。生產環境應該改用 Sealed Secrets 或 External Secrets Operator 加密。
---
Step 3:建 ConfigMap — 應用設定
\\\yaml\
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: api-config
namespace: prod
data:
LOG_LEVEL: "info"
MAX_CONN: "100"
DB_HOST: "mysql-headless"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: frontend-nginx-config
namespace: prod
data:
default.conf: |
server {
listen 80;
location / {
root /usr/share/nginx/html;
}
location /api/ {
proxy_pass http://api-svc/;
}
}
\\
\\\bash\
kubectl apply -f configmap.yaml
kubectl get cm -n prod
# api-config, frontend-nginx-config
\\
前端 nginx 用 ConfigMap 掛載設定檔——這是反向代理 \/api/\ 到 API 的關鍵。
---
Step 4:部署 MySQL — StatefulSet + PVC
\\\yaml\
# mysql-statefulset.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
namespace: prod
spec:
clusterIP: None # Headless!
selector:
app: mysql
ports:
- port: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: prod
spec:
serviceName: mysql-headless
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
envFrom:
- secretRef:
name: mysql-secret
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
\\
\\\bash
kubectl apply -f mysql-statefulset.yaml
kubectl get pods -n prod -w
# mysql-0 0/1 Pending
# mysql-0 0/1 ContainerCreating
# mysql-0 1/1 Running ← 等到這個
kubectl get pvc -n prod
# mysql-data-mysql-0 Bound pvc-xxx 1Gi
\\\
這一步串了 5 個概念:StatefulSet + PVC + Headless Service + Secret + ConfigMap。
---
Step 5:部署 API — 帶 Probe + Resource
\\\yaml\
# api-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: prod
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: my-api:1.0
envFrom:
- configMapRef:
name: api-config
- secretRef:
name: mysql-secret
ports:
- containerPort: 8080
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
\\
\\\bash\
kubectl apply -f api-deployment.yaml
kubectl get pods -n prod -l app=api
# api-xxx-1 1/1 Running
# api-xxx-2 1/1 Running
# api-xxx-3 1/1 Running
\\
單一 YAML 用了 5 個概念:Deployment + ConfigMap + Secret + Probe + Resource。
---
Step 6:部署前端 — 透過 ConfigMap 掛 nginx 設定
\\\yaml\
# frontend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: prod
spec:
replicas: 2
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "128Mi"
volumes:
- name: nginx-config
configMap:
name: frontend-nginx-config
\\
\\\bash\
kubectl apply -f frontend-deployment.yaml
kubectl get pods -n prod -l app=frontend
# frontend-xxx-1 1/1 Running
# frontend-xxx-2 1/1 Running
\\
重點:\subPath\ 只掛單一檔案——ConfigMap 預設掛整個目錄會把 \/etc/nginx/conf.d/\ 蓋掉。
---
重點整理
- 12 步驟 SOP:從 Namespace 開始、一層層加上去
- Step 1-3 是準備:邊界(Namespace)、機密(Secret)、設定(ConfigMap)
- Step 4 最關鍵:StatefulSet + PVC + Headless + Secret 一次串 5 個概念
- Step 5 API 是濃縮版:Deployment + Probe + Resource + ConfigMap + Secret
- Step 6 前端配 nginx + ConfigMap subPath 注意陷阱
下一步
到這裡,6 個 Pod 都跑起來了——但還沒 Service、沒 Ingress、沒 HPA、沒安全設定,外面還連不到。
下一篇:從零部署完整系統(下)— 後 6 步:Service + Ingress + NetworkPolicy + HPA
📅 下一篇:從零部署完整系統(下)— 後 6 步:Service + Ingress + NetworkPolicy + HPA
接著上一篇,把 Service/Ingress/NetworkPolicy/HPA/RBAC 補完——12 步真正跑完一輪。
📚 完整系列總覽:K8s 系列教學首頁(共 40 課,按學習路徑順序排)