Cloudera Flow Management(CFM) Kubernetes Operator 처럼 웹훅을 쓰는 오퍼레이터는 대부분 cert-manager 를 선행 조건으로 요구한다. 인터넷이 닿지 않는 클러스터에서는 차트와 이미지를 밖에서 받아 들여와야 하고, 설치를 한 번 실패하면 CRD 만 남아 재설치가 막히는 함정이 있다.
차트는 helm pull ... --untar 로 디렉터리째 가져간다. crds/ 하위가 함께 따라오므로 디렉터리 전체를 옮긴다.
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm pull jetstack/cert-manager --version ${CM_VERSION} --untar
이미지는 세 개가 필수다. ACME 인증서를 발급하지 않는다면 acmesolver 는 쓰이지 않고, startupapicheck 는 설치 직후 API 가 살아 있는지 확인하는 일회성 Job 이다.
| 구성 요소 | 이미지 | 필수 |
|---|---|---|
| controller | quay.io/jetstack/cert-manager-controller |
예 |
| webhook | quay.io/jetstack/cert-manager-webhook |
예 |
| cainjector | quay.io/jetstack/cert-manager-cainjector |
예 |
| startupapicheck | quay.io/jetstack/cert-manager-startupapicheck |
아니오 |
| acmesolver | quay.io/jetstack/cert-manager-acmesolver |
ACME 쓸 때만 |
이미지 태그는 차트 버전과 같은 값을 쓴다. 실제 목록은 차트 안에서 직접 뽑는 것이 가장 정확하다.
helm template cert-manager ./cert-manager --set installCRDs=true \
| grep -oE 'image: .*' | sort -u
내부 레지스트리로 옮길 때는 docker save / docker load 대신 레지스트리 간 복사 도구를 쓰는 편이 태그 관리가 편하다. 옮긴 뒤 values.yaml 에서 레지스트리를 바꾼다.
image:
repository: harbor.example.local/cert-manager/cert-manager-controller
webhook:
image:
repository: harbor.example.local/cert-manager/cert-manager-webhook
cainjector:
image:
repository: harbor.example.local/cert-manager/cert-manager-cainjector
startupapicheck:
enabled: false
폐쇄망에서 startupapicheck 는 꺼 두는 편이 낫다. 이미지를 빠뜨리면 ImagePullBackOff 로 남아 설치가 실패한 것처럼 보이지만, 실제 cert-manager 기능에는 영향이 없다.
imagePullSecrets 에 적은 시크릿은 같은 네임스페이스에 있는 것만 참조된다. 다른 네임스페이스의 시크릿이 자동으로 따라오지 않는다. 네임스페이스를 새로 만들 때마다 시크릿을 다시 만들거나 복사해야 한다.
kubectl create secret docker-registry regcred \
--docker-server=harbor.example.local \
--docker-username=${REGISTRY_USER} \
--docker-password=${REGISTRY_PASSWORD} \
-n cert-manager
# 기존 시크릿을 다른 네임스페이스로 복사
kubectl get secret regcred -n default -o yaml \
| sed 's/namespace: default/namespace: cert-manager/' \
| kubectl apply -f -
sed 로 복사할 때 resourceVersion · uid · creationTimestamp 가 함께 넘어가면 적용이 거부될 수 있다. 그런 경우 kubectl get secret regcred -n default -o yaml | kubectl neat 같은 정리를 거치거나, 애초에 create secret 으로 다시 만든다.
설치를 한 번 실패한 뒤 다시 helm install 하면 이 메시지가 나온다.
Unable to continue with install: CustomResourceDefinition "certificaterequests.cert-manager.io"
in namespace "" exists and cannot be imported into the current release:
invalid ownership metadata; label validation error: missing key "app.kubernetes.io/managed-by":
must be set to "Helm"
Helm 은 자기가 만들지 않은 리소스를 릴리스에 편입시키지 않는다. 클러스터에 이미 있는 CRD 에는 app.kubernetes.io/managed-by: Helm 라벨과 릴리스 이름 · 네임스페이스 주석이 없으므로 "이건 내 것이 아니다" 라며 멈춘다. 흔한 경위는 셋이다 — kubectl apply -f crds/ 로 CRD 를 먼저 적용했거나, 설치가 중간에 실패해 CRD 만 남았거나, helm uninstall 이 CRD 를 남기고 지워졌거나.
Helm 은 릴리스를 삭제할 때 CRD 를 지우지 않는다. 이것이 설계된 동작이다. CRD 를 지우면 그 CRD 로 만든 모든 사용자 리소스가 함께 사라지기 때문이다.
정리 방법은 상황에 따라 둘로 갈린다.
남은 CRD 를 지우고 다시 설치한다. 이 CRD 로 만들어진 Certificate · Issuer 가 없다는 것을 먼저 확인한다.
# 남아 있는 사용자 리소스 확인 — 비어 있어야 안전하다
kubectl get certificates,issuers,clusterissuers -A
# 릴리스가 남아 있으면 먼저 제거
helm uninstall cert-manager -n cert-manager
# CRD 제거
kubectl get crd -o name | grep cert-manager.io | xargs -r kubectl delete
# 재설치
helm install cert-manager ./cert-manager \
-n cert-manager --create-namespace -f values.yaml
CRD 를 지우면 그 구성 요소의 인증서가 전부 사라진다. 이때는 CRD 를 건드리지 말고 차트 쪽에서 CRD 설치를 끈다.
helm install cert-manager ./cert-manager \
-n cert-manager --create-namespace \
--set installCRDs=false -f values.yaml
대신 기존 CRD 버전이 차트가 기대하는 버전과 맞아야 한다. 맞지 않으면 웹훅이 인식하지 못하는 필드가 생긴다. kubectl get crd certificates.cert-manager.io -o jsonpath='{.spec.versions[*].name}' 로 확인한다.
--force 로 밀어붙이는 방법은 권하지 않는다. Helm 이 CRD 를 강제로 자기 소유로 표시하려 들면서 다른 구성 요소가 만든 리소스가 예기치 않게 정리될 수 있다.
kubectl get pods -n cert-manager
kubectl get crd | grep cert-manager.io
kubectl -n cert-manager logs deploy/cert-manager-webhook --tail=50
파드 세 개가 모두 Running 이고 웹훅 로그에 TLS 오류가 없으면 다음 단계로 넘어간다. 오퍼레이터를 설치했는데 x509 나 failed calling webhook 오류가 난다면 cainjector 가 CA 번들을 주입하기 전에 설치를 시작한 것이므로, 1~2분 기다린 뒤 다시 시도한다.
imagePullSecrets 구성.