티스토리 뷰

반응형

5장. 서비스 : 클라이언트가 파드를 검색하고 통신 가능하게한다.

5장에서 다룰 내용

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

문제점

  • 파드는 임시적이다. 다른 파드를 위한 공간을 만들기 위해 노드에서 파드를 제거, 축소, 장애 가 있을 수 있다.
  • 쿠버네티스는 파드가 노드가 예약된 후, 시작되기 전에 파드에 ip 주소를 할당하므로 클라이언트는 서버 파드의 ip 주소를 미리 알 수 없다.
  • 수평적 확장은 여러 파드가 동일한 서비스를 제공해야한다.
    • 각 파드에는 고유한 ip가 있겠지만, 클라이언트는 관심없어야한다.
    • 대신, 모든 파드는 단일 ip 주소를 통해 접근할 수 있어야한다.

5.1. 서비스 소개

  • 쿠버네티스의 서비스는 동일한 서비스를 제공하는 파드 그룹에 지속적인 단일 접점을 만드려고 할 때, 생성하는 리소스이다.
  • 각 서비스는 서비스가 존재하는 경우 변경되지 않는 ip주소와 포트가 있다.
  • 프런트엔드 포드에 대한 서비스를 생성하고 클러스터 외부에서 액세스할 수 있도록 구성하여 외부 클라이언트가 포드에 연결할 수 있는 단일 고정 IP 주소를 노출합니다. 마찬가지로 백엔드 포드에 대한 서비스도 생성하여 백엔드 포드에 대한 안정적인 주소를 생성합니다. 서비스 주소는포드의 IP 주소가 변경되더라도 변경됩니다. 또한 서비스를 생성하여 프런트엔드 포드가 환경 변수 또는 DNS를 통해 이름으로 백엔드 서비스를 쉽게 찾을 수 있도록 합니다. 시스템의 모든 구성 요소(두 개의 서비스, 해당 서비스를 지원하는 두 개의 포드 세트 및 이들 간의 상호 종속성)는 그림에 나와 있습니다.

img

5.1.1. 서비스 만들기

  • 서비스는 둘 이상의 파드에서 지원될 수 있다.
  • 서비스에 대한 연결은 모든 지원 pod 에 대해서 로드밸런싱된다.
  • pod 선택 = Label Selector
Label Selector는 서비스에 속하는 pod를 결정한다.

img

kubectl expose을 통한 서비스 생성

  • 서비스를 생성하기 가징 쉬운 방법은 이전에 생성한 ReplicationController 를 expose 한 것처럼 kubectl expose 를 이용하는 것이다.
    • 이 명령은 ReplicationController에서 사용하는 것과 동일한 Pod Selector 로 서비스 리소스를 생성해, 단일 ip 주소 및 port를 통해 모든 pod 를 expose 한다.
    • 이제 expose 명령 대신, Kubernetes API 서버에 YAML 을 제시해 서비스를 수동 생성한다.

YAML 디스크립터를 통해 서비스 만들기

# kubia-svc.yaml
apiVersion: v1
kind: Service
metadata: 
 name: kubia
spec:
 ports:
 - port : 80 #1
   targetPort : 8080 #2
 selector: #3
  app : kubia #3
#1 서비스가 사용할 포트
#2 서비스가 포워드할 컨테이너 포트
#3 app=kubia 레이블에 있는 모든 파드가 이 서비스에 속한다.
"""

kubia라는 서비스를 정의한 것이다. 이 서비스는 포트 80의 연결을 허용하고, 각 연결을 app=kubia 레이블 셀렉터와 일치하는 파드의 8080포트로 
라우팅한다.

kubectl create 를 사용해 yaml 파일을 게시해 서비스를 생성하자.

"""

새 서비스 검사하기

yaml 을 게시한 후, 네임스페이스의 모든 서비스 리소스를 조회하고, 서비스에 내부 클러스터 ip (cluster ip)가 할당됐는지 확인할 수 있다.

$kubectl get svc
NAME         CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes   10.111.240.1     <none>        443/TCP   30d
kubia        10.111.249.153   <none>        80/TCP    6m  # 이게 새로 생성된 서비스
  • 이 예제는 서비스에 할당된 ip 주소가 10.111.249.153임을 보여준다.
    • 클러스터 ip 이므로 클러스터 내부에서만 액세스할 수 있다.
    • 서비스의 기본 목적은 파드 그룹은 클러스터와 다른 파드에노출하는 것이지만, 대게 서비스를 외부로 노출하길 원할 것이다. 나중에 그 방법을 알게될 것이다. 지금은 클러스터 내에서 서비스를 사용해보고 어떻게 동작하는지 살펴보자.

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

몇 가지 방법으로 클러스터 내에서 서비스로 요청을 보낼 수 있다.

  • 확실한 방법은 서비스의 클러스터 ip로 요청을 보내고, 응답을 로그로 남기는 파드를 만드는 것이다. 그런 다음 파드의 로그를 검사해 서비스의 응답이 무엇인지 확인할 수 있다.
  • 쿠버네티스의 노드로 ssh 로 접속하고, curl 명령을 실행할 수 있다.
  • kubectl exec 명령어로 기존 파드에서 curl 명령을 실행할 수있다.

마지막 방법으로 기존 파드에서 명령어를 실행하는 방법을 배워보자.

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

  • kubectl exec 명령어를 사용하면, 기존의 파드의 컨테이너 내에서 원격으로 임의의 명령어를 실행할 수 있다.
  • 컨테이너의 내용, 상태, 환경을 검사할 때, 유용하다.
  • kubectl get pods 명령으로 파드를 조회하고 exec 명령어를 대상으로 하나를 선택한다.
  • 또한, 서비스의 클러스터 ip를 알아야한다. (ex. kubectl get svc 명령어 사용)
  • 다음 명령어를 실행할 때는 파드의 이름과 서비스 ip 를 상황에 맞게 변경해 사용한다.
$ kubectl exec kubia-7nog1 -- curl -s http://10.111.249.153
You've hit kubia-gzwli

이제 ssh 를 사용해 원격 시스템에서 명령어를 실행해봤다면, kubectl exec 가 크게 다르지 않다.

