Kubernetes Ingress TLS with cert-manager and Let's Encrypt - 夜莺博客

Kubernetes Ingress TLS with cert-manager and Let's Encrypt

Expired certificates take down services more often than exploits do, because they are a scheduled event nobody is watching. cert-manager removes the whole category: it watches Certificate resources, completes ACME challenges against Let's Encrypt, writes the result into a Kubernetes Secret and renews before expiry. This guide installs it, configures issuing correctly for production, and covers the failure modes that keep people awake during their first automated issuance.

How the pieces fit

  1. You (or an Ingress annotation) create a Certificate resource.
  2. cert-manager creates a CertificateRequest and an ACME Order.
  3. For each domain in the order, it creates a Challenge — HTTP-01 (a token served over HTTP) or DNS-01 (a TXT record).
  4. Let's Encrypt validates the challenge and issues the certificate.
  5. cert-manager writes tls.crt and tls.key into the target Secret; the ingress controller reloads and serves HTTPS.

Responsibility split: the Ingress owns hostnames and paths and names the Secret; cert-manager owns the certificate lifecycle. Keeping that boundary clear explains most "the cert renewed but the site still shows the old one" tickets.

Install

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

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

kubectl get pods -n cert-manager
# three healthy pods: cert-manager, cert-manager-webhook, cert-manager-cainjector

Pin the chart version. Without --version, Helm pulls the newest chart, which is a surprise you do not want during a change window. CRDs are retained on uninstall by design since v1.15, so re-installing does not lose Certificate objects.

Issuers: always create staging first

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    email: platform@example.com
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-staging-account-key
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: platform@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx
kubectl apply -f staging-clusterissuer.yaml
kubectl apply -f prod-clusterissuer.yaml
kubectl get clusterissuer
# READY True on both before you go any further

Staging exists for one reason: rate limits. Let's Encrypt allows 50 certificates per registered domain per week, and a debugging loop that retries issuance can exhaust that in an afternoon. Validate the flow against staging, then switch the annotation.

Wire an Ingress to a certificate

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.com
      secretName: app-example-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app
                port:
                  number: 80
kubectl get certificate -A
kubectl describe certificate app-example-tls -n default
kubectl get certificaterequest,order,challenge -A
kubectl get secret app-example-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -subject -dates

HTTP-01 vs DNS-01

HTTP-01 DNS-01
Validation path HTTP request to /.well-known/acme-challenge/ TXT record via DNS provider API
Wildcard certificates Not possible Required
Broken by HTTP-to-HTTPS-only redirects Yes — unless the challenge path is exempted No
Credentials needed None (public reachability only) DNS API token in a Kubernetes Secret
Internal-only services Requires public reachability Works, if DNS is public

For a wildcard or an internal service, use DNS-01 with a solver for your provider (Route53, Cloudflare and DigitalOcean all have documented webhook or native solvers). Treat the API token exactly like any other cluster secret, scoped to the zone where possible.

Failure modes and how to spot them

  1. Ingress class mismatch — the solver pod creates a temporary Ingress using the class named in the issuer. If the cluster's controller class is not nginx, issuance hangs. Fix by matching ingressClassName in the solver.
  2. Egress network policy blocks ACME — cert-manager must reach acme-v02.api.letsencrypt.org on TCP 443. Restrictive egress policies are a frequent cause of silent timeouts.
  3. Rate limitstoo many certificates already issued. Switch to staging, fix, then back to production; also consider --issuer-... level debugging with kubectl describe challenge.
  4. Secret not reloaded — the certificate renewed but the controller still serves the old copy. Check the ingress controller logs and, on non-automatic controllers, the reload mechanism.
  5. Clock skew in the cluster — ACME is time-sensitive; skewed nodes fail validation in obscure ways. Keep node clocks synchronised.

Monitoring that actually prevents outages

kubectl get certificate -A -o custom-columns=\
NAME:.metadata.name,NS:.metadata.namespace,READY:.status.conditions[0].status,\
EXPIRY:.status.notAfter
# cert-manager exposes metrics such as certmanager_certificate_expiration_timestamp_seconds
# and certmanager_certificate_ready_status - alert on both

Two alerts end the whole category of incident: certificate not Ready for more than 30 minutes, and certificate expiring in less than 21 days (renewal should have happened at 30). With those in place, automated TLS becomes a system you trust rather than one you check.

Related Reading on This Site

原文链接:https://jorijn.com/en/knowledge-base/kubernetes/networking/cert-manager-tls-automated-certificates (Jorijn - Kubernetes TLS with cert-manager)