Aws-external-dns: различия между версиями

Материал из noname.com.ua
Перейти к навигацииПерейти к поиску
 
(не показана 31 промежуточная версия этого же участника)
Строка 1: Строка 1:
 
[[Категория: AWS]]
 
[[Категория: AWS]]
[[Категория: K8S]]
+
[[Категория: k8s]]
 
[[Категория: EKS]]
 
[[Категория: EKS]]
  +
[[Категория: ExternalDNS]]
  +
 
=Постановка задачи=
 
=Постановка задачи=
 
Задача: есть некоторый базовый домен <code>mydomain.tld</code>, который лежит на серверах AWS в Route53
 
Задача: есть некоторый базовый домен <code>mydomain.tld</code>, который лежит на серверах AWS в Route53
 
<BR>
 
<BR>
 
Требуется при разворачивании приложения в EKS автоматически создавать записи типа CNAME и <BR>
 
Требуется при разворачивании приложения в EKS автоматически создавать записи типа CNAME и <BR>
направлять их на балансировщик (Ingress или другой, но пока Ingress),
+
направлять их на балансировщик (<code>Ingress</code> или другой, в перспективе ALB но пока <code>Ingress</code>),
 
при запуске экземпляра приложения, например для <BR>
 
при запуске экземпляра приложения, например для <BR>
 
экземпляра <B><code>staging</code></B> создать записи
 
экземпляра <B><code>staging</code></B> создать записи
Строка 16: Строка 18:
 
Имя экземпляра приложения <B><code>staging</code></B> может быть произвольным
 
Имя экземпляра приложения <B><code>staging</code></B> может быть произвольным
   
  +
=Обзор решения=
=Решение=
 
  +
Для того что бы работать с доменами как с ресурсами k8s требуется обработчик этих ресурсов, <BR>
==IAM Policy==
 
  +