더블 대시를 사용하는 이유

  • 명령어의 더블 대시(--)는 kubectl 명령줄 옵션의 끝을 의미한다.
  • 더블 대시 뒤에 모든 것은 파드 내에서 실행돼야하는 명령이다.
  • 명령줄에 대시로 시작하는 인수가 없는 경우, 더블 대시를 사용할 필요가 없다.
  • 그러나, 이전 예시에서는-s 옵션이 kubectl exec 옵션으로 해석될 수 있다.

img

Figure. 5.3. kubectl exec 을 이용해, 한 파드에서 curl 요청을 보내, 서비스에 커넥션을 테스트한다.

  1. Curl은 3개의 포드가 지원하는 서비스 IP에 HTTP 요청을 보냈습니다.
  2. Kubernetes 서비스 프록시는 연결을 가로채서 3개의 포드 중 임의의 포드를 선택하고 요청을 전달했습니다.
  3. 그런 다음 해당 포드 내에서 실행되는 Node.js가 요청을 처리하고 해당 포드의 이름이 포함된 HTTP 응답을 반환했습니다.
  4. 그리고 나서 Curl은 표준 출력에 대한 응답을 인쇄하였고, "kubectl"에 의해 로컬 컴퓨터의 표준 출력으로 인터셉트되어 인쇄되었다.

이전 예제에서 파드의 컨테이너 내에서 curl 명령을 별도의 프로세스로 실행했다.
이는 컨테이너 내의 주 프로세스가 서비스와 통신하는 것과 크게 다르지 않다.

서비스 세션 affinity(어피니티는 영어로 친화력!) 구성

  • 동일한 명령을 몇 번 더 실행하면 동일한 클라이언트에서 요청하더라도 서비스 프록시가 각 연결을 임의의 파드로 연결을 다시 전달(forward)하기 때문에 요청할 때마다 다른 파드가 선택된다.
  • 반면 특정 클라이언트의 모든 요청을 매번 같은 파드로 리디렉션하려면 서비스의 세션 어피니티(sessionAffinity)속성을 기본값 None 대신, ClientIP로 설정한다.
# 예5.2. 클라이언트 ip 세션 어피니티가 구성된 서비스의 예
apiVersion: v1
kind: Service
spec:
    sessionAffinity : ClientIP
    ...
  • 이로써, 서비스프록시는 동일한 클라이언트 ip의 모든 요청을 동일한 파드로 전달한다.
  • 쿠버네티스는 None과 ClientIP라는 두 가지 유형의 서비스 세션 어피니티를 지원한다.
    • 쿠키 기반 세션어피니티 옵션이 없지만, 쿠버네티스 서비스가 http 수준에서 작동하지 않는다는 것을 이해하면 크게 놀랍지 않다.
      • 서비스는 tcp 와 udp 패킷을 처리하고 그들이 가지고 있는 페이로드(payload)는 신경 쓰지 않는다.
      • 쿠키는 http 프로토콜의 구성이기 때문에 서비스는 쿠키를 알지 못하고, 세션 어피니티를 쿠키 기반으로 할 수 없는 이유이다.
  • OSI 7계층 | Kangflix

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

  • 서비스는 단일 포트만 노출하지만, 여러 포트를 지원할 수 있다. 예를 들어 파드 2개의 포트(예. http 의 경우 8080, https 의 경우 8443)를 수신한다면, 하나의 서비스를 이용해, 포트 80과 443 을 파드의 포트 8080 과 8443으로 전달할 수 ㅣㅇㅆ다.
  • 이 경우 굳이 2개의 서비스를 만들 필요가 없다. 하나의 서비스를 사용해 멀티 포트 서비스를 이용하면, 단일 클러스터 ip 로 모든 서비스 포트가 노출 된다.
    • NOTE: 여러 포트가 있는 서비스를 만들 때는 각 포트의 이름을 지정해야한다.
      • 장점 : 서비스 스펙을 변경하지 않고, 포트 번호를 변경할 수 있다.
      • 새 port 로 파드를 기동하면 연결을 수신하는 파드에 따라 클라이언트 연결이 적절한 포트 번호로 전달된다.
# 예제5.3. 서비스 정의에서 멀티 포트 지정
apiVersion: v1
kind : Service
metadata : 
 name: kubia
spec: 
 ports:
 - name : http # 포트 80은 http 라는 컨테이너 포트에 매핑된다.
  port : 80
  targetPort: http
 - name: https # 포트 443은 컨테이너 포트의 이름이 https인 것에 매핑된다.
  port: 443
  targetPort: httpss

5.1.2. 서비스 검색

  • 서비스를 만들면 파드에 액세스할 수 있는 안정적인 ip 주소와 포트가 생긴다.

Q. 클라이언트 파드는 서비스의 ip 와 파드를 어떻게 알 수 있을까?

  • ( = 서비스를 생성한 다음 ip 주소를 수동으로 찾아서 클라이언트 파드의 구성옵션에 ip 를 전달해야할까?) NO
  • 쿠버네티스는 클라이언트 파드가 서비스의 ip 와 포트를 검색할 수 있는 방법을 제공한다.

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

  • 파드가 시작되면, 쿠버네티스는 해당 시점에 존재하는 각 서비스를 가리키는 환경변수 세트를 초기화한다.
  • 클라이언트 파드를 생성하기 전에 서비스를 생성하면, 해당 파드의 프로세스는 환경변수를 검사해 서비스의 ip 주소와 포트를 얻을 수 있다.
  • 현재 실행 중인 파드 중 하나의 환경을 검사해 이러한 환경변수가 어떻게 보이는지 살펴보자.
    • 이미 kubectl exec 명령을 사용해 파드에서 명령어를 실행할 수 있다는 것은 배웠지만, 파드를 만든 후에 서비스를 만들면, 서비스에 대한 환경 변수를 설정할 수 없다. 이를 먼저 해결해야한다.
    • 서비스에 대한 환경변수를 보려면, 먼저 모든 파드를 삭제하고, 레플리케이션 컨트롤러 에서 새로 파드를 만들어야한다. 다음과 같이 이름을 지정하지 않고, 모든 파드를 삭제할 수 있다.
