Kubernetes Service + NodePort, LoadBalancer 실습

신동수·2024년 5월 5일
0

K8S

목록 보기
6/14

Service


Service는 클러스터 외부로부터 요청을 받을 수 있게 IP를 노출하는 역할을 하는 리소스다. 그리고 Deployment로 Pod를 수평확장하는 상황에, 트래픽을 적절히 분산시키기는 역할을 할 수 있는 리소스이다.
Pod는 클러스터 내부에서만 접근이 가능하며, 클러스터 외부로 노출되어있지 않다. 그래서 클러스터 외부에서 해당 Pod에 직접 요청할 수가 없다. Pod를 외부로 노출시키려면 포트포워딩을 이용하거나 쿠버네티스 Service 오브젝트를 이용해야 한다.

기능

  • 여러 Pod에 대한 요청을 분산하는 로드밸런서 기능을 수행한다.
    - IP/Port 계층 기반으로 로드 밸런서 기능을 수행하게 된다. (L4)
  • 일반적으로 실무에서는 ClusterIP 타입의 Service와 함께 Ingress 리소스를 사용하여 외부 트래픽을 처리한다.
    • Ingress을 이용하면 애플리케이션 계층에서 로드 밸런서 기능을 수행할 수 있다. (L7)

원리

  • Pod의 경우에는 오토스케일링과 같은 동적으로 Pod가 생성/삭제 및 재시작 되면서 IP가 바뀌기 때문에, Service에서 Pod의 목록을 필터할 때 IP주소를 이용하는 것은 어렵다. 그래서 사용하는 것이 label과 label selector 라는 개념이다.
    • spec.metadata 부분에 Pod 생성 시 사용할 label을 정의할 수 있다.
    • spec.selector.machLabels 부분에는 Service 생성 시 어떤 Pod들을 Service로 묶을 것인지 label Selector를 정의한다.
  • Service를 생성하면, label Selector에서 특정 label을 가진 Pod들만 탐지한다. Service는 이렇게 필터된 Pod의 IP들을 엔드포인트로 묶어 관리하게 된다. 그래서 하나의 Service를 통해 여러 Pod에 로드밸런싱이 이루어질 수 있는 것이다.

Service 타입

1. ClusterIP (기본 타입)


ClusterIP는 파드들이 클러스터 내부의 다른 리소스들과 통신할 수 있도록 해주는 가상의 클러스터 전용 IP다. 이 유형의 서비스는 <ClusterIP>로 들어온 클러스터 내부 트래픽을 해당 파드의 <파드IP>:<targetPort>로 넘겨주도록 동작하므로, 오직 클러스터 내부에서만 접근 가능하게 된다. 쿠버네티스가 지원하는 기본적인 형태의 서비스다.

Selector를 포함하는 형태

apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  type: ClusterIP		# 생략 가능
  ports:
  - protocol: TCP
    targetPort: 9376	# 애플리케이션(파드)을 노출하는 포트
    port: 80			# 서비스를 노출하는 포트
  selector:				# 이 서비스가 적용될 파드 정보를 지정 (선택이나 권장 사항)
    app: myapp
    type: frontend

TCP 포트 9376을 수신 대기(listen)하며 app=myapp, type=frontend라는 레이블을 공유하는 파드들에게 myapp-service라는 이름으로 접근할 수 있게 해주는 ClusterIP 유형의 서비스를 정의하면 다음과 같다.

위에서 spec.selector에서 지정된 레이블로 여러 파드들이 존재할 경우, 서비스는 그 파드들을 외부 요청(request)을 전달할 엔드포인트(endpoints)로 선택하여 트래픽을 분배하게 된다. 이를 이용하여 한 노드 안에 여러 파드, 또는 여러 노드에 걸쳐 있는 여러 파드에 동일한 서비스를 적용할 수 있다.

Selector가 제외된 형태

apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
apiVersion: v1
kind: Endpoints
metadata:
  name: myapp-service		# 연결할 서비스와 동일한 name을 메타데이터로 입력
subsets:					# 해당 서비스로 가리킬 endpoint를 명시
  - addresses:
      - ip: 192.0.2.42
    ports:
      - port: 9376

필요에 따라 엔드포인트(Endpoints)를 수동으로 직접 지정해줘야 할 때가 있다. 테스트 환경과 상용 환경의 설정이 서로 다르거나, 다른 네임스페이스 또는 클러스터에 존재하는 파드와의 네트워크를 위해 서비스-서비스 간의 연결을 만들어야 하는 상황 등이 있다.

이런 경우에는 spec.selector 없이 서비스를 만들고, 해당 서비스가 가리킬 엔드포인트(Endpoints) 객체를 직접 만들어 해당 서비스에 맵핑하는 방법이 있다.

2. NodePort


