[EKS] ⑥ Service, Ingress 이용해서 네트워크에 노출하기 - AWS Elastic Load Balancer (ALB, NLB)

이아영·2021년 4월 30일
0

EKS

목록 보기
6/7

앞에서 Bastion 서버에 Pod 읽기 권한을 주었는데 이번 실습을 위하여 cluster-admin 권한을 주겠다. cluster-admin은 쿠버네티스에서 제공하는 기본 ClusterRole로 수퍼 유저 액세스를 허용하여 모든 리소스에 대한 작업을 수행 할 수 있다.

아래과 같이 ClusterRoleBinding을 진행해주면 되겠다.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: bastion-cluster-role-binding
subjects:
- kind: User
  name: L23724-bastion-eks-role
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

ClusterRole 참고 : 공식 문서

이번 글에서 배포할 서비스는 아래 글에서 자세히 다루고 있으니 먼저 보고 오는 것을 추천한다.
[Kubernetes] Minikube로 Front와 API 배포해보기 (Node.js, React)


Service

ServicePod 집합에서 실행중인 애플리케이션을 네트워크 서비스로 노출하는 추상화 방법이다.

ClusterIP, NodePort, LoadBalancer 이렇게 세가지 type이 있다.

ClusterIP

Service의 기본 타입이 ClusterIP이기 때문에 타입을 지정하지 않았을 때는 ClusterIP 타입이다.

Service를 클러스터 내부 IP에 노출시키고 클러스터 내에서만 해당 Service에 접근이 가능하다.

Kubernetes proxy를 통하면 외부 접근이 가능하지만 실제 운영에서는 사용하지 않고 디버깅 용이나 내부 대시보드 등 내부 트래픽을 허용할 때 사용된다고 한다.
https://blog.leocat.kr/notes/2019/08/22/translation-kubernetes-nodeport-vs-loadbalancer-vs-ingress

yaml은 다음과 같이 작성 할 수 있다.

apiVersion: v1
kind: Service
metadata:
  name: test-front
spec:
  selector:
    app: test-front
    tier: front
  ports:
    - port: 80

타입은 지정하지 않아 기본 타입인 ClusterIP이고 CLUSTER-IP가 붙은 것을 볼 수 있다.

$ kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.100.0.1       <none>        443/TCP    7d16h
test-api     ClusterIP   10.100.35.21     <none>        8080/TCP   5d23h
test-front   ClusterIP   10.100.128.100   <none>        80/TCP     19m

ClusterIP 타입은 외부에서 접근할 수 없다. test-front처럼 외부에서 접근해야 하는 서비스는 NodePort 타입이나 LoadBalancer 타입으로 구성해야한다.

👉 Kubernetes에서의 Cluster IP
ClusterIP 는 두 가지를 의미 할 수 있다. Kubernetes 클러스터 내에서만 액세스 할 수있는 Service 타입 또는 Kubernetes 클러스터 내 구성 요소의 내부(가상) IP다.
https://stackoverflow.com/questions/33407638/what-is-the-cluster-ip-in-kubernetes


NodePort 타입

고정 포트(NodePort)로 각 노드의 IP에 서비스를 노출시킨다. NodePort 서비스가 라우팅되는 ClusterIP 서비스를 자동으로 생성한다. NodeIP:NodePort를 요청하여, 클러스터 외부에서 NodePort 서비스에 접속할 수 있다.

이미지는 예시이다. 현재 실습에서는 Pod에 컨테이너가 여러대 떠 있진 않다.

ClusterIPyaml과 달라진 점은 .spec.type.spec.ports[*].nodePort 부분이다.

.spec.ports[*].nodePort는 노드의 고정 포트를 정해주는 부분인데 명세하지 않으면 자동으로 할당 된다.

apiVersion: v1
kind: Service
metadata:
  name: test-front
spec:
  type: NodePort
  selector:
    app: test-front
    tier: front
  ports:
    - port: 80
      nodePort: 30000
$ kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.100.0.1      <none>        443/TCP        7d19h
test-api     ClusterIP   10.100.35.21    <none>        8080/TCP       6d1h
test-front   NodePort    10.100.75.136   <none>        80:30000/TCP   4s

Bastion 서버에서 curl [노드의 IP]:[nodePort]을 실행해 주면 통신이 잘 이루어지는 것을 볼 수 있다.

