K8s cert-manager TLS 자동화

cert-manager란?

cert-manager는 Kubernetes 클러스터에서 TLS 인증서의 발급, 갱신, 관리를 자동화하는 컨트롤러입니다. Let’s Encrypt, Vault, Venafi 등 다양한 CA(Certificate Authority)와 연동하여 인증서 라이프사이클을 완전히 자동화합니다. 수동 인증서 관리의 만료 실수, 갱신 누락 등을 근본적으로 제거하며, Ingress와 자연스럽게 통합됩니다.

설치와 핵심 리소스

cert-manager는 CRD(Custom Resource Definition) 기반으로 동작하며, Issuer, Certificate, CertificateRequest 등의 커스텀 리소스를 사용합니다.

# Helm으로 설치
helm repo add jetstack https://charts.jetstack.io
helm repo update

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

# 설치 확인
kubectl get pods -n cert-manager
# cert-manager-xxx              Running
# cert-manager-cainjector-xxx   Running
# cert-manager-webhook-xxx      Running
리소스 역할 스코프
Issuer 인증서 발급자 설정 (CA 연동) 네임스페이스
ClusterIssuer 클러스터 전역 인증서 발급자 클러스터
Certificate 인증서 요청 및 관리 네임스페이스
CertificateRequest CSR 생성 및 CA 제출 (자동 생성) 네임스페이스

Let’s Encrypt Issuer 설정

가장 일반적인 사용 사례인 Let’s Encrypt 연동입니다. HTTP-01DNS-01 두 가지 챌린지 방식을 지원합니다.

# Staging (테스트용 — 먼저 이것으로 검증!)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-staging-key
    solvers:
      # HTTP-01 챌린지: Ingress를 통해 검증
      - http01:
          ingress:
            ingressClassName: nginx

---
# Production
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx

DNS-01 챌린지: 와일드카드 인증서

와일드카드 인증서(*.example.com)는 DNS-01 챌린지로만 발급 가능합니다. DNS 프로바이더 API를 통해 TXT 레코드를 자동 생성합니다.

# Cloudflare DNS-01 설정
apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token
  namespace: cert-manager
type: Opaque
stringData:
  api-token: "your-cloudflare-api-token"

---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-dns
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-dns-key
    solvers:
      # DNS-01 챌린지: Cloudflare API
      - dns01:
          cloudflare:
            apiTokenSecretRef:
              name: cloudflare-api-token
              key: api-token
        # 특정 도메인에만 DNS-01 적용
        selector:
          dnsZones:
            - "example.com"

---
# 와일드카드 인증서 요청
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-cert
  namespace: default
spec:
  secretName: wildcard-tls
  issuerRef:
    name: letsencrypt-dns
    kind: ClusterIssuer
  dnsNames:
    - "example.com"
    - "*.example.com"
  # 갱신 설정
  renewBefore: 720h  # 만료 30일 전 갱신
챌린지 검증 방식 와일드카드 요구 사항
HTTP-01 HTTP 경로에 토큰 파일 제공 80 포트 접근, Ingress
DNS-01 DNS TXT 레코드 생성 DNS 프로바이더 API 접근

Ingress 자동 인증서 발급

Ingress에 어노테이션을 추가하면 cert-manager가 자동으로 인증서를 발급하고 Secret에 저장합니다. 가장 간편한 방식입니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  annotations:
    # cert-manager가 이 어노테이션을 감지하여 자동 발급
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    # 선택적: HTTP → HTTPS 리다이렉트
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.example.com
        - admin.example.com
      secretName: api-tls  # cert-manager가 이 Secret 자동 생성
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 80
    - host: admin.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: admin-service
                port:
                  number: 80

# cert-manager 동작 흐름:
# 1. Ingress 감지 → Certificate 리소스 자동 생성
# 2. CertificateRequest 생성 → ACME 챌린지 수행
# 3. 인증서 발급 → api-tls Secret에 저장
# 4. Ingress Controller가 Secret의 TLS 인증서 사용
# 5. 만료 전 자동 갱신

Certificate 리소스 상세 설정

Ingress 어노테이션 방식 외에, Certificate 리소스를 직접 생성하면 더 세밀한 제어가 가능합니다.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: api-cert
  namespace: production
