서비스

세션 어피니티

동일한 명령을 실행하더라도 서비스 프록시가 각 연결을 임의의 파드로 전달하기 때문에 연결 할 때 마다 임의의 파드가 선택된다.

반면 특정 클라이언트의 모든 요청을 매번 같은 파드로 전달하려면 서비스의 세션 어피니티 속성을 기본값 None 대신 ClientIP로 설정해야 한다.

apiVersion: v1
kind: Service
spec:
    sessionAffinity: ClientIP
...

DNS를 통한 서비스 검색

  • 클러스터에서 실행중인 다른 모든 팟이 사용하는 kube-dns 라는 시스템 리소스가 있다.
  • kube-dns는 모든 서비스를 알고있는 쿠버네티스의 자체 DNS 서버.
  • 파드가 내부 DNS 서버를 사용할 지 여부는 각 파드 스펙의 dnsPolicy 속성으로 구성 할 수 있다.
  • 서비스 이름을 알고 있는 파드는 FQDN(Fully Quallified Domain Name)으로 통신 할 수 있다.위 curl은 클러스터 내부의 pod을 kubectl exec 명령을 통해 실행한 상태에서 수행한다. (클러스터 내부 통신이기 때문)
    • svc.cluster.local 은 모든 클러스터의 로컬 서비스 이름에 사용되는 클러스터의 도메인 접미사. 생략 가능하다.
    • 파드가 같은 네임스페이스에 있는 경우 네임스페이스도 생략할 수 있다. 단순히 서비스의 이름만 써도 된다.
    • 파드 컨테이너 내부의 DNS resolver 구성 /etc/resolv.conf 를 보면 이해 할 수 있다.
  • Ex) curl http://svc-orra-dev.ns-ordev.svc.cluster.local:8082
  • 서비스에 IP 핑은 할 수 없다.

외부 클라이언트에 서비스 노출

  • 노드포트로 서비스 유형(Type) 설정
  • 서비스 유형을 노드포트 유형의 확장인 '로드밸런서'로 설정
  • 단일 IP 주소로 여러 서비스를 노출하는 인그레스 리소스 만들기

노드포트

노드포트 타입 서비스를 만들면, k8s는 모든 노드에 특정 포트를 할당하고 서비스를 구성하는 파드로 들어오는 요청을 전달한다.

로드밸런서

로드밸런서 타입 서비스를 만들면, 일번적으로 클라우드 인프라에서 로드밸런서를 자동으로 프로비저닝하는 기능을 제공한다. 쿠버네티스가 로드밸런서 서비스를 지원하지 않는 환경에서 실행중인 경우 노드포트 서비스처럼 작동한다.

서비스를 생성하고 클라우드 인프라가 로드밸런서를 생성하고 IP주소를 서비스 오브젝트에 쓰면, 로드밸런서 IP주소가 서비스의 external IP 주소로 표시돤다.

외부 연결 특성의 이해

불필요한 홉

외부에서 노드포트로 서비스에 접속하는 경우, 서비스가 임의로 선택한 파드가 동일 노드일 수도 있고, 아닐수도 있다. 파드에 도달하려면 추가 네트워크 홉이 필요한 경우가 생길 수 있다.

외부 연결을 수신한 노드에서 실행중인 파드로만 외부 트래픽을 전달하도록 서비스를 구성할 수 있다. 서비의 스펙에 externalTrafficPolicy 필드를 설정하면 된다.

spec:
    externalTrafficPolicy: Local

하지만 로컬에 파드가 없으면 연결이 중단되어버린다. (Kubernetes 1.15 이상: 패킷이 다른 노드에 있는 구성원 pod로 전달)

클라이언트 IP가 보존되지 않음

클러스터 내 클라이언트가 서비스로 연결 할 때, 서비스의 파드는 클라이언트의 IP주소를 얻을 수 있다.

하지만 노드포트로 연결을 수신하면 패킷에서 소스 네트워크 주소 변환(source network address translation)이 수행되어 소스 IP가 변경된다.

예를들어 엑세스 로그에 브라우저 IP를 남길 수 없는 문제가 생길 수 있다.

하지만 externalTrafficPolicy를 설정 한 경우, 노드 사이에 추가 홉이 없기 때문에 SNAT이 수행되지 않는다.