$kubectl delete po --all
pod "kubia-7nog1" deleted
pod "kubia-bf50t" deleted
pod "kubia-gzwli" deleted
  • 이제 새 파드를 조회하고 파드를 kubectl exec 명령어의 대상을로 선택할 수 있다.
  • 대상 파드를 선택하면, 다음 예제와 같이 컨테이너 내부에서 env 명령어를 실행해 환경 변수를 조회할 수 있다.
$ kubectl exec kubia-3inly env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=kubia-3inly
KUBERNETES_SERVICE_HOST=10.111.240.1
KUBERNETES_SERVICE_PORT=443
...
KUBIA_SERVICE_HOST=10.111.249.153                           ❶
KUBIA_SERVICE_PORT=80                                       ❷
...
"""
1) 서비스의 클러스터 ip 이다.
2) 서비스가 제공되는 포트이다.
"""
  • 클러스터에는 kubernetes 와 kubia 라는 두 개의 서비스가 정의돼있다. (kubectl get svc)
    • = 두 세트의 서비스 관련 환경 변수가 목록에 있다.
      • 5장의 시작 부분에서 생성한 kubia 서비스와 관련된 변수 중에 서비스 ip 주소와 포트 정보를 가지는 KUBIA_SERVICE_HOSTKUBIA_SERVICE_PORT 환경변수를 확인할 수 있다.
    • 5장에서 시작된 프론트엔드와 백엔드 예제로 돌아가서는 백엔드 데이터베이스 서버 파드를 사용해야하는 프론트엔드 파드가 있는 경우, backend-database라는 서비스로 백엔드 파드를 노출하면, 프론트엔드 파드에서 환경변수 BACKEND_DATABASE_SERVICE_HOSTBACKEND_DATABASE_SERVICE_PORT 로 백엔드 데이터베이스 서비스의 ip 주소와 포트를 찾을 수 있다.
    • NOTE 서비스 이름의 대시(-)는 밑줄(_)로 변환되고, 서비스 이름이 환경변수의 이름의 접두어로 쓰이면서 모든 문자는 대문자로 표기된다.
    • 환경 변수는 서비스의 ip 와 포트를 찾는 한 가지 방법은 될 수 있지만, 대게 DNS 의 도메인을 사용하는 것이 일반적이지 않을까? 대신 쿠버네티스에 DNS 서버를 포함하고,dns 를 통해 서비스 ip를 찾게 하는 것이 어떠할까? 밑에서 보자.

DNS를 통한 서비스 검색

  • kube-system 네임스페이스에 파드를 조회했던 것이 기억이 날 것이다.
    • kube-system : 쿠버네티스 시스템에서 생성한 객체의 네임스페이스
      • 일반적으로 여기에, kube-dns, kube-proxy, kubernetes-dashboard 등등의 파드가 포함되어있다.
      • kube-system 이 네임스페이스에는 kube-dns 파드와 동일한 이름의 서비스가 있다.
        • 이름에서 알 수 있듯이 이 파드는 DNS 서버를 실행하며 클러스터에서 실행중인 다른 파드는 이를 사용하도록 구성된다.
          • (쿠버네티스는 각 컨테이너의 /etc/resolv.conf 파일을 수정해 이를 수행한다.)
        • 파드에서 실행중인 프로세스에서 수행된 모든 DNS 쿼리는 시스템에서 실행중인 모든 서비스를 알고 있는 쿠버네티스 자체 DNS 서버로 처리된다.
        • NOTE : 파드가 내부 DNS 서버를 사용할 지 여부는 각 파드 스펙의 dnsPolicy속성으로 구성할 수 있다.
      • 각 서비스는 내부 DNS 서버에서 DNS 항목을 가져오고, 서비스 이름을 알고 있는 클라이언트 파드는 환경변수 대신 FQDN(정규화된 도메인 이름)으로 액세스할 수 있다.
$ kubectl get po --namespace kube-system
NAME                                 READY     STATUS    RESTARTS   AGE
fluentd-cloud-kubia-e8fe-node-txje   1/1       Running   0          1h
heapster-v11-fz1ge                   1/1       Running   0          1h
kube-dns-v9-p8a4t                    0/4       Pending   0          1h
kube-ui-v4-kdlai                     1/1       Running   0          1h
l7-lb-controller-v0.5.2-bue96        2/2       Running   92 

FQDN을 통한 서비스 연결

FQDN(Fully Qualified Domain Name) 이란?

FQDN은 '절대 도메인 네임' 또는 '전체 도메인 네임' 이라고도 불리는

도메인 전체 이름을 표기하는 방식을 의미한다.

웹 사이트 주소를 예로 들어보자.

 1. www.tistory.com 
 2. test.tistory.com

위의 두 주소 중 www 와 test 부분이 '호스트'이고, tistory.com 부분이 '도메인'이다.

위에 쓴 것처럼 호스트와 도메인을 함께 명시하여 전체 경로를 모두 표기하는 것을 FQDN 이라 한다.

FQDN와 달리 전체 경로명이 아닌 하위 일부 경로만으로 식별 가능하게 하는 이름을 PQDN(Partially~)라 한다.

쿠버네티스의 경우 다른 Pod를 찾을 시 동일 네임스페이스 안에서 찾을 때에는 PQDN을 사용할 수 있지만,

