Today I Learned

[쿠버네티스 인 액션] 5. 서비스: 클라이언트가 파드를 검색하고 통신을 가능하게 함 (3) 본문

쿠버네티스

[쿠버네티스 인 액션] 5. 서비스: 클라이언트가 파드를 검색하고 통신을 가능하게 함 (3)

하이라이터 2022. 9. 30. 14:44
728x90

5.4 인그레스 리소스로 서비스 외부 노출

  • 앞서 설명한 방법 외에도 인그레스 리소스를 생성해 외부 클라이언트에 서비스를 노출할 수 있다.

 

인그레스가 필요한 이유

  • 로드밸런서 서비스는 자신의 공용 IP 주소를 가진 로드밸런서가 필요하지만, 인그레스는 한 IP 주소로 수십 개의 서비스에 접근이 가능하도록 지원해준다.
  • 클라이언트가 HTTP 요청을 인그레스에 보낼 때, 요청한 호스트와 경로에 따라 요청을 전달할 서비스를 결정한다.
  • 인그레스는 네트워크 스택의 애플리케이션 계층(HTTP)에서 작동하며 서비스가 할 수 없는 쿠키 기반 세션 어피니티 등과 같은 기능을 제공할 수 있다.

 

인그레스 컨트롤러가 필요한 경우

  • 인그레스 리소스를 작동시키려면 클러스터에 인그레스 컨트롤러를 실행해야 한다. 쿠버네티스 환경마다 다른 컨트롤러 구현을 사용할 수 있지만 일부는 기본 컨트롤러를 제공하지 않는다.


5.4.1 인그레스 리소스 설정

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: kubia
spec:
  rules:
  - host: kubia.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: kubia-nodeport
          servicePort: 80


5.4.2 인그레스로 서비스 엑세스

  • http://kubia.example.com 서비스에 엑세스하려면 도메인명이 인그레스 컨트롤러의 IP와 매핑되도록 확인해야 한다.

 

인그레스의 IP 주소 얻기

 

인그레스 컨트롤러가 구성된 호스트의 IP를 인그레스 엔드포인트로 지정

  • kubia.example.com을 해당 IP로 확인하도록 DNS 서버를 구성하거나, 다음 줄을 /etc/hosts에 추가할 수 있다.

 

인그레스로 파드 엑세스

 

인그레스 동작 방식

  • 인그레스 컨트롤러는 대부분 요청을 서비스로 전달하지 않으며, 파드를 선택하는 데만 사용한다.

5.4.3 하나의 인그레스로 여러 서비스 노출

  • 인그레스 스펙은 규칙과 경로가 모두 배열이므로 여러 항목을 가질 수 있다.

 

동일한 호스트의 다른 경로로 여러 서비스 매핑

...
  - host: kubia.example.com
    http:
      paths:
      - path: /kubia
        backend:
          serviceName: kubia
          servicePort: 80
      - path: /bar
        backend:
          serviceName: bar
          servicePort: 80
  • 요청은 URL의 경로에 따라 두 개의 다른 서비스로 전송된다. 따라서 클라이언트는 단일 IP 주소로 두 개의 서비스에 도달할 수 있다.

 

서로 다른 호스트로 서로 다른 서비스 매핑하기

spec:
  rules:
  - host: kubia.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: foo
          servicePort: 80
  - host: bar.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: bar
          servicePort: 80
  • 요청은 호스트 헤더에 따라 서비스로 전달되며, DNS는 foo.example.com과 bar.example.com 도메인을 모두 인그레스 컨트롤러의 IP 주소로 지정해야 한다.

5.4.4 TLS 트래픽을 처리하도록 인그레스 구성

  • HTTPS로 트래픽을 전달하기 위해 TLS를 지원하도록 인그레스를 구성하는 방법을 살펴보자.

