상세 컨텐츠

본문 제목

[kubernetes] 외부 요청이 클러스터 내 파드까지 전달되는 과정

프로그래밍/kubernetes

by jisooo 2024. 1. 14. 17:14

본문

 

 

#쿠버네티스 #k8s #kubernetes #파드 #서비스 #인그레스 #로드밸런서 #ingressController

에전에 쿠버네티스를 공부하는 과정에서,

요청을 처리하는데 필요한 오브젝트와 컴포넌트 구성 요소들도 너무 많았고,

그 안에서 각 오브젝트 및 컴포넌트들이 어떻게 상호작용을 하는지 너무 복잡했다🫠

그리고 각 외부 요청이 nginx 서버에서 애플리케이션 이미지가 떠 있는 파드까지 도달하는 과정이

초반에 직접 그려보지 않고는 머릿 속으로 쉽게 그려지지가 않는다!

그래서 각 컴포넌트들에 대한 이해를 복습할 겸,

요청을 처리하는 컴포넌트 구성들을 이미지로 그리며 과정을 정리해봄 :)

먼저 요청을 처리하는데 핵심 쿠버네티스 오브젝트들의

기본 개념들을 차근차근 익혀보자.


쿠버네티스 내 주요 오브젝트들의 기본 개념들

0) 클러스터

클러스터란 컨테이너 형태의 애플리케이션을 호스팅하는

물리 / 가상 환경의 노드라고 하는 워커 머신으로 이루어진 집합이다.

모든 클러스터는 최소 한 개의 워커 노드를 가지며,

쿠버네티스 클러스터는 컨테이너들을 실은 컨테이너선과 이를 관리하는 통제함으로 이루어진 선단의 개념에 비유할 수 있다.


1) 파드

파드는 컨테이너를 다루는 기본 단위이며, 쿠버네티스 오브젝트에서 가장 기초가 되는 오브젝트이다.

쿠버네티스에서는 컨테이너 애플리케이션을 배포하기 위한 기본 단위로 파드라는 개념을 사용한다.

1개의 파드에는 1개의 컨테이너가 존재할 수 있고, 여러 개의 컨테이너가 존재할 수도 있다.

이러한 파드는 각각 고유한 IP를 할당받지만, 이 IP는 외부에서 접근할 수 있는 IP가 아니기 때문에

쿠버네티스 클러스터 내부에서만 접근하여 통신할 수 있다.

아래에서 설명할 예정이지만,

쿠버네티스 외부에서 파드에 접근하려면 서비스라는 별도의 오브젝트를 따로 생성하여 사용해야 한다.

쿠버네티스가 파드를 사용하는 이유중 하나는

여러 리눅스 네임스페이스를 공유하는 여러 컨테이너들을 추상화된 집합으로 사용하기 위함이다.

파드에 컨테이너를 만약 여러개 구성한다고 가정하면,

파드 내의 컨테이너들은 네트워크 네임스페이스 등과 같은

리눅스 네임스페이스를 공유하여 사용하게 된다.

즉 동일한 파드 내의 여러 컨테이너가 동일한 네트워크 환경을 가지게 된다.

따라서 a 컨테이너 내부에서 로컬호스트로 b 컨테이너에 정의해놓은 http 요청을 전송하면

b 컨테이너의 응답을 받을 수 있게 되는 구조이다.

하지만 일반적으로 1개의 파드에는 1개의 컨테이너로 구성하여 사용하는 경우가 많고,

하나의 파드는 하나의 완전한 애플리케이션이 되도록 구성해야 한다.

아래는 간단한 파드를 생성하는 yaml 설정이다.

 

apiVersion: v1
kind: Pod
metadata:
  name: order-read-papi-77b8b86847-d6w6r
  generateName: order-read-papi-77b8b86847-d6w6r
  namespace: order-read-papi-beta
  labels:
    app: delivery-days-papi 
// 각 파드에 라벨을 명시하여, 파드를 찾을 수 있는 오브젝트인 서비스에서 해당 라벨이 붙어있는 파드로 요청을 전달할 수 있도록 한다.


2) 노드

파드는 언제나 노드 상에서 동작하며, 노드는 쿠버네티스에서 워커 머신을 말한다.

하나의 노드는 여러 개의 파드를 가질 수 있고, 쿠버네티스 컨트롤 플레인은 클러스터 내 노드를 통해서

