혹시나 나와 비슷하게 몇몇 ingress만 제외하고 싶다! 라는 상황에 있는 사람이 있다면
참고가 되어 삽질을 최소화 할 수 있지 않을까 싶어 공유한다.(나만 삽질한 것 같다.)
EKS와 external-dns
, 그리고 alb-ingress-controller
와 함께라면 컨트롤러들이 알아서 ALB와 DNS Record를 관리해주니 참 좋은 세상인 것 같다.
그런데 최근 WAF를 적용하게 되면서 서비스에 대한 접근을 WAF를 경유하도록 하기위해 DNS Record를 설정해야하는 일이 있었는데, 이때 external-dns
가 이미 관리하에 두고있는 Record를 변경하니,곧장 원래 상태로 원상복귀 되면서 작업을 진행할 수 없었다.(그게 맞다.)
그렇다. external-dns의 document를 보면 ingress에 아래와 같이 annotation을 달아서 Record를 관리하라고 안내되어있다.
즉, ingress에 external-dns.alpha.kubernetes.io/hostname
annotation이 달려있으면
external-dns가 주기적으로 스윽 살펴보고 해당 설정값을 Route53에 달아주는 것이었다.
그래서 나도 이걸 제거하면 자유롭게 DNS Record를 수정하게 될 줄 알았다.
그런데 ingress의 rule도 보는 것 인지, 이걸 제거해도 변함없이 Record가 달렸다.
(* 정확히 알고 계신분이 있다면 알려주셨으면 좋겠다.)
당장 작업은 내일 다시 시작인데.... 그래서 특정 ingress를 예외처리 할 수 없을까 하며
예외처리에 대해서 찾기 시작했다.
당연히 생각하지 않았다.
몇몇 record를 위해 모두를 포기할 수 없었다.
external-dns
의 github issue를 살펴보면, 이것이 기존의 DNS record를 망치면 어쩌냐? 라는 질문이 종종 보인다. 그에 대한 대답은 아래와 같이, TXT 레코드를 이용해 추적할 수 있는 record만 제어한다고 나와있다.
I'm afraid you will mess up my DNS records!
...
ExternalDNS since v0.3 implements the concept of owning DNS records. This means that ExternalDNS will keep track of which records it has control over, and will never modify any records over which it doesn't have control.
...
For now ExternalDNS uses TXT records to label owned records, and there might be other alternatives coming in the future releases.
- External-dns document
때문에 이러한 것을 역(?)이용 하여, TXT record가 달리기 전, 미리 record를 등록해둘까 싶었다.
그러자, 아래와 비슷한 external-dns는 400 error를 뱉어내기 시작했다.
level=error msg="InvalidChangeBatch: [Tried to create resource record set [name='abc.example.com.', type='TXT'] but it already exists]\n\tstatus code: 400, request id: 474634fd-0cc8-4a08-be3e-45cf2944d6a8"
무시할까 싶었지만 log를 살펴보기에도 좋지 않았고, error를 방치해 예상치 못한 문제가 발생하는걸 원치 않았다. 사실 이러한 생각을 하는 것 자체가 이상했다.
external-dns의 AWS tutorial을 살펴보면 --exclude-domains
설정이 존재한다.
--exclude-domains=ignore.this.example.com to exclude a domain or subdomain
오호... 이런게 있구나 싶어 external-dns에 달아봤더니 아무런 반응이 없었다.
본 글의 초입부에서도 말했지만, 내가 잘못 사용하고 있을 확률이 컸다.
그 외에도, 다양한 filter
옵션을 살펴봤지만 마땅한 것이 보이지 않았다.
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, godaddy, google, azure, azure-dns, azure-private-dns, bluecat, cloudflare, rcodezero, digitalocean, hetzner, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, scaleway, vultr, ultradns, gandi)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "hetzner", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "scaleway", "vultr", "ultradns", "godaddy", "bluecat", "gandi")
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
app.Flag("exclude-domains", "Exclude subdomains (optional)").Default("").StringsVar(&cfg.ExcludeDomains)
app.Flag("regex-domain-filter", "Limit possible domains and target zones by a Regex filter; Overrides domain-filter (optional)").Default(defaultConfig.RegexDomainFilter.String()).RegexpVar(&cfg.RegexDomainFilter)
app.Flag("regex-domain-exclusion", "Regex filter that excludes domains and target zones matched by regex-domain-filter (optional)").Default(defaultConfig.RegexDomainExclusion.String()).RegexpVar(&cfg.RegexDomainExclusion)
app.Flag("zone-name-filter", "Filter target zones by zone domain (For now, only AzureDNS provider is using this flag); specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneNameFilter)
app.Flag("zone-id-filter", "Filter target zones by hosted zone id; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneIDFilter)
그렇게 구글구글 하던 와중, 나와 비슷한 고민을 했던 엔지니어가 github에 등록한 issue를 만났다.
쭉...읽다보니...!! 엇!!
이제야 본론이 나온다.
위 내용을 참고하여 설정을 하기 시작했다.
우선 현재 우리의 상용/개발 환경에서 움직이고 있던 external-dns
의 controller 역할을 하던 놈은 너무나 오래전에 만들어져 상당히 옛날 버전의 image로 움직이고 있었다.
때문에, 먼저 external-dns
의 image를 그나마 최근 버전으로 업데이트 해주었다.
kubectl set image deployments/external-dns external-dns=k8s.gcr.io/external-dns/external-dns:v0.7.
요렇게 deployment
의 image만 쏙 교체해주면 된다.
이렇게 image 교체하면 기존 레코드가 망가지는게 아닐까 했지만 앞서 말했듯, TXT로 추적되는 레코드에 대해서만 제어를 하고, 또한 ingress가 사라지지 않는 이상 record를 삭제하지 않으니 아무런 문제가 없었다.
여기서 image update 후, 아래와 같이 timed out waiting for the condition
에러가 발생한다면 external-dns
가 사용하는 clueterrole
의 권한을 잘 살펴보자.
time="2022-01-12T10:08:20Z" level=fatal msg="failed to sync cache: timed out waiting for the condition"
공식적(?)으로 아래와 같은 권한을 요구한다.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: ['']
resources: ['endpoints', 'pods', 'services']
verbs: ['get', 'watch', 'list']
- apiGroups: ['extensions']
resources: ['ingresses']
verbs: ['get', 'watch', 'list']
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["watch", "list"]
controller 역할을 하는 external-dns
pod에 --annotation-filter
를 추가한다.
이것이 있어야 ingress에 exlude 필터를 인식하고 무시한다.
external-dns
pod에 직접 달면 어떠한 문제로 pod가 재시작 될때 삭제되니 deployment에 달아주자
kubectl edit deployment external-dns
containers:
- args:
- --source=service
- --source=ingress
- --domain-filter=exmaple.com
- --provider=aws
- --policy=upsert-only
- --aws-zone-type=public
- --registry=txt
- --txt-owner-id=my-identifier
+ - --annotation-filter=external-dns.alpha.kubernetes.io/exclude notin (true)
1. External-dns에 annotation-filetr annotation 추가
에서 특정 annotation이 있으면 record 처리는 무시해! 라고 해줬으니 이제부터는 예외처리 하고싶은 ingress의 annotations에 exclude를 달아보자.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/listen-ports: ***
alb.ingress.kubernetes.io/actions.ssl-redirect: '***
alb.ingress.kubernetes.io/inbound-cidrs: 0.0.0.0/0
alb.ingress.kubernetes.io/scheme: "internet-facing"
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/certificate-arn: ***
alb.ingress.kubernetes.io/load-balancer-name: ***
external-dns.alpha.kubernetes.io/hostname: ***
alb.ingress.kubernetes.io/healthcheck-path: ***
alb.ingress.kubernetes.io/load-balancer-attributes: ***
+ external-dns.alpha.kubernetes.io/exclude: "true"
spec:
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
이렇게 되면, 이 ingress가 필요로 하는 record는 처리되지 않고 무시된다.
현재 참여하고 있는 서비스는 infra를 최소한으로 하고 kubernetes의 controller 등을 최대한 사용하고 있다.(사실 이런게 kubernetes를 정말 kubernetes 답게 사용하는 것이라 생각한다.)
때문에 LB나 DNS도 이렇게 kubernetes controller를 이용해 제어하고 있고 이러한 과정에서 기존의 운영 방식과는 다른 방법을 찾아야할 때가 종종 발생한다.
나는 kubernetes를 이 팀에 참여해서 처음 접하게 된 것이라 이런 일이 발생하면 항상 삽질을 하지만 오히려 앞으로는 더 심도 깊은 삽질(?)을 해보고 싶다. 이왕 하는거 resource limit이나 network 제어도 좀 해보고....
안녕하세요 삽질기 감사합니다 도움이 되었습니다.🙏
policy=upsert-only로 사용하고 레코드 삭제가 필요할때는 exclude annotation을 걸어 제외시키신다는 말씀이시죠?