Kubernetes之Ingress自動化https



cert-manager 是一個雲原生證書管理開源項目,用於在 Kubernetes 集群中提供 HTTPS 證書並自動續期,支持 Let’s Encrypt, HashiCorp Vault 這些免費證書的簽發。在Kubernetes集群中,我們可以通過 Kubernetes Ingress 和 Let’s Encrypt 實現外部服務的自動化 HTTPS。

1、前置條件

在Kubernetes集群中使用 HTTPS 協議,需要一個證書管理器、一個證書自動簽發服務,主要通過 Ingress 來發布 HTTPS 服務,因此需要Ingress Controller並進行配置,啟用 HTTPS 及其路由。

本文環境:

  • k8s v1.17.0
  • Ingress Controller為nginx,且有對應暴露的公網ip地址

2、部署cert-manager

從cert-manager v0.11.0開始,Kubernetes的最低支持版本是v1.12.0。仍在運行Kubernetes v1.11或更低版本的用戶應在安裝cert-manager之前升級到受支持的版本。

cert-manager可以通過官方yaml安裝或者通過helm快速安裝,本文記錄通過官方yaml安裝的過程

2.1、創建一個namespace

# namespace.yaml 
---
apiVersion: v1
kind: Namespace
metadata:
  name: cert-manager

或者

kubectl create namespace cert-manager

2.2、安裝cert-manager

官方的yaml地址為

https://github.com/jetstack/cert-manager/releases/download/v0.13.1/cert-manager.yaml

yaml中有三個鏡像,分別為

  • cert-manager-controller:v0.13.1
  • cert-manager-cainjector:v0.13.1
  • cert-manager-webhook:v0.13.1

默認是從quay.io獲取鏡像,如果quay.io的鏡像無法獲取,修改image為國內源,例如Azure中國的地址quay.azk8s.cn

部署,會在集群中創建一系列的crd資源

# kubectl apply -f cert-manager.yaml 
customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/clusterissuers.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/issuers.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/orders.acme.cert-manager.io created
namespace/cert-manager unchanged
serviceaccount/cert-manager-cainjector created
serviceaccount/cert-manager created
serviceaccount/cert-manager-webhook created
clusterrole.rbac.authorization.k8s.io/cert-manager-cainjector created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-cainjector created
role.rbac.authorization.k8s.io/cert-manager-cainjector:leaderelection created
rolebinding.rbac.authorization.k8s.io/cert-manager-cainjector:leaderelection created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-webhook:auth-delegator created
rolebinding.rbac.authorization.k8s.io/cert-manager-webhook:webhook-authentication-reader created
clusterrole.rbac.authorization.k8s.io/cert-manager-webhook:webhook-requester created
role.rbac.authorization.k8s.io/cert-manager:leaderelection created
rolebinding.rbac.authorization.k8s.io/cert-manager:leaderelection created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-issuers created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-clusterissuers created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-certificates created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-orders created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-challenges created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-ingress-shim created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-issuers created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-clusterissuers created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-certificates created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-orders created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-challenges created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-ingress-shim created
clusterrole.rbac.authorization.k8s.io/cert-manager-view created
clusterrole.rbac.authorization.k8s.io/cert-manager-edit created
service/cert-manager created
service/cert-manager-webhook created
deployment.apps/cert-manager-cainjector created
deployment.apps/cert-manager created
deployment.apps/cert-manager-webhook created
mutatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created
validatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created

檢查對應的pod狀態

# kubectl get pods -n cert-manager 
NAME                                      READY   STATUS    RESTARTS   AGE
cert-manager-5cbcb9f4f5-7k6j4             1/1     Running   0          90s
cert-manager-cainjector-8df55567d-bspds   1/1     Running   0          90s
cert-manager-webhook-5d9c55bb4c-tmlck     1/1     Running   0          88s

2.3、測試

在正式使用前,先通過官方的示例做一個測試來確認正確設置了cert-manager並能夠頒發基本證書類型
測試Webhook正常工作

# cat <<EOF > test-resources.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: cert-manager-test
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: test-selfsigned
  namespace: cert-manager-test
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: selfsigned-cert
  namespace: cert-manager-test
spec:
  dnsNames:
    - example.com
  secretName: selfsigned-cert-tls
  issuerRef:
    name: test-selfsigned
EOF

