본문 바로가기

AWS

[EKS] External DNS 설치 및 사용하기

반응형

External DNS란 Ingress 리소스를 생성함과 동시에 Route 53 ALB Node Pod로 트래픽이 단번에 라우팅될 수 있도록 한다.

=> ALB가 생성될 때 자동으로 Route53 Record에 등록되기 위함이다.

 

관련 자료

 

https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/aws.md

https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.2/examples/echo_server/#setup-external-dns-to-manage-dns-automatically

 

+ Security Context 관련 트러블슈팅 자료

https://git.blindage.org/Kubernetes/external-dns/commit/c97781a49dd3a1b7c8a1ccdf6d594217f3a28a17

=> IRSA를 통해 특정 Pod에 대해서만 External DNS에 대한 권한을 부여하고 싶었는데 securityContext 관련 권한 문제가 발생하였음. 그 이후, securityContext 수정한 뒤 아래처럼 정상적으로 배포된 것을 확인할 수 있었다.

 

⇒ /var/run/secrets/eks.amazonaws.com/serviceaccount/token: permission denied"


Controller 파드가 AWS 리소스에 대한 최소한의 권한을 지닌 Role을 생성한 뒤,

External DNS 컨트롤러를 생성하기 위한 매니페스트 파일 배포 후 Ingress를 생성하면 된다.

 

혹시 본 글을 보고 따라하고자 하는 사람이 있다면, ALB Ingress Controller도 추가적으로 생성해줘야 한다.

 

1. IAM Role

IAM Role For Service Account (IRSA)를 활용하여 해당 컨트롤러 역할을 하는 파드에 최소한의 권한만 부여해주었다.

IAM Role 및 Policy는 Terraform 코드로 생성하였다.

 

2. External DNS YAML 파일

Change here 부분을 본인의 환경에 맞게 수정해주면 된다.

 

SecurityContext는 위에 설명했듯이 ExternalDNS가 Kubernetes 및 AWS 토큰 파일을 읽을 수 있도록 하기 위해 설정

# Version : 2.0.0
apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
  namespace: kube-system
  annotations:
    eks.amazonaws.com/role-arn: {EXTERNAL DNS Role ARN}    # Change here
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: external-dns
rules:
- apiGroups: [""]
  resources: ["services"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get","watch","list"]
- apiGroups: ["extensions"]
  resources: ["ingresses"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: external-dns-viewer
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects:
- kind: ServiceAccount
  name: external-dns
  namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: external-dns
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: bitnami/external-dns:0.7.1
        args:
        - --source=service
        - --source=ingress
        - --domain-filter=pingping2.shop # Change Here // # Change here # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
        - --provider=aws
        - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
        - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both)
        - --registry=txt
        - --txt-owner-id=Z0875279DO6K545JISB2  # Change here # Hosted zone ID
      securityContext:
        fsGroup: 65534

 

Deployment 리소스 배포

 

Demo 애플리케이션 배포 및 External DNS 적용 확인

 

그럼, Ingress에서 External DNS를 사용하겠다고 정의하면 정상적으로 Route53 Record에 등록되는지 확인이 필요하다.

 

Namespace 생성

➜  external-dns git:(main) ✗ kubectl create ns demo-springboot 
namespace/demo-springboot created

 

Deployment, Service YAML

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: demo-springboot
  labels:
    app: demo-springboot
  name: demo-springboot
spec:
  replicas: 2
  selector:
    matchLabels:
      app: demo-springboot
  strategy: {}
  template:
    metadata:
      labels:
        app: demo-springboot
    spec:
      containers:
      - image: {AccountID}.dkr.ecr.ap-northeast-2.amazonaws.com/demo-maven-springboot:0.1
        name: demo-maven-springboot
        resources:
          limits:
            memory: 512Mi
            cpu: "0.5"
          requests:
            memory: 256Mi
            cpu: "0.2"
---
apiVersion: v1
kind: Service
metadata:
  namespace: demo-springboot
  labels:
    app: demo-springboot
  name: demo-springboot-svc
spec:
  ports:
  - name: demo-springboot-svc-8080
    port: 8080
    protocol: TCP
    nodePort: 30010
    targetPort: 8080
  selector:
    app: demo-springboot
  type: NodePort

 

배포

➜  EKSProj git:(main) ✗ kubectl apply -f kubernetes/demo-springboot/app.yaml
deployment.apps/demo-springboot created
service/demo-springboot-svc created

➜  EKSProj git:(main) ✗ kubectl get all -n demo-springboot
NAME                                   READY   STATUS    RESTARTS   AGE
pod/demo-springboot-867856fc47-l6bgf   1/1     Running   0          11s
pod/demo-springboot-867856fc47-pf86f   1/1     Running   0          11s

NAME                          TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/demo-springboot-svc   NodePort   172.20.246.38   <none>        8080:30010/TCP   11s

NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/demo-springboot   2/2     2            2           11s

NAME                                         DESIRED   CURRENT   READY   AGE
replicaset.apps/demo-springboot-867856fc47   2         2         2       11s

 

확인

SpringBoot가 배포된 Pod에 접속한 뒤 curl을 다운로드받고 Service에 curl을 날려본다.

 

위에서 볼수 있듯이 Service의 Cluster IP가 172.20.246.38이며, 8080 Port가 Poc Container와 연결되어 있다.

고로, 서비스에 curl을 날리면 백엔드 파드와 연결될 것으로 예상할 수 있다.

root@demo-springboot-867856fc47-pf86f:/data# curl 172.20.246.38:8080
<!DOCTYPE html>
<html>

<head>
    <title>Sample Maven Web</title>
    <link type="text/css" rel="stylesheet" href="css/sample.css" />
</head>

<body class="permission_denied">
    <div id="tsparticles"></div>
    <div class="denied__wrapper">
        <h1>Sample Maven Web</h1>
        <h3>Demo project</h3>
        <h3>Current Version : v0.1</h3>
        <h3>Pipeline Test Application</h3>
        <h3>this application deployed by Nyyang</h3>
        <div class="hynix">
            <img id="astronaut" src="images/astronaut.svg" />
            <img id="planet" src="images/planet.svg" />
        </div>
    </div>

    <script type="text/javascript"
            src="https://cdn.jsdelivr.net/npm/tsparticles@1.18.11/dist/tsparticles.min.js"></script>
    <script type="text/javascript" src="js/page.js"></script>
</body>
</html>root@demo-springboot-867856fc47-pf86f:/data#

 

Ingress Controller 및 External DNS를 사용하여 자동으로 Domain Name 및 ALB에 등록되도록 설정

 

Ingress 생성

자동으로 ACM 인증서가 ALB에 등록 및 ALB 생성, Route 53 레코드에 등록될 것이다.

 

ACM의 ARN은 본인의 ACM을 등록해주면 되고, hostname은 Route 53의 호스트 네임을 기입해주면 된다.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  namespace: demo-springboot
  name: demo-springboot-ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/certificate-arn: {ACM ARN}
    alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-2016-08
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80,"HTTPS": 443}]'
    alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}'
    external-dns.alpha.kubernetes.io/hostname: pingping2.shop