파드에 대한 스케쥴링을 자동으로 처리한다.

아래 이미지 예시는, 총 6개의 워커 노드가 구성되어있으며,

각각 라벨을 통해 노드를 그룹화한 구성을 살펴볼 수 있다.

각 노드에는 특정 노드 그룹의 특성에 맞는

애플리케이션 이미지가 올라간 파드들이 떠있는 모습을 볼 수 있음!

예를 들어 아래 이미지 예시로는

order 노드 그룹엔, order-read-papi, order-open-api와 같이 주문 도메인에 관련된 애플리케이션 파드들,

delivery 노드 그룹엔 delivery-read-papi, delivery-batch-worker 등 배송 도메인에 관련된 애플리케이션 파드들이

배치되어있는 것을 확인 할 수 있다.

2-1) 워커 노드

  • 각기 다른 컨테이너들이 선적된 컨테이너선의 역할이다. 각기 다른 목적과 기능으로 세분화된 컨테이너들이 실제 배치되는 노드를 의미한다.
  • 애플리케이션의 성격이나 도메인에 따라서 "노드 그룹"으로 분류 할 수 있다.
  • (노드 그룹 별로 워커 노드 개수와 노드의 사양을 다르게 설정할 수 있다.)

  • 애플리케이션 성격에 따라서 트래픽이나 cpu, memory 등의 노드 사양을 다르게 구성하여,

각기 다른 애플리케이션 요구사항에 맞는 노드를 적절히 배치하고 파드를 해당 그룹에 배포할 수 있다.

  • 특정 노드 그룹을 설정하면, 애플리케이션을 파드에 배포하고자 할 때, 특정 노드 그룹 라벨이 붙어진 워커 노드에만 파드를 배포할 수 있도록 구성할 수 있다. (node-selector 설정)

  • 워커 노드에서 동작하는 쿠버네티스 관리 컴포넌트에는 kube-proxy, kubelet 등과 같은 컴포넌트들이 있다.

여기서 kube-proxy는 아래에서 설명할 서비스 오브젝트와 어느정도 연관이 있으므로,

kube-proxy만 설명을 간단히 추가한다.


* kube-proxy란?

 

서비스의 주소를 이용한 통신은 서비스만 등록한다고 해서 이루어지지 않는다.

서비스는 특정 호스트 요청이 어떤 라벨의 파드들로 보낼지에 대한 규칙이 명시되어있다.

이외에 추가로 해당 서비스로 향하는 트래픽이, 노드에서는 최종적으로 어떤 파드로 향해야 하는 지를 알려주어야 한다.

kube-proxy는 모든 노드에 하나씩 실행되는 daemonset으로,

각 노드에 서비스의 IP로 들어온 요청을 구체적으로 어떤 Pod로 보내야 할 지 로드밸런싱 알고리즘을 적용하여 알려준다.

대표적으로 iptables, IPVS 프록시 모드가 있으며, kube-proxy 환경설정에 따라 모드를 결정할 수 있다.

각 모드와 자세한 내용은 아래 링크를 통해 확인할 수 있다.

https://kubernetes.io/ko/docs/reference/networking/virtual-ips/
 


2-2) 마스터 노드

  • 컨테이너 선단을 지휘하는 통제함의 역할을 하는 노드이다. 즉, 쿠버네티스 노드를 제어하는 프로세스들이 모여있는 곳이며,

여기서 모든 테스크 할당이 시작되고, 클러스터가 잘 동작할 수 있도록 돕는 역할을 한다.

  • 대규모의 컨테이너를 운영하려면 워커 노드들의 가용 리소스 현황을 고려하여,

최적의 컨테이너 배치와 모니터링, 그리고 각 컨테이너에 대한 효율적인 추적 관리가 필요해진다.

  • 쿠버네티스 클러스터에서 이 역할을 수행하는 노드를 "마스터 노드"라고 칭한다.
  • 마스터 노드에 배포되는 관리 컴포넌트에는 kube-apiserver, etcd, kube-scheduler, kube-controller-manager 같은 것들이 있는데,

이들 각각의 역할은 포스팅의 주제 범위를 넘어서므로 아래에 공식문서 링크만 남겨두고, 자세한 내용은 생략!

https://kubernetes.io/ko/docs/concepts/overview/components/


2-3) 인그레스 노드 (feat. 인그레스 컨트롤러)

