본문으로 건너뛰기

Cert-Manager

cert-manager는 Kubernetes 클러스터에서 TLS 인증서의 발급, 갱신, 관리를 자동화하는 도구입니다. Let's Encrypt ACME 프로토콜을 통해 무료 SSL/TLS 인증서를 자동으로 발급하고 관리합니다.

아키텍처

배포 구성

Helm Values

cert-manager는 공식 Helm 차트를 사용하여 배포됩니다:

# CRD 설치
installCRDs: true

# Controller 설정
replicaCount: 1
resources:
requests:
cpu: 15m
memory: 100Mi
limits:
memory: 100Mi

# Webhook 설정
webhook:
replicaCount: 1
resources:
requests:
cpu: 15m
memory: 100Mi
limits:
memory: 100Mi

# CA Injector 설정
cainjector:
replicaCount: 1
resources:
requests:
cpu: 15m
memory: 138Mi
limits:
memory: 138Mi

고가용성 설정

각 컴포넌트는 Pod Anti-Affinity를 통해 서로 다른 노드에 배포됩니다:

affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app.kubernetes.io/name: cert-manager
topologyKey: kubernetes.io/hostname

ClusterIssuer 구성

Let's Encrypt Production

프로덕션 환경에서 사용하는 ClusterIssuer 설정 (platform/cert-manager/common-values.yaml 기준):

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: bluemayne0213@icloud.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
# HTTP-01 Challenge (Traefik Ingress)
- http01:
ingress:
class: traefik
# DNS-01 Challenge (Cloudflare) — lumie-infra.com 와일드카드
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
selector:
dnsZones:
- lumie-infra.com

Let's Encrypt Staging

개발 및 테스트용 ClusterIssuer:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: bluemayne0213@icloud.com
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- http01:
ingress:
class: traefik

인증서 발급 방식

HTTP-01 Challenge

표준 도메인 인증서 발급에 사용됩니다:

DNS-01 Challenge

와일드카드 인증서 발급에 사용됩니다:

Vault 통합

Cloudflare API 토큰은 Vault에서 안전하게 관리됩니다:

apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: cloudflare-api-token-vss
namespace: cert-manager
spec:
vaultAuthRef: vault/vault-auth
mount: secret
type: kv-v2
path: tokens
refreshAfter: 1h
destination:
create: true
name: cloudflare-api-token
transformation:
excludeRaw: true
templates:
api-token:
text: "{{ .Secrets.CLOUDFLARE_API_KEYS }}"

인증서 사용법

Ingress에서 TLS 인증서 사용

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: lumie-frontend
namespace: lumie-frontend
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: traefik
tls:
- hosts:
- lumie-edu.com
secretName: lumie-frontend-tls
rules:
- host: lumie-edu.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: lumie-frontend
port:
number: 3000

Certificate 리소스 직접 생성

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: lumie-wildcard-tls
namespace: lumie
spec:
secretName: lumie-wildcard-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- "*.lumie-edu.com"
- "lumie-edu.com"

인증서 갱신

cert-manager는 인증서 만료 30일 전에 자동으로 갱신을 시작합니다.

갱신 프로세스

수동 갱신

필요시 인증서를 수동으로 갱신할 수 있습니다:

# 인증서 갱신 강제 트리거
kubectl annotate certificate lumie-wildcard-tls \
cert-manager.io/issuer-name=letsencrypt-prod \
--overwrite -n lumie

# 또는 Certificate 삭제 후 재생성
kubectl delete certificate lumie-wildcard-tls -n lumie

모니터링 및 관리

인증서 상태 확인

# 모든 인증서 상태 확인
kubectl get certificate -A

# 특정 인증서 상세 정보
kubectl describe certificate lumie-wildcard-tls -n lumie

# CertificateRequest 확인
kubectl get certificaterequest -A

# 인증서 만료일 확인
kubectl get certificate -A -o custom-columns=\
NAME:.metadata.name,NAMESPACE:.metadata.namespace,READY:.status.conditions[0].status,SECRET:.spec.secretName,AGE:.metadata.creationTimestamp

로그 확인

# cert-manager controller 로그
kubectl logs -n cert-manager deployment/cert-manager -f

# cert-manager webhook 로그
kubectl logs -n cert-manager deployment/cert-manager-webhook -f

# cert-manager cainjector 로그
kubectl logs -n cert-manager deployment/cert-manager-cainjector -f

이벤트 확인

# Certificate 이벤트
kubectl describe certificate -n lumie

# CertificateRequest 이벤트
kubectl describe certificaterequest -n lumie

# Challenge 이벤트 (ACME 문제 해결시)
kubectl describe challenge -A

Prometheus 메트릭

cert-manager는 다양한 메트릭을 제공합니다:

prometheus:
enabled: true
servicemonitor:
enabled: false # Prometheus Operator 사용시 true로 설정

주요 메트릭

  • certmanager_certificate_expiration_timestamp_seconds: 인증서 만료 시간
  • certmanager_certificate_ready_status: 인증서 준비 상태
  • certmanager_acme_client_request_count: ACME 요청 수
  • certmanager_acme_client_request_duration_seconds: ACME 요청 지연 시간

문제 해결

일반적인 문제

  1. HTTP-01 Challenge 실패

    • Traefik Ingress 설정 확인 (ingressClassName: traefik)
    • 도메인 DNS 설정 확인 (Cloudflare 프록시 상태 확인)
    • 방화벽 포트 80 개방 확인
  2. DNS-01 Challenge 실패

    • Cloudflare API 토큰 권한 확인
    • DNS 전파 시간 대기
    • Vault 시크릿 동기화 상태 확인
  3. 인증서 갱신 실패

    • 인증서 만료일 확인
    • ACME 계정 상태 확인
    • Rate Limit 확인

디버깅 명령어

# Challenge 리소스 확인 (ACME 문제시)
kubectl get challenge -A
kubectl describe challenge <challenge-name> -n <namespace>

# Order 리소스 확인
kubectl get order -A
kubectl describe order <order-name> -n <namespace>

# cert-manager 로그 레벨 증가
kubectl patch deployment cert-manager -n cert-manager -p \
'{"spec":{"template":{"spec":{"containers":[{"name":"cert-manager","args":["--v=2"]}]}}}}'

인증서 수동 정리

# 실패한 CertificateRequest 정리
kubectl delete certificaterequest --field-selector=status.conditions[0].status=False -A

# 만료된 인증서 시크릿 확인
kubectl get secrets -A -o json | jq -r '.items[] | select(.type=="kubernetes.io/tls") | select(.data."tls.crt" != null) | .metadata.namespace + "/" + .metadata.name'

보안 고려사항

  1. API 토큰 보안: Cloudflare API 토큰을 Vault에서 안전하게 관리
  2. 최소 권한 원칙: API 토큰에 필요한 최소 권한만 부여
  3. 인증서 백업: 중요한 인증서는 별도 백업 고려
  4. 모니터링: 인증서 만료 알림 설정
  5. Rate Limit: Let's Encrypt Rate Limit 고려하여 테스트는 Staging 환경 사용