spec:
  # TLS Secret 이름
  secretName: api-tls

  # 발급자 참조
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
    group: cert-manager.io

  # 도메인 목록
  dnsNames:
    - api.example.com
    - api-v2.example.com

  # 인증서 옵션
  duration: 2160h     # 90일 (Let's Encrypt 기본)
  renewBefore: 720h   # 만료 30일 전 갱신

  # 개인 키 설정
  privateKey:
    algorithm: ECDSA
    size: 256
    rotationPolicy: Always  # 갱신 시 키도 교체

  # 추가 Subject 정보
  subject:
    organizations:
      - "My Company"

  # Secret 템플릿: 추가 레이블/어노테이션
  secretTemplate:
    labels:
      app: api-server
    annotations:
      reloader.stakater.com/match: "true"

인증서 모니터링과 트러블슈팅

인증서 상태를 모니터링하고 문제를 진단하는 방법입니다.

# 인증서 상태 확인
kubectl get certificate -A
# NAME        READY   SECRET      AGE
# api-cert    True    api-tls     30d
# web-cert    False   web-tls     5m   ← 문제!

# 상세 이벤트 확인
kubectl describe certificate api-cert -n production
# Events:
#   Type    Reason   Message
#   Normal  Issuing  Issuing certificate as Secret does not exist
#   Normal  Generated  Stored new private key
#   Normal  Requested  Created new CertificateRequest
#   Normal  Issuing   The certificate has been successfully issued

# CertificateRequest 상태 확인
kubectl get certificaterequest -A
kubectl describe certificaterequest api-cert-xxxxx

# ACME Order/Challenge 확인 (디버깅)
kubectl get order -A
kubectl get challenge -A
kubectl describe challenge api-cert-xxxxx

# 인증서 만료일 확인
kubectl get secret api-tls -o jsonpath='{.data.tls.crt}' | 
  base64 -d | openssl x509 -noout -dates
# notBefore=Mar  1 00:00:00 2026 GMT
# notAfter=May 30 00:00:00 2026 GMT

# Prometheus 메트릭으로 알림
# certmanager_certificate_expiration_timestamp_seconds
# certmanager_certificate_ready_status

Self-Signed CA와 내부 인증서

내부 서비스 간 mTLS나 개발 환경에서는 Self-Signed CA를 사용합니다.

# 1. Self-Signed Root CA 생성
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}

---
# 2. CA 인증서 생성
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: internal-ca
  namespace: cert-manager
spec:
  isCA: true
  commonName: internal-ca
  secretName: internal-ca-key
  privateKey:
    algorithm: ECDSA
    size: 256
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer

---
# 3. CA Issuer: 위 CA로 인증서 발급
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: internal-ca-issuer
spec:
  ca:
    secretName: internal-ca-key

---
# 4. 내부 서비스 인증서 발급
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: grpc-service-cert
  namespace: production
spec:
  secretName: grpc-tls
  issuerRef:
    name: internal-ca-issuer
    kind: ClusterIssuer
  dnsNames:
    - grpc-service.production.svc.cluster.local
    - grpc-service.production.svc
  duration: 8760h    # 1년
  renewBefore: 720h  # 30일 전 갱신

Gateway API 연동

Gateway API와 cert-manager를 연동하여 Gateway 리소스에서 자동 인증서를 발급받을 수 있습니다.

# cert-manager Gateway API 지원 활성화 (설치 시)
helm install cert-manager jetstack/cert-manager 
  --set featureGates="ExperimentalGatewayAPISupport=true"

# Gateway에서 자동 인증서 발급
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: api-gateway
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  gatewayClassName: istio
  listeners:
    - name: https
      port: 443
      protocol: HTTPS
      hostname: "api.example.com"
      tls:
        mode: Terminate
        certificateRefs:
          - name: api-gateway-tls  # cert-manager 자동 생성

운영 베스트 프랙티스

  • Staging 먼저: 항상 Let’s Encrypt Staging으로 테스트 후 Production 전환하세요 — Rate Limit 방지
  • DNS-01 선호: 와일드카드가 필요하거나 80포트 접근이 어려우면 DNS-01 챌린지를 사용하세요
  • renewBefore 설정: 만료 30일 전 갱신으로 여유를 두세요 (Let’s Encrypt는 90일 유효)
  • ECDSA 키 사용: RSA보다 키 크기가 작고 성능이 좋은 ECDSA P-256을 권장합니다
  • Prometheus 알림: certmanager_certificate_expiration_timestamp_seconds로 만료 임박 인증서를 감지하세요
  • Secret 레이블링: secretTemplate으로 Reloader 등과 연동하여 인증서 갱신 시 파드를 자동 재시작하세요
위로 스크롤
WordPress Appliance - Powered by TurnKey Linux