네임스페이스 외부에서 찾을 때에는 FQDN을 사용해야 한다.
(출처 : https://onlywis.tistory.com/14)

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

NOTE
클라이언트는 여전히 서비스의 포트번호를 알아야한다. 서비스가 표준 포트를 (http :80, postgress: 5432) 사용하는 경우, 문제가 되지 않는다. 그렇지 않은 경우, 클라이언트는 환경변수에서 포트 번호를 얻어야한다.

  • 서비스에 연결하는 것은 훨씬 간단하다. 프론트엔드 파드가 데이터베이스 파드와 동일한 네임스페이스에 있는 경우, svc.cluster.local 접미사와 네임스페이스는 생략할 수 있다. 따라서 서비스는 단순히 backend-database 라 하면 된다. 매우 간단!
  • IP 대신, FQDN 으로 kubia 서비스에 액세스 한다 하자.
    • 기존 파드 내에서 수행해야함.
    • 이번엔 bash 쉘을 실행해 컨테이너에서 여러 명령어로 실행할 수 있다.

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

  • kubectl exec 명령어를 사용해 파드의 컨테이너 내에서 bash(또는 다른 sh) 를 실행할 수 있다.
    • 이렇게 하면 실행하려는 모든 명령어를 kubectl exec 으로 수행할 필요 없이 원하는 만큼 컨테이너를 자유롭게 할 수 있다.
    • (NOTE: 이 작업을 수행하려면 컨테이너 이미지에서 쉘 바이너리의 실행파일이 사용 가능해야한다.)
    • 셸을 올바르게 사용하려면 -it 옵션을 kubectl exec 에 넣어야한다.
    • $ kubectl exec -it kubia-3inly bash
      root@kubia-3inly:/#
    • curl 명령얼 사용해 다음 방법으로 kubia 서비스에 액세스할 수 있다.
    • root@kubia-3inly:/# curl http://kubia.default.svc.cluster.local
      You've hit kubia-5asi2
      
      root@kubia-3inly:/# curl http://kubia.default
      You've hit kubia-3inly
      
      root@kubia-3inly:/# curl http://kubia
      You've hit kubia-8awf3
      • 요청 url 에서 호스트 이름으로 서비스 이름을 사용해 서비스에 액세스할 수 있다. 각 파드 컨테이너 내부의 DNS resolver 가 구성돼 있기 때문에 네임스페이스와 svc.cluster.local 접미사를 생략할 수 있다.
        • 컨테이너에서 /etc/resolv.conf 파일을 보면 이해할 수 있다.
        • root@kubia-3inly:/# cat /etc/resolv.conf
          search default.svc.cluster.local svc.cluster.local cluster.local ...

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

  • 서비스로 ping 은 동작하지만 핑은 응답이 없다. 이는 서비스의 클러스터 IP 가 가상 ip 이므로 서비스 포트와 결합된 경우에만 의미가 있기 때문이다. (11장에서 더 자세히)
root@kubia-3inly:/# ping kubia
PING kubia.default.svc.cluster.local (10.111.249.153): 56 data bytes
^C--- kubia.default.svc.cluster.local ping statistics ---
54 packets transmitted, 0 packets received, 100% packet loss

5.2. 클러스터 외부에 있는 서비스 연결

  • 지금까지는 클러스터 내부에서 실행주이 하나 이상의 파드와의 통신을 지원하느 서비스를 설명했다.
  • 서비스가 외부 IP + 포트로 연결을 전달하는 것
    • 이 경우, 서비스 로드밸런싱과 서비스 검색 모두 활용할 수 있다.

5.2.1. 서비스 엔드포인트 소개

  • 정의 : 엔드포인트 리소스는 서비스로 노출되는 파드의 ip 주소와 포트 목록이다.
  • img
  • 서비스는 파드에 직접 연결(link) 되지 않는다.
    • 엔드포인트 리소스가 그 사이에 있다.
    • = 서비스는 just 로드밸런서 용도일뿐, 파드의 기능이나 앱을 띄운다던지 그런게 아니라는 뜻
    • 파드 -> 서비스 -> 엔드포인트
    • kubectl describe를 통해 그 존재를 확인할 수 있다.
# `kubectl describe`를 통해 서비스 자세한 명령
$ kubectl describe svc kubia
Name:                kubia
Namespace:           default
Labels:              <none>
Selector:            app=kubia                                          ❶
Type:                ClusterIP
IP:                  10.111.249.153
Port:                <unset> 80/TCP
Endpoints:           10.108.1.4:8080,10.108.2.5:8080,10.108.2.6:8080    ❷
Session Affinity:    None
No events.

"""
1. 서비스의 파드 셀렉터는 엔드포인트 목록을 만드는데 사용된다.
2. 이 서비스의 엔드포인트를 나타내는 파드 ip 와 파드 포트 목록
"""
  • 엔드포인트 리소스는 다른 쿠버네티스리소스와 유사하므로 kubectl get 을 이용해 기본 정보를 표시할 수 있다.
$ kubectl get endpoints kubia
NAME    ENDPOINTS                                         AGE
kubia   10.108.1.4:8080,10.108.2.5:8080,10.108.2.6:8080   1h
  • 파드 셀렉터 : 엔드포인트의 ip 와 포트 목록을 작성하는데 사용되고, 엔드 포인트 리소스에 저장된다.
  • 클라이언트가 서비스에 연결하면, 서비스 프록시는 이들 중 하나의 ip 와 포트 쌍을 선택하고, 들어온 연결을 대상 파드의 수신 대기 서버로 전달한다.

5.2.2. 서비스 엔드포인트 수동 구성

  • 한땀한땀 직접 엔드포인트를 등록해 보자.
    • 서비스와 엔드포인트를 서비스와 분리하면, 엔드포인트를 수동으로 구성하고 업데이트할 수 없다.
    • 파드 셀렉터 없이 서비스를 만들면 쿠버네티스는 엔드포인트 리소스를 만들지 못한다.
      • 파드셀렉터가 없어 서비스에 포함된 파드가 무엇인지 알 수 없다.
    • 서비스를 위한 엔드포인트 등록은 내 손에 달림
      • 직접 서비스도 만들고 엔드포인트도 만들라는 뜻(대체 왜?)

셀렉터 없이 서비스 생성

#external-service.yaml
apiVerision: v1
kind: Service
metadata:
 name : external-service         ❶
spec:                            ❷ 
 ports:
 - port : 80                     3
 """
 1) 서비스 이름이 엔드포인트 오브젝트 이름과 일치해야함
 2) 이 서비스에 셀렉터가 정의돼있지 않는다.
 3) 포트 80으로 들어오는 연결 허용하는 external-service 서비스
 """

셀렉터 없는 서비스에 관한 엔드포인트 리소스 생성

  • 엔드포인터는 별도의 리소스이며, 서비스 속성은 아니다.
#external-service-endpoints.yaml
apiVersion: v1
kind: Endpoints
metadata:
  name: external-service      ❶
subsets:
  - addresses:
    - ip: 11.11.11.11         ❷
    - ip: 22.22.22.22         ❷
    ports:
    - port: 80                ❸
 """

 1) 엔드포인트 오브젝트의 이름은 서비스 이름과 일치해야한다.
 2) 서비스가 연결을 전달할 엔드포인트 ip
 3) 엔드포인트 대상 포트

 """
  • 다 서버에 게시되면, 파드 셀렉터 있는 일반 서비스처럼 이용 가능
  • 서비스가 만들어진 후, 만들어진 컨테이너에는 환경변수가 포함되며, ip:port 쌍에 대한 모든 연결은 서비스 엔드포인트 간에 로드밸런싱된다.

img

  • 그림 5.4.는 외부 엔드포인트를 사용해 서비스에 연결하는 파드 3개를 보여준다.
  • 나중에 외부 서비스를 쿠버네티스 내에서 실행되는 파드로 마이그레이션하기로 결정한 경우, 서비스에 셀렉터를 추가해 자동으로 관리가능하다.
    • 서비스에서 셀렉터를 제거하면, 쿠버네티스는 엔드포인트 업데이트를 멈춘다.
    • = 서비스의 실제 구현이 변경되는 동안에도 서비스 ip 주소가 일정하게 유지될 수 있음을 의미한다.

5.2.3. 외부 서비스를 위한 별칭 생성

  • 서비스의 엔드포인트를 수동으로 구성해 외부 서비스를 노출하는 대신, 좀 더 간단한 방법으로 FQDN(정규화된 도메인 이름)으로 외부 서비스를 참조할 수 있다.

ExternalName 서비스 생성

# external-service-externalname.yaml
apiVersion: v1
kind : Service
metadata: 
 name : external-service
spec:
 type: ExternalName                         ❶
 externalName:  someapi.somecompany.com     ❷
 ports:
 - port : 80
 """
 1) 서비스 유형이 ExternalName 으로 설정된다.
 2) 실제 서비스의 정규화된 도메인 이름(fully qualified domain name)
 """
  • 클러스터 내부에서 파드가 외부 서비스의 접속하는 경우는 FQDN 을 사용하지 않고, external-service.default.svc.cluster.local domain name (or external-service) 이것만을 이용해 외부 서비스에 접속할 수 있다.
    • 효과
      • 서비스를 사용하는 파드 입장에서 실제 서비스 이름과 위치가 숨겨져 externalName 변경, 혹은 서비스 스펙을 수정에 불변일 수 있다.
      • ExternalName 서비스는 DNS 레벨에서만 구현된다. 서비스에 관한 간단한 CNAME, DNS 레코드가 생성된다.
* 따라서 서비스에 연결하는 클라이언트는 서비스 프록시를 완전히 무시하고, 외부 서비스에 직접 연결된다. 

  * 이런 이유로 ExternalName 유형의 서비스는 ClusterIP 를 얻지 못한다.
    * NOTE: CNAME 레코드는 ip 주소대신  FQDN(정규화된 도메인 이름)을 가르킨다.
  • img


*


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

  • 지금까진 클러스터 내부에서 파드가 외부 서비스를 사용하는 방법을 알아보았다.
  • 이번엔 밑 그림과 같이 웹 프론트 엔드 웹서버와 같은 특정 서비스를 외부에 노출해 외부 클라이언트가 액세스할 수 있게 해보자.

img

  • 외부 클라이언트에 서비스 노출 방법
    • 노드 포트로 서비스 유형 설정
      • NodePort(노드포트) 서비스의 경우, 각 클러스터 노드는 노드 자체에서 포트를 열고, 해당 포트로 수신된 트래픽을 서비스로 전달한다.
      • 이 서비스는 내부 클러스터 ip와 포트로 액세스할 수 있을 뿐만 아니라, 모든 노드의 전용 포트로 액세스 가능함.
    • 서비스 유형을 노드포트유형의 확장인 로드밸런서로 설정
      • 쿠버네티스가 실행중인 클라우드 인프라에서 프로비저닝된 전용 로드밸런서로 서비스에 액세스할 수 있다.
      • 로드밸런서는 트래픽을 모든 노드의 노드포트로 전달한다. 클라이언트는 로드밸런서의 ip 로 서비스에 액세스한다.
    • 단일 ip 주소로 여러 서비스를 노출하는 인그레스 리소스 만들기
      • http 레벨(7 계층)에서 작동하므로 4계층 서비스보다 더 많은 기능을 제공할 수 있다.
      • 5.4절에서 다룰 예정

img

5.3.1. 노드포트 서비스 사용

  • 파드 세트를 외부클라이언에 노출시키는 방법=서비스를 생성하고 유형을 노드포트로 지정하기
  • 기능 : 노드 포트 서비스를 만들면, 쿠버네티스는 모든 노드에 특정 포트를 할당하고(모든 노드에서 동일한 포트 번호가 사용된다.) 서비스를 구성하는 파드로 들어오는 연결을 전달한다.
  • 이건 일반 서비스(실제 유형은 ClusterIP)와 유사하지만, 서비스의 내부 클러스터 IP 뿐 아니라, 모든 노드의 ip 와 할당된 노드포트로 서비스에 액세스할 수 있다.
  • 이것은 노드포트 서비스와 상호작용할 때 의미가 있다.

노드포트 서비스 생성

#kubia-svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: kubia-nodeport
spec:
  type: NodePort             ❶
  ports:
  - port: 80                 ❷
    targetPort: 8080         ❸
    nodePort: 30123          ❹
  selector:
    app: kubia
"""
1) 서비스 유형을 노드 포트로 설정
2) 서비스 내부 클러스터 ip의 포트
3) 서비스 대상 파드의 포트
4)  각 클러스터 노드의 30123 포트로 서비스에 액세스 할 수 있다
"""
  • 유형을 노드포트로 지정하고, 이 서비스가 모든 클러스터 노드에 바인딩 돼야하는 노드포트를 지정한다.
  • 노드 포트를 반드시 지정해야하는 것은 아니고, 생략하면 쿠버네티스가 임의의 포트를 선택할 것이다.

NOTE:

GKE에서 서비스를 생성하면 kubectl에서 방화벽 규칙을 구성해야 한다는 경고가 출력됩니다. 어떻게 하는지 곧 알게 될 겁니다.

img

노드포트 서비스 확인

$ kubectl get svc kubia-nodeport
NAME             CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubia-nodeport   10.111.254.223   <nodes>       80:30123/TCP   2m
  • EXTERNAL-IP 열을 보자. <nodes> 라 표기되고, 클러스터 노드의 ip 주소로 서비스에 액세스 할 수 있음을 나타낸다.
  • PORTS 에는 클러스터 Ip 의 내부 포트 80 과 노드포트 30123 이 모두 표시된다. 이 서비스는 다음 주소에서 액세스 할 수 있다.
    • 10.111.254.223:80
    • <첫 노드 ip > : 30123
    • <두번째 노드 ip > : 30123
    • ...

img

  • 위와 같은 그림 .
  • 그림 5.6.(노드1,노드2의 서비스의 노드포트로 연결하는 외부 클라이언트)은 두 클러스터 노드의 포트 30123에 노출된 서비스를 보여준다. (gke는 이렇게 동작하고, 미니쿠베는 단일노드지만, 원칙은 동일)
  • 이런 포트에 대한 수신연결은 임의로 선택된 파드에 전달되고, 연결중인 노드에 실행중인 포트일 수 있고, 아닐 수 있다.

외부 클라이언트가 노드포트 서비스에 액세스할 수 있도록 방화벽 규칙 변경

  • 앞서 언급했듯이 노드포트로 서비스에 액세스하려면 해당 포트에 노드포트에 대한 외부 연결을 허용하도록, 구글 클라우드 플랫폼에 방화벽을 구성해야한다.
$ gcloud compute firewall-rules create kubia-svc-rule --allow=tcp:30123
Created [https://www.googleapis.com/compute/v1/projects/kubia-
     1295/global/firewalls/kubia-svc-rule].
NAME            NETWORK  SRC_RANGES  RULES      SRC_TAGS  TARGET_TAGS
kubia-svc-rule  default  0.0.0.0/0   tcp:30123
  • 노드 ip 와 포트 30123 으로 서비스에 액세스할 수 있다. 노드 ip 를 확이해보자.

Json path 를 이용해 모든 노드의 ip 가져오기

노드 의 json , yaml 요약에서 ip 찾을 수 있다. 이쁘게 원하는 출력형태로 가지자.(더 자세한 설명은 책 229쪽. 생략함)

$ kubectl get nodes -o jsonpath='{.items[*].status.
➥  addresses[?(@.type=="ExternalIP")].address}'
130.211.97.55 130.211.99.206

