kubernetes + Kops + ExternalDNS 정리 – 2

kubernetes + kops + externalDNS 정리 두번째

첫번째 내용은 여기서 확인 가능합니다.

여기서는 Route53 연동을 위한 ExternalDNS를 알아봅니다.

kubernetes + kops with ExternalDNS


이제 service expose 가 필요한 Services 들을 위해 Route53 연동을 시작해봅시다.

Install ExternalDNS

external-dns/aws.md at master · kubernetes-incubator/external-dns · GitHub

위 링크에 가면 ExternalDNS on AWS 에 대한 설명이 나옵니다.
여기서 중요하게 집고 넘어가야할 부분이 IAM 설정입니다.
저는 처음에 kops IAM 설정에 위의 설정들이 다 들어가 있기 때문에 괜찮을 거라고 생각하고 넘어갔다가 상당히 고생했습니다.

위 링크에서 설명하고 있는 IAM은 k8s의 node role 에 대한 설정입니다.
kops 로 클러스터를 구성하면, master node와 nodes 들의 role들이 별도로 관리가. 됩니다.

아래에서 IAM permission for nodes 를 설정 하겠습니다.

Edit Cluster

kops 를 통해 cluster 에 ExternalDNS 사용을 위한 설정을 추가해줍니다.

kops edit cluster $NAME

위 명령 실행 시 vim 화면이 뜹니다.
내용을 살펴보면 cluster 설정에 대한 yaml 파일 입니다.

Route53 설정을 위해 아래 내용을 하단에 추가해줍니다.

  additionalPolicies:
    node: |
      [
        {
          "Effect": "Allow",
          "Action": [
            "route53:ChangeResourceRecordSets"
          ],
          "Resource": [
            "arn:aws:route53:::hostedzone/*"
          ]
        },
        {
          "Effect": "Allow",
          "Action": [
            "route53:ListHostedZones",
            "route53:ListResourceRecordSets"
          ],
          "Resource": [
            "*"
          ]
        }
      ]

위 내용은 node role 에 추가로 IAM policy 설정을 해준다는 의미입니다.
node 이외에도 master, bastion이 있습니다.
관련링크 입니다.
kops/iam_roles.md at master · kubernetes/kops · GitHub

이제 cluster 에 변경사항을 적용해줍니다.

kops update cluster --yes $NAME

이제 AWS console 에서 IAM 의 Role 에서 추가된 Policy 를 확인 하실 수 있습니다.

The additional Policy image

ExternalDNS on AWS


이제 어플을 배포 해보겠습니다.
배포하기전에 앞에 해야할 일이 ExternalDNS 를 kubernetes 에 생성하는 것입니다.

ExternalDNS 생성

external-dns/aws.md at master · kubernetes-incubator/external-dns · GitHub
위 링크에 있는 external-dns example 을 사용하겠습니다.
RBAC enabled 된 example 입니다.

external-dns.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
---
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"]
---
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: default
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: registry.opensource.zalan.do/teapot/external-dns:v0.5.0
        args:
        - --source=service
        - --source=ingress
        - --domain-filter=k8s.example.com. # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
        - --provider=aws
        - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both)

저장 후에 생성합니다.

kubectl create -f external-dns.yaml

Output:

serviceaccount "external-dns" created
clusterrole.rbac.authorization.k8s.io "external-dns" created
clusterrolebinding.rbac.authorization.k8s.io "external-dns-viewer" created
deployment.extensions "external-dns" create

Pod가 잘 실행 되었는지 확인

kubectl get po

Output

9to5-macbook:~/projects/k8s ktg$ kubectl get po
NAME                            READY     STATUS    RESTARTS   AGE
curl-545bbf5f9c-rlpwb           1/1       Running   1          6d
external-dns-75d57587fc-dkk8s   1/1       Running   0          43s
hello-go-6db785ccbd-94vdh       1/1       Running   0          6d
nginx-proxy-7b775767b6-fngcz    1/1       Running   0          6d

ExternalDNS Pod 의 로그 확인

kubectl logs -f $(kubectl get po -l app=external-dns -o name)