인그레스 컨트롤러 (ex: nginx) 파드가 떠 있는 노드를 칭한다.

이러한 인그레스 컨트롤러는 nginx, kong 등 여러 종류가 있으며,

컨트롤러를 사용할 경우 인그레스에 정의한 내용을 nginx 환경 설정으로 변경해서 nginx에 적용시킨다.

이 인그레스 컨트롤러에서 클러스터 내에 모든 정의된 인그레스 규칙을 로딩하여

요청에 따라 알맞은 서비스로 요청을 라우팅한다.

인그레스는 즉 외부에서 서비스로 접속이 가능한 URL, 로드 밸런스 트래픽, SSL / TLS 연결 설정,

그리고 이름 - 기반의 가상 호스팅을 제공하도록 구성할 수 있다.

인그레스 컨트롤러는 일반적으로 로드 밸런서를 사용하여 인그레스를 수행할 책임이 있으며,

인그레스 컨트롤러가 있어야만 인그레스 규칙을 적용할 수 있고, 인그레스 리소스만 생성한다면 효과가 없다.

위 이미지는 인그레스 컨트롤러가 인그레스 오브젝트에 정의된 라우팅 규칙을 읽어서

요청을 파드까지 전달되는 과정을 간단히 요약한 것이다.

아래는 인그레스 규칙을 적용할 수 있는 인그레스 오브젝트 설정 예시이다.

 

 

apiVersion: networking.k8s.io/v1
kind: Ingress
  name: order-read-papi
  namespace: order-read-papi-real
spec:
  rules:
  - host: order-read-papi.com
    http:
      paths:
      - backend:
          serviceName: order-read-papi    // 위 host로 들어오는 요청은 order-read-api 서비스로 전달한다.
          servicePort: 80
        path: /
  tls:
  - hosts:
    - order-read-papi.com
    secretName: tls-certification  // tls 인증서를 설정한 secret 오브젝트 이름을 명시한다.

 

 

이런식으로 여러 애플리케이션의 인그레스 규칙들을 인그레스 오브젝트로 생성하면,

인그레스 컨트롤러에서 규칙들을 로딩하여, 특정 host로 요청이 들어올 때 그에 매핑된 인그레스 규칙을 찾아

특정 서비스로 요청을 전달하는 과정을 거친다.

 

 

 

 

 


3) 서비스

 

서비스의 목적은 포드를 연결하고 외부에 노출하기 위함이다.

쿠버네티스에서 생성되는 파드는 자체 IP를 갖고 있어 다른 파드들과 통신할 수 있지만,

쉽게 사라지고 새로 생성되는 파드의 특징 때문에, Pod의 IP와 직접 통신하는 일은 번거롭고 권장하지 않는다.

여러 개의 디플로이먼트를 하나의 완벽한 애플리케이션으로 연동하려면,

파드의 IP가 아닌, 서로를 발견할 수 있는 다른 방법이 필요하다.

이렇게 파드와 직접 통신하는 대신,

별도의 고정된 IP를 가진 서비스를 만들고, 그 서비스를 통해 파드에 접근하는 방식을 사용한다.

이러한 서비스는 포드에 접근하기 위한 규칙을 정의하고 아래와 같은 핵심 기능이 있다.

  • 여러 개의 파드에 쉽게 접근할 수 있도록 고유의 도메인 이름을 부여한다.
  • 여러 파드에 접근할 때, 요청을 분산하는 로드 밸런서 기능을 수행한다.
  • 클라우드 플랫폼의 로드 밸런서, 클러스터 노드의 포트 등을 통해 파드를 외부로 노출시킨다.

이러한 서비스는 ClusterIP, NodePort, LoadBalancer 타입이 있는데,

타입에 따라서 파드에 접근할 수 있는 방법이 달라지기 때문에 구분해서 사용해야 한다.

아래는 간단한 서비스 오브젝트 구성 예시이다.

 

 

apiVersion: v1
kind: Service
metadata:
  labels:
    app: order-read-papi
  name: order-read-papi // 서비스 오브젝트의 이름을 명시한다.
  namespace: order-read-papi-real
spec:
  ports:
  - port: 80 // 서비스의 IP에 접근할 때 사용할 포트 설정
    protocol: TCP
    targetPort: 10000 // selector 항목에서 정의한 라벨이 달린 파드들이 내부적으로 사용하고 있는 포트 설정
  selector:
    app: order-read-papi // order-read-papi라는 라벨을 가진 파드에 접근할 수 있는 서비스를 생성한다.