인그레스를 위한 TLS 인증서 생성

  • 클라이언트가 인그레스 컨트롤러에 대한 TLS 연결을 하면 컨트롤러는 TLS 연결을 종료한다.
  • 클라이언트와 컨트롤러 간의 통신은 암호화되지만 컨트롤러와 백엔드 파드 간의 통신은 암호화되지 않는다.
  • 파드에서 실행 중인 애플리케이션은 TLS를 지원할 필요가 없다.
  • 이를 위해 개인키와 인증서가 필요하다.

  • 그리고 두 파일로 시크릿을 만든다. (시크릿은 7장에서 자세히 설명한다)

  • 개인키와 인증서는 tls-secret이라는 시크릿에 저장되며, 인그레스 오브젝트를 업데이트하면 kubia.example.com에 대한 https 요청도 수락할 수 있다.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: kubia
spec:
  tls:
  - hosts: 
    - kubia.example.com
    secretName: tls-secret
  rules:
  - host: kubia.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: kubia-nodeport
          servicePort: 80

  • 명령어 출력에는 애플리케이션의 응답과 인그레스에 구성한 서버 인증서가 표시된다.


5.5 파드가 연결을 수락할 준비가 됐을 때 신호 보내기

  • 적절한 레이블을 가진 새 파드가 만들어지자마자 서비스의 일부가 돼 요청이 파드로 전달되기 시작한다.
  • 하지만 파드의 구성 시간이나 데이터 로드 등으로 인해 파드가 즉시 요청을 처리할 준비가 되어있지 않을 수 있다.

5.5.1 레디니스 프로브 소개

  • 라이프니스 프로브와 마찬가지로 세 가지 유형의 레디니스 프로브가 있다.
    • Exec 프로브는 프로세스를 실행하며, 컨테이너 상태를 프로세스의 종료 상태 코드로 결정한다.
    • HTTP GET 프로브는 HTTP GET 요청을 컨테이너로 보내고 응답의 HTTP 상태코드를 통해 컨테이너가 준비됐는지 결정한다.
    • TCP 소켓 프로브는 컨테이너로 지정된 포트로 TCP 연결을 연다. 소켓이 연결되면 컨테이너가 준비된 것으로 간주한다.

 

레디니스 프로브의 동작

  • 첫 번째 레디니스 점검을 수행하기 전에 구성 가능한 시간이 경과하기를 기다리도록 구성할 수 있다.(initialDelaySecond)
  • 그리고 주기적으로 프로브를 호출하면서 레디니스 프로브의 결과에 따라 작동한다.
  • 파드가 준비되지 않았다면 서비스에서 제거하고, 준비되면 다시 서비스에 추가한다.
  • 라이브니스 프로브와 달리 컨테이너가 준비 상태 점검에 실패하더라도 컨테이너가 종료되거나 다시 시작되지 않는다.

 

레디니스 프로브가 중요한 이유

  • 파드 그룹이 다른 파드에 제공하는 서비스에 의존한다고 가정할 때, 파드 중 하나가 요청을 처리할 준비가 되지 않았다면 레디니스 프로브를 통해 쿠버네티스에게 알리고 다른 파드 인스턴스로 요청을 보내 정상적으로 처리할 수 있다.

5.2.2 파드에 레디니스 프로브 추가

파드 템플릿에 레디니스 프로브 추가

$kubectl edit rc kubia
apiVersion: v1
kind: ReplicationController
metadata:
  name: kubia