Output:

9to5-macbook:~/projects/k8s ktg$ kubectl logs -f $(kubectl get po -l app=external-dns -o name)
time="2018-05-17T09:18:45Z" level=info msg="config: {Master: KubeConfig: Sources:[service ingress] Namespace: AnnotationFilter: FQDNTemplate: CombineFQDNAndAnnotation:false Compatibilit$
: PublishInternal:false Provider:aws GoogleProject: DomainFilter:[k8stest.example.com.] ZoneIDFilter:[] AWSZoneType:public AWSAssumeRole: AzureConfigFile:/etc/kubernetes/azure.json AzureRe
sourceGroup: CloudflareProxied:false InfobloxGridHost: InfobloxWapiPort:443 InfobloxWapiUsername:admin InfobloxWapiPassword: InfobloxWapiVersion:2.3.1 InfobloxSSLVerify:true DynCustomerN
ame: DynUsername: DynPassword: DynMinTTLSeconds:0 InMemoryZones:[] PDNSServer:http://localhost:8081 PDNSAPIKey: Policy:sync Registry:txt TXTOwnerID:default TXTPrefix: Interval:1m0s Once:
false DryRun:false LogFormat:text MetricsAddress::7979 LogLevel:info}"
time="2018-05-17T09:18:45Z" level=info msg="Connected to cluster at https://100.64.0.1:443"
time="2018-05-17T09:18:46Z" level=info msg="All records are already up to date"
...

정상 동작을 확인했습니다.

이제 실제 우리가 서비스에 사용할 app 을 배포해봅시다.
여기서 예제는 nginx 입니다.

NGINX 배포

nginx.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx
  annotations:
    external-dns.alpha.kubernetes.io/hostname: nginx.k8s.example.com.
spec:
  type: LoadBalancer
  ports:
  - port: 80
    name: http
    targetPort: 80
  selector:
    app: nginx
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 80
          name: http

Deploy and Service 생성

9to5-macbook:~/projects/k8s ktg$ kubectl create -f example/nginx.yaml
service "nginx" created
deployment.extensions "nginx" created

Service nginx 정보 확인

9to5-macbook:~/projects/k8s ktg$ kubectl describe svc nginx
Name:                     nginx
Namespace:                default
Labels:                   <none>
Annotations:              external-dns.alpha.kubernetes.io/hostname=nginx.k8s.example.com.
Selector:                 app=nginx
Type:                     LoadBalancer
IP:                       100.68.183.89
LoadBalancer Ingress:     af2addc7959b811e888e41234567890-1234567890.us-east-1.elb.amazonaws.com
Port:                     http  80/TCP
TargetPort:               80/TCP
NodePort:                 http  31243/TCP
Endpoints:                100.96.2.16:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type    Reason                Age   From                Message
  ----    ------                ----  ----                -------
  Normal  EnsuringLoadBalancer  1m    service-controller  Ensuring load balancer
  Normal  EnsuredLoadBalancer   1m    service-controller  Ensured load balancer

Load Balancer url test

curl af2addc7959b811e888e41234567890-1234567890.us-east-1.elb.amazonaws.com

ExternalDNS Pod 의 로그 다시 확인

kubectl logs -f $(kubectl get po -l app=external-dns -o name)

Output:

...
time="2018-05-17T09:59:49Z" level=info msg="Desired change: CREATE nginx.k8s.example.com A"
time="2018-05-17T09:59:49Z" level=info msg="Desired change: CREATE nginx.k8s.example.com TXT"
time="2018-05-17T09:59:49Z" level=info msg="Record in zone k8s.example.com. were successfully updated"
...

로그에서 위의 메시지를 찾으면 정상적으로 Route53 에 A record가 생성 되었습니다.

잠시 후에 해당 url 에 request 를 보내봅니다.

curl nginx.k8s.example.com

Load balancer url 에 request 를 보냈을 때와 같은 결과가 나온다면 정상적으로 kubernetes cluster 설정이 완료된 것입니다.

글쓴이

Kwon

github: https://github.com/9to6