Зачем сравнивать cert-manager и связку external-dns + ingress-shim
Когда настраиваете ACME в Kubernetes (Let’s Encrypt или любой ACME-совместимый УЦ), быстро становится ясно: выписать один сертификат — не проблема. Проблема начинается с операционки: десятки Ingress, разные namespace, wildcard на поддомены, ротации, rate limits, и главный вопрос — кто и как управляет приватными ключами и обновляет Secret типа kubernetes.io/tls.
На практике обычно встречаются два подхода:
- cert-manager как центральный контроллер: сам проходит ACME-челленджи (HTTP-01/DNS-01), создаёт/обновляет сертификаты и пишет их в Secrets.
- external-dns + ingress-shim как «склейка» автоматики вокруг Ingress: external-dns управляет DNS-записями, а shim переводит аннотации Ingress в заявки на сертификаты (часто всё равно через cert-manager).
Важный нюанс: ingress-shim исторически — часть cert-manager. То есть речь обычно не о двух конкурирующих решениях, а о выборе стиля управления: «явные CRD Certificate» против «магия аннотаций на Ingress».
Карта компонентов: кто за что отвечает
cert-manager
cert-manager добавляет в кластер CRD и контроллеры, которые описывают и исполняют жизненный цикл сертификатов:
Issuer/ClusterIssuer— где и как выпускать (ACME endpoint, email, solvers, ссылки на секреты с токенами DNS-провайдера).Certificate— декларация «выпусти SAN/wildcard и положи вsecretName».OrderиChallenge— внутренняя механика ACME (очень полезно для дебага).
Формально вы описываете желаемое состояние (YAML), а cert-manager доводит его до факта: генерирует ключи, проходит проверку домена, обновляет секрет и следит за продлением.
external-dns
external-dns читает источники в Kubernetes (Ingress, Service, Gateway API и т.д.) и создаёт/обновляет DNS-записи у провайдера. Он решает задачу «куда должен указывать домен», но не выпускает TLS-сертификаты сам по себе.
В контексте ACME external-dns полезен для автоматизации A/AAAA/CNAME, чтобы HTTP-01-челленджи и трафик на Ingress вообще доходили до кластера без ручной возни с DNS.
ingress-shim
ingress-shim превращает TLS-описание и аннотации на Ingress в ресурсы Certificate. Дальше уже cert-manager выпускает сертификат и пишет его в secretName, указанный в Ingress.
ingress-shim не заменяет cert-manager. Он только создаёт
Certificateпо Ingress, а ACME-протокол выполняет cert-manager.
Если вы строите более «платформенный» подход (контроль доменов, лимиты, изоляция), обычно проще жить с явными Certificate и минимальной «магией» в Ingress.