(노드의 보안그룹 인바운드에 Bastion 서버에 대한 30000 포트를 오픈해 주어야한다.)

워커 노드가 Private 서브넷에 있기 때문에 현재는 Bastion 서버에서만 접근이 가능하지만 ALB를 추가하여 서비스가 가능하도록 뒤(Ingress 파트)에서 구성해보도록 하겠다.


LoadBalancer 타입 - NLB

클라우드 공급자의 로드 밸런서를 사용하여 서비스를 외부에 노출시킨다. 외부 로드 밸런서가 라우팅되는 NodePortClusterIP 서비스가 자동으로 생성된다.

AWS에서는 Classic LoadBalancerNetwork LoadBalancer를 사용할 수 있다.

Classic LoadBalancer는 사용이 권장되지 않기 때문에 Network LoadBalancer를 기준으로 실습을 진행했다.

yaml을 살펴보면 다음과 같다.

apiVersion: v1
kind: Service
metadata:
  name: test-front
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    service.beta.kubernetes.io/aws-load-balancer-subnets: subnet-0d8d50653b7935fee,subnet-088cab1b1e8d2966c
spec:
  type: LoadBalancer
  selector:
    app: test-front
    tier: front
  ports:
    - port: 80

.metadata.annotationsAWS 로드밸런서 타입을 정해줄 수 있다. 타입을 지정하지 않으면 기본으로 clb가 선택된다. 그리고 로드밸런서가 만들어질 subnet을 지정해준다.

.spec.typeLoadBalancer이고 .spec.ports[*].nodePort로 노드 포트를 정해줄 수 있지만 여기서는 지정하지 않았다.

yamlapply 해준다.

$ kubectl apply -f front-lb-k8s.yml
deployment.apps/test-front created
service/test-front created

성공적으로 서비스가 만들어지면 ServiceEXTERNAL-IP가 붙고 이는 NLBDNS이다.

$ kubectl get svc
NAME         TYPE           CLUSTER-IP       EXTERNAL-IP                                                                          PORT(S)        AGE
kubernetes   ClusterIP      10.100.0.1       <none>                                                                               443/TCP        7d21h
test-api     ClusterIP      10.100.35.21     <none>                                                                               8080/TCP       6d4h
test-front   LoadBalancer   10.100.227.160   a1782fddd39664564b99704ed3844e6c-aa159285faf1acd0.elb.ap-northeast-1.amazonaws.com   80:31357/TCP   106s

AWS 콘솔에서도 실제로 NLB가 생성된 것을 확인할 수 있다.

NLB의 상태가 active로 바뀌면 브라우저에서 NLBDNS로 접근할 수 있다.


Ingress

클러스터 내의 서비스에 대한 외부 접근을 관리하는 API 오브젝트이며, 일반적으로 HTTP를 관리한다.

다른 유형의 컨트롤러와는 달리 Ingress 컨트롤러는 클러스터와 함께 자동으로 시작되지 않는다. AWS Load Balancer Controller는 Kubernetes 클러스터 용 Elastic Load Balancer를 관리하는 컨트롤러이다. 이 컨트롤러를 클러스터에 직접 설치해주어야 한다.

AWS Load Balancer Controller 설치

AWS Load Balancer Controller 설치는 AWS 문서에 있는 과정을 따라 했다. 이미 AWS Load Balancer Controller가 있다면 이 부분은 건너뛰어도 된다.

먼저 AWS Load Balancer Controller에 대하여 API를 호출할 수 있도록 해주는 AWS 정책을 생성해보도록 하겠다.

iam_policy.json의 내용으로 정책을 생성해준다.
(정책을 제대로 넣었는데 UnauthorizedOperation 에러가 난다면 AWS 공식문서의 최신 정책을 찾아서 넣어준다. - 며칠 사이 정책이 바껴서 몇시간 날렸다ㅠㅠ)

정책의 이름은 L23724-AWSLoadBalancerControllerIAMPolicy라고 정해줬다.

방금 만든 정책을 사용할 수 있도록 역할을 만들어 주겠다.

역할 만들기로 들어가서 웹 ID를 선택해주고 이전 글에서 만든 자격 증명 공급자를 선택해 준다.

좀 전에 만든 정책 L23724-AWSLoadBalancerControllerIAMPolicy를 연결해준다.

역할 이름을 정해주고 역할 만들기를 완료한다.

만들어진 역할에 대해 신뢰 관계 편집을 해주어야 한다.

