Today I Learned

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

쿠버네티스

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

하이라이터 2022. 9. 13. 20:51
728x90

5장에서 다루는 내용

  • 단일 주소로 파드를 노출하는 서비스 리소스 만들기
  • 클러스터 안에서 서비스 검색
  • 외부 클라이언트에 서비스 노출
  • 클러스터 내에서 외부 서비스 접속
  • 파드가 서비스할 준비가 됐는지 제어하는 방법
  • 서비스 문제 해결

  • 일반적으로 시스템 관리자가 클라이언트 구성 파일에 서비스를 제공하는 서버의 정확한 IP 주소나 호스트 이름을 지정해 각 클라이언트 애플리케이션을 구성한다.
  • 하지만 쿠버네티스에서 동일한 작업을 수행하면 다음과 같은 이유로 동작하지 않는다.
    • 파드는 일시적이다. 파드는 언제든 늘어나거나 줄어들거나 이동할 수 있다.
    • 노드에 파드를 스케줄링한 후 파드가 시작되기 바로 전에 파드의 IP 주소를 할당한다. 클라이언트는 서버인 파드의 IP 주소를 미리 알 수 없다.
    • 수평 스케일링은 여러 파드가 동일한 서비스를 제공할 수 있음을 의미한다. 클라이언트는 서비스를 지원하는 파드의 수와 개별 IP에 상관 없이 모든 파드에 단일 IP 주소로 엑세스할 수 있어야 한다.

5.1 서비스 소개

  • 쿠버네티스의 서비스는 동일한 서비스를 제공하는 파드 그룹에 지속적인 단일 접점을 만들기 위한 리소스이다.
  • 각 서비스는 변경되지 않는 IP 주소와 포트가 있으며, 클라이언트는 해당 IP와 포트로 접속한 다음 해당 서비스를 지원하는 파드 중 하나로 연결된다.

 

예제를 통한 서비스 설명

  • 프론트엔드 웹서버와 백엔드 데이터베이스 서버 예제에서 프론트엔드 역할을 하는 파드는 여러 개 있을 수 있지만, 백엔드 데이터베이스는 하나만 있을 것이다.
  • 시스템이 기동하려면 두 가지 문제를 해결해야 한다.
    • 웹 서버 수에 상관 없이 외부 클라이언트는 프론트엔드 파드에 연결할 수 있어야 한다.
    • 프론트엔드 파드는 벡엔드 데이터베이스에 연결해야 한다. 데이터베이스는 파드 내에서 실행되므로 시간이 IP 주소가 변경될 수 있고, 프론트엔드와의 연결을 유지해야 한다.
  • 프론트엔드 파드와 백엔드 파드 각각에 대한 서비스를 생성해서 안정적인 주소를 만들어야 한다.


5.1.1 서비스 생성

  • 서비스 연결은 서비스 뒷단의 모든 파드로 로드밸런싱된다.
  • 레이블 셀렉터를 통해 정확히 어떤 파드가 서비스의 일부분인지 정의한다.

 

kubectl expose로 서비스 생성

  • expose 명령어는 레플리케이션컨트롤러에서 사용된 것과 동일한 파드 셀렉터를 사용해 서비스 리소스를 생성하고 모든 파드를 단일 IP와 포트로 노출한다.

 

YAML 디스크립터를 통한 서비스 생성

  • kubernetes API 서버에 YAML을 게시해 서비스를 수동으로 생성할 수도 있다.
apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  ports:
  - port: 80 # 서비스가 사용할 포트
    targetPort: 8080 # 서비스가 포워드할 컨테이너 포트
  selector:
    app: kubia # app=kubia 레이블이 있는 모든 파드가 이 서비스에 포함

 

새 서비스 검사하기

  • YAML을 게시한 후 네임스페이스의 모든 서비스 리소스를 호죄하고 서비스에 내부 클러스터 IP가 할당됐는지 확인할 수 있다.

  • 할당된 주소는 클러스터 IP 이므로 클러스터 내부에서만 엑세스할 수 있다.

 

클러스터 내에서 서비스 테스트

  • 몇 가지 방법으로 클러스터 내에서 서비스 요청을 보낼 수 있다.
    1. 서비스의 클러스터 IP로 요청을 보내고 응답을 로그로 만드는 파드를 만든다.
    2. 쿠버네티스 노드로 ssh 접속하고 curl 명령을 실행한다.
    3. kubectl exec 명령어로 기존 파드에서 curl 명령을 실행한다.

 

실행중인 컨테이너에 원격으로 명령어 실행

  • kubectl exec 명령어로 기존 파드의 컨테이너 내에서 원격으로 임의의 명령어를 실행할 수 있으며, 컨테이너의 내용, 상태, 환경을 검사할 때 유용하다.
  • kubectl get pods 명령어로 파드를 조회할 수 있고, kubecl get svc 명령어로 서비스의 클러스터 IP를 알 수 있다.
$kubectl exec kubia-9g9xb -- curl -s http://10.96.4.219

  • 명령을 실행하면 파드의 컨테이너 내에서 curl 명령을 실행하고, curl은 HTTP 요청을 서비스 IP로 보낸다.
  • 쿠버네티스 서비스 프록시는 연결을 가로채서 서비스가 연결된 임의의 파드 중 하나로 요청을 전달하고 응답을 반환한다.
  • curl은 표준 출력으로 응답을 출력하고 이를 kubectl이 있는 로컬 시스템의 표준 출력에 다시 표시한다.

 

서비스 세션 어피니티 구성

  • 특정 클라이언트의 모든 요청을 매번 같은 파드로 리디렉션하려면 서비스의 세션 어피니티(sessionAffinity) 속성을 기본값 대신 ClientIP로 설정한다.
apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  sessionAffinity: ClientIP
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: kubia
  • 세션어피니티는 None과 ClientIP 옵션만 지원하며, 쿠키 기반 세션 어피니티 옵션은 없다.
  • 서비스는 TCP와 UDP 패킷을 처리하고 payload는 신경 쓰지 않기 때문에 HTTP 프로토콜의 구성인 쿠키를 알지 못하기 때문이다.

 

동일한 서비스에서 여러 개의 포트 노출

  • 멀티 포트 서비스를 사용해서 단일 클러스터 IP로 모든 서비스 포트를 노출할수 있다.
  • 예를 들어 파드가 두개의 포트(8080, 8443)을 수신한다면, 하나의 서비스를 사용해 포트 80과 443을 파드의 포트 8080과 8443으로 전달할 수 있다.

apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: https
    port: 443
    targetPort: 8443
  selector:
    app: kubia

 

이름이 지정된 포트 사용

  • 지금까지는 포트 번호를 참조해왔지만 각 파드의 포트에 이름을 지정하고 서비스 스펙에서 이름으로 참조할 수도 있다.
  • 이렇게하면 서비스 스펙을 변경하지 않고도 포트 번호를 변경할 수 있다는 장점이 있다.
#파드 정의에 포트 이름 사용
kind: Pod
spec:
  containers:
  - name: kubia
    ports:
    - name: http
      containerPort: 8080
    - name: https
      containerPort: 8443
#서비스에 이름이 정의된 포트 참조
apiVersion: v1
kind: Service
spec:
  ports:
  - name: http
    port: 80
    targetPort: http
  - name: https
    port: 443
    targetPort: https

5.1.2 서비스 검색

  • 쿠버네티스는 클라이언트 파드가 서비스의 IP와 포트를 검색할 수 있는 방법을 제공한다.

 

환경변수를 통한 서비스 검색

  • 파드가 시작되면 쿠버네티스는 해당 시점에 존재하는 각 서비스를 가리키는 환경변수 세트를 초기화한다.
  • 클라이언트 파드를 생성하기 전에 서비스를 생성하면 해당 파드의 프로세스는 환경변수를 검사해 서비스의 IP 주소와 포트를 얻을 수 있다.
  • kubectl exec 명령을 사용해 파드에서 명령어를 실행할 수 있지만, 파드를 만든 후에 서비스를 만들면 서비스에 대한 환경변수를 설정할 수 없다.
  • 서비스에 대한 환경변수를 보려면 먼저 모든 파드를 삭제하고 새로 파드를 만들어야 한다.
$kubectl delete po --all

  • 이제 kubectl exec 명령으로 컨테이너 내부에서 env 명령어를 실행해 환경변수를 조회할 수 있다.
$kubectl exec kubia-6tbqr -- env

 

DNS를 통한 서비스 검색

  • kube-system 네임스페이스에는 kube-dns 라는 파드가 있으며, 동일한 이름의 해당 서비스가 있다.
  • 이 파드는 DNS 서버를 실행하며 클러스터에서 실행 중인 다른 모든 파드는 자동으로 이를 사용하도록 구성된다.
  • 쿠버네티스 자체 DNS 서버에서 파드에서 실행중인 프로세스에서 수행된 모든 DNS 쿼리를 처리한다.

  • 각 서비스는 내부 DNS 서버에서 DNS 항목을 가져오고 서비스 이름을 알고 있는 클라이언트 파드는 환경변수 대신 FQDN(정규화된 도메인 이름)으로 엑세스 할 수 있다.

 

FQDN을 통한 서비스 검색

  • 프론트엔드-백엔드 예제에서 프론트엔드 파드는 다음 FQDN로 백엔드 데이터베이스 서비스에 연결할 수 있다.
backend-database.default.svc.cluster.local
  • backend-database는 서비스 이름이고 default는 서비스가 정의된 네임스페이스, svc.cluster.local은 모든 클러스터의 로컬 서비스 이름에 사용되는 클러스터의 도메인 접미사다.

  • 프론트엔드 파드가 벡앤드 파드와 동일한 네임스페이스에 있는 경우 svc.cluster.local 접미사와 네임스페이스는 생략할 수 있다.
    따라서 서비스를 단순히 backend-database라 할 수 있다.

 

파드의 컨테이너 내에서 셸 실행

  • kubectl exec 명령어에 -it 옵션을 사용해 파드의 컨테이너 내에서 bash를 실행할 수 있다.
  • -it  --stdin(-i) 및 --tty(-t) 옵션을 사용하는 것과 동일하다. 이는 kubectl에게 터미널의 stdin 입력 스트림을 컨테이너(-i)로 라우팅하고 이를 TTY(-t)로 처리하도록 지시한다.

$kubectl exec -it kubia-6tbqr -- bash
  • 이제 curl 명령어를 사용해 kubia 서비스에 엑세스할 수 있다.

  • 각 파드 컨테이너 내부의 DNS resolver가 구성돼 있기 때문에 네임스페이스와 svc.cluster.local 접미사를 생략할 수 있다.
    컨테이너의 /etc/resolv.conf 파일에서 확인할 수 있다.

 

서비스 IP에 핑을 할 수 없는 이유

  • 서비스에 엑세스할 수 없는 경우 서비스 IP를 ping해 작동 여부를 확인하려 할 것이다.

  • 서비스로 curl은 동작하지만 ping은 응답이 없는데, 서비스의 클러스터 IP가 가상 IP이므로 서비스 포트와 결합된 경우에만 의미가 있기 때문이다.
728x90
Comments