[kubernetes] 쿠버네티스의 서비스(Service)

2해승·2024년 10월 21일

쿠버네티스?!

목록 보기
9/16

파드는 쿠버네티스 안에서 IP 주소를 부여받는데 쿠버네티스의 서비스는 파드에 탑재된 애플리케이션이 외부와 상호 통신 가능하도록 만들어준다. 이번 글에서는 쿠버네티스 운영에 필요한 서비스에 대해 알아보는 시간을 가져보도록 하자.

  • 서비스 개념
  • 서비스 타입
  • 서비스 사용하기
  • Headless Service
  • kube-proxy

서비스(Service)란?

파드는 클러스터 내부에서만 접근이 가능하며 클러스터 외부로 노출되어 있지 않은 특성을 가지고 있다. 따라서 클러스터 외부에서는 해당 파드에 직접 요청을 할 수 없는데 쿠버네티스의 서비스는 클러스터의 외부로부터 요청을 받을 수 있게 IP를 노출시키고 파드가 수평 확장하는 상황에서 트래픽을 적절히 분산시키는 역할을 한다.

서비스(Service) 타입

Cluster IP(default)

  • 기본 서비스 타입이며 파드 그룹의 단일 진입점(Virtual IP)를 생성한다.
  • 클러스터 내부만 통신이 가능하고 외부 트래픽은 받을 수 없는 특징을 가졌다.
  • 세개의 파드가 존재할때 동일한 비율로 로드밸런싱하는 역할을 한다.

아래처럼 3개의 webui 파드가 동작 중 이라고 할때 Cluster IP 서비스를 생성해보자.

$ kubectl get pod -w wide
NAME                    READY   STATUS    RESTARTS   AGE   IP            NODE                   NOMINATED NODE   READINESS GATES
webui-5f67dcf6d-2pplj   1/1     Running   0          6s    10.244.3.17   test-cluster-worker    <none>           <none>
webui-5f67dcf6d-2tkwb   1/1     Running   0          6s    10.244.3.16   test-cluster-worker    <none>           <none>
webui-5f67dcf6d-rlb7f   1/1     Running   0          6s    10.244.1.22   test-cluster-worker2   <none>           <none>
apiVersion: v1
kind: Service
metadata:
  name: clusterip-service
spec:
  type: clusterIP
  clusterIP: 10.96.100.100 // 생략 가능
  selector:
    app: webui
  ports:
  - protocol: TCP
    port: 80 // cluster ip 의 포트
    targetPort: 80 // 파드의 포트
$ kubectl create -f clusterip-service.yaml
$ kubectl get service
NAME                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
clusterip-service   ClusterIP   10.96.100.100   <none>        80/TCP    6s

서비스를 생성한 후 서비스를 확인해보면 다음과 같이 진입점이 되는 IP가 부여된 것을 확인할 수 있다. describe 명령어를 통해 더 자세히 확인해보자.

$ kubectl describe svc
Name:              clusterip-service
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=webui
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.96.100.100
IPs:               10.96.100.100
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.22:80,10.244.3.16:80,10.244.3.17:80
Session Affinity:  None
Events:            <none>

Endpoints 부분을 확인해보면 동작중인 3개의 webui 파드의 IP가 나열되어 있다. 이 세 파드를 묶어서 서로 연결한 것을 확인할 수 있다.

이제 로드밸런싱이 잘 동작하는지 각 파드에에 접속해서 index.html 파일을 변경하는 것으로 테스트 해보자.

$ kubectl exec webui-5f67dcf6d-2pplj -it -- /bin/bash
$ echo "webui #1" > /usr/share/nginx/html/index.html

위처럼 파드에 접속하여 index.html의 내용을 각각 webui #1, webui #2, webui #3로 변경하는 것으로 로드밸런싱을 확인할 것이다.


NodePort

  • 클러스터 내외부 통신이 가능한 서비스 타입으로 Cluster IP가 생성된 후 모든 워커 노드에 외부에서 접속이 가능한 port가 예약된다.
  • 클러스터를 구성하는 노드에 동일한 port를 열게되면 열린 port를 통해서 외부 트래픽을 수용하고 각 파드로 로드밸런싱하는 역할을 한다.
  • Default-NodePort 범위: 30000-32767
apiVersion: v1
kind: Service
metadata:
  name: nodeport-service
spec:
  type: NodePort
  clusterIP: 10.96.100.200
  selector:
    app: webui
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 30200               
$ kubectl create -f nodeport-nginx.yaml 
$ kubect get svc
NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE 
nodeport-service   NodePort    10.96.100.200   <none>        80:30200/TCP   4s

nodeport-nginx.yaml에 정의된 대로 port가 매핑된 것을 확인할 수 있다. 만일 External IP가 설정되어 있다면 EXTERNAL-IP:30200를 통해 외부에서 접근이 가능할테지만 지금은 설정된 Cluster IP를 이용하여 내부에서 로드밸런싱의 여부를 확인할 수 있다.

우선 워커 노드에서 32000번 포트가 열려있는지 확인후 curl 명령어로 로드밸런싱 테스트를 해보자.

$ iptables -t nat -L KUBE-NODEPORTS 
Chain KUBE-NODEPORTS (1 references)
target     prot opt source               destination         
KUBE-EXT-QATUYZLPU4I4HE4S  tcp  --  anywhere             anywhere             /* default/nodeport-service */ tcp dpt:30200


LoadBalancer

  • 클라우드 프로바이더와 함께 사용되며 자동으로 LB를 프로비전하는 기능을 지원한다.
  • 외부에 존재하는 LoadBalancer와 열려있는 NodePort를 연결하여 로드밸런싱한다.
apiVersion: v1
kind: Service
metadata:
  name: lb-service
spec:
  type: LoadBalancer
  selector:
    app: webui
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
$ kubectl create -f lb-nginx.yaml 
$ kubect get sec
NAME               TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
lb-service         LoadBalancer   10.96.167.165   <pending>     80:32686/TCP   2s
nodeport-service   NodePort       10.96.100.200   <none>        80:30200/TCP   11m

LoadBalancer는 로드밸런서를 자동으로 구성 요청하고 NodePort를 예약 후 해당 NodePort로 외부 접근을 허용한다. 즉, 외부 로드밸런서로 접근했을 때 현재 존재하는 노드로 먼저 분산시키고 이후 워커 노드 내부에서 동작하는 여러 파드로 분산시키는 과정을 거친다.


ExternalName

  • 위 3가지와 전혀 다른 서비스 타입으로 클러스터 내부에서 외부로 나가는 트래픽을 변환하기 위한 용도이다.
  • 설정된 값을 etcd에 저장하여 클러스터 도메인을 실제 외부 도메인으로 치환하는 것으로 동작한다.
apiVersion: v1
kind: Service
metadata:
  name: externalname-service
spec:
  type: ExternalName
  externalName: google.com
$ kubectl create -f externalname.yaml 
$ kubectl get svc
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
externalname-service   ExternalName   <none>          google.com    <none>    2s

ExternalName 서비스를 생성하고 새로운 파드를 동작시켜 곧바로 테스트 해보도록하자.

$ kubectl run testpod -it --image=centos:7
[root@testpod /]# curl externalname-service.default.svc.cluster.local


쿠버네티스 헤드리스 서비스(Headless Service)

쿠버네티스 서비스를 생성할 때 clusterIP를 None으로 설정하면 클러스터 IP가 없는 서비스를 만들 수 있다. 이런 서비스를 '헤드리스 서비스(Headless Service)'라고 하며 단일 진입점이 필요 없을때 사용되며 서비스와 연결된 파드의 엔드포인트로 DNS 레코드가 생성된다.

생성된 DNS 레코드를 쿠버네티스의 coreDNS에 등록시키고 파드들의 엔드포인트에 DNS resolving service를 지원한다. 즉, 파드의 DNS 주소로 접근이 가능하다는 것이다.

Pod DNS addr: pod-ip-addr.namespace.pod.cluster.local

$ kubectl get pod -o wide
NAME                    READY   STATUS    RESTARTS   AGE   IP            NODE                   NOMINATED NODE   READINESS GATES
webui-5f67dcf6d-5jfvx   1/1     Running   0          11s   10.244.1.25   test-cluster-worker2   <none>           <none>
webui-5f67dcf6d-dfx69   1/1     Running   0          11s   10.244.1.24   test-cluster-worker2   <none>           <none>
webui-5f67dcf6d-zt5pd   1/1     Running   0          11s   10.244.3.18   test-cluster-worker    <none>           <none>

우선 테스트를 위해 새로운 Nginx 파드를 만들어주자.

apiVersion: v1
kind: Service
metadata:
  name: headless-service
spec:
  type: ClusterIP
  clusterIP: None
  selector:
    app: webui
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
$ kubectl create -f headless-nginx.yaml

$ kubect get svc
NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
headless-service   ClusterIP   None            <none>        80/TCP    6s

$ kubectl describe svc headless-service
Endpoints:         10.244.1.24:80,10.244.1.25:80,10.244.3.18:80

생성된 헤드리스 서비스의 상세 정보를 확인해보면 Endpoints에 동작중인 파드들이 묶여있는 것을 확인할 수 있다.

테스트 파드 컨테이너에서 다음과 같이 curl 명령어를 통해 접근해보면 실제 DNS resolve된 파드와 연결시켜 존다.

$ kubectl run -it testpod --image=centos:7 /bin/bash

쿠버네티스 헤드리스 서비스는 자주 변경이 이루어지는 서비스보다는 statefaulset과 같이 파드 이름이 보존되어있는 서비스에서 유용하게 사용할 수 있다.


kube-proxy

쿠버네티스 네트워크 뒤에서는 내부에서 작동하는 구성요소가 있는데 이는 서비스를 사용가능한 네트워킹 규칙으로 변환하는 작업을 한다. 이것이 바로 kube-proxy이다.

kube-proxy는 쿠버네티스 클러스터의 모든 노드에 agent로 설치되어 서비스 오브젝트와 엔드포인트 간 일어나는 모든 변화를 모니터링 하고 있다.

엔드포인트 연결을 위한 iptables를 구성하고 NodePort로의 접근과 파드 연결을 구현하는 역할을 한다.



[참고자료]
https://kimjingo.tistory.com/151
https://kgw7401.tistory.com/114

profile
주니어 데브옵스 엔지니어

0개의 댓글