#쿠버네티스 #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) 워커 노드
각기 다른 애플리케이션 요구사항에 맞는 노드를 적절히 배치하고 파드를 해당 그룹에 배포할 수 있다.
여기서 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) 마스터 노드
여기서 모든 테스크 할당이 시작되고, 클러스터가 잘 동작할 수 있도록 돕는 역할을 한다.
최적의 컨테이너 배치와 모니터링, 그리고 각 컨테이너에 대한 효율적인 추적 관리가 필요해진다.
이들 각각의 역할은 포스팅의 주제 범위를 넘어서므로 아래에 공식문서 링크만 남겨두고, 자세한 내용은 생략!
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계층 레벨에서 정의하는 오브젝트이며, 아래와 같은 핵심 기능들이 있다.
이렇게 인그레스를 설정하게 되면,
라우팅 정의나 보안 연결과 같은 세부 설정은 각각의 서비스와 디플로이먼트가 아닌 위에서 인그레스에 의해 수행된다.
위에서 인그레스 컨트롤러 개념을 설명했듯이,
이러한 인그레스 규칙은 인그레스 오브젝트 자체만으로 설정이 불가하고,
인그레스 컨트롤러라는 특수한 서버에 적용해야만 인그레스 오브젝트들을 로딩하여 규칙을 적용할 수 있다.
여기까지 요청이 전달되는 과정을 정리해보면?
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
[kubernetes] 파드의 상태와 생애주기 및 애플리케이션 상태 검사 (livenessProbe, readinessProbe) (1) | 2024.01.28 |
---|---|
[kubernetes] Deployment 배포 전략 RollingUpdate 세부 설정하기 (1) | 2024.01.14 |
댓글 영역