Route53과 External DNS를 이용하여 외부에서 클라이언트가 IP주소나 복잡한 로드밸런서의 주소를 입력하는 것이 아니라, 우리가 흔히 보는 도메인을 입력하여 서비스에 접근할 수 있도록 한다.
External DNS는 kubernetes dns(kube-dns)와 상반되는 개념으로 내부 도메인서버가 아닌 Public한 도메인서버(AWS Route53, GCP DNS 등)를 사용하여 쿠버네티스의 리소스를 쿼리할 수 있게 해주는 오픈소스 솔루션이다.
External DNS를 사용하면 public도메인 서버가 무엇이든 상관없이 쿠버네티스 리소스를 통해서 DNS레코드를 동적으로 관리 할 수 있다.
External DNS 배포 참조 1 - AWS공식문서
External DNS 배포 참조 2
1 Router53 도메인 등록
AWS콘솔의 Route53에 들어가 도메인 등록 클릭 후 원하는 도메인을 검색한다. 해당 도메인이 사용 가능하다면 구입한다.
📢 AWS Router53 도메인 이름 등록하는 방법
호스팅영역은 특정 도메인과 하위 도메인의 트래픽을 라우팅하는 방식에 대한 정보를 나타낸다. 쉽게 말해, DNS서버에 도메인이 입력되었을 때 DNS쿼리로 해당 도메인에 대한 결과를 얻게 되는데 , 이 결과에 대한 종류라고 생각하면 된다.
2 IAM 정책 생성
쿠버네티스 서비스 계정에 IAM 역할을 사용하려면 OIDC 공급자가 필요하다. IAM OIDC 자격 증명 공급자를 생성한다.
eksctl utils associate-iam-oidc-provider --cluster {cluster name} --approve
kubectl create namespace external-dns
vi AllowExternalDNSUpdates.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:ChangeResourceRecordSets"
],
"Resource": [
"arn:aws:route53:::hostedzone/*"
]
},
{
"Effect": "Allow",
"Action": [
"route53:ListHostedZones",
"route53:ListResourceRecordSets"
],
"Resource": [
"*"
]
}
]
}
# 생성한 json파일로 IAM Policy를 생성
aws iam create-policy --policy-name AllowExternalDNSUpdates --policy-document file://AllowExternalDNSUpdates.json
3 IAM 역할을 생성하고 Kubernetes 서비스 계정과 연결
eksctl create iamserviceaccount --name external-dns \
--namespace external-dns \
--cluster finalproject \
--attach-policy-arn arn:aws:iam::<Account ID>:policy/<IAM정책이름> \
--approve \
--role-name route53_external_dns
주의사항
클러스터 노드 IAM Role에 Route53 접근권한이 설정되지 않는 경우 배포가 되지 않는다. 확인해보고 적용이 안되었으면 IAM Role 콘솔에서 직접 적용해야한다.
💡 AWS Identity and Access Management(IAM)
IAM은 AWS 리소스에 대한 액세스를 안전하게 제어할 수 있는 웹 서비스입니다. IAM을 사용하면 사용자가 액세스할 수 있는 AWS 리소스를 제어하는 권한을 중앙에서 관리할 수 있습니다. IAM을 사용하여 리소스를 사용하도록 인증(로그인) 및 권한 부여(권한 있음)된 대상을 제어합니다.
4 EKS에 External-DNS 파드 배포
EKS는 파드를 띄워서 External-DNS를 통해 도메인과 연결한다.
# RBAC 활성여부 확인
kubectl api-versions | grep rbac.authorization.k8s.io
# External-DNS 파드용 파일 작성
vi external-dns.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
namespace: external-dns
# If you're using Amazon EKS with IAM Roles for Service Accounts, specify the following annotation.
# Otherwise, you may safely omit it.
annotations:
# Substitute your account ID and IAM service role name below.
eks.amazonaws.com/role-arn: arn:aws:iam::<Account ID>:role/<생성한 Role>
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-dns
namespace: external-dns
rules:
- apiGroups: [""]
resources: ["services","endpoints","pods"]
verbs: ["get","watch","list"]
- apiGroups: ["extensions","networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list","watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
namespace: external-dns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
namespace: external-dns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: k8s.gcr.io/external-dns/external-dns:v0.10.2 # external-dns를 가능하게 하는 파드를 생성하는 이미지, 버전이 지속적으로 바뀌기때문에 고려해서 지정
args:
- --source=service
- --source=ingress
- --domain-filter= # 등록한 도메인 입력
- --provider=aws
- --policy=upsert-only
- --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= # Route53 호스팅영역 ID입력
securityContext:
fsGroup: 65534 # For ExternalDNS to be able to read Kubernetes and AWS token files
# yaml파일 api-server에 전송
kubectl create -f external-dns.yaml
policy의 경우 Route53에서 레코드 업데이트를 제어하는 정책, Sync정책은 삭제권한이 존재하기 때문에 사용에 주의
서비스계정은 처음에는 이미 존재하기때문에 만들어지지 않는데, 이것은 3번에서 IAM롤로 이미 생성해놨기 때문이다.
-> 이미 IAM롤로 서비스계정을 만들어놨는데도 yaml파일에 굳이 다시 IAM롤이 부여된 서비스계정을 다시 만드는 이유는 나중에 혹시라도 서비스계정이 삭제되었을때 일일이 다시 3번처럼 서비스 계정을 만드는 것이 아니라, yaml파일 실행 한번으로 External-DNS에 필요한 서비스계정 포함 총 4가지 리소스를 쉽게 배포하기 위해서다.
또한 해당 yaml파일을 ArgoCD와 연동중인 Git에 Push하여 ArgoCD로 배포할까 생각했지만, 생각해보니 굳이 DNS서버와 연결하는 External-DNS파드를 외부에 노출시킬 필요가 없고, 서비스계정, RBAC 코드도 다 노출되기 때문에 그냥 로컬에서 수동으로 클러스터에 배포하였다.
5 Ingress수정 및 결과 테스트
한마디로, External-DNS를 가능하게 하는 파드를 띄웠고 이를 통해 프론트엔드 파드를 외부에 노출시키는 Ingress(=ALB)를 도메인에 연결한다. 따라서 아래와 같이 수정한다.
vi front-ing.yaml
. . .
spec:
rules:
- host: "k8s-finalproject.com" # 도메인 추가
http:
. . .
# external-dns의 로그를 확인해서 레코드가 정상적으로 생성되는지 확인
kubectl logs -f {external-dns pod name} -n external-dns
구매한 도메인을 브라우저에 입력했을 때 정상적으로 프론트엔드로 구성된 웹페이지가 나타나는 것을 볼 수 있다.
지금까지 도메인을 입력하여 Frontend로 구성된 웹페이지에 접속하는 것은 http 프로토콜을 이용한 통신이였다. 하지만 http통신은 평문통신이기 때문에 보안에 취약하다. 따라서 https통신을 구현할 것이다.
1 TLS인증서 발급받기
이전에 Route53으로 구매한 도메인에 대해 유효한 TLS인증서를 발급받아야한다.
퍼블릭 AWS Certificate Manager(ACM)을 이용하여 TLS를 발급받는다.
📢 ACM인증서 발급받기 참고 - 공식문서
2 . Frontend - Ingress 수정
주석부분에서 TLS인증서의 arn을 추가하고, listen-port로 80/http, 443/https 포트를 추가함으로써 외부사용자의 80/http OR 443/https 포트를 통한 요청을 둘다 허용한다.
이때 tls-redirect를 443으로 지정함으로써 80/http요청에 대해 443/https로 리디렉션한다.
vi front-ing.yaml
. . .
annotations:
. . .
alb.ingress.kubernetes.io/certificate-arn: # 위에서 식별한 인증서의 arn값 입력
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
alb.ingress.kubernetes.io/tls-redirect: '443'
. . .