iT邦幫忙

2026 iThome 鐵人賽

DAY 28
0
Kubernetes

從零到 CKA:30 天掌握 Kubernetes 核心觀念與實作系列 第 28

Day 28|cert-manager — 自動申請、管理與續約 Kubernetes TLS 憑證

  • 分享至 

  • xImage
  •  

前言

昨天,我們學會了 CRD 與 Operator 的原理,還用 Shell Script 寫了一個簡易 Controller。

今天我們要來認識一個 Kubernetes 生態系中非常常見的憑證管理工具 —— cert-manager

你有沒有遇過這些痛點?

  • TLS 憑證需要手動申請,快過期時才發現
  • 不同應用各自管理憑證與 Secret,容易混亂
  • Let's Encrypt 憑證有效期只有 90 天,手動續約相當麻煩

cert-manager 就是為了解決這些問題而生。

它可以透過 Kubernetes 的 Custom Resource,自動完成憑證的申請、儲存與續約,大幅降低 TLS 憑證的手動維護成本。

今天我們會先認識 cert-manager 的架構與核心概念,接著透過兩個由淺入深的實作:

SelfSigned → 內部 CA

一步一步理解憑證簽發流程,最後再看看 cert-manager 的自動續約機制,以及常見的除錯方式。

今天內容涵蓋:

  1. 什麼是 cert-manager? — 認識 cert-manager 解決的問題與核心用途
  2. 安裝 cert-manager(用 Helm) — 使用 Helm 安裝 cert-manager 與相關 CRD
  3. 核心概念:Issuer 與 Certificate 的關係 — 理解簽發者、憑證與 Secret 之間的流程
  4. 實作一:建立 Self-Signed 憑證 — 用 SelfSigned Issuer 建立第一張憑證
  5. 實作二:建立內部 CA 並簽發憑證 — 建立統一的內部 CA,替服務簽發憑證
  6. 憑證續約機制 — 了解 cert-manager 如何在到期前自動續約
  7. 常見問題與除錯 — 學會排查 Certificate、Issuer、Secret 與續約問題

以下操作皆在 master 節點執行。


一、什麼是 cert-manager?

cert-manager 是一套憑證自動化 Controller

還記得昨天學的 Operator Pattern 嗎?

可以先簡化理解成:

Custom Resource + Controller + 領域自動化邏輯

cert-manager 就是把這套模式應用在 TLS 憑證管理上。

它透過一組 Custom Resource 描述「我要什麼憑證、要由誰簽發」,再由 Controller 自動完成申請、簽發、儲存與續約。

主要包含:

  • CRD:定義 CertificateIssuerClusterIssuerCertificateRequest 等資源
  • Controller:持續 Reconcile 這些資源,向指定的 Issuer 取得憑證,並將憑證與 Private Key 存入 Kubernetes Secret
  • 自動續約:在憑證到期前重新執行簽發流程

https://ithelp.ithome.com.tw/upload/images/20260821/20181928y2AeaLekOg.png

cert-manager 的核心 CRD

CRD 用途 範圍
Issuer 定義 Namespace 內可使用的憑證簽發者 Namespace
ClusterIssuer 定義整個 Cluster 可共用的憑證簽發者 Cluster
Certificate 描述需要的憑證,例如 DNS Name、有效期與使用哪個 Issuer Namespace
CertificateRequest 代表一次實際的憑證簽發請求 Namespace
Order ACME 流程中的憑證訂單 Namespace
Challenge ACME 流程中的網域驗證挑戰 Namespace

💡 連結 Day 27

cert-manager 安裝後會加入上述 CRD,讓 Kubernetes API 認得這些新的資源類型。

Controller 再持續監看並 Reconcile 這些資源,這就是前一篇學到的 Custom Resource + Controller 模式在真實工具中的應用。


二、安裝 cert-manager(用 Helm)

了解了 cert-manager 的架構後,接下來就動手把它安裝起來吧!

還記得 Day 20–23 學過的 Helm 嗎?這裡正好派上用場。

Step 1:新增 Helm Repository

helm repo add jetstack https://charts.jetstack.io
helm repo update

Step 2:安裝 cert-manager

helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true

💡 crds.enabled=true 是做什麼的?

這個設定會讓 Helm 在安裝 cert-manager 時,一併建立所需要的 CRD。

如果你希望將 CRD 與 Helm Release 分開管理,也可以先手動安裝 CRD:

