K8s 證書自動化:使用 Cert-manager + alidns-webhook 自動簽發 Let's Encrypt 公有證書
一、背景介紹:為什麼選擇 Cert-manager
1. 為什麼使用 Cert-manager
在傳統的環境中,為服務配置TLS通常意味着:手動生成CSR,提交給CA,等待審核,下載證書,並定時在90天內完成續期。這是一個高風險、重複性的工作。
Cert-manager 是 Kubernetes 上一個強大的證書管理工具,它通過 Custom Resource Definitions (CRDs) 實現了對證書的聲明式管理。它能夠監聽 Certificate 資源,並自動化地與各種證書頒發機構(如 Let's Encrypt)進行交互,確保證書在過期前自動續期並更新到對應的 Secret 中。
2. Cert-manager 在 K8s 中的實現路徑
Cert-manager 支持 HTTP-01 和 DNS-01 兩種 ACME 挑戰方式。
對於公網域名,如果 K8s 集羣沒有暴露在公網或存在複雜的網絡環境,HTTP-01 挑戰可能會遇到困難。此時,DNS-01 挑戰是最佳選擇。
結合 alidns-webhook: 為了讓 Cert-manager 能夠通過 DNS 記錄證明域名所有權,我們需要一箇中間件來適配特定的DNS服務商(阿里雲)。alidns-webhook 就是一個外部的 ACME Solver。它接收 Cert-manager 的指令,使用提供的阿里雲 API 密鑰,在目標域名的 DNS 解析記錄中添加特定的 TXT 記錄,完成所有權驗證,從而實現證書的順利簽發。
二、部署 Cert-manager 核心組件
我們使用 Helm 進行部署,確保組件的版本穩定性和安裝的便捷性。
首先安裝 Cert-manager 所需的 CRDs:
# 1. 安裝 CRDs (必須在 Helm Chart 部署前完成)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.1/cert-manager.crds.yaml
接着通過 Helm 部署 Cert-manager Controller:
# 2. 添加 Jetstack Helm 倉庫
helm repo add jetstack https://charts.jetstack.io
helm repo update
# 3. 拉取 Chart 到本地(可選,方便查看配置,此處直接使用本地部署)
mkdir cert-manager && cd cert-manager
helm pull --version v1.13.1 jetstack/cert-manager --untar
cd cert-manager/
# 4. 部署 Cert-manager
helm install cert-manager --namespace cert-manager --create-namespace .
# 驗證部署狀態
kubectl -n cert-manager get pod
三、部署 alidns-webhook 外部 Solver
alidns-webhook 負責處理阿里雲 DNS API 交互邏輯。注意,這裏的 --set groupName 是連接 ClusterIssuer 配置的關鍵橋樑。
helm upgrade --install alidns-webhook alidns-webhook \
--repo https://wjiec.github.io/alidns-webhook \
--namespace cert-manager --create-namespace \
--set groupName=ubusaito.top # **注意:此處的 groupName 必須與 ClusterIssuer 配置中的 groupName 嚴格一致**
groupName確保了 Cert-manager 知道當它遇到一個需要alidns驗證的訂單時,應該把挑戰任務轉發給哪個 Webhook Deployment 來處理。
四、配置阿里雲 DNS 權限與 ClusterIssuer
1. 獲取阿里雲 DNS Access Key
為了讓 alidns-webhook 有權限操作我們的 DNS 記錄,我們需要創建具有 AliyunDNSFullAccess 權限的 RAM 用户 AccessKey ID 和 AccessKey Secret。
⚠️ 安全警告: 這是一個高權限密鑰。請務必遵循最小權限原則,並在 K8s Secret 中妥善保管,僅授權給操作 DNS 解析的權限。
假設我們已經將獲取到的密鑰設置到環境變量:export id="<Your_ID>", export secret="<Your_Secret>"。
2. 創建 Secret 存儲憑證
我們將憑證存儲在一個名為 alidns-secret 的 Secret 中。
# 請將 $id 和 $secret 替換為實際值
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: alidns-secret
namespace: cert-manager
stringData:
access-key-id: "${id}"
access-key-secret: "${secret}"
EOF
# 清理環境變量,防止泄露
unset id && unset secret
3. 創建 ClusterIssuer (集羣證書頒發者)
ClusterIssuer 是 Cert-manager 中定義證書籤發規則的核心資源。我們配置它使用 Let's Encrypt 的生產環境 ACME 服務,並通過 dns01 塊引用我們部署的 alidns-webhook。
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: aliyun-acme # ClusterIssuer 的名稱
spec:
acme:
# 接收證書到期通知的郵箱
email: example@qq.com
# 存儲 ACME 賬户私鑰的 Secret 名稱
privateKeySecretRef:
name: aliyun-acme
# Let's Encrypt 生產環境 ACME 服務地址
server: https://acme-v02.api.letsencrypt.org/directory
solvers:
- dns01:
webhook:
# Webhook 的配置細節
config:
# 引用 Access Key ID
accessKeyIdRef:
key: access-key-id
name: alidns-secret
# 引用 Access Key Secret
accessKeySecretRef:
key: access-key-secret
name: alidns-secret
# 阿里雲的地域 (API 調用需要)
region: cn-hangzhou
# 必須與 alidns-webhook 部署時的 groupName 匹配
groupName: ubusaito.top
# 必須與 alidns-webhook 內部定義的 solverName 匹配
solverName: alidns
EOF
五、使用 Cert-manager 簽發公有證書
現在,我們創建一個 Certificate 資源,請求 Cert-manager 使用剛才定義的 aliyun-acme ClusterIssuer 來簽發證書。
kubectl apply -f - <<'EOF'
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: es.ubusaito.top-cert-public
namespace: default # 證書將生成在這個命名空間
spec:
secretName: es.ubusaito.top-tls # 證書籤發成功後,將生成的 Secret 名稱
dnsNames:
- "es.ubusaito.top" # 目標域名
issuerRef:
name: aliyun-acme # 引用我們創建的 ClusterIssuer
kind: ClusterIssuer
EOF
六、簽發狀態追蹤與故障排查
Cert-manager 的簽發流程是多步的,涉及 Certificate -> CertificateRequest -> Order -> Challenge。需要追蹤這些中間狀態進行排查。
1. 追蹤證書籤發流程
使用 kubectl describe 是最強大的追蹤工具。
步驟 1: 檢查 Certificate 狀態
檢查是否成功引用了 Issuer,以及是否創建了請求。
kubectl describe certificate es.ubusaito.top-cert-public -n default
關注 Events 和 Status.Conditions。如果一切正常,它會顯示 Ready: False,然後是創建 CertificateRequest 的事件。
步驟 2: 檢查 CertificateRequest (CR)
CR是Cert-manager發起的實際請求。
kubectl describe certificaterequest -n default
CR會引用一個 Order。
步驟 3: 檢查 Order (訂單)
Order 資源代表與 ACME 服務器(Let's Encrypt)協商簽發證書的訂單。
kubectl describe order -n default
關注 Status.State。訂單狀態會從 pending 變為 ready 或 valid。如果卡在 pending,通常是因為挑戰尚未完成。
步驟 4: 檢查 Challenge (挑戰)
Challenge 資源描述了具體的驗證方法(此處為 DNS-01)。這是 Cert-manager 與 Webhook 交互的關鍵點。
kubectl describe challenge -n default
如果挑戰狀態顯示 Presenting,則説明 Cert-manager 已經將請求轉發給 alidns-webhook,要求它去創建 TXT 記錄。
2. 查看日誌進行故障排查
如果挑戰卡住或失敗,我們需要檢查兩個關鍵 Pod 的日誌:
A. Cert-manager Controller 日誌: 確認 Cert-manager 是否正確處理了資源對象,以及與 ACME 服務器的通信是否正常。
kubectl logs -f -n cert-manager deploy/cert-manager --tail 100
B. alidns-webhook 日誌: 檢查 Webhook 是否接收到了挑戰請求,以及它調用阿里雲 DNS API 時是否遇到了鑑權或網絡錯誤。
kubectl logs -f -n cert-manager deploy/alidns-webhook --tail 100
例如,如果 alidns-webhook 日誌中出現類似 "Forbidden" 或 "InvalidAccessKeyId",則説明 alidns-secret 中的憑證是錯誤的或權限不足。如果看到成功添加 TXT 記錄的日誌,則挑戰很快會完成。
3. 驗證最終 Secret
當 Certificate 資源的 Ready 狀態變為 True 時,證書籤發成功。
kubectl get secret es.ubusaito.top-tls -n default
# 驗證 Secret 中包含 tls.crt, tls.key, ca.crt 字段
通過這一套流程,我們成功地將證書管理從人工干預中解耦出來,實現了 K8s 證書的自動化生命週期管理。