신뢰 관계 편집에 들어가면 아래와 비슷한 내용일 것이다.

블록 부분을 아래와 같이 바꿔주고 업데이트 해준다.

aud": "sts.amazonaws.com"sub": "system:serviceaccount:kube-system:aws-load-balancer-controller"

IAM 역할 설정은 모두 마쳤다.
이제 이 IAM 역할을 사용하는 ServiceAccount를 만들어주자.

아래와 같이 ServiceAccount yaml을 작성해준다.

apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/name: aws-load-balancer-controller
  name: aws-load-balancer-controller
  namespace: kube-system
  annotations:
      eks.amazonaws.com/role-arn: arn:aws:iam::900184409169:role/L23724-eks-loadbalancer-controller-role-tokyo

ServiceAccount 생성한다.

$ kubectl apply -f aws-load-balancer-controller-service-account.yaml
serviceaccount/aws-load-balancer-controller created

아래 명령어를 통해 TargetGroupBinding 사용자 지정 리소스 정의를 설치한다.

$ kubectl apply -k "github.com/aws/eks-charts/stable/aws-load-balancer-controller//crds?ref=master"

eks-charts 리포지토리를 추가한다.

$ helm repo add eks https://aws.github.io/eks-charts
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/aylee/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /home/aylee/.kube/config
"eks" has been added to your repositories

AWS Load Balancer Controller 설치를 완료해 준다.

$ helm upgrade -i aws-load-balancer-controller eks/aws-load-balancer-controller \
--set clusterName=L23724-cluster-tokyo \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller \
-n kube-system
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/aylee/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /home/aylee/.kube/config
Release "aws-load-balancer-controller" does not exist. Installing it now.
NAME: aws-load-balancer-controller
LAST DEPLOYED: Fri Apr 23 08:18:25 2021
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
AWS Load Balancer controller installed!

aws-load-balancer-controller를 조회해 보면 아래와 같이 잘 배포된 것을 볼 수 있다.

$ kubectl get deployment -n kube-system aws-load-balancer-controller
NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
aws-load-balancer-controller   1/1     1            1           24m

AWS ALB

드디어 AWS ALB를 사용하여 애플리케이션과 통신해보도록 하겠다.

아키텍처는 AWS Blog에 잘 나와있다.

먼저 ServiceNodePort 타입으로 생성해준다.

앞에서 실습했던 NodePort와 같은 yaml이다.

apiVersion: v1
kind: Service
metadata:
  name: test-front
spec:
  type: NodePort
  selector:
    app: test-front
    tier: front
  ports:
    - port: 80
      nodePort: 30000

Ingresyaml은 아래와 같이 작성해 준다.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-alb
  labels:
    app.kubernetes.io/name: external-ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/subnets: subnet-0d8d50653b7935fee,subnet-088cab1b1e8d2966c
    alb.ingress.kubernetes.io/target-type: instance
spec:
  rules:
    - http:
        paths:
          - path: /*
            backend:
              serviceName: test-front
              servicePort: 80

.metadata.annotations에 필요한 값들을 넣어주고 .spec.rules[*].http.paths[*].backend.serviceName에는 우리가 배포한 NodePort 타입Service 이름을 넣어준다.


Ingress를 배포하고 조회해 보면 다음과 같이 ADDRESS(ALB DNS)가 붙은 것을 볼 수 있고 AWS 콘솔에 들어가 보면 ALB가 생성된 것을 볼 수 있다.

$ kubectl get ing
NAME          CLASS    HOSTS   ADDRESS                                                                      PORTS   AGE
ingress-alb   <none>   *       k8s-default-ingressa-21e1e3916d-171521608.ap-northeast-1.elb.amazonaws.com   80      5s

ALB의 규칙에 들어가서 보면 우리가 설정한 path(/*)가 임의의 이름의 대상그룹으로 전달된다.

해당 대상그룹으로 가보면 nodePort로 설정한 포트로 워커노드가 자동으로 등록되어 있다.

브라우저에서 ADDRESS(ALB DNS)로 접속해 보면 잘 실행되는 것을 볼 수 있다.


참고

1개의 댓글

comment-user-thumbnail
2022년 10월 24일

이제까지 읽은 글들이 정말로 정리가 잘되어있고, 이해하기 좋았습니다.
좋은글 작성해주셔서 감사합니다. :)

답글 달기