kubectl apply -f \
  https://github.com/cert-manager/cert-manager/releases/download/v1.17.1/cert-manager.crds.yaml

接著安裝 cert-manager 時設定:

--set crds.enabled=false

本篇實作固定使用 v1.17.1 對應的 CRD,方便與後續操作和截圖保持一致。

💡 補充

cert-manager 後續版本的官方安裝方式可能會有所調整,例如改用 OCI Helm Chart。實際部署到正式環境時,建議再確認目前版本的官方安裝文件。

Step 3:確認安裝成功

先確認 cert-manager 的 Pod 是否正常運行:

kubectl get pods -n cert-manager

https://ithelp.ithome.com.tw/upload/images/20260824/20181928F3lLDVyodr.png

正常情況下,應該會看到三個主要元件:

  • cert-manager:主要的 Controller,負責處理 Certificate、Issuer、CertificateRequest 等資源的 Reconciliation
  • cert-manager-cainjector:負責將 CA Bundle 注入指定的 Kubernetes 資源
  • cert-manager-webhook:負責 cert-manager Custom Resource 的 Admission Webhook,例如驗證與轉換

接著確認 CRD 是否已經建立:

kubectl get crd | grep cert-manager

應該會看到像下面這些 CRD:

https://ithelp.ithome.com.tw/upload/images/20260824/20181928USDv4AEBug.png

如果有啟用 ACME 相關功能,也會看到:

challenges.acme.cert-manager.io
orders.acme.cert-manager.io

💡 看到 Pod Running + CRD 存在,基本上就代表 cert-manager 已成功安裝。

如果要再進一步確認,也可以使用:

kubectl get pods -n cert-manager
kubectl get crd | grep cert-manager

三、核心概念:Issuer 與 Certificate 的關係

cert-manager 已經跑起來了。

在開始實作之前,我們先搞懂它最核心的幾個角色:

  • Certificate:描述「我要什麼樣的憑證」
  • Issuer / ClusterIssuer:描述「由誰來簽發憑證」
  • Secret:儲存最後產生的憑證與 Private Key

https://ithelp.ithome.com.tw/upload/images/20260824/201819284F70CieACP.png

Certificate 會透過 issuerRef 指定要使用哪個 Issuer,cert-manager Controller 再完成後續簽發流程,並將憑證存入 secretName 指定的 Secret。

Issuer vs ClusterIssuer

Issuer ClusterIssuer
範圍 只能被同一個 Namespace 內的資源引用 可以被不同 Namespace 的 Certificate 引用
適用場景 各 Namespace 分別管理自己的憑證簽發設定 全叢集共用同一套憑證簽發設定
Certificate 引用方式 issuerRef.kind: Issuer issuerRef.kind: ClusterIssuer

支援的簽發類型

類型 說明 適用場景
SelfSigned 使用憑證自己的 Private Key 進行自簽 測試環境、建立 Root CA 的 bootstrap
CA 使用既有 CA 憑證與 Private Key 簽發 企業內部 PKI、內部服務憑證
ACME 使用 ACME 協議向 CA 申請憑證,例如 Let's Encrypt 公開網站、自動化公開 TLS 憑證

四、實作一:建立 Self-Signed 憑證

概念講完了,來動手!我們從最簡單的 Self-Signed(自簽) 開始,跑一遍完整流程,感受 cert-manager 的運作方式。

Step 1:建立 ClusterIssuer(自簽)

vim selfsigned-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}
kubectl apply -f selfsigned-issuer.yaml
# 確認 Issuer 狀態
kubectl get clusterissuer selfsigned-issuer

https://ithelp.ithome.com.tw/upload/images/20260824/20181928ZIZqD4Z3jq.png

READY 欄位應該顯示 True

Step 2:建立 Certificate

vim my-cert.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: my-selfsigned-cert
  namespace: default
spec:
  # 憑證會被存入這個 Secret
  secretName: my-selfsigned-tls

  # 憑證的有效期
  duration: 2160h    # 90 天
  renewBefore: 360h  # 到期前 15 天自動續約

  # 主體資訊
  subject:
    organizations:
      - my-company

  # 這張憑證要保護的域名
  dnsNames:
    - myapp.example.com
    - www.myapp.example.com

  # 使用哪個 Issuer 來簽發
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer
kubectl apply -f my-cert.yaml

Step 3:驗證憑證已產生

# 查看 Certificate 狀態
kubectl get certificate my-selfsigned-cert

https://ithelp.ithome.com.tw/upload/images/20260824/20181928CXEZ6M3UfW.png

