Featured image of post 서비스 API

서비스 API

서비스 API

서비스 API 카테고리

  • 클러스터 컨테이너에 대한 엔드포인트를 제공하거나 레이블과 일치하는 컨테이너의 디스커버리에 사용되는 리소스

    • 서비스
      • ClusterIP
      • ExternalIP(ClusterIP의 한 종류)
      • NodePort
      • LoadBalancer
      • Headless(None)
      • ExternalName
      • Node-Selector
    • 인그레스
  • 파드는 서비스를 사용하지 않고도 파드간 통신이 가능하나, 서비스를 사용하면 두 가지 큰 장점이 있음

    • 파드에 트래픽 로드 밸런싱
    • 서비스 디스커버리와 클러스터 내부 DNS

파드에 트래픽 로드 밸런싱

  • 수신한 트래픽을 여러 파드에 로드 밸런싱

ClusterIP

  • 클러스터 내부에서만 사용 가능한 가상 IP를 가진 엔드포인트 제공하는 로드 밸런서 구성
  • spec.selector에 정의한 조건에 따라 트래픽 전송
1
2
3
4
5
6
7
8
## 서비스 생성
$ kubectl apply -f sample-clusterip.yaml

## 지정한 레이블을 가진 파드 중 특정 JSON Path를 컬럼으로 출력
$ kubectl get pods -l app=sample-app -o custom-columns="NAME:{metadata.name},IP:{status.podIP}"

## 서비스 상세 정보 확인
$ kubectl describe service sample-clusterip
  • Endpoints에 app=sample-app 라벨을 가진 파드의 IP 정보가 있음
  • Endpoints 항목에 아무것도 없을 경우 셀렉터 조건이 맞지 않을 가능성 있음
1
2
3
4
5
## 일시적으로 파드를 시작하여 서비스 엔드포인트로 요청
### (여러 번 실행 시 비슷한 빈도로 세 개의 파드명 표시)
$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- curl -s http://{ClusterIP}:8080
Host=10.100.50.111  Path=/  From=sample-deployment-687d589688-8nbcs  ClientIP=172.31.9.8  XFF=
pod "testpod" deleted

여러 포트 할당

  • 하나의 서비스에 여러 포트 할당 가능 (바람직)

이름을 사용한 포트 참조

  • 파드의 포트 정의에 이름을 지정하면 이름을 사용하여 참조 가능
1
2
3
4
5
## 서비스 목적지 엔드포인트 확인
$ kubectl describe service sample-named-port-service

## 파드 IP 주소 확인
$ kubectl get pods -o wide

클러스터 내부 DNS와 서비스 디스커버리

  • 서비스에 속한 파드를 보여주거나 서비스명에서 엔드포인트를 반환하는 것
  • 서비스 디스커버리 방법
    • 환경 변수 사용
    • DNS A 레코드 사용
    • DNS SRV 레코드 사용
환경 변수를 사용한 서비스 디스커버리
  • 파드 내부에서는 환경 변수에서도 같은 네임스페이스 서비스 확인 가능
  • -이 포함된 서비스명은 _로 변경 후 대문자 변환됨
  • 파드 생성 후 서비스 생성 또는 삭제에 따라 변경된 환경 변수가 기존 파드에 자동 등록되지 않음
    • 먼저 생성한 파드 재생성 필요
1
2
## 환경 변수에 등록된 서비스 정보 확인
$ kubectl exec -it sample-deployment-{}-{} -- env | grep -i kubernetes
  • spec.enableServiceLinks를 false로 지정시 환경 변수 추가 비활성화 (기본값 true)
DNS A 레코드를 사용한 서비스 디스커버리
  • IP 주소를 편하게 관리하기 위해 기본적으로 자동 할당된 IP 주소에 연결된 DNS 명을 사용하는 것이 좋음
  • 서비스명의 이름 해석이 수행되고 해당 ip로 요청이 발송
  • 다른 네임스페이스의 경우 sample-cluster.default와 같이 네임스페이스명을 붙여 이름 해석해야 함
1
2
3
## 일시적으로 파드 기동하여 컨테이너 내부에서 sample-clusterip:8080으로 HTTP 요청
$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod \
--command -- curl -s http://sample-clusterip:8080
DNS SRV 레코드를 사용한 서비스 디스커버리
  • 포트명과 프로토콜을 사용해 서비스를 제공하는 포트 번호를 포함한 엔드포인트를 DNS로 해석
  • _서비스 포트명._프로토콜.서비스명.네임스페이스명.svc.cluster.local
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
## 일시적으로 파드 기동하여 SRV 레코드가 다른 파드에서 해석이 가능한지 확인
$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod \
--command -- dig _http-port._tcp.sample-clusterip.default.svc.cluster.local SRV
;; QUESTION SECTION:
;_http-port._tcp.sample-clusterip.default.svc.cluster.local. IN SRV