ACME HTTP-01 vs DNS-01 в Kubernetes
HTTP-01: быстро, но зависит от маршрутизации
HTTP-01 подтверждает владение доменом запросом по пути /.well-known/acme-challenge/.... В Kubernetes это обычно означает, что cert-manager создаёт временные ресурсы для отдачи challenge и ожидает, что внешний мир дойдёт до ingress-controller.
Что нужно, чтобы HTTP-01 стабильно работал:
- внешний DNS указывает на ваш ingress/LB;
- входящие 80/tcp доступны;
- маршрутизация не ломает challenge (редиректы, кастомные правила, несколько ingress-контроллеров).
Плюсы: минимум прав в DNS и простая модель для отдельных FQDN. Минусы: wildcard не поддерживается, а ошибки часто связаны не с ACME, а с сетевыми/ingress нюансами.
DNS-01: универсально и подходит для wildcard
DNS-01 подтверждает владение доменом через TXT-запись _acme-challenge. В Kubernetes это почти всегда лучший выбор для:
- wildcard-сертификатов (
*.example.com); - закрытых контуров без входящего HTTP;
- сложных схем с несколькими ingress-контроллерами.
Цена — доступ к DNS API. В идеале TXT для ACME создаёт только cert-manager (через встроенные интеграции или webhook solver), а external-dns параллельно занимается «прикладными» A/AAAA/CNAME. Подробно про автоматизацию wildcard через DNS-01 — в отдельном разборе: wildcard TLS и DNS-01 в автоматизации.
Issuer и ClusterIssuer: как не запутаться
Одна из самых частых ошибок: сделать Issuer в одном namespace, а потом ждать, что сертификаты выпустятся в другом.
Issuerдействует только в пределах namespace.ClusterIssuerдействует на весь кластер.
Практический подход для платформенной команды: используйте ClusterIssuer для типовых сценариев (HTTP-01, DNS-01) и выдавайте командам право создавать Certificate в их namespace. Если у команд разные DNS-зоны/аккаунты/провайдеры и строгая изоляция, тогда Issuer действительно может быть логичнее.
RBAC и безопасность: где чаще всего “протекает”
Почти все «почините cert-manager» в итоге упираются в доступы к Secrets и в соблазн выдать кому-то cluster-admin. Так делать не стоит: это ухудшает безопасность и затрудняет аудит.
Критичные зоны ответственности:
- Секреты с DNS API-токенами. Храните их в контролируемом namespace (часто
cert-manager) и давайте cert-manager ровно те права, которые нужны для чтения этого секрета. - TLS Secrets с приватными ключами. Они живут в namespace приложения. Любой, у кого есть read на этот
Secret, фактически получает приватный ключ — это нормально, но должно быть осознанно. - external-dns. Ему не нужен доступ к TLS secrets. Его зона ответственности — чтение источников и обновление DNS у провайдера.
Цель по безопасности простая: external-dns не видит приватные ключи и ACME/DNS-токены, а cert-manager не получает лишних прав на ресурсы приложения.
Что проще в эксплуатации: реальные сценарии
Сценарий A: отдельные домены без wildcard
Если у вас публичный Ingress и стабильный доступ по 80/tcp, то cert-manager + HTTP-01 закрывает задачу быстро. external-dns в этой схеме опционален: он полезен, когда DNS настраивать вручную уже тяжело, но сам выпуск сертификатов не заменит.
Где помогает ingress-shim: добавили TLS-секцию и аннотацию на Ingress — shim создал Certificate, cert-manager выпустил, секрет появился.
Сценарий B: wildcard на окружение/подзону
Для wildcard почти всегда выбирают DNS-01. В эксплуатации это выглядит просто: один Certificate на *.apps.example.com, один secretName, много Ingress, которые на него ссылаются.
Учитывайте “blast radius”: компрометация одного wildcard-секрета затрагивает все поддомены этой зоны. Если риск не устраивает — разделяйте зоны (например, по namespace/командам) или уходите от wildcard там, где это оправдано.
Сценарий C: мультикластер и/или несколько DNS-провайдеров
Здесь возникает соблазн разнести всё: external-dns делает DNS, команды где-то отдельно выпускают сертификаты и приносят их в виде Secrets. Это работает, но вы теряете единый жизненный цикл, ротации и наблюдаемость. На проде это часто заканчивается «сертификат истёк, никто не заметил».
Если по организационным причинам сертификаты должны выпускаться «снаружи», фиксируйте процесс: кто обновляет секреты, где мониторинг истечения, как делать rollback. Иначе платформа получится хрупкой.
cert-manager vs external-dns + ingress-shim: сравнение по критериям
Источник истины (source of truth)
- cert-manager-first: источником истины становится
Certificate(или Ingress, если вы сознательно используете shim). Состояние прозрачно в API Kubernetes. - аннотации как “контракт”: источником истины становится Ingress. Удобно на старте, но сложнее стандартизировать в большой платформе.
Контроль над DNS и ACME
- Для DNS-01 TXT должен создавать один компонент (обычно cert-manager). Это снижает гонки и упрощает дебаг.
- external-dns отлично подходит для A/AAAA/CNAME, но попытки «заставить его помогать» с
_acme-challengeчасто приводят к конфликтам владения записями.
Наблюдаемость и дебаг
С cert-manager удобнее: вы смотрите события и статусы Certificate, Order, Challenge и быстро понимаете, где сломалось: нет прав, DNS не обновился, HTTP недоступен, упёрлись в rate limit.
В размазанной схеме «external-dns + shim + ещё что-то» проблема часто выглядит одинаково (секрета нет), а причин может быть много. Если вы регулярно упираетесь в лимиты при массовых изменениях, пригодится отдельный разбор: лимиты Let’s Encrypt и массовая автоматизация.
Риски по безопасности
- Чем меньше компонентов имеют доступ к секретам и API-токенам, тем лучше.
- С cert-manager проще выстроить строгую модель: где лежат токены, где создаются TLS Secrets, кто может заказывать сертификаты и на какие домены.

