External-dns에 exclude filter를 걸어보자

KEUN·2022년 1월 13일
1
post-thumbnail

혹시나 나와 비슷하게 몇몇 ingress만 제외하고 싶다! 라는 상황에 있는 사람이 있다면
참고가 되어 삽질을 최소화 할 수 있지 않을까 싶어 공유한다.(나만 삽질한 것 같다.)

🚪external-dns는 참 편해! 어라...?


EKS와 external-dns, 그리고 alb-ingress-controller 와 함께라면 컨트롤러들이 알아서 ALB와 DNS Record를 관리해주니 참 좋은 세상인 것 같다.

그런데 최근 WAF를 적용하게 되면서 서비스에 대한 접근을 WAF를 경유하도록 하기위해 DNS Record를 설정해야하는 일이 있었는데, 이때 external-dns가 이미 관리하에 두고있는 Record를 변경하니,곧장 원래 상태로 원상복귀 되면서 작업을 진행할 수 없었다.(그게 맞다.)

ingress에서 annotation을 제외하면 되잖아?

그렇다. external-dns의 document를 보면 ingress에 아래와 같이 annotation을 달아서 Record를 관리하라고 안내되어있다.

즉, ingress에 external-dns.alpha.kubernetes.io/hostname annotation이 달려있으면
external-dns가 주기적으로 스윽 살펴보고 해당 설정값을 Route53에 달아주는 것이었다.

그래서 나도 이걸 제거하면 자유롭게 DNS Record를 수정하게 될 줄 알았다.
그런데 ingress의 rule도 보는 것 인지, 이걸 제거해도 변함없이 Record가 달렸다.
(* 정확히 알고 계신분이 있다면 알려주셨으면 좋겠다.)

당장 작업은 내일 다시 시작인데.... 그래서 특정 ingress를 예외처리 할 수 없을까 하며
예외처리에 대해서 찾기 시작했다.

👷🏽삽질


external-dns를 내릴까?

당연히 생각하지 않았다.
몇몇 record를 위해 모두를 포기할 수 없었다.

그럼 external-dns가 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를 방치해 예상치 못한 문제가 발생하는걸 원치 않았다. 사실 이러한 생각을 하는 것 자체가 이상했다.

--exclude-domains 설정이 있다!

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)

Disable external-dns for specific ingresses #1910

그렇게 구글구글 하던 와중, 나와 비슷한 고민을 했던 엔지니어가 github에 등록한 issue를 만났다.

쭉...읽다보니...!! 엇!!

🛠filter 설정

이제야 본론이 나온다.

위 내용을 참고하여 설정을 하기 시작했다.

0. External-dns Image Update

우선 현재 우리의 상용/개발 환경에서 움직이고 있던 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"]

1. External-dns에 annotation-filetr annotation 추가

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)

2. 예외처리 하고싶은 ingress에 exclud annotation 추가

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 제어도 좀 해보고....

profile
Let's make something for comfortable development

2개의 댓글

comment-user-thumbnail
2022년 2월 10일

안녕하세요 삽질기 감사합니다 도움이 되었습니다.🙏
policy=upsert-only로 사용하고 레코드 삭제가 필요할때는 exclude annotation을 걸어 제외시키신다는 말씀이시죠?

1개의 답글