;; ANSWER SECTION:
_http-port._tcp.sample-clusterip.default.svc.cluster.local. 5 IN SRV 0 100 8080 sample-clusterip.default.svc.cluster.local.

;; ADDITIONAL SECTION:
sample-clusterip.default.svc.cluster.local. 5 IN A 10.100.50.111
  • 목적지 호스트명 sample-clusterip.default.svc.cluster.local과 포트번호 8080 해석 가능

클러스터 내부 DNS와 클러스터 외부 DNS

  • 파드의 DNS 서버 설정을 명시적으로 하지 않으면 클러스터 내부 DNS 사용하여 이름 해석 수행
  • 내부 이외의 레코드는 외부 DNS에 재귀 질의 해야함

노드 로컬 DNS 캐시

  • 대규모 클러스터에서 성능 향상을 위해 각 노드의 로컬에 DNS 캐시 서버를 포함하는 구조도 존재
    • 활성화한 환경에서 파드의 질의 대상은 같은 노드에 있는 로컬 DNS 캐시 서버

ClusterIP 서비스

  • 클러스터 내부에서만 통신 가능한 가상 IP 할당
  • 클러스터 외부에서 트래픽을 수신할 필요가 없는 환경에서 내부 로드 밸런서로 활용

ExternalIP 서비스

  • 지정한 쿠버네티스 노드 IP 주소:포트에서 수신한 트래픽을 컨테이너로 전달하는 형태로 외부와 통신
  • 특별한 이유가 없다면 NodePort 서비스를 사용하는것이 좋음
  • type: ExternalIP를 지정하는 것이 아님 (ClusterIP에 해당함)

NodePort 서비스

  • 모든 쿠버네티스 노드 IP 주소:포트에서 수신한 트래픽을 컨테이너로 전달하는 형태로 외부와 통신
  • 모든 쿠버네티스 노드의 IP 주소에서 해당 포트를 listen 하기 때문에 충돌 주의
  • 할당된 포트 번호를 지정할 필요가 없을 경우, 포트를 지정하지 않으면 빈 포트 번호가 자동으로 선택됨
설정 항목 개요
spec.ports[].port ClusterIP에서 수신할 포트 번호
spec.ports[].targetPort 목적지 컨테이너 포트 번호
spec.ports[].nodePort 모든 쿠버네티스 노드 IP 주소에서 수신할 포트 번호

NodePort 주의점

  • 가용한 포트 범위는 대부분 30000 ~ 32767
  • 여러 NodePort 서비스에서 같은 포트 사용 불가

LoadBalancer 서비스

  • 클러스터 외부로부터 트래픽을 수신할 때 가장 실용적인 서비스
  • 쿠버네티스 노드와 별도로 외부 로드 밸런스를 사용 -> 노드 장애에도 크게 문제가 되지 않음 (장애 발생한 노드에는 트래픽을 전송하지 않음)
    • 장애 감지 까지 일시적으로 서비스 중단 현상이 발생할 수 있음
  • 생성시 EXTERNAL-IP가 <pending> 상태임
    • 로드밸런서를 백그라운드에서 생성중이기 때문에 아직 미할당된 상태
  • 정적으로 외부 LB에서 사용하는 IP 주소 지정 가능
  • 방화벽을 지정하여 접속 제한 가능
1
2
3
## 외부에서 통신
## 여러번 실행 시 비슷한 빈도로 세 개의 파드명 표시
$ curl -s http://{lb EXTERNAL-IP}:8080

그 외 서비스

세션 어피니티

  • 트래픽을 서비스에 연결된 어느 하나의 파트에 전송되면, 그 파드에 계속 보내고 싶을 때 사용
  • 최대 세션 고정 시간 설정 가능

노드 간 통신 제외와 발신 측 IP 주소 유지

  • NodePort, LoadBalancer 서비스에서 노드에 도착한 요청은 2단계 로드 밸런싱이 이루어짐(레이턴시 오버헤드 발생)
  • 발신 측 IP 주소가 유실되는 특징
  • 데몬셋은 하나의 노드에 하나의 파드만 배치되므로 같은 노드에만 통신하고 싶은 경우
  • NodePort 서비스의 spec.externalTrafficPolicylocal로 설정
    • 해당 노드의 요청은 그 노드상에 있는 파드에만 전송
      • 두 개 이상의 파드 존재시 균등 전송
      • 파드가 없다면 요청에 응답할 수 없으므로 가능하면 사용 X
  • LoadBalancer 서비스의 spec.externalTrafficPolicylocal로 설정
    • 별도의 헬스 체크용 NodePort가 있어 파드가 존재하지 않는 노드에는 요청이 전송되지 않음