type: ClusterIP // 쿠버네티스 내부에서만 파드에 접근할 수 있는 서비스를 생성한다

 

 

 

위 설정 예시에서는

ClusterIP타입으로 서비스를 생성하였으므로,

클러스터 내부에서만 사용할 수 있다.

해당 서비스의 IP나 서비스 이름을 통해서 서비스에 연결된 파드에 접근할 수 있다.

즉 서비스 이름을 DNS 이름으로 사용할 수 있게 클러스터 내부 DNS를 제공해준다.

 

 

$ kubectl get service order-read-papi
NAME              CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE  
order-read-papi   10.101.98.33   <none>        80/TCP    11s


$ curl 10.101.98.33:80
$ curl order-read-papi:80

 

 

 

서비스를 생성하여 서비스의 라벨 셀렉터와 파드의 라벨이 매칭되어 연결되면,

쿠버네티스는 자동으로 엔드포인트(endpoint) 오브젝트를 별도로 생성한다.

생성된 엔드포인트 정보를 보면,

생성한 서비스 이름과 동일한 이름의 오브젝트가 생성된 것을 확인할 수 있으며,

해당 서비스에 라벨로 매칭된 파드들의 IP와 파드명, 파드가 올라간 노드명, 파드의 port 정보 등을 갖고 있다.

아래는 서비스 생성 후 쿠버네티스에서 자동으로 생성된 endPoint 오브젝트 설정 정보이다.

 

 

 

apiVersion: v1
kind: Endpoints
metadata:
  name: order-read-papi
  namespace: order-read-papi
  labels:
    app: order-read-papi
subsets:
  - addresses:
      - ip: 10.240.10.137
        nodeName: order-worker-595x
        targetRef:
          kind: Pod
          namespace: order-read-papi-real
          name: order-read-papi-65d4d9f74d-p9s2b
          uid: 13674959-86f0-414b-8f54-9c346348c249
          resourceVersion: '322709967'
      - ip: 10.240.10.162
        nodeName: order-worker-595x
        targetRef:
          kind: Pod
          namespace: order-read-papi-real
          name: order-read-papi-65d4d9f74d-47bjb
          uid: 3195173a-4690-4780-8a42-8bb6d1c5843b
          resourceVersion: '322710007'
      - ip: 10.240.10.94
        nodeName: order-worker-kmvz
        targetRef:
          kind: Pod
          namespace: order-read-papi-real
          name: order-read-papi-65d4d9f74d-6bkhm
          uid: 49bdd3c8-2813-4777-937a-b084d16020b1
          resourceVersion: '322709951'
      - ip: 10.240.11.253
        nodeName: order-worker-6hcb
        targetRef:
          kind: Pod
          namespace: order-read-papi-real
          name: order-read-papi-65d4d9f74d-nskln
          uid: bf5048da-878f-4de5-9771-5586b831982f
          resourceVersion: '322709991'
    ports:
      - port: 10000
        protocol: TCP

 

 

 

그래서 서비스 오브젝트로 요청이 들어오면,

서비스는 엔드포인트 오브젝트를 통해 연결할 파드들의 IP 정보를 얻고,

로드밸런싱한 파드를 연결해줄 수 있다.


4) 인그레스

위에서 설명한 서비스 중의한가지 타입으로,

클라우드 플랫폼 환경에서만 사용할 수 있는 외부에서 파드에 접근하도록 하는 서비스이다.

클라우드 플랫폼에서 제공하는 로드 밸런서를 동적으로 프로비저닝하여 파드에 연결한다.

즉 외부의 요청을 어떻게 처리할 것인지

네트워크 7계층 레벨에서 정의하는 오브젝트이며, 아래와 같은 핵심 기능들이 있다.

  • 외부 요청의 라우팅 : 특정 경로로 들어온 요청을 어떠한 서비스로 전달할지 정의하는 라우팅 규칙을 설정한다.
  • 가상 호스트 기반의 요청 처리 : 같은 IP에 대해 다른 도메인 이름으로 요청이 도착했을 때 어떻게 처리할지 정의할 수 있다.
  • SSL / TLS 보안 연결 처리 : 여러 개의 서비스로 요청을 라우팅할 때, 보안 연결을 위한 인증서를 쉽게 적용할 수 있다.