spec:
  rules:
    - http:
        paths:
          - path: /*
            backend:
              serviceName: demo-springboot-svc
              servicePort: 8080

 

어떤 일들이 발생하는지 확인하기 위해 -f 옵션으로 파드의 로그를 확인해준다.

 

External DNS Controller 파드 로그

time="2021-11-23T08:33:01Z" level=info msg="All records are already up to date"
time="2021-11-23T08:34:01Z" level=info msg="All records are already up to date"
time="2021-11-23T08:35:01Z" level=info msg="Desired change: CREATE pingping2.shop A [Id: /hostedzone/Z0875279DO6K545JISB2]"
time="2021-11-23T08:35:01Z" level=info msg="Desired change: CREATE pingping2.shop TXT [Id: /hostedzone/Z0875279DO6K545JISB2]"
time="2021-11-23T08:35:02Z" level=info msg="2 record(s) in zone pingping2.shop. [Id: /hostedzone/Z0875279DO6K545JISB2] were successfully updated"

 

Route 53 Record에 자동으로 등록

 

ALB 자동 생성 및 타겟 그룹 정상 등록

 

정상적으로 트래픽이 연결된 모습

 

 

수정 ! 

External DNS 및 ALB Ingress Controller 부분은 잘 세팅이 되었지만 Ingress YAML 파일에서 수정할 부분이 있다.

 

          - path: /*
            backend:
              serviceName: ssl-redirect
              servicePort: use-annotation
          - path: /*
            backend:
              serviceName: demo-springboot-svc
              servicePort: 80

 

=> 이렇게 해야 Redirect가 가능하다. 관련 문서는 공식 Docs를 통해 확인할 수 있다.

https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.2/guide/tasks/ssl_redirect/

 

SSL Redirect - AWS Load Balancer Controller

Redirect Traffic from HTTP to HTTPS We'll use the alb.ingress.kubernetes.io/actions.${action-name} annotation to setup an ingress to redirect http traffic into https Example Ingress Manifest apiVersion: extensions/v1beta1 kind: Ingress metadata: namespace:

kubernetes-sigs.github.io

 

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: demo-springboot
  labels:
    app: demo-springboot
  name: demo-springboot
spec:
  replicas: 2
  selector:
    matchLabels:
      app: demo-springboot
  strategy: {}
  template:
    metadata:
      labels:
        app: demo-springboot
    spec:
      containers:
      - image: {ACCOUNT_ID}.dkr.ecr.ap-northeast-2.amazonaws.com/demo-maven-springboot:{VERSION}
        name: demo-maven-springboot
        resources:
          limits:
            memory: 512Mi
            cpu: "0.5"
          requests:
            memory: 256Mi
            cpu: "0.2"
---
apiVersion: v1
kind: Service
metadata:
  namespace: demo-springboot
  labels:
    app: demo-springboot
  name: demo-springboot-svc
spec:
  ports:
  - name: demo-springboot-svc-8080
    port: 80
    protocol: TCP
    nodePort: 30010
    targetPort: 8080
  selector:
    app: demo-springboot
  type: NodePort
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  namespace: demo-springboot
  name: demo-springboot-ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-2:{ACCOUNT_ID}:certificate/28ff71d5-f0d6-4958-abcb-ece79efb6323
    alb.ingress.kubernetes.io/tags: createdBy=aws-load-balancer-controller
    alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-2016-08
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
    alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}'
    external-dns.alpha.kubernetes.io/hostname: pingping2.shop
spec:
  rules:
    - http:
        paths:
          - path: /*
            backend:
              serviceName: ssl-redirect
              servicePort: use-annotation
          - path: /*
            backend:
              serviceName: demo-springboot-svc
              servicePort: 80

 

후에 배포해보면 정상적으로 리디렉션이 되는 것을 확인 가능

반응형