в этом случае это [https://github.com/kubernetes-sigs/external-dns ExternalDns]
  +
<BR>
  +
Далее настройки касаются только AWS, в случае других провайдеров/регистраторо настройки будут отличаться
  +
<BR>
  +
* Создать роль которая разрешает модификацию DNS
  +
* Создать политику Trust Policy — это специальный документ JSON, ВСТРОЕННЫЙ В РОЛЬ, НЕ ОТДЕЛЬНАЯ СУЩНОСТЬ, который определяет, кто имеет право "представлять" IAM роль, то есть использовать её права.
  +
Она не определяет, что роль может делать. Она определяет, кто может её использовать. Например:
  +
** EC2-инстанс
  +
** Lambda-функция
  +
** Конкретный экзкмпляр Kubernetes пода (через запуск пода от имени Service Account) через OIDC
  +
  +
* Policy привязывается к <code>IAM ROLE с trust policy</code>
  +
* Создается сераисный аккаунт с аннотацией
  +
<PRE>
  +
apiVersion: v1
  +
kind: ServiceAccount
  +
metadata:
  +
name: my-sa
  +
namespace: my-namespace
  +
annotations:
  +
eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/MyEksIamRole
  +
</PRE>
  +
Который будет обладать всеми правами прописанными в полиси
  +
* Под этим аккаунтом запускается POD который следит за ДНС и вносит изменения при необходимости
  +
  +
=IAM Policy=
 
Для начала потребуется создать политику следующего вида:
 
Для начала потребуется создать политику следующего вида:
   
Строка 58: Строка 86:
 
<code> aws route53 list-hosted-zones-by-name | jq '.HostedZones[] | select(.Name=="<b>mydomain.tld</b>") | .Id' </code>
 
<code> aws route53 list-hosted-zones-by-name | jq '.HostedZones[] | select(.Name=="<b>mydomain.tld</b>") | .Id' </code>
   
  +
=Создание IAM Role (Trust Policy)=
==1==
 
  +
Далее требуется создать роль (которая далее будет привязана к Service Account).
  +
<BR>
  +
Это отдельный вид роли который позвоялет получить права этой роли для Service Account (отсюда и Trust Policy - часть роли, описываюший разрешение на то кто может представляться этой рольую)
  +
<BR>
  +
Пример роли (в web console я не нашл как вставить сразу json но результат можно посмотреть в таком виде и сравнить с желаемым):
  +
<BR>
 
<PRE>
 
<PRE>
 
{
 
{
Строка 83: Строка 117:
 
}
 
}
 
</PRE>
 
</PRE>
  +
Значения
  +
* <code>arn:aws:iam::54XXXXXXXXXX</code> это ID аккаунта
  +
* <code>oidc.eks.us-east-1.amazonaws.com/id/5918A6B3305CXXXXXXXXXXXXXXXXXXXX</code> как правильно называется это значение,
  +
я не знаю, но получить его можно следующей коммандой: <BR>
  +
  +
<code>aws eks describe-cluster --name education-eks-1o3RFCvh --query "cluster.identity.oidc.issuer"</code>
  +
<BR>
  +
* <code>education-eks-1o3RFCvh</code> это имя кластера
  +
  +
Вывод команды будет примерно таким: <BR>
  +
  +
<PRE>
  +
"https://oidc.eks.us-east-1.amazonaws.com/id/5918A6B3305CXXXXXXXXXXXXXXXXXXXX"
  +
</PRE>
  +
{{caution|text=
  +
В интиерентаах пишут что нужно отдельно включать OIDC командой
  +
<PRE>
  +
eksctl utils associate-iam-oidc-provider --region=us-east-2 --cluster=education-eks-1o3RFCvh --approve
  +
</PRE>
  +
но возможно эта информация уже устарела или зависит от того как именно создавался кластер
  +
}}
  +
  +
=Привязка политики к роли=
  +
Я "накликал" это в Web Console :(
  +
  +
=External DNS=
  +
  +
* https://github.com/kubernetes-sigs/external-dns
  +
  +
<code>ExternalDNS</code> это код который работает внутри кластера k8s от имени сервисного аккаунта, <BR>
  +
связанного с <code>IAM</code> ролью и за счет этого получает все права, которые есть у роли
  +
==Подготовительная работа==
  +
<BR>
  +
Добавить репозиторий
  +
<PRE>
  +
helm repo add bitnami https://charts.bitnami.com/bitnami
  +
</PRE>
  +
При желании для изучения можно скачать чарт:
  +
<PRE>
  +
helm pull bitnami/external-dns
  +
</PRE>
  +
Создать отдельный <code>namespace</code> (опционально - но лучше конечно создавать):
  +
<PRE>
  +
kubectl create namespace external-dns
  +
</PRE>
  +
Подготовить конфигурационный файл <code>SERVICE-ACCOUNT-external-dns.yaml</code> для сервисного аккаунта следующего содержания:
  +
<PRE>
  +
---
  +
apiVersion: v1
  +
kind: ServiceAccount
  +
metadata:
  +
name: external-dns
  +
namespace: external-dns
  +
annotations:
  +
eks.amazonaws.com/role-arn: arn:aws:iam::54XXXXXXXXXX:role/ExternalDNSServiceAccountRole
  +
</PRE>
  +
В этом файле ключевой является аннотация:
  +
* <code>arn:aws:iam::54XXXXXXXXXX:role/ExternalDNSServiceAccountRole</code> - это ссылка на созданную IAM ROLE с правами работы с DNS
  +
  +
Создать Service Account
  +
<PRE>
  +
kubectl apply -f SERVICE-ACCOUNT-external-dns.yaml
  +
serviceaccount/external-dns created
  +
</PRE>
  +
<BR>
  +
  +
{{caution|text=
  +
ExternalDNS будет обрабатывать только те ресурсы, у которых аннотация <BR>
  +
<code>external-dns.alpha.kubernetes.io/controller</code> <BR>
  +
совпадает с указанной в параметре <code>--controller</code>. <BR>
  +
Остальные <code>Ingress</code>, даже с нужными доменами, игнорируются. <BR>
  +
}}
  +
  +
==Установка==
  +
Устанавлить <code>External DNS</code> с нужными параметрами
  +
<PRE>
  +
helm upgrade \
  +
--install external-dns bitnami/external-dns \
  +
--namespace external-dns \
  +
--set serviceAccount.name=external-dns \
  +
--set serviceAccount.create=false \
  +
--set provider=aws \
  +
--set sources={ingress} \
  +
--set domainFilters[0]=mydomain.tld \
  +
--set domainFilters[1]=myseconddomain.tld \
  +
--set policy=upsert-only \
  +
--set registry=txt \
  +
--set txtOwnerId=external-dns \
  +
--set logLevel=debug
  +
</PRE>
  +
  +
* <code> serviceAccount.name=external-dns</code> - использовать предсозданный <code>Service Account</code>
  +
* <code> serviceAccount.create=false</code> - не создавать <code>Service Account</code> при установке chart
  +
* <code> provider=aws</code> - очевидно
  +
* <code> sources={ingress}</code> - смотреть на домены только на <code>Ingress</code> (это массив, его элементы можно перечислить, я не уверен что это елинственно-возможный синтаксис)
  +
* <code> domainFilters[0]=mydomain.tld</code> - оционально, разрешить работу только с 2 зонами (это тоже массив)
  +
* <code> domainFilters[1]=myseconddomain.tld</code>
  +
* <code> policy=upsert-only</code> насколько я понимаю, эта опция означает "не удалять", другими словами при удалении <code>Ingress</code> запись останется (вероятно это более безопасный вариант?)
  +
* <code> registry=txt</code>
  +
* <code> txtOwnerId=external-dns</code>
  +
* <code> logLevel=debug</code> (options: panic, debug, info, warning, error, fatal, trace)
  +
  +
{{caution|text=
  +
Нигде в документации толком не сказано, что за опция <code>txt-owner-id</code> — она определяет значения <code>TXT</code>-записи,<BR>
  +
которая создаётся <code>ExternalDNS</code> для записей, которые он создаёт, что бы отменить их как «свои», т.е. те, которые управляются им.
  +
<BR>
  +
Если, к примеру, у вас в домене уже есть запись <code>subdomain.example.com</code>, и к ней нет <code>TXT</code>, <BR>
  +
которую добавляет <code>ExternalDNS</code>, то при создании <code>Ingress</code> с host: <code>subdomain.example.com</code> — <code>ExternalDNS</code> <BR>
  +
ничего с существующей записью не сделает.
  +
<BR>
  +
(https://rtfm.co.ua/ru/kubernetes-obnovlenie-dns-v-route53-pri-sozdanii-ingress/)
  +
}}
  +
  +
==Настройка <code>Ingress</code>==
  +
Настройка <code>Ingress</code> сводится к указанию правильной аннотации
  +
<PRE>
  +
apiVersion: networking.k8s.io/v1
  +
kind: Ingress
  +
metadata:
  +
annotations:
  +
...
  +
external-dns.alpha.kubernetes.io/controller: dns-controller
  +
...
  +
</PRE>
  +
  +
=Дополнительно проверить=
  +
* Для домашней лабы - интеграцию с Mikrotk (Вроде бы заявлено)

Текущая версия на 11:10, 29 июня 2025


Постановка задачи

Задача: есть некоторый базовый домен mydomain.tld, который лежит на серверах AWS в Route53
Требуется при разворачивании приложения в EKS автоматически создавать записи типа CNAME и
направлять их на балансировщик (Ingress или другой, в перспективе ALB но пока Ingress), при запуске экземпляра приложения, например для
экземпляра staging создать записи

  • backend.staging.mydomain.tld
  • frontend.staging.mydomain.tld
  • что-то-еще.staging.mydomain.tld

Имя экземпляра приложения staging может быть произвольным

Обзор решения

Для того что бы работать с доменами как с ресурсами k8s требуется обработчик этих ресурсов,
в этом случае это ExternalDns
Далее настройки касаются только AWS, в случае других провайдеров/регистраторо настройки будут отличаться

  • Создать роль которая разрешает модификацию DNS
  • Создать политику Trust Policy — это специальный документ JSON, ВСТРОЕННЫЙ В РОЛЬ, НЕ ОТДЕЛЬНАЯ СУЩНОСТЬ, который определяет, кто имеет право "представлять" IAM роль, то есть использовать её права.

Она не определяет, что роль может делать. Она определяет, кто может её использовать. Например:

    • EC2-инстанс
    • Lambda-функция
    • Конкретный экзкмпляр Kubernetes пода (через запуск пода от имени Service Account) через OIDC
  • Policy привязывается к IAM ROLE с trust policy
  • Создается сераисный аккаунт с аннотацией
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-sa
  namespace: my-namespace
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/MyEksIamRole

Который будет обладать всеми правами прописанными в полиси

  • Под этим аккаунтом запускается POD который следит за ДНС и вносит изменения при необходимости

IAM Policy

Для начала потребуется создать политику следующего вида:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ExternalDNSAccess",
      "Effect": "Allow",
      "Action": [
        "route53:ChangeResourceRecordSets",
        "route53:ListResourceRecordSets"
      ],
      "Resource": [
        "arn:aws:route53:::hostedzone/Z1038XXXXXXXXXXXXXXX",
        "arn:aws:route53:::hostedzone/Z0235XXXXXXXXXXXXXXX"
    ]
    },
    {
      "Effect": "Allow",
      "Action": "route53:ListHostedZones",
      "Resource": "*"
    }
  ]
}