創建測試資源

# kubectl apply -f test-resources.yaml
namespace/cert-manager-test created
issuer.cert-manager.io/test-selfsigned created
certificate.cert-manager.io/selfsigned-cert created

查新創建證書的狀態。可能需要等待幾秒鍾,然后cert-manager才能處理證書請求

# kubectl -n cert-manager-test describe certificate selfsigned-cert
...
Spec:
  Dns Names:
    example.com
  Issuer Ref:
    Name:       test-selfsigned
  Secret Name:  selfsigned-cert-tls
Status:
  Conditions:
    Last Transition Time:  2020-03-05T10:01:06Z
    Message:               Certificate is up to date and has not expired
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2020-06-03T10:01:06Z
Events:
  Type    Reason        Age   From          Message
  ----    ------        ----  ----          -------
  Normal  GeneratedKey  61s   cert-manager  Generated a new private key
  Normal  Requested     61s   cert-manager  Created new CertificateRequest resource "selfsigned-cert-504566127"
  Normal  Issued        61s   cert-manager  Certificate issued successfully

清理測試資源

# kubectl delete -f test-resources.yaml 
namespace "cert-manager-test" deleted
issuer.cert-manager.io "test-selfsigned" deleted
certificate.cert-manager.io "selfsigned-cert" deleted

3、創建clusterissuer

為了配置cert-manager以開始頒發證書,必須先創建IssuerClusterIssuer資源。這些資源代表特定的簽名機構,並詳細說明如何滿足證書請求。Issuer只能用來簽發自己所在namespace下的證書,ClusterIssuer可以簽發任意namespace下的證書,這里以ClusterIssuer為例創建一個簽發機構

# cat clusterissuer.yaml
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ssgeek@ssgeek.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - http01:
        ingress:
          class: nginx

說明:

  • metadata.name 創建的簽發機構的名稱,創建證書的時候會引用
  • spec.acme.email 郵箱,證書快過期的時候會有郵件提醒,不過cert-manager會利用acme協議自動給我們重新頒發證書來續期
  • spec.acme.server acme 協議的服務端,由官方給出
  • spec.acme.privateKeySecretRef 指示此簽發機構的私鑰將要存儲到哪個Secret對象中
  • pec.acme.solvers.http01 指示簽發機構使用HTTP-01的方式進行acme協議 (還可以用DNS方式,acme協議的目的是證明這台機器和域名都是屬於你的,然后才准許給你頒發證書)

4、為域名創建certificate

這里通過一個我自己的域名blog.ssgeek.com來進行測試,此域名已經修改dns為公網地址

# cat certificate.yaml 
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: blog
  namespace: default
spec:
  secretName: blog-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  duration: 2160h
  renewBefore: 360h
  keyEncoding: pkcs1
  dnsNames:
  - blog.ssgeek.com

說明:

  • spec.secretName 指示證書最終存到哪個 Secret 中
  • spec.issuerRef.kind 值為 ClusterIssuer 說明簽發機構不在本 namespace 下,而是在全局
  • spec.issuerRef.name 我們創建的簽發機構的名稱 (ClusterIssuer.metadata.name)
  • spec.duration 證書過期時間
  • spec.renewBefore 在過期前自動更新
  • spec.dnsNames 指示該證書的可以用於哪些域名
  • 更多選項可以參照官方文檔

創建並檢查相應資源

# kubectl apply -f certificate.yaml 
certificate.cert-manager.io/blog created
# kubectl get certificate
NAME   READY   SECRET     AGE
blog   True    blog-tls   36s
# kubectl get secrets |grep blog-tls
blog-tls               kubernetes.io/tls                     3      52s
# kubectl describe secrets blog-tls 
Name:         blog-tls
Namespace:    default
Labels:       <none>
Annotations:  cert-manager.io/alt-names: blog.ssgeek.com
              cert-manager.io/certificate-name: blog
              cert-manager.io/common-name: blog.ssgeek.com
              cert-manager.io/ip-sans: 
              cert-manager.io/issuer-kind: ClusterIssuer
              cert-manager.io/issuer-name: letsencrypt-prod
              cert-manager.io/uri-sans: 

Type:  kubernetes.io/tls

Data
====
ca.crt:   0 bytes
tls.crt:  3558 bytes
tls.key:  1675 bytes