READY 應該很快變成 True

# 查看 cert-manager 自動建立的 Secret
kubectl get secret my-selfsigned-tls

https://ithelp.ithome.com.tw/upload/images/20260824/20181928IJ9BaHURox.png

# 查看憑證內容
kubectl get secret my-selfsigned-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -noout | head -20

https://ithelp.ithome.com.tw/upload/images/20260824/20181928IfGosH0OYs.png

會看到憑證的詳細資訊,例如:

  • Issuer:憑證的簽發者
  • Subject:憑證所屬的主體
  • Validity:憑證的有效期間

如果 Certificate 有設定 dnsNames,也可以在憑證的 Subject Alternative Name 中看到對應的網域名稱。

💡 整個流程回顧

我們只需要透過 Certificate 描述「我要什麼樣的憑證」,cert-manager Controller 就會依照指定的 Issuer 完成簽發流程,並將憑證與 Private Key 寫入 Secret。

這正是 Operator Pattern 的實際應用:透過宣告期望狀態,讓 Controller 自動完成後續的維運工作。

Step 4:CertificateRequest — 一次實際的簽發請求

cert-manager 在處理 Certificate 時,會建立對應的 CertificateRequest,代表一次實際的憑證簽發請求。

kubectl get certificaterequest

https://ithelp.ithome.com.tw/upload/images/20260824/20181928ZJ6EedUzos.png

可以把它理解成:

  • Certificate:描述「我希望拿到什麼樣的憑證」
  • CertificateRequest:代表其中一次具體的簽發請求

這和昨天的 Controller 概念很像:高層資源描述期望狀態,Controller 再建立後續所需的資源來完成實際流程。


五、實作二:建立內部 CA 並簽發憑證

上一節我們成功使用 SelfSigned Issuer 簽發了憑證,但 Self-Signed 憑證有一個明顯限制:每張憑證都是自己簽署自己,彼此之間沒有共同的信任鏈

因此,Client 或瀏覽器預設不會信任這些憑證,除非另外將它們加入信任清單。

在企業內部環境中,更常見的做法是建立一個統一的 內部 CA(Certificate Authority),再由這個 CA 為不同服務簽發憑證。

這樣只需要讓 Client 信任這個內部 CA,就可以信任由它簽發的所有服務憑證。

Step 1:先用 Self-Signed Issuer 建立 CA 憑證

vim internal-ca.yaml
# 先建立 CA 的根憑證
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: internal-ca
  namespace: cert-manager
spec:
  isCA: true                    # 這是一張 CA 憑證!
  secretName: internal-ca-key-pair
  commonName: my-internal-ca
  duration: 43800h              # 5 年
  renewBefore: 720h             # 到期前 30 天續約
  privateKey:
    algorithm: ECDSA
    size: 256
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer
kubectl apply -f internal-ca.yaml

Step 2:建立使用這個 CA 的 ClusterIssuer

vim ca-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: internal-ca-issuer
spec:
  ca:
    secretName: internal-ca-key-pair
kubectl apply -f ca-issuer.yaml
kubectl get clusterissuer internal-ca-issuer

https://ithelp.ithome.com.tw/upload/images/20260824/20181928J5ierxMG0X.png

Step 3:用 CA Issuer 簽發服務憑證

vim app-cert.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: app-tls
  namespace: default
spec:
  secretName: app-tls-secret
  duration: 2160h
  renewBefore: 360h
  dnsNames:
    - app.internal.example.com
  issuerRef:
    name: internal-ca-issuer
    kind: ClusterIssuer
kubectl apply -f app-cert.yaml
# 驗證憑證鏈
kubectl get secret app-tls-secret -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -noout | grep -A 2 'Issuer:'

你會看到這張服務憑證的 Issuer 是:

https://ithelp.ithome.com.tw/upload/images/20260824/20181928ovEGNIygUt.png

這表示它不是自己簽自己,而是由我們建立的內部 CA 簽發。

接下來其他服務也可以使用同一個 CA Issuer 來申請憑證,形成一致的內部信任體系。

https://ithelp.ithome.com.tw/upload/images/20260824/201819287wfQYKU9oe.png

💡 為什麼要分兩層?

SelfSigned Issuer 在這裡只用來 bootstrap 一張 Root CA 憑證。

接著再建立 CA Issuer,使用這張 CA 憑證與 Private Key 為其他服務簽發憑證。