Практические YAML-скелеты для ориентира
Ниже — минимальные «контуры» объектов, чтобы быстро сопоставить термины: ClusterIssuer/Issuer, Certificate, secretName. Обратите внимание: это примеры структуры, их нужно адаптировать под ваш ingress-класс и DNS-провайдера.
ClusterIssuer для ACME HTTP-01 (пример)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-http01
spec:
acme:
email: admin@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-http01-account-key
solvers:
- http01:
ingress:
class: nginx
ClusterIssuer для ACME DNS-01 (пример-скелет)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns01
spec:
acme:
email: admin@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-dns01-account-key
solvers:
- dns01:
webhook:
groupName: acme.example.com
solverName: dns-provider
config:
apiTokenSecretRef:
name: dns-api-token
key: token
Certificate для wildcard
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-apps
namespace: apps
spec:
secretName: wildcard-apps-tls
issuerRef:
kind: ClusterIssuer
name: letsencrypt-dns01
dnsNames:
- "*.apps.example.com"
- "apps.example.com"
Ingress, который использует готовый TLS secret
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
namespace: apps
spec:
tls:
- hosts:
- app1.apps.example.com
secretName: wildcard-apps-tls
rules:
- host: app1.apps.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app
port:
number: 80
Типовые грабли и как их распознать
Грабля 1: конфликт TXT-записей при DNS-01
Если два контроллера пытаются управлять _acme-challenge, появляются флаппинг TXT, validation failed и гонки. Правило простое: TXT для ACME создаёт только один выбранный компонент (обычно cert-manager).
Грабля 2: Ingress ссылается на secret, который не создался
Если shim создал Certificate, но issuerRef неверный (например, ожидался ClusterIssuer, а указан Issuer), TLS secret не появится, а ingress-controller будет ругаться на отсутствие секрета. Диагностика начинается со статуса Certificate и событий Order/Challenge, а не с самого Ingress.
Грабля 3: RBAC “починили” cluster-admin’ом и забыли
Такое «лечится» до первого инцидента. Если видите, что cert-manager или external-dns требуют слишком широких ролей, остановитесь и разберите: какие ресурсы читаются, какие создаются, в каких namespace и почему.
Грабля 4: rate limits и массовые перевыпуски
При миграциях и массовом пересоздании Ingress легко устроить лавину заявок в ACME и упереться в лимиты. Рабочие практики:
- используйте staging endpoint для прогонов и тестов;
- делайте wildcard там, где это действительно уменьшает количество выпусков;
- стабилизируйте
secretNameи не пересоздавайтеCertificateбез необходимости.
Рекомендации по выбору
Берите cert-manager как основу, если
- нужны декларативные объекты и предсказуемая ротация;
- планируются wildcard и DNS-01;
- важны наблюдаемость и единый стандарт для команд.
Подключайте external-dns дополнительно, если
- хотите автоматически создавать A/AAAA/CNAME по Ingress/Service;
- у вас много доменов/окружений и ручной DNS стал узким местом.
Осторожнее со ставкой на ingress-shim как на единственный интерфейс, если
- несколько ingress-контроллеров и разные классы Ingress;
- нужно строго контролировать, кто может выпускать сертификаты и для каких доменов;
- вы хотите разнести ответственность: платформа задаёт
Certificate, команды только подключают готовый TLS secret.
Итог
Самый устойчивый базовый вариант в Kubernetes — cert-manager как единый контроллер жизненного цикла сертификатов, а external-dns как отдельный слой для управления DNS-записями приложений. ingress-shim воспринимайте как удобный ускоритель на старте, но в зрелой платформе часто выгоднее явно описывать Certificate и контролировать, где и как создаются TLS Secrets.
Если важны wildcard, предсказуемость ротаций и понятный дебаг — выбирайте DNS-01 в cert-manager и аккуратно выстраивайте RBAC. Если нужна простота для небольшого набора доменов и стабильный публичный Ingress — HTTP-01 будет работать отлично, пока сеть и маршрутизация не мешают challenge.
Для инфраструктуры под Kubernetes, где приложения удобно разводить по окружениям и доменам, обычно также заранее решают вопрос с доменами и зоной: регистрация доменов и управление DNS лучше держать в предсказуемой системе, чтобы автоматизация ACME не упиралась в ручные правки.


