최근 동료 개발자들에게 Kubernetes Service와 Ingress에 대해 설명하는 시간을 가졌다.
Kubernetes Service와 Ingress에 대한 개념은 알아도, 어떻게 외부 트래픽이 Pod까지 전달되는지 제대로 이해하지 못한 사람들이 많았다.
그래서 각 구성요소와 트래픽의 전달 과정을 단계별로 설명하니 이해하기 좋았다는 평을 받았고, 이번 아티클에서 그 내용을 정리해보려 한다.
Kubernetes Ingress와 Service라는 리소스는 이미 우리에게 익숙할 수 있지만, 그 동작 방식을 제대로 이해하는 것은 너무나 중요하다. 클러스터 운영 중 트래픽 관련 문제가 발생했을 때 좀 더 빠르게 트러블슈팅을 할 수 있기 때문이다.
1. Ingress - 외부 트래픽이 클러스터에서 가장 먼저 만나는 관문
Kubernetes 클러스터에 웹 앱을 배포해서 운영 중이라고 가정해보자. 이 클러스터에는 외부 로드밸런서와 도메인 주소(mywebapp.biz)까지 모두 준비가 된 상태이다.
해당 앱은 2개의 마이크로서비스로 구성되어있다. 각 Pod에 연결된 Service의 이름은 myapp과 blog이다.
우리는 주소 기반 규칙으로 트래픽을 분산시키는 Ingress Controller 역시 클러스터에 설치했다. 그리고 아래와 같은 규칙의 Ingress 리소스를 정의해 배포한 상태이다.
- Host:
mywebapp.biz - Path: /
- Service 이름:
myapp - 포트 번호:
5000
- Service 이름:
- Path: /blog
- Service 이름:
blog - 포트 번호:
5002
- Service 이름:
사용자가 mywebapp.biz로 접속한다면 Ingress가 사용자의 트래픽을 클러스터에 배포된 myapp Service의 5000 포트로 보낼 것이고, mywebapp.biz/blog으로 접속한다면 blog Service의 5002 포트로 보낼 것이다.
우리의 웹 앱에 접속하고 싶은 사용자가 브라우저를 통해 mywebapp.biz를 입력했다면, 사용자의 트래픽은 DNS 서버를 통해 클러스터의 외부 로드밸런서를 거친 다음, 클러스터 내부의 Ingress Controller에 도착할 것이다.
Ingress Controller는 들어온 트래픽을 분석해 Host와 Path를 확인한다. 그리고 이미 정의된 Ingress 리소스를 참조해서 해당 트래픽을 올바른 Service로 보내는 것이다.

사용자 트래픽의 Host는 mywebapp.biz, Path는 /이기 때문에 해당 트래픽은 myapp Service의 5000 포트로 보내진다.
2. Service와 Endpoint - 외부 트래픽과 Pod를 이어주는 연결 다리
이제 트래픽은 myapp Service로 전해졌다. 클러스터에 Service가 배포될 때 Endpoint라는 리소스도 자동으로 생성되는데, Endpoint는 해당 Service와 연결된 Pod의 IP와 포트번호를 가지고 있다.
myapp Pod의 IP와 포트 번호는 192.168.33.45:8000이라고 해보자.
사용자의 트래픽을 전달받은 myapp Service는 자신의 Endpoint 리소스에 저장된 Pod의 IP와 포트번호(192.168.33.45:8000)로 트래픽을 다시 전달한다.

자, 여기서 조금 더 깊이 알아볼 때가 됐다.
지금까지 Service 리소스가 트래픽을 받아서 Pod로 전달한다고 했지만, 사실 클러스터 내부로 들어온 트래픽을 전달하는 것은 각 클러스터 노드에 설치된 kube-proxy가 수행하는 역할이다.
즉, 클러스터 내부에 들어온 트래픽이 이동시키는 것은 kube-proxy이며 Ingress와 Service는 해당 트래픽이 어디로 가야 할지 알려주는 논리적인 표지판이라고 할 수 있다.
이런 트래픽의 흐름을 좀 더 쉽게 설명하기 위해 Ingress와 Service가 트래픽을 받아서 전달한다고 많이들 표현하는 것이다.
3. Pod와 Container - 외부 트래픽의 최종 목적지
이제 사용자의 트래픽이 Service와 연결된 Pod의 IP와 포트로 전해졌다.
Pod도 결국 하나 이상의 Container로 구성된 Kubernetes 리소스이기 때문에, Pod를 통해 Container 내부 애플리케이션으로 트래픽이 전달되려면 Container에 접근할 포트 번호가 필요하다.
하지만 Container의 포트 번호는 이미 주어졌다. 우리의 Service와 Endpoint가 알려준 8000이 바로 그것이다.
Deployment처럼 Pod를 포함한 리소스의 매니페스트를 정의할 때 containers 필드에 containerPort를 지정하는 걸 본 적 있을 것이다.
containerPort는 해당 Container가 어떤 포트 번호를 리스닝(Listening)하고 있는지 명시하는 필드이다.
사실 containerPort 필드를 명시하지 않아도 Pod에서 Container로 트래픽을 전달하는 데에는 문제 없다.
Container는 내부 애플리케이션이 리스닝하는 포트 번호가 기본적으로 열려있기 때문이다.
containerPort 필드는 나중에 이 컨테이너에 접근할 때 필요한 포트 번호가 무엇인지 명시할 때 쓰이거나, 추후 트래픽 관련 트러블슈팅에 참고 지점으로 쓰이기 때문에 지정하는 것이 좋다.

Pod로 들어온 트래픽은 8000 포트 번호를 통해 Container로 전해지고, Container 내부에서 사용자를 향해 응답을 보내는 것으로 외부 트래픽의 Kubernetes 클러스터 모험은 끝이 난다.
마무리
이번 아티클에서는 외부 사용자의 트래픽이 Kubernetes Ingress를 거쳐 서비스가 동작하는 Pod까지 도달하는 과정을 함께 살펴봤다. 각 과정의 전체 흐름도를 표현하면 아래와 같다.

이렇게 큰 흐름을 같이 따라감으로써, 흩어져있던 개념과 지식이 모여 더 깊게 이해할 수 있으리라 생각한다.
이제 여러분은 클러스터 운영 중 트래픽과 관련된 문제가 발생했을 때 좀 더 빨리 원인 지점을 추려낼 수 있을 것이다.
Ingress 규칙의 문제인지, Service나 Pod의 포트 번호가 잘못 지정된 건 아닌지 스스로 유추하면서 Kubernetes 리소스의 로그를 살펴보고 트러블슈팅을 해낼 수 있는 것이다.
이는 AI와 협업하는 경우에도 도움이 된다. 개발자가 문제 원인 지점을 짚어서 질문해야 AI도 그에 맞춰 문제 해결에 도움이 되는 답을 더 빠르게 낼 수 있기 때문이다.
혹시 Kubernetes의 다른 리소스나 컴포넌트의 동작 방식에 대해 궁금한 것이 있다면 하단 피드백 페이지를 통해 공유해주길 바란다. 여러분의 다양한 의견과 피드백은 Aiden’s Lab 아티클을 더욱 발전시켜주고 있다.