K8s · 設定與儲存 · 第 26 課 · · 13min read

Ingress + ConfigMap + Secret 整合實作:完整網站部署 demo

前面零件學完,這篇把它們全部串起來:nginx 用 ConfigMap 注入設定、MySQL 用 Secret 拿密碼、Ingress 對外路由。完整實作一個有資料庫的網站。

#Kubernetes #整合 #Ingress #ConfigMap #Secret
章節目錄 · 15

把零件組成一個網站

前面 25 篇我們學了 K8s 的所有基本零件:

這篇把它們全部串起來部署一個有資料庫的網站,模擬真實工作的長相:
瀏覽器 myshop.local
    ↓
Ingress
    ↓
frontend Service ──┬─→ frontend Pod (用 ConfigMap 注入 MESSAGE / USERNAME)
                   │     └─ envFrom Secret 拿 PASSWORD
                   │
                   └─→ MySQL Pod (用 Secret 注入帳密)

我們要部署什麼?

兩個 Deployment:

  • frontendyanchen184/k8s-demo-app(簡單的 PHP,回應四行:Server / Message / Username / Password)

  • MySQLmysql: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 通但 503kubectl get endpoints frontend-svc 看 Pod 接上沒
    Pod 起不來kubectl describe pod,看 ConfigMap / Secret 是否找不到
    環境變數空的kubectl exec ... -- env 確認注入了
    MySQL 連不上在 frontend Pod 內 nslookup mysql-svc 看 DNS
    從外往內,每層用 get / describe 看一眼。

    工作上的下一步

    到這個架構你已經能上線一個簡單的網站。但實際工作環境會再加:

    重點整理

    • 一個完整網站 = Namespace + ConfigMap + Secret + Deployment×N + Service×N + Ingress
    • ConfigMap 放公開設定、Secret 放密碼,兩者用 envFrom 同時注入
    • MySQL 用 Service name 就能被 frontend 連到(CoreDNS)
    • 改設定 → edit ConfigMap → rollout restart不用重 build
    • 排錯:從外往內每層 get / describe

    下一步

    到這裡 MySQL Pod 重啟,資料會全部不見(容器是無狀態的)。要在 K8s 上跑資料庫,必須學持久化儲存。

    下一篇:PV / PVC:K8s 怎麼讓 Pod 用磁碟

    📅 下一篇PV / PVC:K8s 怎麼讓 Pod 用磁碟
    Pod 重啟資料就不見?把 K8s 的「持久化儲存」三層架構講清楚——Pod 不死、資料不丟。
    📚 完整系列總覽K8s 系列教學首頁(共 40 課,按學習路徑順序排)