클러스터 내부에서 가상 IP를 할당받고, 각 노드에 파드로 떠있는 kube-proxy 를 통해서 파드와 통신하는 내부용 로드 밸런서이다.

쿠버네티스 API 에 접속 가능하게 하기 위해 클러스터 생성시 기본적으로 기동되는 Kubernetes 서비스도 ClusterIP 이다.

kube-proxy 는 각 노드에 파드로 떠있으며, kube-system 네임스페이스에서 조회 가능하다.

$ kubectl get pods -o wide -n kube-system | grep proxy
kube-proxy-6pn2g                                1/1     Running       1 (45d ago)   119d   172.18.0.4   desktop-worker          <none>           <none>
kube-proxy-c8r4l                                1/1     Running       1 (45d ago)   119d   172.18.0.3   desktop-worker2         <none>           <none>
kube-proxy-l52jn                                1/1     Running       0             119d   172.18.0.3   desktop-control-plane   <none>           <none>

 

1. ClusterIP 생성

아래와 같은 매니페스트로 생성한다.

apiVersion: v1
kind: Service
metadata:
  name: sample-clusterip
spec:
  type: ClusterIP
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
  selector:
    app: sample-app

 

spec.ports[].port 는 ClusterIP 에서 수신하는 포트를 지정하고, spec.ports[].targetPort 는 목적지 파드의 포트번호를 지정한다.

 

이제 클러스터 내부 파드에서 방금 생성한 서비스명인 sample-clusterip 를 도메인명으로 호출하면, 서비스가 바라보고 있는 파드들(app=sample-app 라벨을 가지고 있는 파드들)로 로드밸런싱 된다.

$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- curl -s http://sample-clusterip:8080
Host=sample-clusterip  Path=/  From=sample-deployment-75c768d5fb-9qdjm  ClientIP=10.244.1.158  XFF=
pod "testpod" deleted
$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- curl -s http://sample-clusterip:8080
Host=sample-clusterip  Path=/  From=sample-deployment-75c768d5fb-g5vkv  ClientIP=10.244.1.159  XFF=
pod "testpod" deleted
$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- curl -s http://sample-clusterip:8080
Host=sample-clusterip  Path=/  From=sample-deployment-75c768d5fb-2lkwp  ClientIP=10.244.1.163  XFF=
pod "testpod" deleted

 

2. ClusterIP 의 서비스 디스커버리

한가지 의문이 생긴다. 파드에서 sample-clusterip 도메인으로 호출했을 뿐인데 어떻게 도메인을 해석하고 목적지 파드까지 도달하게 되는 걸까? 아래와 같은 순서로 해석된다.

 

1) 파드 내에서 질의

파드 내에서 http://sample-clusterip:8080 질의

 

2) 파드 내 DNS 레코드 해석

파드 내 /etc/resolv.conf DNS 레코드에 따라 suffix 를 붙여 kube-dns 로 질의한다.

/etc/resolv.conf 는 아래 커맨드로 조회할 수 있다.

$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5

이에 따라 10.96.0.10 DNS(kube-dns) 에서 해석이 가능 할 때까지 search 에 지정된 suffix 를 붙여 아래 순서대로 질의한다.

sample-clusterip.default.svc.cluster.local
sample-clusterip.svc.cluster.local
sample-clusterip.cluster.local
sample-clusterip

 

3) kube-dns 의 블록 매칭

kube-dns 에서는 coredns configMap 의 설정에 따라 kubernetes cluster.local 블록에 매칭되어 쿠버네티스 API 서버인 kube-apiserver 에 질의한다.
해당 설정은 아래 커맨드로 조회 할 수 있다.

$ kubectl get configmaps coredns -n kube-system -o yaml
...
kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
...

 

 

4) kube-apiserver 에서 네임스페이스와 서비스 명칭으로 서비스 IP 를 조회

쿠버네티스가 자동 생성하는 서비스의 DNS 이름 규칙에 따라 해당하는 서비스의 IP 를 조회한다.

규칙은 아래와 같다.

{서비스명}.{네임스페이스명}.svc.cluster.local

 

5) kube-dns 에서 이를 A 레코드로 응답하여 해석됨

실제로 dig 명령어를 통해 10.96.0.10 DNS 에서 도메인을 어떻게 해석하는지 질의 가능하다.

 

$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- dig @10.96.0.10 sample-clusterip.default.svc.cluster.local
;; ANSWER SECTION:
sample-clusterip.default.svc.cluster.local. 30 IN A 10.96.82.60

 

10.96.82.60 서비스가 응답함을 의미한다.

 

6) 해당 서비스를 호출하여 각 파드로 로드밸런싱 됨

해석된 서비스 IP 인 10.96.82.60:8080 으로 호출한다.

 

3. ClusterIP 의 가상 IP 직접 지정

일반적으로 ClusterIP 를 생성할 때 IP 를 지정하지 않기 때문에 할당 가능한 대역 내에서 자동 할당된다. 하지만, 직접 IP 로 서비스를 참조하고자 하는 상황이 있을 수 있다.

  • DNS 캐시나 방화벽 정책을 고정 IP로 관리할 때(IP 기반 ACL)
  • 테스트 환경에서 동일한 IP 를 사용해야 할 때

직접 IP 를 지정하기 위해서는 spec.clusterIP 에 지정할 IP 를 설정한다.

apiVersion: v1
kind: Service
metadata:
  name: clusterip-vip
spec:
  type: ClusterIP
  clusterIP: 10.96.82.70
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
  selector:
    app: sample-app

 

아래 커맨드로 직접 지정한 IP 가 설정되었는지 확인 가능하다.

$ kubectl get services clusterip-vip
NAME               TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
clusterip-vip      ClusterIP   10.96.82.70   <none>        8080/TCP   13m

 

다만, 지정하는 IP 는 아래와 같은 조건이 있으므로 주의하여 사용한다.

Service CIDR 대역 내 IP 로 지정해야 함 예: 10.96.0.0/12 범위 안이어야 함(kube-apiserver의 --service-cluster-ip-range 옵션으로 설정되는 범위)
이미 다른 Service 가 사용중이지 않아야 함 IP 충돌 발생 시 생성 실패 (The Service "X" is invalid: spec.clusterIP: Invalid value: "10.96.100.50": provided IP is already allocated)
한번 IP 를 지정하여 생성하면 변경할 수 없음(immutable) IP 를 변경하려면 삭제 후 재생성 해야 함

 

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,