노드에 ip 를 알고나면, 서비스에 액세스할 수 있다.

$ curl http://130.211.97.55:30123
You've hit kubia-ym8or
$ curl http://130.211.99.206:30123
You've hit kubia-xueq1

Minikube이용중이면, minikube service <service-name> [-n <namespace>] 로 쉽게 브라우저로 노드포트서비스에 액세스 가능하다.

  • 이제 인터넷에서 어떤 노드든 포트 30123으로 액세스 가능하다. 클라이언트가 어떤 노드에 보내는게 중요하지 않으나, 장애가 날 수 있기 떄문에, 노드 앞에 로드밸런서를 배치하는 것이 좋다.
    • 쿠버네티스 클러스터가 이를 지원하는 경우, 노드포트서비스 대신 로드밸런서를 생성해 로드밸런서를 자동으로 프로비저닝할 수 있다..

5.3.2. 외부 로드밸런서로 서비스 노출

  • 로드밸런서는 공개적으로 액세스 가능한 고유 ip 주소를 가지며, 모든 연결을 서비스로 전달한다. = 로드밸런서의 Ip 주소로 서비스에 액세스할 수 있다.
    • 로드밸런서는 노드 포트 서비스 확장
    • 쿠베가 로드밸런서 지원 안하면 노드포트 서비스처럼 작용
    • 미니쿠베 아직 없을 수도...

