서로 다른 두 파드가 통신하려면 서로의 IP 주소를 먼저 알아야 함

  • pod 간 통신을 위해서 IP 주소를 사용하는데 IP 주소는 pod 대체 시 변경됨
  • service address discovery를 통해 문제 해결
  • pod의 교체된 IP 주소는 kubernetes API를 통해서만 찾을 수 있음


쿠버네티스 내부의 네트워크 트래픽 라우팅

  • 언제든지 pod IP 주소는 바뀔 수 있으므로 고정된 도메인 네임 도입
  • 쿠버네티스 내부에는 전용 DNS 서버가 있음

img.png

  • 서비스 생성 시 서비스의 IP 주소가 DNS 서버에 등록되고 서비스 삭제 전까지 변경되지 않음
  • 서비스는 label selector를 이용하여 파드와 느슨한 연결
  • sleep-1 파드는 sleep-2 파드에 통신 시 도메인 네임을 가지고 수행하며 DNS 조회는 DNS 서버에서 처리됨. DNS 서버가 서비스의 IP 주소를 반환
  • sleep-1 파드는 서비스가 없으므로 도메인 네임으로 접근 불가
# sleep2-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: sleep-2 # 서비스 이름이 도메인명으로 사용됨
spec:
  selector:
    app:sleep-2 # 대상 pod label
  ports:
    - port: 80 # 80번 포트를 주시하다가 pod의 80번 포트로 트래픽 전달
kubectl apply -f sleep2-service.yaml
kubectl get svc sleep-2
kubectl exec deploy/sleep-1 -- ping -c | sleep-2
  • ping은 k8s에서 지원하는 TCP/UDP 프로토콜이아닌 ICMP 이므로 실패함



파드와 파드 간 통신

  • 서비스 유형 중 가장 기본은 ClusterIP
  • ClusterIP는 클러스터 전체에서 통용되는 IP 주소를 생성하며 파드가 어느 노드에 있더라도 접근이 가능
  • ClusterIP는 클러스터 내에서만 유효하므로 파드와 파드간 통신에만 사용

웹 사이트와 API를 담당할 두 개의 디플로이먼트 실행

kubectl apply -f api.yaml -f web.yaml
kubectl port-forward deploy/numbers-web 8080:80
  • http://localhost:8080 접속 시 deploy/numbers-web의 80 port로 포워딩 되고 web 페이지가 출력됨
  • web에서는 http://numbers-api/... api를 사용하지만 현재 등록된 서비스가 없으므로 도메인을 찾지 못해서 오류 발생
# api-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: numbers-api # 도메인 등록
spec:
  selector:
    app:numbers-api
  ports:
    - port: 80
  type: ClusterIP # 클러스터 내부에서만 사용

kubectl apply -f api-service.yaml
kubectl get svc numbers-api

    NAME      TYPE      CLUSTER-IP    EXTERNAL-IP   PORTS   AGE
numbers-api ClusterIP   10.109.24.16    <none>        80    25s
  • api 파드를 삭제 후 deploy에서 재생성하여 IP 주소가 변경되어도 web 파드는 서비스 도메인을 통해 api 파드에 요청하므로 정상 수행됨



외부 트래픽을 파드로 전달

LoadBalancer

  • 로컬 개별 환경과 운영 환경 모두 적용 가능 방법
  • 트래픽을 받은 노드가 아닌 다른 노드에서 실행되는 pod에도 트래픽 전달 가능

img.png

# web-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: numbers-web
spec:
  selector:
    app:numbers-web # 대상 pod label
  ports:
    - port: 8080 # 서비스에서 주시하는 포트
      targetPort: 80 # 트래픽이 전달된 파드의 포트
  type: LoadBalancer
kubectl apply -f web-service.yaml 
kubectl get svc numbers-web
  • 로드 밸런서 등록 시 external IP를 부여 받음
  • EKS 클러스터에서 수행 시 공인 IP 주소 부여 받음
  • port forwarding 필요 없음

NodePort

  • 외부 로드밸런서가 필요 없음
  • 클러스터의 모든 노드가 서비스에 지정된 port를 오픈하고 트래픽을 파드에 전달

img.png

