Service?

Service 개념

  • Pod는 deployment, replicaset, statefulset 같은 controller에 의해 관리되기 때문에 새로 생겨나거나 없어지기도 하고 한 Node에만 떠있는 게 아니라 여러 Node를 옮겨다닐 수 있음
  • Pod는 이렇듯 동적으로 변하여 고정된 endpoint로의 호출이 어려운데 (Pod의 IP,hostname이 변하니까 ) 이러한 이슈를 해결하기 위한 오브젝트가 Service임
  • Client는 클러스터 안에서 Pod IP가 바뀌던 없어지건 새로 생기건 신경쓸 필요 없이 Service만 바라보면 고정된 endpoint로 접근이 가능함
  • Service는 크게 아래와 같이 네가지 Type으로 나뉨
    • ClusterIp : kubernets cluster 내부에서 사용하는 service. 외부에선 접근할 수 없음
    • LoadBalancer : 토클,aws,gcp 등 퍼블릭 클라우드 서비스를 이용할 때 사용가능한 type으로 loadbalancer의 IP를 이용하여 외부에서 접근가능함
    • NodePort : kubernetes cluster의 각 노드의 지정된 port를 service에 할당함. node의 port를 이용하기 때문에 외부에서도 접근가능함
    • ExternalName : kubernetes cluster 내부에서 외부로 접근하는 경우 사용, Service를 ExternalName 으로 설정하여 CNAME을 타고 외부로 접근함
  • 이 글에서 다루는 redis-cluster 에서는 ClusterIP type의 service를 사용하고 있음 ( DB는 내부통신만 하면 되니까)

Service 사용현황

$ kubectl get service
NAME                                  TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)              AGE
kimdubi-test-redis-cluster            ClusterIP   10.254.8.60   <none>        6379/TCP             6d
kimdubi-test-redis-cluster-headless   ClusterIP   None          <none>        6379/TCP,16379/TCP   6d
kubernetes                            ClusterIP   10.254.0.1    <none>        443/TCP              6d1h
  • kubernetes cluster 내부에서 Pod 간 연결을 위해 사용하는 ClusterIP type의 kimdubi-test-redis-cluster 라는 이름의 service
  • ClusterIP type이지만 IP가 설정되지 않은 kimdubi-test-redis-cluster-headless 라는 이름의 service, headless service라고 함
  • 두개의 service가 생성되었지만 실제로는 kimdubi-test-redis-cluster-headless 서비스가 사용됨

headless service란?

  • kimdubi-test-redis-cluster 같은 보통의 service는 받은 요청을 매핑된 Pod들에게 loadbalancing 형태로 뿌려주는데 이는 stateless 한 web server 처럼, 어떤 web server로 요청이 가도 동일한 결과를 내는 구조에서 의미가 있음 
  • DB처럼 Master / Slave 같은 stateful 한 구성에서는 loadbalancing 형태로 뿌려주는 게 아니라 Master 인지 Slave인지 파악하여 특정한 Pod에 요청을 뿌려줘야함
  • 이처럼 loadbalancing이 필요없고 client 에서 모든 Pod에 접근해야하는 구성에서는 headless service를 사용하며 아래와 같은 특징이 있음
    • headless service는 IP 대신 dns 를 가짐
    • headless service의 dns에 대해 nslookup을 수행하면 binding 된 모든 Pod들의 IP를 return 함
    • Client는 return 받은 모든 Pod의 IP를 통해 원하는 Pod 에 직접 접근할 수 있음
# nslookup kimdubi-test-redis-cluster-headless
Server:		10.254.0.10
Address:	10.254.0.10#53
    
Name:	kimdubi-test-redis-cluster-headless.default.svc.cluster.local
Address: 10.100.71.3
Name:	kimdubi-test-redis-cluster-headless.default.svc.cluster.local
Address: 10.100.14.7
Name:	kimdubi-test-redis-cluster-headless.default.svc.cluster.local
Address: 10.100.79.12
Name:	kimdubi-test-redis-cluster-headless.default.svc.cluster.local
Address: 10.100.14.8
Name:	kimdubi-test-redis-cluster-headless.default.svc.cluster.local
Address: 10.100.79.11
Name:	kimdubi-test-redis-cluster-headless.default.svc.cluster.local
Address: 10.100.71.4