헤드리스 서비스(None)

  • 대상이 되는 개별 파드의 IP 주소가 직접 반환되는 서비스
  • 로드 밸런싱을 위한 IP 주소는 제공되지 않음
  • DNS 라운드 로빈을 사용한 엔드포인트 제공
  • 스테이트풀셋이 헤드리스 서비스를 사용하는 경우에만 파드명으로 IP주소를 찾을 수 있음
  • 생성 조건
    • 서비스의 spec.type이 ClusterIP일 것
    • 서비스의 spec.ClusterIP가 None일 것
    • [옵션] 서비스의 metadata.name이 스테이트풀셋의 spec.serviceName과 같을 것
      • 파드명으로 디스커버리하는 경우

ExternalName 서비스

  • 외부도메인으로 CNAME 반환
  • 다른 이름을 설정하고 싶거나, 클러스터 내부에서의 엔드포인트를 쉽게 변경하고 싶을 때 사용

외부 서비스와 느슨한 결합 확보

  • 목적지가 변경되었을 때 ExternalName 서비스를 변경하는 것만으로 가능
  • ClusterIP 서비스에서 ExternalName 서비스로 전환할 경우 spec.clusterIP를 명시적으로 공란으로 두어야함

None-Selector 서비스

  • 서비스명으로 이름 해석시 자신이 설정한 멤버에 대해 로드 밸런싱 수행
  • externalName을 지정하지 않고 셀렉터가 존재하지 않는 서비스 생성 + 엔드포인트 리소스 수동 생성하면 유연한 서비스 생성 가능
    • 쿠버네티스 외부에 애플리케이션 서버에 대한 요청을 분산하는 경우에도 쿠버네티스 서비스 사용 가능
    • 서비스 환경과 스테이징 환경에서 클러스터 내외부를 분리해도 애플리케이션에서 같은 ClusterIP로 보여줄 수 있음

인그레스

  • 서비스들을 묶는 서비스들의 상위 객체

리소스와 컨트롤러

  • 매니페스트를 쿠버네티스에 등록하는 것만으로는 아무 처리도 일어나지 않음
    • 실제 처리를 하는 컨트롤러라는 시스템 구성 요소 필요
  • 인그레스 리소스: 매니페스트에 등록된 API 리소스
  • 인그레스 컨트롤러: 인그레스 리소스가 쿠버네티스에 등록되었을 때 어떠한 처리 수행

인그레스 종류

  • 클러스터 외부 로드 밸런서를 사용한 인그레스
    • GKE 인그레스
  • 클러스터 내부 로드 밸런서를 사용한 인그레스
    • Nginx 인그레스
클러스터 외부 로드 밸런서를 사용한 인그레스
  • 인그레스 리소스 생성만으로 로드 밸런서의 가상 IP가 할당되어 사용 가능
  • 순서(단계)
    1. 클라이언트
    2. -> L7 로드 밸런서(NodePort 경유)
    3. -> 목적지 파드
클러스터 내부에 인그레스용 파드를 배포하는 인그레스
  • 인그레스 리소스에서 정의한 L7 수준의 로드 밸런싱 처리를 하기 위해 인그레스용 파드를 클러스터 내부에 생성해야 함
  • 클러스터 외부에서 접속할 수 있도록 별도로 인그레스용 파드에 LoadBalancer 서비스를 생성하는 등의 준비 필요
  • SSL 터미네이션이나 경로 기반 라우팅 등과 같은 L7 수준의 처리를 위해 부하에 따른 레플리카 수의 오토 스케일링 고려해야 함
  • 순서(단계)
    1. 클라이언트
    2. -> L4 로드 밸런서(type: LoadBalancer)
    3. -> Nginx 파드(Nginx 인그레스 컨트롤러)
    4. -> 목적지 파드

정리

  • 파드 서비스 디스커버리나 L4 로드 밸런싱 기능을 제공하기 위한 서비스 리소스
  • L7 로드 밸런싱 기능을 제공하는 인그레스
  • 서비스
    • L4 로드 밸런싱
    • 클러스터 내부 DNS를 사용한 이름 해석
    • 레이블을 사용한 파드의 디스커버리
  • 인그레스
    • L7 로드 밸런싱
    • SSL 터미네이션
    • 경로 기반 라우팅
서비스 종류 IP 엔드포인트 내용
ClusterIP 쿠버네티스 클러스터 내부에서만 통신 가능한 가상 IP
ExternalIP 특정 쿠버네티스 노드의 IP 주소
NodePort 모든 쿠버네티스 노드의 모든 IP 주소(0.0.0.0)
LoadBalancer 클러스터 외부에서 제공되는 로드 밸런서의 가상 IP
Headless(None) 파드의 IP 주소를 사용한 DNS 라운드 로빈
ExternalName CNAME을 사용한 느슨한 연결 확보
None-Selector 원하는 목적지 멤버를 설정할 수 있는 다양한 엔드 포인트
인그레스 종류 구현 예제
클러스터 외부 로드 밸런서를 사용한 인그레스 GKE
클러스터 내부에 인그레스용 파드를 배포하는 인그레스 Nginx 인그레스
Hugo로 만듦
JimmyStack 테마 사용 중