앞에서 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는 Pod 집합에서 실행중인 애플리케이션을 네트워크 서비스로 노출하는 추상화 방법이다.
ClusterIP, NodePort, LoadBalancer 이렇게 세가지 type이 있다.
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)로 각 노드의 IP에 서비스를 노출시킨다. NodePort 서비스가 라우팅되는 ClusterIP 서비스를 자동으로 생성한다. NodeIP:NodePort를 요청하여, 클러스터 외부에서 NodePort 서비스에 접속할 수 있다.

이미지는 예시이다. 현재 실습에서는 Pod에 컨테이너가 여러대 떠 있진 않다.
ClusterIP의 yaml과 달라진 점은 .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 파트)에서 구성해보도록 하겠다.
클라우드 공급자의 로드 밸런서를 사용하여 서비스를 외부에 노출시킨다. 외부 로드 밸런서가 라우팅되는 NodePort와 ClusterIP 서비스가 자동으로 생성된다.

AWS에서는 Classic LoadBalancer와 Network 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.annotations에 AWS 로드밸런서 타입을 정해줄 수 있다. 타입을 지정하지 않으면 기본으로 clb가 선택된다. 그리고 로드밸런서가 만들어질 subnet을 지정해준다.
.spec.type은 LoadBalancer이고 .spec.ports[*].nodePort로 노드 포트를 정해줄 수 있지만 여기서는 지정하지 않았다.
yaml을 apply 해준다.
$ kubectl apply -f front-lb-k8s.yml
deployment.apps/test-front created
service/test-front created
성공적으로 서비스가 만들어지면 Service에 EXTERNAL-IP가 붙고 이는 NLB의 DNS이다.
$ 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로 바뀌면 브라우저에서 NLB의 DNS로 접근할 수 있다.

클러스터 내의 서비스에 대한 외부 접근을 관리하는 API 오브젝트이며, 일반적으로 HTTP를 관리한다.
다른 유형의 컨트롤러와는 달리 Ingress 컨트롤러는 클러스터와 함께 자동으로 시작되지 않는다. AWS Load Balancer Controller는 Kubernetes 클러스터 용 Elastic Load Balancer를 관리하는 컨트롤러이다. 이 컨트롤러를 클러스터에 직접 설치해주어야 한다.
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 Blog에 잘 나와있다.
먼저 Service를 NodePort 타입으로 생성해준다.
앞에서 실습했던 NodePort와 같은 yaml이다.
apiVersion: v1
kind: Service
metadata:
name: test-front
spec:
type: NodePort
selector:
app: test-front
tier: front
ports:
- port: 80
nodePort: 30000
Ingres의 yaml은 아래와 같이 작성해 준다.
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)로 접속해 보면 잘 실행되는 것을 볼 수 있다.

참고
- https://www.ovh.com/blog/getting-external-traffic-into-kubernetes-clusterip-nodeport-loadbalancer-and-ingress/
- https://blog.leocat.kr/notes/2019/08/22/translation-kubernetes-nodeport-vs-loadbalancer-vs-ingress
- https://kubernetes.io/ko/docs/concepts/services-networking/ingress/
- https://kubernetes.io/ko/docs/concepts/services-networking/service/
이제까지 읽은 글들이 정말로 정리가 잘되어있고, 이해하기 좋았습니다.
좋은글 작성해주셔서 감사합니다. :)