這樣多個服務就可以共用同一個 CA。只要 Client 信任這個內部 CA,就能驗證由它簽發的服務憑證。


六、憑證續約機制

到目前為止,我們已經會建立 Issuer 和簽發 Certificate 了。

但憑證都有有效期限,總不能每次到期都手動重新申請。這也是 cert-manager 很重要的功能之一 —— 自動續約

續約邏輯

https://ithelp.ithome.com.tw/upload/images/20260824/20181928NZKTjJb7YW.png

關鍵參數

spec:
  duration: 2160h      # 希望憑證有效 90 天
  renewBefore: 360h    # 到期前 15 天開始續約
  • duration:希望簽發的憑證有效時間
  • renewBefore:距離憑證到期還有多久時開始續約
  • cert-manager 會根據實際簽發憑證的有效期限計算 RenewalTime
  • 續約成功後,cert-manager 會更新對應的 TLS Secret
  • 使用該 Secret 的元件是否會立即載入新憑證,則要看應用或 Controller 本身是否支援自動 Reload
  • 如果續約暫時失敗,cert-manager 會再次嘗試簽發

💡 如果沒有設定 renewBefore

cert-manager 會自動計算續約時間,預設約在憑證生命週期經過 2/3 時開始續約。

例如一張有效期 90 天的憑證,預設大約會在簽發後 60 天,也就是到期前約 30 天開始續約。

查看憑證狀態

# 查看所有憑證
kubectl get certificate -A

# 查看特定憑證的詳細狀態
kubectl describe certificate my-selfsigned-cert

# 查看憑證簽發相關事件
kubectl get events --field-selector reason=Issuing -A

七、常見問題與除錯

問題 原因與解法
Certificate READY=False 先用 kubectl describe certificate <name> 查看 Condition 與 Event,常見原因包括 Issuer 未就緒、簽發失敗或 ACME 驗證失敗
HTTP-01 驗證失敗 確認 DNS 已指向正確入口、Port 80 可從外部存取,並檢查 Ingress / Gateway 與 Challenge 狀態
Secret 沒有建立 檢查 Certificate、CertificateRequest 與 cert-manager Controller log
憑證續約後服務仍使用舊憑證 cert-manager 會更新 Secret,但應用或 Controller 是否自動重新載入,要看元件本身是否支援
Let's Encrypt 簽發失敗或被限流 測試階段優先使用 Staging Endpoint,並查看目前 Let's Encrypt 官方 Rate Limits
刪除 Certificate 後 Secret 還在 Secret 的刪除行為取決於 cert-manager 設定與資源關係;不要假設一定會跟著刪除

常用除錯指令

# 查看 Issuer / Certificate
kubectl get issuer,clusterissuer,certificate -A

# 查看 CertificateRequest
kubectl get certificaterequest -A

# 如果使用 ACME,再查看 Order / Challenge
kubectl get orders,challenges -A

# 查看特定 Certificate 詳細狀態
kubectl describe certificate <name> -n <namespace>

# 查看 cert-manager Controller log
kubectl logs -n cert-manager deploy/cert-manager --tail=50

小結

今天我們學會了如何使用 cert-manager,在 Kubernetes 中自動化管理 TLS 憑證。

學到的東西 一句話總結
cert-manager 採用 Operator Pattern,自動處理 TLS 憑證的申請、儲存與續約
Issuer / ClusterIssuer 定義「由誰來簽發憑證」,例如 SelfSigned、CA、ACME
Certificate 描述「需要什麼樣的憑證」,cert-manager 會完成簽發並寫入指定的 Secret
SelfSigned → CA 兩層架構 先用 SelfSigned bootstrap Root CA,再透過 CA Issuer 簽發服務憑證
自動續約 cert-manager 會在憑證到期前進行續約,降低憑證過期造成服務中斷的風險

今天我們建立了 Certificate、Issuer 與 Secret,而這些 Kubernetes API Resource 的狀態最終都需要被持久化保存。

那麼,Kubernetes 到底把這些資料存在哪裡?

答案就是 etcd —— Kubernetes Control Plane 用來保存叢集狀態的重要 Key-Value Store。

下一篇我們將深入 etcd,了解它在 Kubernetes 中扮演的角色、Raft 共識機制,以及備份與還原等重要維運操作!


參考資源


上一篇
Day 27|CRD 與 Operator — 擴展 Kubernetes API,打造自訂資源與控制器
系列文
從零到 CKA:30 天掌握 Kubernetes 核心觀念與實作28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言