K8s · 網路與服務 · 第 23 課 · · 11min read

Ingress Host-based 路由 + TLS:怎麼讓網站走 HTTPS?

Path-based 是 myapp.com/api,Host-based 是 api.myapp.com。實務上微服務通常用 Host-based + TLS。這篇實作 cert-manager 自動申請 Let's Encrypt 憑證。

#Kubernetes #Ingress #TLS #HTTPS #cert-manager
章節目錄 · 10

Path-based 的天花板

Ingress 入門篇我們用 path-based 路由:

myapp.com/         → frontend-svc
myapp.com/api      → api-svc
myapp.com/admin    → admin-svc

簡單清楚,但有個前提:所有服務都掛在同一個域名底下。

工作上常常不是這樣。比如:

  • 公開站 www.example.com 給使用者
  • API api.example.com 給開發者
  • 後台 admin.example.com 給內部
三個域名 = 三個不同的團隊 / 不同的權限 / 不同的證照。硬塞同一個域名管理會很亂。

這就是 Host-based routing 的場景。

Host-based vs Path-based

方式URL 範例適合
Path-basedmyapp.com/ + myapp.com/api前後端同一產品
Host-basedwww.myapp.com + api.myapp.com微服務 / 多團隊
Docker 的對照:
  • Path-based ≈ Nginx 一個 server 底下多個 location
  • Host-based ≈ Nginx 多個 server 各自有不同的 server_name

Host-based YAML

跟 path-based 結構幾乎一樣,差別在 rules 裡多一個 host 欄位:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
spec:
  ingressClassName: traefik
  rules:
    - host: www.myapp.local         # ← 第一個域名
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-svc
                port:
                  number: 80
    - host: api.myapp.local         # ← 第二個域名
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-svc
                port:
                  number: 3000

兩個 host 各自一條規則。底下也可以再接 paths(host + path 混用)。

本機驗證

開發環境用 /etc/hosts 把網域指到本機:

$ sudo vim /etc/hosts

# 加兩行(IP 是 Ingress Controller 的 ADDRESS)
192.168.97.2 www.myapp.local
192.168.97.2 api.myapp.local

然後驗證:

$ curl http://www.myapp.local/
Server: 10.42.0.5:80 (frontend-deploy-abc)
Message: Hello from frontend

$ curl http://api.myapp.local/
Server: 10.42.0.7:80 (api-deploy-xyz)
Message: Hello from api

同一個 IP 192.168.97.2,不同域名導到不同 Service。 Ingress Controller 看 HTTP 請求的 Host: header 來分流。

TLS:為什麼一定要 HTTPS?

把網站改成 HTTPS 三個理由:

  • 瀏覽器強制 — Chrome / Firefox 對 http:// 顯示「不安全」,使用者直接退出

  • 資安 — 明文傳輸密碼 / Token 會被中間人攔截

  • API 規範 — 大部分第三方服務(Google、Stripe)只接受 HTTPS callback
  • K8s 怎麼做?把 TLS 憑證放進 Secret,Ingress 引用它。

    手動配 TLS(理解原理)

    Step 1:產生憑證並存進 Secret

    # 開發用:自簽憑證
    $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
        -keyout tls.key -out tls.crt \
        -subj "/CN=www.myapp.local"
    

    # 把憑證存進 K8s Secret
    $ kubectl create secret tls myapp-tls \
    --cert=tls.crt --key=tls.key \
    -n demo

    kubectl create secret tls 是專門存 TLS 的指令。type: kubernetes.io/tls 是固定型別。

    Step 2:Ingress 加上 tls 區塊

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: app-ingress
    spec:
      ingressClassName: traefik
      tls:                              # ← 新增這段
        - hosts:
            - www.myapp.local
            - api.myapp.local
          secretName: myapp-tls         # ← 對應 Secret 名稱
      rules:
        - host: www.myapp.local
          http:
            paths:
              - path: /
                pathType: Prefix
                backend:
                  service:
                    name: frontend-svc
                    port:
                      number: 80

    Step 3:用 HTTPS 連

    $ curl -k https://www.myapp.local/    # -k 忽略自簽憑證警告

    正式環境絕對不能用自簽,瀏覽器會擋。下面用 cert-manager 自動申請正式憑證。

    cert-manager + Let's Encrypt

    工作上沒人手動產憑證 — 90 天到期、要續、要 rotate,全人工搞會出事。

    cert-manager 是 K8s 上的標準解:

    • Let's Encrypt(免費的公信 CA)自動申請憑證
    • 自動寫進 Secret(你只要在 Ingress 引用名字)
    • 到期前自動續,零維護

    工作原理

    Ingress 加 annotation
      ↓
    cert-manager 偵測到,向 Let's Encrypt 申請
      ↓
    Let's Encrypt:「證明你擁有 www.example.com」
      ↓
    cert-manager 用 HTTP-01 challenge 自動回答
      ↓
    拿到憑證 → 寫進 Secret
      ↓
    Ingress Controller 自動 reload,HTTPS 上線

    Ingress 接 cert-manager

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: app-ingress
      annotations:
        cert-manager.io/cluster-issuer: letsencrypt-prod   # ← 這行
    spec:
      ingressClassName: nginx
      tls:
        - hosts:
            - www.example.com
          secretName: www-tls           # cert-manager 會自動建這個 Secret
      rules:
        - host: www.example.com
          http:
            paths:
              - path: /
                pathType: Prefix
                backend:
                  service:
                    name: frontend-svc
                    port:
                      number: 80

    部署完幾分鐘後 www-tls Secret 自動出現,HTTPS 就通了。

    排錯重點

    症狀檢查
    HTTPS 證書錯誤kubectl describe certificate -n demo 看 cert-manager 狀態
    連不到 host/etc/hosts 沒設好,ping www.myapp.local 確認 IP
    一直走 HTTPIngresstls 區塊忘了加
    自簽憑證瀏覽器擋Production 換成 cert-manager + Let's Encrypt

    重點整理

    • Path-based:同域名分路徑,前後端同產品
    • Host-based:多域名各分流,微服務 / 多團隊
    • TLS = 憑證放進 type: kubernetes.io/tls 的 Secret + Ingress spec.tls 引用
    • 開發用 openssl 自簽,正式用 cert-manager + Let's Encrypt
    • cert-manager 自動申請 / 自動續期 / 寫進 Secret,Ingress 只要加一個 annotation

    下一步

    到這裡你已經能把整個服務從 Pod 部到對外 HTTPS 網域。

    設定值 怎麼管?比如資料庫連線字串、API key、debug flag — 寫死在 image 裡不行(換環境就要重 build),寫進 YAML 也不對(密碼裸奔)。

    下一篇進入第 4 組:ConfigMap:把設定從程式裡抽出來