spec:
  replicas: 3
  selector:
    app: kubia
  template:
    metadata:
      labels:
        app: kubia
    spec:
      containers:
      - name: kubia
        image: luksa/kubia
        ports:
        - name: http
          containerPort: 8080
        readinessProbe:
          exec:
            command:
            - ls
            - /var/ready
  • 레디니스 프로브는 컨테이너 내부에서 ls /var/ready 명령어를 주기적으로 수행한다.
  • ls 명령어는 파일이 존재하면 종료 코드 0을 반환하고 그렇지 않으면 다른 값을 반환한다. 파일이 있다면 레디니스 프로브가 성공한다.
  • 레플리케이션컨트롤러의 파드 템플릿을 변경해도 기존 파드에는 영향을 미치지 않으므로, 기존 파드는 여전히 레디니스 프로브가 정의되어 있지 않다.
  • 파드를 삭제했다가 레플리케이션컨트롤러로 인해 재생성되면, 새 파드는 레디니스 점검에 실패하고 각각에 /var/ready 파일을 만들 때까지 서비스의 엔드포인트에 포함되지 않는다.

 

파드의 레디니스 상태 확인과 수정

  • 파드를 삭제하고 다시 조회하면 READY 열에 준비된 컨테이너가 없음으로 표시된다.

  • /var/ready 파일을 만들어 레디니스 프로브가 성공하도록 반환해야 한다.
$kubectl exec kubia-zsrht -- touch /var/ready
  • 레디니스 프로브는 기본으로 10초마다 호출되므로, 파드가 아직 준비되지 않았다면 레디니스 프로브가 호출되지 않은 것이다.

 

하나의 READY 파드로 서비스 호출

  • 세 개의 파드가 실행 중이지만 하나의 파드만 준비됐음을 보고하기 때문에 이 파드가 요청을 수신하는 유일한 파드다.

5.3.3 실제 환경에서 레디니스 프로브가 수행해야 하는 기능

  • 실제 환경에서 레디니스 프로브는 애플리케이션이 클라이언트의 요청을 수신할 수 있는지 여부에 따라 성공 또는 실패를 반환해야 한다.
  • 서비스에서 파드를 수동으로 제거하려면 수동으로 프로브의 스위치를 전환하는 대신 파드를 삭제하거나 파드의 레이블을 변경해야 한다.

 

레디니스 프로브를 항상 정의하라

  • 레디니스 프로브를 추가하지 않으면 파드가 시작하는 즉시 서비스 엔드 포인트가 된다.
  • 애플리케이션이 수신 연결을 시작하는 데 오래걸리는 경우 클라이언트 요청은 수신 연결을 수락할 준비가 되지 않은 상태에서 파드로 전달된다.
  • 따라서 클라이언트는 "Connection refused" 유형의 에러를 보게 된다.

 

레디니스 프로브에 파드의 종료 코드를 포함하지 마라

  • 파드가 종료할 때, 실행되는 에플리케이션은 종료 신호를 받자마자 연결 수락을 중단한다. 그렇기 때문에 종료 절차가 시작되는 즉시 레디니스 프로브가 실행하도록 만들어 파드가 모든 서비스에서 확실하게 제거되어야 한다고 생각할 수 있다.
  • 하지만 쿠버네티스는 파드를 삭제하자마자 모든 서비스에서 파드를 제거하기 때문에 불필요하다.

5.6 헤드리스 서비스로 개별 파드 찾기

  • 서비스의 연결은 임의의 파드로 전달된다. 하지만 클라이언트가 모든 파드에 연결해야하는 경우라면 각 파드의 IP를 알아야 한다.
  • 클라이언트가 쿠버네티스 API 서버를 호출해 파드의 IP 주소 목록을 가져올 수 있지만 애플리케이션과 쿠버네티스는 무관하게 유지하도록 해야하므로 바람직하지 않다.
  • 일반적으로 DNS 조회를 수행하면 하나의 IP(서비스의 클러스터 IP)를 반환하지만, 서비스를 헤드리스로 설정해놓으면 DNS 서버는 서비스 IP 대신 파드 IP들을 반환한다.

5.6.1 헤드리스 서비스 생성

  • 서비스 스펙에서 clusterIP 필드를 None으로 설정하면 쿠버네티스는 클라이언트가 서비스의 파드에 연결할 수 있는 클러스터 IP를 할당하지 않기 때문에 서비스가 헤드리스 상태가 된다.
