K8s cert-manager란?
cert-manager는 Kubernetes에서 TLS 인증서를 자동으로 발급·갱신·관리하는 컨트롤러다. Let’s Encrypt 같은 ACME CA부터 사내 CA, Vault까지 다양한 발급자를 지원한다. 수동 인증서 관리의 실수와 만료 사고를 근본적으로 방지한다.
1. cert-manager 설치
# 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, cert-manager-cainjector, cert-manager-webhook 3개 Pod
2. Issuer와 ClusterIssuer
인증서 발급자를 정의하는 두 리소스의 차이:
| 리소스 | 범위 | 사용 시나리오 |
|---|---|---|
Issuer |
네임스페이스 내 | 팀별 다른 CA 사용 시 |
ClusterIssuer |
클러스터 전체 | 공통 Let’s Encrypt 사용 시 |
# Let's Encrypt 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:
- http01:
ingress:
ingressClassName: nginx
---
# Let's Encrypt 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
⚠️ 반드시 staging으로 먼저 테스트한 후 production으로 전환하자. Let’s Encrypt production은 Rate Limit이 있어 잦은 재시도 시 차단될 수 있다.
3. Certificate 리소스로 발급
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: api-tls
namespace: default
spec:
secretName: api-tls-secret # 인증서가 저장될 Secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: api.example.com
dnsNames:
- api.example.com
- www.api.example.com
duration: 2160h # 90일
renewBefore: 360h # 만료 15일 전 갱신
privateKey:
algorithm: ECDSA
size: 256
# 인증서 상태 확인
kubectl get certificate
kubectl describe certificate api-tls
# 발급된 Secret 확인
kubectl get secret api-tls-secret -o yaml
# tls.crt, tls.key, ca.crt 포함
4. Ingress 자동 TLS (Annotation)
가장 간편한 방법은 Ingress에 annotation을 추가하는 것이다. cert-manager가 자동으로 Certificate를 생성하고 갱신한다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
# 선택: 인증서 옵션
cert-manager.io/private-key-algorithm: "ECDSA"
cert-manager.io/private-key-size: "256"
cert-manager.io/duration: "2160h"
cert-manager.io/renew-before: "360h"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
- admin.example.com
secretName: api-tls-auto # cert-manager가 자동 생성
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- host: admin.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: admin-service
port:
number: 3000
Ingress 설정 심화는 K8s Ingress NGINX 심화 글을 참고하자.
5. DNS01 챌린지: 와일드카드 인증서
HTTP01은 와일드카드(*.example.com)를 지원하지 않는다. DNS01 solver가 필요하다.
# Cloudflare DNS01 Solver
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:
- dns01:
cloudflare:
email: admin@example.com
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
---
# Cloudflare API Token Secret
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: Certificate
metadata:
name: wildcard-tls
spec:
secretName: wildcard-tls-secret
issuerRef:
name: letsencrypt-dns
kind: ClusterIssuer
dnsNames:
- "example.com"
- "*.example.com"
| 챌린지 | 와일드카드 | 요구사항 |
|---|---|---|
| HTTP01 | ❌ | 포트 80 오픈, Ingress 필요 |
| DNS01 | ✅ | DNS 프로바이더 API 접근 |
6. 사내 CA와 자체 서명 인증서
# 자체 서명 CA
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-issuer
spec:
selfSigned: {}
---
# CA 인증서 생성
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: internal-ca
namespace: cert-manager
spec:
isCA: true
commonName: internal-ca
secretName: internal-ca-secret
duration: 87600h # 10년
issuerRef:
name: selfsigned-issuer
kind: ClusterIssuer
---
# CA 기반 Issuer
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: internal-ca-issuer
spec:
ca:
secretName: internal-ca-secret
내부 서비스 간 mTLS에 자체 CA를 사용하면 Let’s Encrypt Rate Limit 걱정 없이 인증서를 발급할 수 있다. K8s Secret 관리 전반은 K8s 시크릿 암호화 3가지 글을 참고하자.
7. 모니터링과 트러블슈팅
# 인증서 상태 전체 확인
kubectl get certificates --all-namespaces
kubectl get certificaterequests --all-namespaces
kubectl get orders --all-namespaces
kubectl get challenges --all-namespaces
# 이벤트 확인
kubectl describe certificate api-tls
# Events:
# Normal Issuing cert-manager Issuing certificate...
# Normal Generated cert-manager Stored new private key
# Normal Requested cert-manager Created new CertificateRequest
# Normal Issuing cert-manager The certificate has been successfully issued
# Prometheus 메트릭
# certmanager_certificate_ready_status
# certmanager_certificate_expiration_timestamp_seconds
# 갱신 실패 알림 (PrometheusRule)
- alert: CertificateExpiringSoon
expr: certmanager_certificate_expiration_timestamp_seconds - time() < 7 * 24 * 3600
for: 1h
labels:
severity: warning
annotations:
summary: "인증서 {{ $labels.name }} 7일 내 만료"
마무리
cert-manager는 K8s 환경에서 TLS 인증서 관리를 완전히 자동화한다. Ingress annotation 한 줄로 Let's Encrypt 인증서를 발급받고, DNS01로 와일드카드를 처리하며, 자체 CA로 내부 mTLS를 구축할 수 있다. 핵심은 staging에서 테스트 → production 전환과 만료 모니터링 알림 설정이다.