=> kimdubi-test-redis-cluster-headless service에 대해 nslookup 수행 이 service에 binding 된 모든 Pod의 IP를 return 받음

Service 생성 template을 살펴보자

kimdubi-test-redis-cluster

$ kubectl get service kimdubi-test-redis-cluster -o yaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    meta.helm.sh/release-name: kimdubi-test
    meta.helm.sh/release-namespace: default
  creationTimestamp: "2021-02-21T07:33:55Z"
  labels:
    app.kubernetes.io/instance: kimdubi-test
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: redis-cluster
    helm.sh/chart: redis-cluster-4.3.0
  name: kimdubi-test-redis-cluster
  namespace: default
  resourceVersion: "24613"
  selfLink: /api/v1/namespaces/default/services/kimdubi-test-redis-cluster
  uid: c7a73d74-a532-4689-8d9e-45287df887b3
spec:
  clusterIP: 10.254.8.60
  ports:
  - name: tcp-redis
    port: 6379
    protocol: TCP
    targetPort: tcp-redis
  selector:
    app.kubernetes.io/instance: kimdubi-test
    app.kubernetes.io/name: redis-cluster
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

.spec.clusterIP

  • clusterIP type으로 생성된 service를 지정한 IP로 생성되도록 설정
  • 이 service는 10.254.8.60 IP를 할당받게됨

.spec.ports[]

  • 해당 서비스의 label selector 에 매치되는 Pod의 특정 Port (targetPort) 를 service의 6379 port에 매핑시킴

.spec.selector

  • 해당 service가 연결해야하는 대상 Pod의 label 조건
  • kimdubi-test , redis-cluster 의 label이 달린 Pod는 이 service를 통해 접근할 수 있음

.spec.sessionAffinity

  • 특정 클라이언트가 특정 Pod에 붙어야할 때 사용하는 설정

.spec.type

  • service의 type으로 default는 ClusterIP

kimdubi-test-redis-cluster-headless

$ kubectl get service kimdubi-test-redis-cluster-headless -o yaml

apiVersion: v1
kind: Service
metadata:
  annotations:
    meta.helm.sh/release-name: kimdubi-test
    meta.helm.sh/release-namespace: default
  creationTimestamp: "2021-02-21T07:33:55Z"
  labels:
    app.kubernetes.io/instance: kimdubi-test
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: redis-cluster
    helm.sh/chart: redis-cluster-4.3.0
  name: kimdubi-test-redis-cluster-headless
  namespace: default
  resourceVersion: "24611"
  selfLink: /api/v1/namespaces/default/services/kimdubi-test-redis-cluster-headless
  uid: eb2da26a-9264-46a7-91f1-7a3cfa241889
spec:
  clusterIP: None
  ports:
  - name: tcp-redis
    port: 6379
    protocol: TCP
    targetPort: tcp-redis
  - name: tcp-redis-bus
    port: 16379
    protocol: TCP
    targetPort: tcp-redis-bus
  publishNotReadyAddresses: true
  selector:
    app.kubernetes.io/instance: kimdubi-test
    app.kubernetes.io/name: redis-cluster
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

.spec.clusterIP

  • 위에서 본 kimdubi-test-redis-cluster service와는 다르게 clusterIP 값이 None 으로 설정하여 headless service를 생성함

.spec.ports[]

  • 해당 서비스의 label selector 에 매치되는 Pod의 특정 Port (targetPort) 를 service의 6379 port에 매핑시킴

.spec.selector

  • 해당 service가 연결해야하는 대상 Pod의 label 조건
  • kimdubi-test , redis-cluster 의 label이 달린 Pod는 이 service를 통해 접근할 수 있음