apiVersion: v1
kind: Service
metadata:
  name: kubia-headless
spec:
  clusterIP: None
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: kubia

5.6.2 DNS로 파드 찾기

  • 클러스터에서 실행중인 파드 내부에서 DNS 조회를 위해 필요한 바이너리가 포함된 이미지를 기반으로 새 파드를 실행하자
    (kubia 컨테이너에는 nslookup이 포함되어있지 않으므로 DNS 조회를 할 수 없다).
  • 도커 허브의 nslookup 및 dig 바이너리를 모두 포함하는 tutum/dnsutils 컨테이너 이미지를 사용할 수 있다.

 

YAML 메니페스트를 쓰지 않고 파드 실행

  • kubectl run 명령어를 사용해 YAML 메니페스트를 작성하지 않고 파드만 생성한다.

 

Unknown tag generator

Hi, When I try this command in Google kubernetes: kubectl run --generator=run-pod/v1 --image=nginx:alpine myfirstpod -- labels=example=myfirstpod I got the following error: Error: unknown flag: --generator I’m not sure why but --generator tag is definite

community.kodekloud.com

  • 대신 다음 명령어로 수행할 수 있다.
$kubectl run dnsutils --image=tutum/dnstuils --command -- sleep infinity

 

헤드리스 서비스를 위해 반환된 DNS A 레코드

$kubectl exec dnsutils -- nslookup kubia-headless

  • DNS 서버는 kubia-headless.default.svc.cluster.local FQDN 에 대해 준비되 파드들의 IP를 각각 반환한다.
  • 이는 kubia 서비스와 같이 일반 서비스를 DNS가 반환하는 것과 다르다.

  • 헤드리스 서비스를 사용하더라도 클라이언트는 일반 서비스와 마찬가지로 서비스의 DNS 이름으로 파드에 연결할 수 있지만, DNS가 파드의 IP를 반환하기 때문에 클라이언트는 서비스 프록시 대신 파드에 직접 연결한다.


5.6.3 모든 파드 검색 - 준비되지 않은 파드 소개

  • 준비되지 않은 파드를 포함해서 서비스 레이블 셀렉터에 매칭되는 모든 파드를 찾고자 한다면 DNS 조회 메커니즘을 사용할 수 있다.
  • 쿠버네티스가 파드의 레디니스 상태에 관계없이 모든 파드를 서비스에 추가되게 하려면 서비스에 다음 어노테이션을 추가해야 한다.


5.7 서비스 문제 해결

  • 서비스로 파드에 엑세스할 수 없는 경우 다음과 같은 내용을 확인한 후에 다시 시작해보자.
    • 외부가 아닌 클러스터 내에서 서비스 클러스터 IP에 연결되는지 확인한다.
    • 서비스에 엑세스할 수 있는지 확인하려고 서비스 IP로 핑을 할 필요 없다(서비스 클러스터 IP는 가상 IP이므로 핑되지 않는다).
    • 레디니스 프로브를 정의했다면 성공했는지 확인하라. 그렇지않으면 파드는 서비스에 포함되지 않는다.
    • 파드가 서비스의 일부인지 확인하려면 kubectl get endpoints를 사용해 해당 엔드포인트 오브젝트를 확인한다.
    • FQDN이나 그 일부로 서비스에 엑세스하려고 하는데 작동하지 않는 경우, FQDN 대신 클러스터 IP를 사용해 엑세스할 수 있는지 확인한다.
    • 대상 포트가 아닌 서비스로 노출된 포트에 연결하고 있는지 확인한다.
    • 파드 IP에 직접 연결해 파드가 올바른 포트에 연결돼 있는지 확인한다.
    • 파드 IP로 애플리케이션에 엑세스 할 수 없는 경우 애플리케이션이 로컬호스트에만 바인딩하고 있는지 확인한다.
728x90
Comments