이번 포스팅에서는 Ingress가 무엇이고 우리 서비스에서 어떻게 활용되었는 지 전반적인 내용을 공유드리고자 합니다.
요구사항 정의
요구사항에 앞서 Ingress를 적용하기 전에 우리의 사전 진행 상황에 대해 간단하게 짚고 넘어가겠습니다.
우리 팀은 eks 클러스터를 CLI에서 구축하여 노드그룹을 생성하였고, 해당 클러스터 내부에서 여러가지 작업들을 진행하며 각각의 역할을 수행하는 컨테이너 환경을 구축하였습니다.
-> 이 지점에서 해당 포스팅을 쓰게 된 계기가 등장합니다.
우리의 목적은 이렇게 생성된 Pod들에 접근하는 것이었습니다.
위 스크린샷은 예시로 가져온 nginx Pod 입니다. Pod는 생성될 때 nginx pod처럼 고유의 IP를 갖게 됩니다.
그럼 이 IP로 통신하면 문제는 해결될까요? 당연하게도 답은 어지간하면 NO입니다!
Deployment로 Pod를 띄우고 나면 Kubernetes의 Kubelet이라는 객체가 Pod들의 상태를 감시합니다. 이에 따라 Pod에 장애가 발생하면 다른 IP로 새로 실행합니다. 즉, 정적 라우팅으로는 가용성 있는 통신을 할 수 없게 됩니다. 이를 해결하기 위해 우리는 Kubernetes의 Service라는 오브젝트를 사용하게 됩니다.
서비스 타입이란?
Kubernetes에서는 크게 4가지의 서비스타입이 존재합니다.
ClusterIP, NodePort, LoadBalancer, Ingress
이 중에서 외부에서 클러스터 내 서비스에 접근하기 위해서는 NorePort, LoadBalancer와 Ingress는 클러스터 외부에서 내부 서비스에 접근하기 위한 세 가지 주요 방법입니다. 서비스 타입에 대한 설명과 각각의 방식은 다른 포스팅에서 면밀하게 진행해보도록 하겠습니다.
서비스마다 달랐지만 우리는 주요 서비스 타입으로 Ingress를 활용하여 통신했습니다.
기본구조
현재까지 설명한 내용을 전체적인 그림으로 그린다면 위와 같습니다.
이론을 정립했으니 실제로 실습을 진행해보겠습니다.
구축과정
구축과정은 다음과 같은 단계로 진행됩니다.
1. Ingress-controller 설치
2. Ingress 리소스 정의
3. Route53 서브도메인 설정
1. Ingress-controller 설치
Ingress-controller는 사실 여러가지가 있습니다. Ingress Controller는 사실 Nginx, Apache 심지어 관리자용 UI를 제공해주는 Traefik 등등 여러가지가 있습니다. 우리의 구축 과정에서는 교육 과정 중 진행하여 친숙한 Nginx Controller를 사용하겠습니다.
Ingress-nginx.yaml을 생성하고 ingress-nginx 네임스페이스에 apply 해줍니다.
(ingress-nginx.yaml 소스는 공식 깃허브에 있습니다. 본인 환경과 버전에 맞도록 yaml 파일을 다운받아 사용하시면 될 것 같습니다.)
$ kubectl apply -f ingress-nginx.yaml -n ingress-nginx
서비스가 제대로 배포되었는 지 확인하고 ingress-nginx namespace에 올라온 Pods들을 확인합니다.
$ kubectl get deployments -n ingress-nginx
NAME READY UP-TO-DATE AVAILABLE AGE
ingress-nginx-controller 1/1 1 1 3m55s
$ kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-r6p2f 0/1 Completed 0 4m18s
ingress-nginx-admission-patch-x4v25 0/1 Completed 0 4m18s
ingress-nginx-controller-6d858b598b-v2x5q 1/1 Running 0
각 Pod들의 역할을 간단하게 설명하자면 다음과 같습니다.
ingress-nginx-admission-create : 컨트롤러의 admission webhook을 설치하기 위한 초기화 작업 담당
-> 일회성 작업으로 수행 후 종료
ingress-nginx-admission-patch : 웹훅이 제대로 동작할 수 있도록 패치 작업 수행
-> 일회성 작업으로 수행 후 종료
ingress-nginx-controller : 실제로 Ingress NGINX 컨트롤러를 실행하는 Pod. 클러스터 내에서 외부 트래픽을 관리하고 라우팅하는 역할 담당
-> Running 상태로 존재
ingress-nginx-controller와 연결된 ALB의 DNS이름을 확인합니다.
2. Ingress 리소스 정의
Ingress Controller를 설치하였기 때문에 Ingress 리소스를 정의하여 Ingress Controller가 해당 서비스에 대한 트래픽을 관리할 수 있도록 정의해줍니다. 예시에서는 기존에 올라와있던 Springapp에 대하여 진행해보겠습니다.
기존에는 아래에서 볼 수 있듯이 springapp의 서비스 타입을 LoadBalancer로 배포하여 로드밸런서의 dns로 접근했습니다. 이를 역할의 분리를 위해 Ingress로 통신하도록 수정해보겠습니다.
기존 Spring App Pod의 배포 스크립트에서 서비스 타입 부분을 ClusterIP로 수정하여 내부통신만 가능하게 배포합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: springapp-deploy
spec:
replicas: 2
selector:
matchLabels:
app: springapp-pod
template:
metadata:
labels:
app: springapp-pod
spec:
containers:
- name: springapp-container
imagePullPolicy: Always
image: { YOUR_IMAGE }
resources:
limits:
memory: "512Mi"
cpu: "1000m"
ports:
- containerPort: 8080
imagePullSecrets:
- name: ecr-registry-secret
---
apiVersion: v1
kind: Service
metadata:
name: springapp-svc
spec:
selector:
app: springapp-pod
ports:
- port: 80
targetPort: 8080
type: ClusterIP
3. Route53 서브도메인 설정
aea344e~~로 정의
서브도메인과 Ingress Controller의 DNS를 연결합니다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: springapp-ingress
annotations:
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: # { Your subdomain }
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: springapp-svc
port:
number: 80
이처럼 설정한 후 해당 ingress 리소스를 적용하면 우리가 정의한 서브도메인으로 원하는 서비스에 접근할 수 있습니다.
기대효과
이로써 우리는 도메인 이름으로 접근할 수 있게 되었습니다. 또한 Ingress의 L7통신의 여러 이점을 경험하며 클러스터 내부의 서비스에 접근할 수 있게 되었습니다.
개선점
하지만 이렇게 구축한 Ingress는 함정이 있습니다. AWS 콘솔에서 확인했을 때 이는 NLB타입이었던 것입니다.
이 부분에 대한 오해와 개념적인 부족함은 포스팅을 링크를 걸어보겠습니다.