# web-service-nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: numbers-web-node
spec:
  selector:
    app:numbers-web
  ports:
    - port: 8080 # 파드가 서비스에 접근하기 위해 사용
      targetPort: 80  # 파드에 트래픽을 전달하는 포트
      nodePort: 30080 # 서비스 외부 공개 포트
  type: NodePort # 노드의 IP 주소를 통해 직접 클러스터 접속



쿠버네티스 외부로 트래픽 전달

  • 쿠버네티스 클러스터 내에서 실행하는 서비스가 아닌 경우 (ex DB) 외부 서비스와 연동 필요성 존재

ExternalName 서비스

img.png

  • 서비스는 db-service라는 명으로 도메인 등록 그리고 externalName으로 외부 도메인 지정
  • pod에서 db-service 도메인으로 접근 시 외부 도메인에 연결됨
# api-service-externalName.yaml

apiVersion: v1
kind: Service
metadata:
  name: db-service
spec:
  type: ExternalName
  externalName: app.mydb.io
  • 하지만 externalName은 app이 가리키는 주소를 치환할 뿐 요청 내용 자체를 바꾸지는 못함
  • HTTP 요청의 경우 요청 헤더에 대상 호스트 명이 들어가는데 이 호스트 명이 externalName과 다른 경우 요청 실패함
    • app에서 host명을 바꾸는 작업 수행 필요

헤드리스 서비스

  • ExternalName과 유사하게 로컬 도메인 네임을 치환하지만 도메인 네임 대신 IP 주소를 대체함
  • 헤드리스 서비스는 ClusterIP 형태로 정의되고 레이블 셀렉터가 없으므로 대상 파드가 없음
  • 대신 자신이 제공해야 할 IP 목록이 담긴 엔드 포인트 리소스와 함께 배포됨
# api-service-headless.yaml

apiVersion: v1
kind: Service
metadata:
  name: numbers-api
spec:
  type: ClusterIP
  ports:
    - port: 80

---

apiVersion: v1
kind: Endpoints
metadata:
  - name: numbers-api
subsets:
  - addresses:
      - ip: 192.168.124.234 # IP 주소가 유효하지 않아도 서비스 등록됨
    ports:
      - ports: 80 # 각 IP 주소에서 주시할 포드
  • headless 서비스 사용 시나리오
    • 만약 같은 서비스에 속하는 pod A, B가 통신하고 싶다면?
      • 서비스를 이용 시 서비스는 본인에게 속한 pod 중 1개로 트래픽을 보내므로 부적합
    • 클러스터에 속한 클라이언트가 여러 pod와 통신하고 싶다면?
      • 모든 엔드포인트 IP 주소가 필요
kubectl apply -f api-service-headless.yaml
kubectl get svc numbers-api
kubectl get endpoints numbers-api



쿠버네티스 서비스의 해소 과정

img.png

  • 파드 속 컨테이너가 요청한 도메인 네임 조회는 DNS 서버가 응답함
  • 조회 대상이 서비스라면 클러스터 내 IP 주소 혹은 외부 도메인 네임 반환
  • 파드에서 나온 모든 통신은 네트워크 프록시가 라우팅 담당
  • 각 노드에서 모든 서비스의 엔드포인트에 대한 최신 정보 유지하고 OS의 네트워크 패킷 필터(iptables)를 사용해서 트래픽 라우팅함
  • ClusterIP는 실재하지 않은 가상 IP 이므로 네트워크 프록시가 패킷 필터링을 통해 이를 실제 엔트포인트에 연결

네임스페이스

  • 리소스는 모든 네임스페이스 안에 존재함
  • 네임스페이스는 다른 리소스를 묶기 위한 리소스

img.png

  • 같은 네임스페이스 안에서는 도메인 네임 number-api로 접근 가능
  • 다른 네임스페이스에서 접근하려면 number-api.default.svc.cluster.local이라는 도메인 네임으로 접근 가능
kubectl get svc --namespace default

** 주의

  • 리소스 삭제 시 pod나 deployment와 달리 서비스는 default namespace에 쿠버네티스 API가 존재하므로 주의해야 함
  • kubectl delete svc --all 대신 지정해서 삭제 필요!