로드밸런서 서비스 생성

# kubia-svc-loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
  name: kubia-loadbalancer
spec:
  type: LoadBalancer                ❶
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: kubia


"""
1) 이 유형의 서비스는 쿠버네테스 클러스터를 호스팅하는 인프라에서 로드밸런서를 얻을 수 있다.
"""

로드 밸런서를 통한 서비스 연결

  • 로드밸런서 ip 주소가 서비스의 EXTERNAL-IP로 표기된다.
$ kubectl get svc kubia-loadbalancer
NAME                 CLUSTER-IP       EXTERNAL-IP      PORT(S)         AGE
kubia-loadbalancer   10.111.241.153   130.211.53.173   80:32143/TCP    1m
  • 이제 해당 ip 로 서비스에 액세스 할 수 있다. (이경우, 방화벽 설정 불필요)
$ curl http://130.211.53.173
You've hit kubia-xueq1

세션 어피니티와 웹 브라우저

  • 브라우저는 keep-alive 연결을 이용하고, 같은 연결로 모든 요청을 보내고, curl 은 매번 새로운 연결이다. 따라서 웹 브라우저에서 열땐, 세션 어피니티가 None 이여도, 사용자는 항상 동일 파드에 연결된다.(연결 종료 전까지)

img

  • 파드에 http 요청 전달되는 방법
    • kubectl describe로 어떤 서비스의 노드포트가 선택된지 알 수 있다.
    • 방화벽 열면, 노드 ip 로 액세스 가능하다.
    • minikube 의 경우, 로드밸런서는 프로비저닝되지 않지만, 노드포트로 서비스에 계속 접근 가능하다.

5.3.3. 외부 연결의 특성 이해

외부에서 서비스로 들어오는 연결에 대한 주의

불필요한 네트워크 홉에 대한 이해와 예방

  • 문제 : 외부 클라이언트가 노드포트로 서비스에 접근한 경우 (로드밸런서 경우 포함) 임의로 선택한 파드가 뒤져있을 수 있다.(추가 네트워크 홉 필요경운)
  • 해결 : 외부 연결을 수신한 노드에서 실행중인 파드로만 외부 트래픽을 전달하게 시스템을 구성해 추가홉 방지!(파드 없는 경우, 연결 중단)
    • # 서비스 스펙에 추가하면됨.
      spec:
       externalTraffixPolicy : Local
    • 단점
      • 연결이 모든 파드에 균등 분배에서 불균등 분배 여지.
        • 노드간 5050 분배. 파드간에는 결국 불균등 분배
        • 로컬 외부 트래픽 정책을 사용하는 서비스는 파드간 부하가 고르지 않을 수 있다.
        img

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

  • 일반적인 경우, 클라이언트 내의 클라이언트가 서비스로 연결될 때, 클라이언트의 ip 주소를 얻을 수 있다.
    • 그러나 노드포트로 연결 수신하면, 패킷에서 소스네트워크 주소 변환(snat)이 수행돼 패킷 소스 Ip 가 변경된다.
  • externalTraffixPolicy : Local 이거 하는 경우, 추가 홉이 없어 클라이언트 Ip 보존에 영향을 끼칠 수 있다. (snat 이 수행되지 않음)
    • 파드는 실제 클라이언트 ip 를 볼 수 없다. 일부 클라이언트 ip 알아야하는 애플리케이션에서 문제
      • 예. 웹서버의 경우 액세스 로그에 브라우저 ip 표시 불가

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

