Service
이전까지 컨테이너를 구성하는 오브젝트들을 살펴봤다면
이 포스트에서는 생성한 파드를 외부에 노출시킬 수 있는 오브젝트를 다룬다.
외부에 노출이라 하면 파드 외부, 노드 외부, 클러스터 외부 등을 생각할 수 있는데 자세히 살펴보자.
Service는 무엇을 해주나
파드를 생성할 때로 돌아가보자.
apiVersion: v1
kind: Pod
metadata:
name: my-nginx-pod
spec:
containers:
- name: my-nginx-container
image: nginx
ports:
- containerPort: 80
protocol: TCP
nginx 컨테이너 1개를 가지는 파드이다.
nginx 컨테이너는 80포트를 사용하므로, containerPort를 80으로 설정해 파드와 80포트로 연결한다.
파드를 직접 만들어 보자.
아래 포스트에서 파드는 고유한 IP를 갖는다고 했었다. 172.16.132.36은 파드의 고유한 내부 IP이다.
[Kubernetes] 쿠버네티스 오브젝트 [Pod]
Pod 쿠버네티스의 핵심 오브젝트인 파드이다. 이번 포스팅에서는 파드의 생명주기, 생성과 삭제, 관리등을 다루겠다. 쿠버네티스에서는 컨테이너 어플리케이션의 기본 단위를 Pod라고 부른다. Pod
imsongkk.tistory.com
파드가 배치된 w3-k8s는 매니저 노드와 같은 클러스터에 소속되어있어 매니저 노드에서 파드에 접근이 가능하다.
이때 같은 컨테이너를 가지는 파드를 하나 더 만들어보자.
apiVersion: v1
kind: Pod
metadata:
name: my-nginx-pod2
spec:
containers:
- name: my-nginx-container
image: nginx
ports:
- containerPort: 80
protocol: TCP
그런다음, 새롭게 생성된 my-nginx-pod2에서 my-nginx-pod에 접근해보자.
새롭게 생성된 파드에서도 접근이 잘 된다!
그렇다면 호스트 네임으로는 접근이 될까?
호스트 네임으로는 안되는것을 확인할 수 있다.
그럼 그냥 IP로만 통신하지 뭐~~ 라고 생각할 수 있지만
파드의 기본 개념중 가장 중요한것은 언제든지 생성과 삭제가 이루어질 수 있다는 점이다.
다시 말해, 172.16.132.36이라는 IP는 파드의 생명이 끝나면 사라진다. IP로 파드간 통신은 영원하지 않다.
또 문제가 있다.
우리는 이전에 ReplicaSet, Deployment들을 봤었다.
보통 단일 파드 오브젝트로 컨테이너를 운용하지 않고, 그보다 상위 오브젝트들을 사용한다.
파드가 1개가 아닌 여러 개라면?
같은 역할을 하는 파드들에게 골고루 트래픽을 분배할 수 있을까?
또, 지금까지는 클러스터 내부에서만 접속이 가능했는데 외부에서는 접속을 어떻게 할까?
서비스는 이러한 문제를 해결해준다.
- 여러 개의 파드에 쉽게 접근할 수 있도록 고유한 도메인 이름을 부여
- 여러 개의 파드에 요청을 분배하는 로드밸런서 기능
- 파드를 외부로 노출
Service의 종류
서비스는 크게 3가지의 종류로 나눌 수 있다.
예시로 알아보기 전에, Deployment를 하나 생성하자.
apiVersion: apps/v1
kind: Deployment
metadata:
name: de-nginx
spec:
replicas: 4
selector:
matchLabels:
app: my-nginx
template:
metadata:
name: my-nginx-pod
labels:
app: my-nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
kubectl apply -f de-nginx.yaml
ClusterIP
쿠버네티스 클러스터 내부에서만 파드들에 접근하고 싶을 때 사용한다.
따로 외부로 파드를 노출하지 않는다.
ClusterIP를 정의하는 YAML
apiVersion: v1
kind: Service
metadata:
name: svc-clusterip
spec:
ports:
- name: web-port
port: 8080
targetPort: 80
selector:
app: my-nginx
type: ClusterIP
spec.selector는 해당 서비스에서, 어떤 라벨을 가지는 파드에 접근해야 하는지 알려주는 역할을 한다.
우리가 생성한 Deployment는 app: my-nginx라는 라벨을 가지고 있으므로 그대로 적는다.
spec.ports.port는 서비스에 접근할 때 사용할 port를 명시한다. targetPort는 파드가 내부적으로 사용하는 port를 명시한다.
spec.type은 해당 서비스가 ClusterIP 서비스라는것을 명시한다.
YAML파일로 ClusterIP Service 생성하기
우리가 만든 서비스에 Cluster-IP가 부여 된것을 볼 수 있다.
YAML파일에서, spec.ports.port를 8080으로 설정해 서비스에 접근할 때 8080포트를 이용하겠다 했으니,
8080포트로 요청을 보내보자.
요청이 잘 된다! 해당 요청은 누가 처리한것일까?
아마 이 4개의 파드들중 하나가 요청을 처리했을것이다.
서비스는 해당 요청을 처리할 파드들을 내부에 로드밸런싱 알고리즘을 두어 정한다.
ClusterIP 흐름
ClusterIP 서비스는 클러스터 내부의 요청만 처리할 수 있다.
클러스터 내부의 요청을 서비스에 명시해놓은 selector로 파드를 찾은다음
해당 라벨이 붙어있는 파드들에게 요청을 분배한다.
이때 점선으로 표시된 파드는 나중에 Scale된 파드를 나타내는데,
클러스터 내부에서는 해당 파드의 IP를 몰라도 서비스의 호스트네임 혹은 IP로 접근하면 되므로 문제 없다!
NodePort
ClusterIP 타입의 서비스는 클러스터 내부에서만 접근이 가능했다.
NodePort는 클러스터 외부에서도 접근할 수 있게 해주는 타입의 서비스이다.
NodePort를 정의하는 YAML
apiVersion: v1
kind: Service
metadata:
name: svc-nodeport
spec:
ports:
- name: web-port
port: 8080
targetPort: 80
selector:
app: my-nginx
type: NodePort
YAML파일로 NodePort Service 생성하기
우리가 만든 서비스에 Cluster-IP가 부여 된것을 볼 수 있다.
우선, 클러스터 내부에서 접근이 되는지 확인해보자.
여기까지는 ClusterIP랑 똑같은데? 라고 생각할 수 있다.
사실, NodePort 서비스는 ClusterIP의 기능을 완전히 포함하고 있다.
ClusterIP 타입의 서비스에서, 외부와의 통신 기능을 추가한 서비스라고 이해하면 된다.
하나 특이한것은, 31114라는 포트가 갑자기 보인다는것이다.
이는 모든 노드에서 동일하게 접근할 수 있는 포트를 의미하며,
클러스터의 모든 노드에 내부, 외부 IP를 통해 31114 포트로 접근하면 동일한 NodePort 서비스에 연결된다는 것이다.
그렇다면 그 추가된 기능을 테스트 해보자.
클러스터 외부에서 파드에 요청 보내기
제목은 외부이나, 현재 구성된 클러스터는 하나의 PC에서 VM을 통해 구성한것이어서, 그렇다할 외부 IP가 없는 상황이다.
따라서 내부 IP를 외부 IP라고 생각하면 된다.
아까 봤던 31114 포트를 통해 노드에 요청을 보내보자.
요청이 제대로 되는것을 볼 수 있다.
NodePort 흐름
NodePort 서비스는 ClusterIP 서비스의 기능을 포함하고 있으므로 클러스터 내부의 요청도 처리할 수 있다.
파드뿐만 아니라, 노드의 생성과 삭제까지 관계없이 엔드포인트를 제공한다.
클러스터 외부의 트래픽 흐름은
- 클러스터 외부에서 노드에 특정포트(31114)로 접근한다.
- 노드는 NodePort 서비스에게 요청을 넘긴다.
- NodePort 서비스는 해당 요청을 처리할 적절한 파드를 선택한 후 파드에게 요청을 넘긴다
여기서 특정한 포트인 31114는 랜덤값이며, 32000~32768 포트중에 랜덤하게 선택 된다.
무언가 이상하다.
예를 들어 nginx 파드가 처리해야 하는 요청이 worker1로 들어왔다.
그런데 이 요청을 worker1 노드가 가지고 있는 파드에게 던지지 않고 서비스에게 다시 던진다.
뭔가 비효율적이다! 네트워크 홉(hop)을 더 타므로 성능상 안좋을 것 같다.
아무리 로드밸런싱 기능때문이라지만 썩 내키지 않는다.
쿠버네티스는 그래서 이 흐름을 개발자가 제어할 수 있도록 해놨다.
externalTrafficPolicy
외부로부터 들어온 요청의 흐름을 제어하는 속성이다.
위의 상황은 externalTrafficPolicy가 Cluster로 설정된 상황이다.
속성값을 Local 로 바꾸게 된다면, 해당 노드에 있는 파드로 바로 요청이 전달된다.
LoadBalancer
LoadBalancer는 NodePort와 마찬가지로 클러스터 외부에서도 접근할 수 있게 해주는 타입의 서비스이다.
다만, 이 타입의 서비스는 보통 동적으로 로드밸런서를 프로비저닝할 수 있는
클라우드 플랫폼에서 사용한다. (온프레미스나 가상머신은 적용하기 까다롭다)
로드밸런서 타입의 서비스는 실습없이 그림으로 대체한다.
LoadBalancer를 정의하는 YAML
apiVersion: v1
kind: Service
metadata:
name: svc-lb
# annotations:
# service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
ports:
- name: web-port
port: 80
targetPort: 80
selector:
app: my-nginx
type: LoadBalancer
AWS를 기준으로, 별 다른 설정을 하지 않으면 clb 타입의 로드밸런서가 생성된다.
clb는 Classic Load Balancer로, L7단의 로드밸런싱을 제공하지 않는다.
주석 처리된 부분은, clb 대신 nlb로 설정하는 부분이다.
이런식으로 annotation에 따라 리소스에 특별한 설정값을 부여할 수 있다.
LoadBalancer 흐름
LoadBalancer 서비스는, 생성과 동시에 모든 워커 노드가 포드에 접근할 수 있는 랜덤 포트를 개방한다.
LoadBalancer 서비스는 노드를 신경쓰지 않아도 되게 하는 단일 엔드포인트를 제공해준다.
LoadBalancer 서비스에 대해서는 Ingress를 설명하면서 더 자세하게 다루겠다.
ExternalName
앞에서 설명한 서비스의 3가지 종류 외에도, ExternalName 타입의 서비스도 존재한다.
이는 쿠버네티스 클러스터를 외부와 연동해야 할 때 사용할 수 있는 서비스이다.
예를들어, 클러스터 내부의 모든 포드들이 DB에 접근이 필요할 때, DB 서버에 대한 단일 엔드포인트를 제공해줄 수 있다.
ExternalName을 정의하는 YAML
apiVersion: v1
kind: Service
metadata:
name: svc-externalname
spec:
type: ExternalName
externalName: my-db-server.com
'Infra > Kubernetes' 카테고리의 다른 글
[Kubernetes] 쿠버네티스 오브젝트 [ConfigMap, Secret] (0) | 2023.07.27 |
---|---|
[Kubernetes] 쿠버네티스 오브젝트 [Namespace] (0) | 2023.07.05 |
[Kubernetes] 쿠버네티스 오브젝트 [Deployment] (0) | 2023.07.04 |
[Kubernetes] 쿠버네티스 오브젝트 [ReplicaSet] (0) | 2023.07.03 |
[Kubernetes] 쿠버네티스 오브젝트 [Pod] (0) | 2023.07.03 |