이렇게 인그레스를 설정하게 되면,

라우팅 정의나 보안 연결과 같은 세부 설정은 각각의 서비스와 디플로이먼트가 아닌 위에서 인그레스에 의해 수행된다.

위에서 인그레스 컨트롤러 개념을 설명했듯이,

이러한 인그레스 규칙은 인그레스 오브젝트 자체만으로 설정이 불가하고,

인그레스 컨트롤러라는 특수한 서버에 적용해야만 인그레스 오브젝트들을 로딩하여 규칙을 적용할 수 있다.


여기까지 요청이 전달되는 과정을 정리해보면?

1) 외부에서 요청이 들어오면,

인그레스 매니지드 로드밸런서를 통해 Ingress nginx Controller에 외부 요청이 들어온다.

(로드밸런서 서비스는 Ingress Nginx Controller를 외부로 노출하기 위한 오브젝트이다.)

* 여기서 Ingress nginx Controller는 모든 네임스페이스별로 정의된 인그레스 규칙들을 로딩하여 nginx 설정에 적용해둔 상태이다.

Ingress nginx Controller는 항상 인그레스 리소스의 상태를 지켜보고 있어,

인그레스 오브젝트의 상태가 삭제되거나 생성, 수정되는 등의 이벤트가 감지되면

Ingress nginx Controller는 최신 인그레스 규칙으로 적용해둔다.

* 추가로 네임스페이스에 대한 개념도 간략이 정리하자면,

네임스페이스를 사용하면 쿠버네티스 리소스들을 가상으로, 논리적으로 묶어서 관리할 수 있다.

사용 목적에 따라 파드, 서비스 등의 리소스를 격리함으로써 편리하게 구분할 수 있다.

예를 들어 애플리케이션의 종류나 phase를 묶어 하나의 네임스페이스로 쿠버네티스 오브젝트들을 관리할 수 있다.

ex) order-read-papi-beta, order-read-papi-real ....

2) Ingress nginx Controller는 요청의 host를 보고, 해당 host에 대한 라우팅을 정의한 규칙이 있는지 찾는다.

위 이미지로 예시를 들면, delivery-read-papi.com/v1/delivery-days.... 라는 요청을 받으면

delivery-read-papi 이름의 서비스에 해당 호스트 요청에 대한 라우팅 규칙을 찾는다. (이는 이미 nginx 설정에 로딩 되어 있는 상태)

3) Ingress nginx Controller는 delivery-read-papi 서비스에 의해 선택된 파드로 요청을 직접 전달한다.

실제 요청 자체는 서비스를 거치지 않고, 특정 파드의 IP로 byPass로 요청을 전달한다.

(이미지는 이해의 편의상 서비스로 요청이 전달되도록 그려졌으나, 실제로는 특정 pod로 직접 요청이 전달되는 형태)

4) delivery-read-papi 디플로이먼트에 파드는 3개의 replia로 구성되어있다.

즉, 디플로이먼트 오브젝트의 관리 하에 delivery-read-papi 파드가 유지되고 있다.

이 각각의 파드는 app=delivery-read-papi 라벨을 갖고 있어, delivery-read-papi 서비스의 타겟 파드가 된다.

delivery-read-papi 서비스의 로드밸런싱 기능을 통해 트래픽을 처리할 적절한 파드의 IP를 선택한다.

(위처럼 스터디 진행한 책 내용에서는 인그레스 컨트롤러가 서비스를 거치지 않고 바로 파드의 IP로 요청이 가능하다는데 그럼 이는 서비스나 kube-proxy를 거치지 않는다는 말인가?

로드밸런싱된 결과의 파드 IP를 ingress-nginx-controller에서 바로 얻을 수 있는지 이해가 잘안간다..

kube-proxy는 이 부분은 좀더 파봐야겠음)

5) 로드밸런싱 알고리즘을 통해 반환된 특정 파드의 IP로, ingress-nginx-controller 해당 요청을 전달하고,

파드는 요청을 받아 응답을 반환한다.

* 각 오브젝트의 개념에 대한 내용은 아래 책의 내용과 공식 문서의 내용을 참고하여 작성함.

https://product.kyobobook.co.kr/detail/S000001766450

 

관련글 더보기

댓글 영역