ingress(인그레스): 들어가거나, 들어가는 행위, 들어갈 권리, 들어갈 수단이나 장소, 진입로

인그레스가 필요한 이유

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

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

  • 인그레스 리소스를 작동시키려면 클러스터에 인그레스 컨트롤러를 실행해야한다.
  • 예를들어 gke(구글 쿠베엔진)은 구글 클라우드 플랫폼의 고유한 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                 ❷
"""
1) 인그레스는 kubia.example.com 
2) 모든 요청은 kubia-nodeport 서비스의 포트 80번으로 전달된다.
"""
  • host kubia.example.com 으로 요청되는 인그레스 컨트롤러에 수신된 모드 http 요청을 포트 80 의 kubia-nodeport 서비스로 전송하도록 하는 인그레스 규칙을 정의했다.

5.4.2. 인그레스로 서비스 액세스

http://kubia.example.com 서비스에 액세스하려면 도메인 이름이 인그레스 컨트롤러의 ip 와 매칭되도록 확인해야합니다.

인그레스의 ip 주소 얻기

$ kubectl get ingresses
NAME      HOSTS               ADDRESS          PORTS     AGE
kubia     kubia.example.com   192.168.99.100   80        29m
  • ip는 ADDRESS 로 표기된다.

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

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

인그레스로 파드 액세스

  • 이제 브라우저, 또는 curl 을 이용해 http://kubia.example.com 서비스에 액세스할 수 있습니다.
  • $ curl http://kubia.example.com 
    You've hit kubia-ke823
  • 인그레스로 서비스에 성공적으로 액세스 했습니다.

인그레스 동작방식

img

  • 클라이언트가 인그레스 컨트롤러로 파드에 연결하는 방식이다.
    1. 클라이언트가 kubia.example.com 을 찾는다.
    2. 클라이언트는 헤더 host :kubia.example.com 과 함께 http get 요청을 인그레스 콘트롤러로 보낸다.
    3. 컨트롤러가 해당헤더에서 클라이언트가 액세스 하려는 서비스를 결정하고, 파드에 요청을 보낸다.
  • 보다 싶이 인그레스 컨트롤러는 요청을 서비스로 전달하지 않는다. 파드를 선택하는데만 이용한다.
  • 모두는 아니지만 대부분의 컨트롤러는 이와 같이 동작한다.

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

  • 규칙과 경로가 모두 배열이므로 여러 항목을 가질 수 있다.
  • 인그레스 다음에서 볼 수 있듯이 여러 호스트(host) 와 경로(path)를 여러 서비스(backend Service Name)에 매핑할 수 있다.

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

# 동일한 호스트의 다른 경로로 여러 서비스를 노출하는 인그레스
- host : kubia.example.com
 http:
  paths:
  - path : /kubia
    backend:
     serviceName: kubia
     servicePort : 80 
  - path :  /bar 
    backend: 
     serviceName : bar
     servicePort : 80
  • 이 요청은 url 의 경로에 따라 두 개의 다른 서비스로 전송된다.

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

spec:
  rules:
  - host: foo.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 를 지우너할 필요가 없다.
  • 예를 들어, 파드가 웹 서버를 실행하는 경우, http 트래픽만 허용하고, 인그레스 컨트롤러가 TLS 와 관련된 모든 것을 처리하게 한다.
    • 인증서와 개인키를 인그레스에 첨부해야한다.
    • => 시크릿(secret) 이라는 쿠버네티스 리소스에 저장하며, 인그레스 매니페스트에서 참조한다.(7장 참고)
  • 시크릿을 만들어보자.

  • 먼저 개인키와 인증서를 만들자.
$ openssl genrsa -out tls.key 2048
$ openssl req -new -x509 -key tls.key -out tls.cert -days 360 -subj
➥  /CN=kubia.example.com
  • 그런다음 두 파일로 시크릿을 만든다.
    • 개인키와 인증서는 tls-secret 이라는 시크릿에 저장된다.
    • 인그레스 오브젝트를 업데이트하면 kubia.example.com 에 대한 https 요청도 수락할 수 있습니다.
$kubectl create secret tls tls-srcret --cert=tls.cert --key=tls.key
secret "tls-secret" created
# kubia-ingress-tls.yaml
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
"""
1) 전체 tls 구성이 이 속성 아래에 있다.
2) kubia.example.com 호스트 이름의 tls 연결이 허용된다.
3) 개인키와 인증서는 이전에 작성한 tls-secret 을 참조한다.
"""

TIP : 인그레스를 삭제하고 다시 만드는 대신, kubectl apply -f kubia-ingress-tls.yaml 을 호출하면 파일에 지정된 내용으로 인그레스 리소스가 업데이트 된다.

  • 이제 https 로 인그레스를 통해 서비스에 액세스 할 수 있다.
    • 명령어의 출력에는 애플리케이션의 응답과 인그레스에 구성한 서버 인증서가 표시된다.
    • tip : 인그레스 쪽은 인그레스 컨트롤러 구현마다 다르므로 해당 독스에서 지원 내용 확인해야한다.
    • 인그레스 기능은 비교적 새거라 앞으로 많은 개선을 기대한다. 현재는 L7 로드밸런서만 지원하지만 L4 로드밸런서 지원 계획이 있다.