NodePort는 외부에서 노드 IP의 특정 포트(<NodeIP>:<NodePort>)로 들어오는 요청을 감지하여, 해당 포트와 연결된 파드로 트래픽을 전달하는 유형의 서비스다. 이때 클러스터 내부로 들어온 트래픽을 특정 파드로 연결하기 위한 ClusterIP 역시 자동으로 생성된다.

이 유형의 서비스에서는 spec.ports 아래에 nodePort를 추가로 지정할 수 있다. nodePort는 외부에서 노드 안의 특정 서비스로 접근할 수 있도록 지정된 노드의 특정 포트를 의미한다. nodePort로 할당 가능한 포트 번호의 범위는 30000에서 32767 사이이며, 미지정시 해당 범위 안에서 임의로 부여된다.

apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  type: NodePort
  ports:
  - targetPort: 80		# 애플리케이션(파드)을 노출하는 포트
    port: 80			# 서비스를 노출하는 포트
    nodePort: 30008		# 외부 사용자가 애플리케이션에 접근하기 위한 포트번호(생략 가능)
  selector:				# 이 서비스가 적용될 파드 정보를 지정
    app: myapp
    type: frontend

spec.selector에 해당하는 모든 파드들에 동일한 로드 밸런싱이 적용된다. 만약 같은 레이블의 파드들이 다른 여러 노드에 걸쳐 존재한다면, 해당 노드들에도 같은 서비스가 자동으로 생성되면서 같은 번호의 노드포트를 통한 해당 파드들의 접근이 허용된다. 예를 들어 192.168.1.2 노드와 192.168.1.3 노드에 각각 같은 레이블의 파드가 존재할 경우, NodePort 서비스를 통해 192.168.1.2:<nodePort> 또는 192.168.1.3:<nodePort> 중 어떤 경로를 이용하더라도 해당되는 파드들에 연결이 가능해진다.

3. LoadBalancer


별도의 외부 로드 밸런서를 제공하는 클라우드(AWS, Azure, GCP 등) 환경을 고려하여, 해당 로드 밸런서를 클러스터의 서비스로 프로비저닝할 수 있는 LoadBalancer 유형도 제공된다.

이 유형은 서비스를 클라우드 제공자 측의 자체 로드 밸런서로 노출시키며, 이에 필요한 NodePort와 ClusterIP 역시 자동 생성된다. 이때 프로비저닝된 로드 밸런서의 정보는 서비스의 status.loadBalancer 필드에 게재된다.

apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 80				# 서비스를 노출하는 포트
      targetPort: 80		# 애플리케이션(파드)를 노출하는 포트
  clusterIP: 10.0.171.239	# 클러스터 IP
  selector:
    app: myapp
    type: frontend
status:
  loadBalancer:				# 프로비저닝된 로드 밸런서 정보
    ingress:
    - ip: 192.0.2.127

이렇게 구성된 환경에서는, 외부의 로드 밸런서를 통해 들어온 트래픽이 서비스의 설정값을 따라 해당되는 파드들로 연결된다. 이 트래픽이 어떻게 로드 밸런싱이 될지는 클라우드 제공자의 설정에 따르게 된다.

노드포트 서비스로 외부에서 접속

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: np-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: np-pods
  template:
    metadata:
      labels:
        app: np-pods
    spec:
      containers:
      - name: np-pods
        image: sysnet4admin/echo-hname
        ports:
        - containerPort: 80

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: np-svc
spec:
  selector:
      app: np-pods
  ports:
      - name: http
        protocol: TCP
        port: 80
        targetPort: 80
        nodePort: 30000
  type: NodePort
# deployment, service 배포
$ kubectl apply -f deployment.yaml
$ kubectl apply -f service.yaml

# kubectl 를 통한 deployment, service 확인
$ kubectl get all -o wide
NAME                                 READY   STATUS    RESTARTS   AGE     IP               NODE     NOMINATED NODE   READINESS GATES
pod/np-deployment-6b96849c57-r5c9h   1/1     Running   0          6m44s   172.16.132.6     w3-k8s   <none>           <none>
pod/np-deployment-6b96849c57-spcwz   1/1     Running   0          6m44s   172.16.103.138   w2-k8s   <none>           <none>

NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE     SELECTOR
service/kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP        4d21h   <none>
service/np-svc       NodePort    10.103.126.111   <none>        80:30000/TCP   6m41s   app=np-pods

NAME                            READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES                    SELECTOR
deployment.apps/np-deployment   2/2     2            2           6m44s   np-pods      sysnet4admin/echo-hname   app=np-pods

NAME                                       DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES                    SELECTOR
replicaset.apps/np-deployment-6b96849c57   2         2         2       6m44s   np-pods      sysnet4admin/echo-hname   app=np-pods,pod-template-hash=6b96849c57

Cluster 에서 확인