В том пример присутствуют две записи:

"arn:aws:route53:::hostedzone/Z1038XXXXXXXXXXXXXXX",
"arn:aws:route53:::hostedzone/Z0235XXXXXXXXXXXXXXX"

так как приложение использует более чем одну зону.


Для получения Id (Z1038XXXXXXXXXXXXXXX в примере) для домена mydomain.tld использовать комманду

aws route53 list-hosted-zones-by-name | jq '.HostedZones[] | select(.Name=="mydomain.tld") | .Id'

Создание IAM Role (Trust Policy)

Далее требуется создать роль (которая далее будет привязана к Service Account).
Это отдельный вид роли который позвоялет получить права этой роли для Service Account (отсюда и Trust Policy - часть роли, описываюший разрешение на то кто может представляться этой рольую)
Пример роли (в web console я не нашл как вставить сразу json но результат можно посмотреть в таком виде и сравнить с желаемым):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Principal": {
                "Federated": "arn:aws:iam::54XXXXXXXXXX:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/5918A6B3305CXXXXXXXXXXXXXXXXXXXX"
            },
            "Condition": {
                "StringEquals": {
                    "oidc.eks.us-east-1.amazonaws.com/id/5918A6B3305CXXXXXXXXXXXXXXXXXXXX:sub": [
                        "system:serviceaccount:external-dns:external-dns"
                    ],
                    "oidc.eks.us-east-1.amazonaws.com/id/5918A6B3305CXXXXXXXXXXXXXXXXXXXX:aud": [
                        "sts.amazonaws.com"
                    ]
                }
            }
        }
    ]
}