인그레스

필요성

로드밸런서 서비스는 공용 IP주소를 가진 로드밸런서가 필요하지만, 인그레스는 한 IP주소로 수십개의 서비스에 접근이 가능하도록 지원해준다.

클라이언트가 http 요청을 인그레스에 보낼 때, 요청한 호스트와 경로에 따라 요청을 전달할 서비스를 결정한다.

인그레스는 L7에서 동작하며, 쿠키 기반 세션 어피니티 등과 같은 기능을 제공 할 수 있다.

레디니스 프로브(Readiness Probe)

주기적으로 호출되며 특정 파드가 클라이언트의 요청을 수신 할 수 있는 상태인지 결정한다.

Exec Probe

프로세스를 실행시켜 종료 상태 코드로 결정한다.

Http Get Probe

http get 요청을 보내 http 응답 상태 코드를 보고 준비 여부를 결정한다.

TCP Socket Probe

지정된 포트로 TCP 연결을 수행하여 소켓이 연결되면 준비된것으로 간주한다.

레디니스 프로브 동작

컨테이너가 시작 될 때 첫번째 점검을 수행하기 전 기다리는 시간을 구성할 수 있다.(initialDelaySeconds)

그 다음 주기적으로 레디니스 프로브를 수행한다. 파드가 준비 상태가 되지 않으면 서비스의 엔드포인트에서 제거된다. 준비되면 다시 서비스에 추가된다.

Liveness Probe와 다르게 준비 상태가 아니더라도 컨테이너가 재시작 되지 않는다.

레디니스 프로브 중요성

만약 프론트엔드 파드중 하나에 연결 문제가 발생해 더 이상 DB에 연결 할 수 없는 경우, 레디니스 프로브를 통해 쿠버네티스에 알려야 한다.

레디니스 프로브를 항상 정의해야 하는 이유

파드에 레디니스 프로브를 추가하지 않으면, 파드가 시작하는 즉시 서비스의 엔드포인트가 된다.

애플리케이션을 시작하는데 오래 걸리는 경우, 준비가 되지 않은 파드로 요청이 전달되어 "Connection refused"와 같은 에러를 보게 된다.

레디니스 프로브에 파드의 종료 코드를 포함하지 말아야 한다

쿠버네티스는 파드를 삭제하자마자 모든 서비스에서 파드를 제거하기 때문에, 레디니스 프로브에 파드의 종료 코드를 포함할 필요가 없다.

헤드리스 서비스

쿠버네티스가 서비스에 대한 DNS 조회를 수행하면 DNS 서버는 서비스의 클러스터 IP를 반환한다.

하지만 서비스 스펙에서 clusterIP 필드를 None으로 설정하면 서비스 IP 대신 파드 IP들을 반환한다.

이를 이용해 클라이언트는 하나 이상의 파드에 연결 할 수 있다.

이는 READY 상태가 된 파드만 보여주며 준비되지 않은 파드도 포함하려는 경우 서비스에 다음과같이 어노테이션을 추가해야 한다.

kind: Service
metadata:
    annotations:
        publishNotReadyAddresses: "true"

서비스 트러블슈팅

  • 외부가 아닌 클러스터 내에서 서비스 클러스터 IP에 연결되는지 확인한다.
  • 서비스에 액세스 할 수 있는지 확인할 때, 클러스터 IP에 핑을 때리는건 의미없다.(서비스의 클러스터 IP는 가상 IP임으로 핑에 응답하지 않음.)
  • Readiness Probe를 정의했다면 확인한다. publishNotReadyAddresses 어노테이션이 없다면 준비되지 않은 파드는 서비스에 포함되지 않는다.
  • 파드가 서비스에 포함되어있는지 확인하려면 'kubectl get endpoints'를 조회해 확인한다.
  • FQDN이나 그 일부로 서비스에 접근하는 경우, FQDN 대신 클러스터 IP로 접근해본다.
  • 대상 포트(target port)가 아닌 서비스의 포트로 연결되는지 확인한다.
  • 파드 IP에 직접 연결해 파드가 올바른 포트에 연결되어 있는지 확인한다.
  • 파드 IP로 접근할 수 없는 경우, 애플리케이션이 localhost에만 바인딩하는지 확인한다.
728x90
반응형

+ Recent posts