[root@m-k8s ~]# curl -v 10.103.126.111
* About to connect() to 10.103.126.111 port 80 (#0)
*   Trying 10.103.126.111...
* Connected to 10.103.126.111 (10.103.126.111) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 10.103.126.111
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.16.1
< Date: Sun, 05 May 2024 08:51:45 GMT
< Content-Type: text/html
< Content-Length: 31
< Connection: keep-alive
<
np-deployment-6b96849c57-spcwz                   # 접속 시 확인할 내용
* Connection #0 to host 10.103.126.111 left intact
[root@m-k8s ~]# curl -v 10.103.126.111
* About to connect() to 10.103.126.111 port 80 (#0)
*   Trying 10.103.126.111...
* Connected to 10.103.126.111 (10.103.126.111) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 10.103.126.111
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.16.1
< Date: Sun, 05 May 2024 08:51:48 GMT
< Content-Type: text/html
< Content-Length: 31
< Connection: keep-alive
<
np-deployment-6b96849c57-r5c9h                   # 접속 시 확인할 내용
* Connection #0 to host 10.103.126.111 left intact

위에서 확인된 Service의 Cluster IP는 10.103.126.111 이고, <ClusterIP>:<ServicePort> 를 통해 연결된 파드에 로드밸런싱을 해주는 것을 확인할 수 있다.

웹페이지에서 확인


[root@m-k8s ~]# curl  192.168.1.102:30000
np-deployment-6b96849c57-spcwz
[root@m-k8s ~]# curl  192.168.1.103:30000
np-deployment-6b96849c57-r5c9h

Cluster 에서 <NodeIP>:<NodePort> 로도 동일한 내용을 확인할 수 있다.

AWS LoadBalancer 서비스로 외부에서 접속

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: np-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: np-pods
  template:
    metadata:
      labels:
        app: np-pods
    spec:
      containers:
      - name: np-pods
        image: sysnet4admin/echo-hname
        ports:
        - containerPort: 80

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: np-svc
spec:
  selector:
      app: np-pods
  ports:
      - name: http
        protocol: TCP
        port: 80
        targetPort: 80
        nodePort: 30000
  type: LoadBalancer
# deployment, service 배포
$ kubectl apply -f deployment.yaml
$ kubectl apply -f service.yaml

# kubectl 를 통한 deployment, service 확인
$ kubectl get all -o wide
NAME                                 READY   STATUS    RESTARTS   AGE   IP              NODE                                              NOMINATED NODE   READINESS GATES
pod/np-deployment-66787546c9-gdjr4   1/1     Running   0          44s   172.21.18.179   ip-172-21-19-13.ap-northeast-2.compute.internal   <none>           <none>
pod/np-deployment-66787546c9-vcvxq   1/1     Running   0          44s   172.21.13.114   ip-172-21-14-25.ap-northeast-2.compute.internal   <none>           <none>

NAME                 TYPE           CLUSTER-IP       EXTERNAL-IP                                                                  PORT(S)        AGE    SELECTOR
service/kubernetes   ClusterIP      172.16.0.1       <none>                                                                       443/TCP        103m   <none>
service/np-svc       LoadBalancer   172.16.107.200   ac0c6f11e2b974fe0a44b970b63be449-52265006.ap-northeast-2.elb.amazonaws.com   80:30000/TCP   44s    app=np-pods

NAME                            READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES                    SELECTOR
deployment.apps/np-deployment   2/2     2            2           44s   np-pods      sysnet4admin/echo-hname   app=np-pods

NAME                                       DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES                    SELECTOR
replicaset.apps/np-deployment-66787546c9   2         2         2       44s   np-pods      sysnet4admin/echo-hname   app=np-pods,pod-template-hash=66787546c9

AWS Console


Service를 LoadBalancer로 설정 시 기본적으로 CLB로 생성이 된다. 24년 기준으로 현재 CLB는 공식적으로 제공을 하지는 않는 서비스이다.

리스너에서는 위 yaml에서 정의한 것처럼 리스너 포트는 80이며, 인스턴스의 포트는 30000으로 설정이 되어 있는 것을 확인할 수 있다.

서비스 접속 확인


VPC 의 Bastion EC2 에서 curl을 통해 확인했을 때 각 각의 Pod로 접속이 되는 것을 확인하였지만, 순차적인 로드밸런싱은 되지 않는 것으로 보인다.



웹페이지에서도 정상적인 로드밸런싱이 되지 않았지만, 새로운 브라우저에서는 다른 Pod로 접속을 하였다.

참고 :
쿠버네티스 사용해보기 - Service
Kubernetes 리소스 Service에 대해 이해하고 실습해보기
노드포트 서비스로 외부에서 접속하기

profile
조금씩 성장하는 DevOps 엔지니어가 되겠습니다. 😄

0개의 댓글

관련 채용 정보