在創建時查看cert-namager的日志

# kubectl -n cert-manager logs -f cert-manager-5cbcb9f4f5-4kks2
...
I0305 05:50:13.817322       1 controller.go:129] cert-manager/controller/certificates "msg"="syncing item" "key"="default/blog" 
I0305 05:50:14.317351       1 conditions.go:155] Setting lastTransitionTime for Certificate "blog" condition "Ready" to 2020-03-05 05:50:14.317341236 +0000 UTC m=+2213.785243238
I0305 05:50:14.525738       1 controller.go:135] cert-manager/controller/certificates "msg"="finished processing work item" "key"="default/blog" 
I0305 05:50:14.525812       1 controller.go:129] cert-manager/controller/certificates "msg"="syncing item" "key"="default/blog" 
I0305 05:50:14.526251       1 sync.go:367] cert-manager/controller/certificates "msg"="no existing CertificateRequest resource exists, creating new request..." "related_resource_kind"="Secret" "related_resource_name"="blog-tls" "related_resource_namespace"="default" "resource_kind"="Certificate" "resource_name"="blog" "resource_namespace"="default" 
I0305 05:50:14.774094       1 controller.go:129] cert-manager/controller/certificaterequests-issuer-ca "msg"="syncing item" "key"="default/blog-109727931" 
I0305 05:50:14.774118       1 controller.go:129] cert-manager/controller/certificaterequests-issuer-selfsigned "msg"="syncing item" "key"="default/blog-109727931" 
I0305 05:50:14.774135       1 sync.go:379] cert-manager/controller/certificates "msg"="created certificate request" "related_resource_kind"="Secret" "related_resource_name"="blog-tls" "related_resource_namespace"="default" "resource_kind"="Certificate" "resource_name"="blog" "resource_namespace"="default" "request_name"="blog-109727931"

如果創建出來的certificate狀態為False,可以通過以下命令查看相關信息

# kubectl get challenge

如果有相應的challenge,通過kubectl describe檢查,例如我這里之前創建失敗時檢查的錯誤信息如下

出現此問題的原因是我把此域名的解析設置為了內網地址,官方的頒發證書機構接口地址無法訪問到,因此必須解析在公網,並保證服務暴露在公網

5、在ingress中引用對應的secret

生成的證書最終綁定在對應的域名服務下,這里我運行了一個nginx pod,創建了對應的serviceingress資源,在ingress資源中聲明了此secret,由於部署了cert-maganer,在ingress中,還支持更多的注解,可以參考官方文檔

yaml內容如下

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.15 
        imagePullPolicy: IfNotPresent
        name: nginx

---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: default
spec:
  selector:
    app: nginx
  ports:
  - name: nginx
    port: 80
    targetPort: 80

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx
  namespace: default
  annotations:
    kubernietes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - blog.ssgeek.com
    secretName: blog-tls
  rules:
    - host: blog.ssgeek.com
      http:
        paths:
        - path: /
          backend:
            serviceName: nginx
            servicePort: 80

然后通過域名訪問,檢查證書是否正常

6、自動化頒發證書

上述內容是通過根據域名創建certificate最終得到的簽名證書,再配置到ingress中使用,還不夠自動化。沒錯,其實官方給出了自動通過ClusterIssuer頒發證書的做法,只需要在ingress中添加相應注解即可

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx
  namespace: default
  annotations:
    kubernietes.io/ingress.class: "nginx"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    kubernetes.io/tls-acme: "true"
spec:
  tls:
  - hosts:
    - blog.ssgeek.com
    secretName: blog-tls
  rules:
    - host: blog.ssgeek.com
      http:
        paths:
        - path: /
          backend:
            serviceName: nginx
            servicePort: 80

創建ingress資源,就會發現自動創建了certificate,得到secret
瀏覽器訪問,出現307http臨時重定向到https,也可以繼續添加一個注解強制進行強制重定向

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx
  namespace: default
  annotations:
    kubernietes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    kubernetes.io/tls-acme: "true"
spec:
  tls:
  - hosts:
    - blog.ssgeek.com
    secretName: blog-tls
  rules:
    - host: blog.ssgeek.com
      http:
        paths:
        - path: /
          backend:
            serviceName: nginx
            servicePort: 80

參考:
官方文檔
DIGITALOCEAN社區
Happiness"Blog


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM