본문 바로가기

CI-CD/Kubernetes

Kubernetes- Service

Service란? 

 동일한 서비스를 제공하는 Pod 그룹의 단일 진입점을 제공한다. 동일한 Label을 가지고 있는 Pod들을 하나의 ip로 묶어서 Virtual Ip를 부여한다. 이렇게 부여된 Virtual Ip가 단일 진입점이 되고 외부 트래픽이 여러개의 파드 중 특정한 파드로 진입하게끔 로드 밸런싱 기능을 제공한다.

 

예시

- Deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webui
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webui
  template:
    metadata:
      name: nginx-pod
      labels:
        app: webui
    spec:
      containers:
      - name: nginx-container
        image: nginx:latest

위의 deployment는 세개의 nginx-pod를 유지한다. 이때 pod들의 labels는 'app: webui'이다.

 

- Service.yaml

apiVersion: v1
kind: Service
metadata:
  name: webui-svc
spec:
  type: ClusterIP  # 생략가능
  clusterIP: 10.96.100.100  # 단일 진입점. clusterIP는 생략 가능, 생략시 랜덤 IP 생성
  selector:
    app: webui
  ports:
  - protocol: TCP
    port:8080  # clusterIP의 포트
    targetPort:80  # app: webui 파드들의 포트

 이는 서비스를 정의하기 위한 yaml 파일로 위에서 말했던 virtual ip를 cluster ip로 10.96.100.100로 지정해놓았다.

이 서비스는 app: webui 라벨의 파드들을 하나의 서비스로 묶어서 단일진입점인 cluster ip에서 진입할 수 있도록 관리한다.

 

예를들어서 위의 deployment에서 생성한 세개의 파드들의 ip가 각각

  1. 10.44.0.1
  2. 10.45.0.1
  3. 10.46.0.1

이라고 하자.

이때 각 파드의 80번 포트에서 nginx 컨테이너가 실행 중이다. 그렇다면 해당 서비스를 이용하게 되면 10.96.100.100:8080의 단일 진입점을 이용하여 세개의 파드 중 하나의 80번 포트로 진입하여 nginx 컨테이너에 진입할 수 있게 된다.

Service Type

서비스에는 ClusterIP, NodePort, LoadBalncer, ExternelName 총 네 종류가 존재한다.

 

  • ClusterIP(defalut)
    • Pod 그룹의 단일 진입점(Virtual IP) 생성
  • NodePort
    • ClusterIP가 생성된 후 모든 Worker Node에 외부에서 접속 가능한 포트가 예약
  • LoadBalncer
    • 클라우드 인프라스트럭처(AWS, Azure, GCP등)나 오픈스택 클라우드에 적용
    • LoadBalancer를 자동으로 프로 비전하는 기능 지원
  • ExternalName
    • 클러스터 안에서 외부에 접속 시 사용할 도메인을 등록해서 사용
    • 클러스터 도메인이 실제 외부 도메인으로 치환되어 동작

 

- ClusterIP

 selector의 label이 동일한 파드들을 단일한 서비스 그룹으로 묶어 단일 진입점(Virtual IP, cluster ip)을 생성한다. 이때 clusterIP는 클러스터의 내부에서만 사용할 수 있고, 외부에서는 접근이 불가하다.  서비스 타입을 clusterIP로 지정하기 위해서는 type을 ClusterIP로 지정해주거나 디폴트로 설정되어있기 때문에 생략시 10.96.0.0/12의 범위에서 할당되게 된다.

 

cluster-service.yaml

apiVersion: v1
kind: Service
metadata: 
  name: clusterip-service
spec:
  type: ClusterIP
  clusterIP: 10.100.100.100
  selector:
    app: webui
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

cluster-service의 정의는 위와 같다.

 

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

세개의 replicas를 가지는 서비스 cluster-service를 생성해두었다. 서비스의 ClusterIP는 10.100.100.100이고 해당 ip의 80포트는 세개의 엔드포인트  10.244.1.153:80,10.244.1.154:80,10.244.1.155:80에 랜덤하게 연결된다(로드밸런싱).

 

- NodePort

 NodePort는 기존 clusterIP의 기능에 노드의 포트를 외부에 노출시키는 기능을 더한 서비스 타입이다. 즉, NodePort는 ClutserIP의 확장판이다. 따라서 type을 NodePort로 지정한 경우에 ClusterIP가 생성된다.

 Default NodePort의 범위는 30000-32767이며, 노출시킬 포트 번호를 지정하게 되면 노드의 <public ip:포트 번호>로 파드에 접근이 가능하다. 물론 파드의 레플리카들 중 어떠한 파드에 접근할 것인지는 쿠버네티스에서 랜덤하게 정해준다.

 

nodeport-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: nodeport-service
spec:
  type: NodePort
  clusterIP: 10.100.100.200
  selector:
    app: webui
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 30200  # 노드의 30200 포트를 외부에 노출(생략시 30000-32767에서 랜덤 할당)

노드 포트 서비스의 정의는 위와 같다.

 

NodePort로 노드의 30200 포트가 외부에 노출되었다. 따라서 해당 포트로 이제 파드에 접근이 가능하다.

 

- LoadBalancer

 LoadBalncer는 NodePort 타입의 확장판이다. 따라서, 서비스 타입을 LoadBalancer로 지정한 경우 NodePort, ClusterIP 또한 생성된다.

 AWS, Azure와 같은 퍼블릭 클라우드에서 운영 가능한 기능이다. 이는 외부 로드 밸런서를 자동으로 세팅해주는 기능이다.

사용자가 로드 밸런서 장치에 접근하면 로드 밸런서는 노드의 <public ip:포트 번호>로 포워딩을 해준다. 그 후 NodePort 타입에서 그러했듯이 파드 레플리카들 중 특정한 파드로 접근하게 된다. 즉, NodePort 서비스에서는 우리가 직접 특정 노드의 <public ip:포트 번호>를 통해 파드에 접근해야 했다면, LoadBalancer 타입에서는 우리가 직접 명시할 필요 없이 로드밸런서 장비가 이를 자동으로 연결해준다.

 

- ExternalName

클러스터 내부에서 외부의 도메인으로 연결해주는 서비스 타입이다.

apiVersion: v1
kind: Service
metadata:
  name: externalname-svc
spec:
  type: ExternalName
  externalName: google.com  # 연결할 외부의 도메인

위 yaml 파일로 ExternalName 서비스를 생성하고 svc를 확인해보자.

 

$ kubectl get svc externalname-svc
NAME               TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
externalname-svc   ExternalName   <none>       google.com    <none>    2m21s

보다시피 외부의 google.com으로 도메인이 연결된 것을 볼 수 있다. 따라서 externalname-svc를 호출하면 google.com이 연결된다.

Headless Service

 기존 서비스의 네가지 타입과는 달리 ClusterIP가 없는 서비스단일 진입점이 필요 없을 때 사용한다. Service와 연결된 Pod의

엔드 포인트로 DNS 레코드가 생성된다. 생성된 DNS 레코드는 control-plane의 coreDNS에 등록된다. 

각 Pod의 DNS 주소는 다음과 같은 형태로 생성된다.

[pod-ip-address].[namespace].pod.cluster.local

 

다음은 headless service를 생성하기 위한 예시의 yaml이다.

apiVersion: v1
kind: Service
metadata:
  name: headless-service
spec:
  type: ClusterIP
  clusterIP: None  # clusterIP가 없음!
  selector:
    app: webui
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

headless service의 경우 타입 자체는 ClusterIP이지만 clusterIP를 None으로 설정하여 단일 진입점을 가지지 않도록 하였다.

 

$ kubectl get pods -o wide

NAME                    READY   STATUS    RESTARTS   AGE   IP             NODE       NOMINATED NODE   READINESS GATES
webui-c8b84568c-9z7fn   1/1     Running   0          67m   10.244.1.177   minikube   <none>           <none>
webui-c8b84568c-lkdwk   1/1     Running   0          67m   10.244.1.176   minikube   <none>           <none>
webui-c8b84568c-rc4vx   1/1     Running   0          63m   10.244.1.179   minikube   <none>           <none>

현재 app:webui 라벨을 가지는 세개의 파드가 존재한다. 각각의 파드는 headless-service를 통해서 서비스를 가지고 있다. 이를 확인하기 위해서 describe 명령어로 headless-service를 확인해보자.

 

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

headless-service에 세개의 파드의 엔드 포인트가 정상적으로 연결된 모습이다. 각 파드들은 headless-service를 통해서 DNS주소를 가지게 되었으므로 'curl DNS 주소'를 통해서 해당 파드에 접근이 가능하다.

$ curl 10-244-1-177.default.pod.cluster.local  # webui-c8b84568c-9z7fn에 접근

나의 경우 centos:7 이미지를 가지는 testpod라는 이름의 파드를 run 후 해당 파드 내에서 위의 curl 명령을 실행하였다.

testpod 파드에서 webui-c8b84568c-9z7fn 파드에 HeadLess Service로 생성된 DNS를 통해서 성공적으로 접근을 한 모습이다.

Kube-Proxy

 kubernetes Service를 구현해주는 backend 시스템이다. 각 노드별(master, worker1, worker2, ...)로 한개의 Kube-Proxy를 보유하며, 이는 nodePort로의 접근과 Pod 연결을 구현해준다(iptables 구성).

 구체적으로 kube-proxy는 iptables의 rule을 만들어서 service의 단일 진입점을 통해서 접근한 경우, 특정 파드로 접근할 수 있도록 해준다.

지금껏 clusterIP나 nodePort로 접근한 경우 특정 파드에 접근이 가능했다. 이러한 것을 가능하게 구현해준 것이 바로 Kube-Proxy이다.

 Kube-Proxy mode

- userspace

 클라이언트의 서비스 요청을 iptables를 거쳐 kube-proxy가 받아서 연결해줌. kubernetes 초기 버전에서 잠깐 사용됨.

 

- iptables

 kubernetes network 디폴트 모드. kube-proxy는 service API 요청 시 iptables rule을 생성하고, 클라이언트의 연결을 kube-proxy가 받아 iptables rule을 통해서 연결해줌.

 

- IPVS

 리눅스 커널이 지원하는 L4 로드밸런싱 기술을 이용한다. 이를 사용하기 위해서는 별도의 ipvs 지원 모듈을 설정한 후 적용이 가능하다.

'CI-CD > Kubernetes' 카테고리의 다른 글

Kubernetes- Controller  (0) 2024.06.03
Kubernetes- Multi Container Pod 생성  (0) 2024.06.03
Kubernetes- Context  (0) 2024.06.01
Kubernetes- 동작원리  (0) 2024.06.01
Kubernetes- 기본 개념 정리  (0) 2024.05.27