$ curl -k -v https://kubia.example.com/kubia
* About to connect() to kubia.example.com port 443 (#0)
...
* Server certificate:
*   subject: CN=kubia.example.com
...
> GET /kubia HTTP/1.1
> ...
You've hit kubia-xueq1

5.5. 파드가 연결을 수락할 준비가 됐을 때 신호보내기= 레디니스 프로브

  • 파드의 레이블이 서비스의 파드 셀렉터와 일치할 경우, 파드가 서비스의 엔드포인트로 포함되는 것을 이미 배웠다.
    • 적절한 레이블을 가진 새 파드가 만들어지자마자 요청이 파드로 바로 전달
    • 파드가 준비 안됐다면?
      • 파드 구성에 시간이 걸리거나
      • 데이터 로드에 시간이 필요
    • 완전히 준비될 때까지 기동중인 파드에 요청을 전달 안하는 것이 좋다.

5.5.1. 레디니스 프로브 소개

  • 레디니스 프로브는 주기적으로 호출되며, 특정 파드가 클라이언트 요펑을 수신할 수 있는지를 결정한다.
  • 컨테이너의 레디니스 프로브가 성공이라면, 컨테이너가 요청 처리 가능하다는 뜻
  • https://kils-log-of-develop.tistory.com/777
    • http 점검 : 컨테이너 ip 주소에 대해 http GET 요청을 수행하려 200- 399 사이에 성공적인 http 응답 코드를 기대한다.
    • tcp 소켓 점검 : 성공적인 tcp 연결을 가정
    • exec 점검: 컨테이너 커널 네임스페이스에서 임의의 명령을 실행하고, 성공적인 종료 코드(0)를 기대한다.
  • 라이브니스 프로블와 달리 컨테이너가 준비상태에서 실패해도, 컨테이너가 종료되거나 다시 시작하지 않는다.

img

  • 레디니스 프로브가 실패한 파드는 서비스의 엔드포인트에서 제거된다.

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

  • 레디니스 프로브는 기본적으로 10초마다 주기적으로 프로브가 실행된다.
apiVersion: v1
kind: ReplicationController
...
spec:
  ...
  template:
    ...
    spec:
      containers:
      - name: kubia
        image: luksa/kubia
        readinessProbe:           ❶
          exec:                   ❶
            command:              ❶
            - ls                  ❶
            - /var/ready          ❶
        ...
"""
1) 파드의 각 컨테이너에 레디니스 프로브가 정의될 수 있다.
ls 는 파일 존재하면 종료 코드 0을 반환하고, 그렇지 않으면 0이 아닌 값을 반환한다.
파일이 있으면, 레디니스 프로브가 성공한다.
"""

TIP

서비스에서 파드를 수동으로 추가하거나 제거하려면 파드와 서비스의 레이블 셀렉터에 enabled=true 레이블을 추가한다.
서비스에서 파드를 제거하려면 레이블을 제거하자.

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

  • 기본 url 에 http 요청을 보내더라도 항상 레디닌스 프로브를 정의해야한다.
  • 레디니스 프로브를 추가하지 않으면 파드가 시작하는 즉시 서비스 엔드포인트가 된다.
  • 레디니스 프로브에 파드 종료 코드를 포함하지 마라. 쿠버네티스는 파드를 삭제하자마자 모든 서비스에서 파드를 제거한다.

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

Q. 클라이언트가 모든 파드에 연결해야하는 경우 어떻게 해야할까?

A. 대표를 없애면 된다.

  1. 쿠버네티스 클라이언트가 dns 조회로 파드 ip 를 찾을 수 있게 한다.
    일반적으로 서비스에 대한 DNS 조회를 수행하면, DNS 서버는 하나의 ip(서비스의 클러스터 ip)를 반환한다.
  2. 그러나 클러스터 ip 가 필요 없다면, dns 서버는 하나의 서비스 ip 대신, 파드 ip들을 반환한다.
  3. = dns 서버는 하나의 DNS A레코드 반환하는 대신, 서비스에 대한 여러 개의 A 레코드( 파드 ip들)를 반환한다.
  4. = 클라이언트는 간단히 dns A 레코드만 조회하면됨.

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
"""
1)이 부분이 서비스를 헤드리스 상태로 만든다.
"""

kubectl create 로 서비스 생성 후 kubectl getkubectl describe 로 서비스를 살펴볼 수 있다.

5.6.2. DNS 로 파드 찾기

  • dns 관련 작업 수행하기 위한 컨테이너 이미지 : tutum/dnsutils
    • nslookup 및 dig 바이너리를 모두 포함한다.
  • yaml 매니페스트를 쓰지 않고 파드 실행
    • kubectl run 명령어를 이요해 yaml 매니페스트를 작성하지 않고 파드를 만든다.
    • $ kubectl run dnsutils --image=tutum/dnsutils --generator=run-pod/v1 --command -- sleep infinity
      pod "dnsutils" created
    • run : https://kubernetes.io/ko/docs/reference/kubectl/conventions/#kubectl-run
    • 참고: 모든 kubectl run의 생성기(generator)는 더 이상 사용 할 수 없다. 생성기 목록 및 사용 방법은 쿠버네티스 v1.17 문서를 참고한다.
  • 헤드리스 서비스를 위해 반환된 DNS A 레코드
    • 새 파드로 DNS 조회를 수행해보자.
    • $ kubectl exec dnsutils nslookup kubia-headless
      ...
      Name:    kubia-headless.default.svc.cluster.local
      Address: 10.108.1.4
      Name:    kubia-headless.default.svc.cluster.local
      Address: 10.108.2.5
    • 이 주소들은 kubectl get pods -o wide 로 파드를 조회하면 이를 확인할 수 있는데, 이것은 파드의 ip 를 보여준다.
    • 헤드리스는 여전히 파드간 로드 밸런싱을 제공하지만, 서비스 프록시 대신 dns 라운드 로빈 메커니즘으로 제공한다.

5.6.3. 모든 파드 검색 - 준비되지 않은 파드도 포함

  • 쿠버네티스가 파드의 레디니스 상태에 관계 없이 모든 파드를 서비스에 추가되게 하려면, 서비스에 다음 어노테이션을 추가해야한다.
  • $ kubectl exec dnsutils nslookup kubia
    ...
    Name:    kubia.default.svc.cluster.local
    Address: 10.111.249.153

5.7. 서비스 문제 해결(디버깅 방법을 다루다니 충격)

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

참고문헌



  1.  
반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함