Значения

  • arn:aws:iam::54XXXXXXXXXX это ID аккаунта
  • oidc.eks.us-east-1.amazonaws.com/id/5918A6B3305CXXXXXXXXXXXXXXXXXXXX как правильно называется это значение,

я не знаю, но получить его можно следующей коммандой:

aws eks describe-cluster --name education-eks-1o3RFCvh --query "cluster.identity.oidc.issuer"

  • education-eks-1o3RFCvh это имя кластера

Вывод команды будет примерно таким:

"https://oidc.eks.us-east-1.amazonaws.com/id/5918A6B3305CXXXXXXXXXXXXXXXXXXXX"

Icon-caution.gif

В интиерентаах пишут что нужно отдельно включать OIDC командой

eksctl utils associate-iam-oidc-provider --region=us-east-2 --cluster=education-eks-1o3RFCvh --approve

но возможно эта информация уже устарела или зависит от того как именно создавался кластер

Привязка политики к роли

Я "накликал" это в Web Console :(

External DNS

ExternalDNS это код который работает внутри кластера k8s от имени сервисного аккаунта,
связанного с IAM ролью и за счет этого получает все права, которые есть у роли

Подготовительная работа


Добавить репозиторий

helm repo add bitnami https://charts.bitnami.com/bitnami

При желании для изучения можно скачать чарт:

helm pull bitnami/external-dns

Создать отдельный namespace (опционально - но лучше конечно создавать):

kubectl create namespace external-dns

Подготовить конфигурационный файл SERVICE-ACCOUNT-external-dns.yaml для сервисного аккаунта следующего содержания:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
  namespace: external-dns
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::54XXXXXXXXXX:role/ExternalDNSServiceAccountRole

В этом файле ключевой является аннотация:

  • arn:aws:iam::54XXXXXXXXXX:role/ExternalDNSServiceAccountRole - это ссылка на созданную IAM ROLE с правами работы с DNS

Создать Service Account

kubectl apply -f SERVICE-ACCOUNT-external-dns.yaml
serviceaccount/external-dns created


Icon-caution.gif

ExternalDNS будет обрабатывать только те ресурсы, у которых аннотация
external-dns.alpha.kubernetes.io/controller
совпадает с указанной в параметре --controller.
Остальные Ingress, даже с нужными доменами, игнорируются.

Установка

Устанавлить External DNS с нужными параметрами

helm upgrade \
    --install external-dns bitnami/external-dns \
    --namespace external-dns \
    --set serviceAccount.name=external-dns \
    --set serviceAccount.create=false \
    --set provider=aws \
    --set sources={ingress} \
    --set domainFilters[0]=mydomain.tld \
    --set domainFilters[1]=myseconddomain.tld \
    --set policy=upsert-only \
    --set registry=txt \
    --set txtOwnerId=external-dns \
    --set logLevel=debug
  • serviceAccount.name=external-dns - использовать предсозданный Service Account
  • serviceAccount.create=false - не создавать Service Account при установке chart
  • provider=aws - очевидно
  • sources={ingress} - смотреть на домены только на Ingress (это массив, его элементы можно перечислить, я не уверен что это елинственно-возможный синтаксис)
  • domainFilters[0]=mydomain.tld - оционально, разрешить работу только с 2 зонами (это тоже массив)
  • domainFilters[1]=myseconddomain.tld
  • policy=upsert-only насколько я понимаю, эта опция означает "не удалять", другими словами при удалении Ingress запись останется (вероятно это более безопасный вариант?)
  • registry=txt
  • txtOwnerId=external-dns
  • logLevel=debug (options: panic, debug, info, warning, error, fatal, trace)

Icon-caution.gif

Нигде в документации толком не сказано, что за опция txt-owner-id — она определяет значения TXT-записи,
которая создаётся ExternalDNS для записей, которые он создаёт, что бы отменить их как «свои», т.е. те, которые управляются им.
Если, к примеру, у вас в домене уже есть запись subdomain.example.com, и к ней нет TXT,
которую добавляет ExternalDNS, то при создании Ingress с host: subdomain.example.comExternalDNS
ничего с существующей записью не сделает.
(https://rtfm.co.ua/ru/kubernetes-obnovlenie-dns-v-route53-pri-sozdanii-ingress/)

Настройка Ingress

Настройка Ingress сводится к указанию правильной аннотации

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    ...
    external-dns.alpha.kubernetes.io/controller: dns-controller
    ...

Дополнительно проверить

  • Для домашней лабы - интеграцию с Mikrotk (Вроде бы заявлено)