티스토리 뷰

728x90
반응형

서비스(Service): 포드에 연결하고 외부에 노출

디플로이먼트를 통해 생성된 포드에 어떻게 접근할 수 있을까요? 이전 까지는 kubectl describe 명령어로 포드의 내부 IP를 직접 확인한 뒤 포드로 직접 접근할 수는 있었지만, 이 방법은 로컬 개발 환경 또는 쿠버 클러스터 내부에서만 사용할 수 있었습니다.

게다가 도커 컨테이너와 마찬가지로 포드의 IP는 영속적이지 않아 항상 변할 수 있다는 점도 유의해야 합니다. 여러 개의 디플로이먼트를 하나의 완벽한 애플리케이션으로 연동하려면 포드 IP가 아닌, 서로를 발견할 수 있는 다른 방법이 필요합니다.

도커 사용 방법을 되살펴 보면 도커 컨테이너는 -p 옵션으로 손쉽게 컨테이너를 외부로 노출할 수 있었습니다. 즉, 컨테이너가 생성됨과 동시에 외부로 노출되는 방식이 있습니다.

도커와 달리 쿠버네티스는 디플로이먼트를 생성할 때 포드를 외부로 노출하는 옵션(-p)를 사용하지 않으며, 디플로이먼트의 YAML 파일에는 단지 포드의 애플리케이션이 사용할 내부 포트만 정의합니다(containerPort 항목). 이 포트를 외부로 노출해 사용자들이 접근하거나, 다른 디플로이먼트의 포드들이 내부적으로 접근하려면 서비스라고 부르는 별도의 쿠버네티스 오브젝트를 생성해야 합니다. 서비스의 핵심 기능만 나열해보면 다음과 같습니다.

  • 여러 개의 포드에 쉽게 접근할 수 있도록 고유한 도메인 이름을 부여합니다.
  • 여러 개의 포드에 접근할 때, 요청을 분산하는 로드 밸런서 기능을 수행합니다.
  • 클라우드 플랫폼의 로드 밸런서, 클러스터 노드의 포트 등을 통해 포드를 외부로 노출합니다.

서비스(Service)의 종류

서비스의 개념과 사용 방법을 익히기 위해 포드와 서비스를 연결해봅시다. 먼저 컨테이너의 호스트 이름을 반환하는 간단한 웹서버 이미지를 사용한 디플로이먼트를 생성합니다.

# deployment-hostname.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostname-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webserver
  template:
    metadata:
      name: my-webserver
      labels:
        app: webserver
    spec:
      containers:
      - name: my-webserver
        image: alicek106/rr-test:echo-hostname
        ports:
        - containerPort: 80
$ kubectl apply -f deployment-hostname.yaml

Kubectl get pods -o wide 명령어를 이용해 포드이 IP를 확인한 뒤, curl 등과 같은 도구로 HTTP 요청을 보내 포드의 이름을 확인할 수 있다.

$ kubectl apply -f deployment-hostname.yaml
$ kubectl get pods -o wide
$ kubectl run -i --tty --rm debug --image=alicek106/ubuntu:curl --restart=Never curl 172.17.0.3 | grep Hello
<p>Hello,  hostname-deployment-7dfd748479-sxkjg</p>     </blockquote>

포드에 접근할 수 있는 규칙을 정의하는 서비스 리소스를 새롭게 생성해봅시다. 한 가지 알아둬야 할 점은 쿠버네티스의 서비스는 포드에 어떻게 접근할 것이냐에 따라 종류가 여러 개로 세분화 돼 있다는 점입니다. 따라서 목적에 맞게 적절한 서비스의 종류를 선택해야 합니다. 주로 사용하는 서비스의 타입은 크게 3가지 입니다.

  • ClusterIP 타입 : 쿠버네티스 내부에서만 포드들에 접근할 때 사용합니다. 외부로 포드를 노출하지 않기 때문에 쿠버네티스 클러스터 내부에서만 사용되는 포드에 적합합니다.
  • NodePort 타입 : 포드에 접근할 수 있는 포트를 클러스터의 모든 노드에 동일하게 개방합니다. 따라서 외부에서 포드에 접근할 수 있는 서비스 타입입니다.
  • LoadBalancer 타입 : 클라우드 플랫폼에서 제공하는 로드 밸런서를 동적으로 프로비저닝해 포드에 연결합니다. NodePort 타입과 마찬가지로 외부에서 포드에 접근할 수 있는 서비스 타입입니다.

ClusterIP 타입의 서비스 - 쿠버네티스 내부에서만 포드에 접근

야믈 파일을 작성해보자.

apiVersion: v1
kind: Service
metadata:
  name: hostname-svc-clusterip
spec:
  ports:
    - name: web-port
      port: 8080 # 서비스 포트
      targetPort: 80 # 접근 대상이 된 포드들이 내부적으로 사용하고 있는 포트
  # spec.selector   
  # 이 서비스에서 어떠한 라벨을 가지는 포드에 접근할 수 있게 만들 것인지
  # 좀있다 돌릴 deployment-hostname 야믈 파일의 디플로이먼트안의 포드가 이 라벨로 되어 있고, 
  # 이 서비스에 의해 접근 가능한 대상이 됨
  selector: 
    app: webserver
  type: ClusterIP # 어떤 타입인지

두 리소스 간의 라벨이 동일할 때에만 쿠버네티스의 기능을 온전히 사용할 수 있는 경우가 많고, 단순히 리소스의 부가적인 정보를 표시하는 것 이상의 기능을 가지므로 매우 중요합니다.

$ kubectl get -f hostname-svc-clusterip.yaml
$ kubectl get svc

ip, port를 통해 요청을 보내면 된다. 쿠버네티스 클러스터에서만 사용할 수 있는 내부 IP 입니다. kubectl run 명령어로 임시 포드를 만들어 요청해봅시다.

$ kubectl run -i --tty --rm debug --image=alicek106/ubuntu:curl --restart=Never -- bash
# If you don't see a command prompt, try pressing enter.
root@debug:/# curl 10.100.174.121:8080 --silent | grep Hello
    <p>Hello,  hostname-deployment-7dfd748479-wn7b7</p>     </blockquote>
root@debug:/# curl 10.100.174.121:8080 --silent | grep Hello
    <p>Hello,  hostname-deployment-7dfd748479-sxkjg</p>     </blockquote>

서비스와 연결된 여러 개의 포드에 자동으로 요청이 분산되고 있습니다. 서비스를 생성할 떄 별도의 설정을 하지 않아도 서비스는 연결된 포드에 대해 로드 밸런싱을 수행합니다.

서비스에는 IP뿐만 아니라 서비스 이름 그 자체로도 접근할 수 있습니다. 쿠버는 애플리케이션이 서비스나 포드를 쉽게 찾을 수 있도록 내부 DNS를 구동하고 있으며, 포드들은 자동으로 이 DNS를 사용하도록 설정됩니다.

root@debug:/# curl hostname-svc-clusterip:8080 --silent | grep Hello
    <p>Hello,  hostname-deployment-7dfd748479-sxkjg</p>     </blockquote>

포드가 서로 상호작용 해야 할 때는 포드의 IP를 알 필요가 없으며, 대신 포드와 연결된 서비스 이름을 사용함으로써 간단히 포드에 접근할 수 있습니다.

다시 정리하자면,

image

  1. 특정 라벨을 가지는 포드를 서비스와 연결하기 위해 서비스의 야믈 파일에 selector 항목을 정의
  2. 포드에 접근할 때 사용하는 포트(containerPort)를 야믈 파일의 targetPort 항목에 정의
  3. 서비스를 생성할 때, 야믈 파일의 port 항목에 8080을 명시해 서비스의 Cluster IP와 8080 포트로 접근할 수 있게 설정
  4. kubectl appply -f 명령어로 Cluster IP 타입의 서비스가 생성되면 서비스는 쿠버 클러스터 내부에서만 사용할 수 있는 고유한 내부 IP를 할당 받음
  5. 쿠버 클러스터에서 서비스의 내부 IP 또는 서비스 이름으로 포드에 접근 가능

서비스의 라벨 셀렉터와 포드의 라벨이 매칭돼 연결되면 쿠버는 자동으로 엔드포인트라고 부르는 오브젝트를 별도로 생성합니다.

$ kubectl get ep

엔드포인트 오브젝트는 서비스가 가리키고 있는 도착점을 나타냅니다.

서비스를 삭제하려면 kubectl delete 명령어를 사용합니다.

$ kubectl delete svc hostname-svc-clusterip # or
$ kubectl delete -f hostname-svc-clusterip.yaml

NodePort 타입의 서비스 - 서비스를 이용해 포드를 외부에 노출하기

(로컬에서 진행이 안됐음.. 클라우드 vm에서 진행했음..)

NodePort 타입의 서비스는 모든 노드의 특정 포트를 개방해 서비스에 접근하는 방식입니다. 야믈파일을 작성해보겠습니다.

apiVersion: v1
kind: Service
metadata:
  name: hostname-svc-nodeport
spec:
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
  selector:
    app: webserver
  type: NodePort

ClusterIP와 동작 방법이 다른 것일 뿐, 동일한 서비스 리소스이기 떄문에 기본 항목의 사용 방법은 모두 같습니다.

$ kubectl apply -f hostname-svc-nodeport.yaml
$ kubectl get svc

서비스의 목록을 확인해보면 NodePort 타입의 서비스가 생성됐을을 알 수 있습니다. PORT(S) 항목에 출력된 숫자는 모든 노드에서 동일하게 접근할 수 있는 포트를 의미합니다. 즉, 클러스터의 모든 노드에 내부 IP 또는 외부 IP를 통해 해당 포트로 접근하면 동일한 서비스에 연결할 수 있습니다.

NodePort 타입의 서비스는 생성하면 자동으로 ClusterIP의 기능을 사용할 수 있기 떄문에 쿠버네티스 클러스터에서 서비스의 내부 IP와 DNS 이름을 사용해 접근할 수 있습니다. 즉, NodePort 타입의 서비스는 내부 네트워크와 외부 네트워크 양쪽에서 접근할 수 있습니다.

image

  1. 외부에서 포드에 접근하기 위해 각 노드에 개방된 포트로 요청을 전송합니다. 위 그림에서 31514 포트로 들어온 요청은 서비스와 연결된 포드 중 하나로 라우팅 됩니다.
  2. 클러스터 내부에서는 ClusterIP 타입의 서비스와 동일하게 접근할 수 있습니다.

실제 운영 환경에서 NodePort로 서비스를 외부에 제공하는 경우는 많지 않습니다. NodePort에서 포트 번호를 80 또는 443으로 설정하기에는 적절하지 않으며, SSL 인증서 적용, 라우팅 드오가 같은 복잡한 설정을 서비스에 적용하기가 어렵기 떄문입니다. 따라서 인그레스(Ingress)라고 부르는 쿠버네티스의 오브젝트에서 간접적으로 사용되는 경우가 많습니다(외부 요청을 실제로 받아들이는 관문).

참고

특정 클라이언트가 같은 포드로부터만 처리되게 하려면 서비스의 YAML 파일에서 sessionAffinity 항목을 ClientIP로 설정합니다.

...
spec:
  sessionAffinity: ClientIP
...

클라우드 플랫폼의 로드 밸런서와 연동하기 - LoadBalancer 타입의 서비스

LoadBalancer 타입의 서비스는 서비스 생성과 동시에 로드 밸런서를 새롭게 생성해 포드와 연결합니다. NodePort를 사용할 때는 각 노드의 IP를 알아야만 포드에 접근할 수 있었지만, 이번에 사용해 볼 LoadBalancer 타입의 서비스는 클라우드 플랫폼으로부터 도메인 이름과 IP를 할당받기 때문에 NodePort보다 더욱 쉽게 포드에 접근할 수 있습니다.

단, LoadBalancer 타입의 서비스는 로드 밸런서를 동적으로 생성하는 기능을 제공하는 환경에서만 사용할 수 있다는 점을 알아둬야 합니다(AWS, GCP. 온프레미스 환경에서는 어려움).

이 이후에는 책 설명이 조금 부족하다고 생각해서 블로그를 뒤져가면서 공부했음. aws로 진행.

kops를 설치하여 진행했는데, kops는 쿠버네티스를 클라우드 플랫폼 로드벨런서 기능과 연동을 시켜주는 자동화된 프로비저닝 시스템입니다. kops 설치는 ec2 서버를 만들어서 진행했는데 로컬 컨테이너로 작업을 해도 될 것 같습니다. 이 블로그를 참고해서 kops를 설치하면 kops가 마스터 노드, 워커 노드를 로드밸런싱과 연동시켜서 사용할 수 있는 모든 셋팅을 다 해주는 겁니당. 그럼 이제 마스터 워커 노드에 접속해서 kubectl 명령어를 통하여 deploy, service 스크립트를 실행해서 동작시키면 워커 노드로 포드가 보내지고, 로드밸린싱과 연계되어 로드밸런싱 dns로 테스트 서버를 확인해볼 수 있습니다. (새로고침 계속 하면 결과 바뀌는거 확인)

image

위의 AWS 로드 밸런서 관리 페이지에서 유형을 확인해보면 'classic'으로 되어 있는 것을 볼 수 있습니다. 서비스의 야믈 파일에서 아무런 설정을 하지 않으면 AWS의 클래식 로드 밸런서를 생성합니다. 야믈 파일에 metadata.annotation 항목을 다음과 같이 정의하면 NLB를 사용할 수 있습니다.

# hostname-svc-nlb.yaml
apiVersion: v1
kind: Service
metadata:
  name: hostname-svc-nlb
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
  ports:
    - name: web-port
      port: 80
      targetPort: 80
  selector:
    app: webserver
  type: LoadBalancer

쿠버네티스에서 주석은 라벨처럼 해당 리소스의 추가적인 정보를 나타내기 위한 키-값 쌍으로 이뤄져 있습니다. 그렇지만 리소슫의 종류에 따라 특정 용도로 사용할 수 있게 쿠버네티스에 미리 정의된 몇가지 주석이 있습니다. 이 주석들은 리소스에 특별한 설정값을 부여하기 위해 사용됩니다. (dns 접속 안됨)

트래픽의 분배를 결정하는 서비스 속성 : externalTrafficPolicy

LoadBalancer 타입의 서비스를 사용하면 외부로부터 들어온 요청은 각 노드 중 하나로 보내지며, 그 노드에서 다시 포드 중 하나로 전달됩니다. 마찬가지로 NodePort 타입을 사용했을 때도 각 녿그로 들어오는 요청은 다시 포드 중 하나로 전달됩니다.

이러한 요청 전달 원리는 경우에 따라 효율적이지 않을 때도 있습니다.

image

우리가 주목해야 할 부분은 A 노드로 들어오는 요청은 굳이 B 노드로 전달되지 않고 A 노드 내붇에서 처리될 수 있다는 점입니다. 그런데도 A 노드로 들어온 요청이 b 포드로 전달되어 처리되면 불필요한 네트워크 홉이 한단계 더 발생하게 됩니다. 게다가 리다이렉트가 발생하게 되어 트래픽의 출발지 주소가 바뀌는 SNAT가 발생하게 되고, 이로 인해 클라이언트의 IP 주소 또한 보존되지 않는다는 단점이 있습니다.

이러한 요청 전달 메커니즘은 서비스의 속성 중 externalTrafficPolicy 항목에 정의돼 있습니다. kubectl get -o yaml 명령어로 서비스의 모든 속성을 출력해 보면 externalTrafficPolicy가 Cluster로 설정된 것을 알 수 있습니다. (hostname-svc-nodeport 실행 후 확인)

이처럼 externalTrafficPolicy에서 기본적으로 설정된 값인 Cluster는 NodePort, LoadBalancer 타입의 서비스가 기본적으로 동작하는 방식입니다. 이 속성을 Local로 설정하면 포드가 생성된 노드에서만 포드로 접근할 수 있으며, 로컬 노드에 위치한 포드 중 하나로 요청이 전달됩니다. 즉, 추가적인 네트워크 홉이 발생하지 않으며, 전달되는 요청의 클라이언트IP 또한 보존됩니다.

externalTrafficPolicty가 Local인 로드 밸런서 서비스를 생성해보자. (deployment-hostname.yaml 디플로이먼트 실행 중)

# hostname-svc-lb-local.yaml
apiVersion: v1
kind: Service
metadata:
  name: hostname-svc-lb-local
spec:
  externalTrafficPolicy: Local
  ports:
    - name: web-port
      port: 80
      targetPort: 80
  selector:
    app: webserver
  type: LoadBalancer
$ kubectl apply -f hostname-svc-lb-local.yaml

위에서 생성했던 deployment-hostname.yaml 디플로이먼트의 3개 포드를 1개로 줄여 1개의 노드에서만 포드가 존재하도록 변결해보자.

$ kubectl scale --replicas==1 deploy hostname-deployment

AWS에서 쿠버네티스를 사용하고 있다면 로드 밸런서 페이지에서 단 하나의 노드만이 InService 상태로 되어 있으며 이 노드로만 트래픽이 전달되는 것을 확인 할 수 있습니다.

image

externalTrafficPolicy의 값이 Local로 설정된 서비스의 트래픽 흐름을 다시 보자면,

image

  1. 서비스는 기본적으로 externalTrafficPolicy 속성이 Cluster로 설정됩니다. 이 경우 모든 워커 노드에서 동일한 랜덤 포트가 개방됩니다. 로드 밸런서는 노드 중 하나로 요청을 전달하고, 노드는 다시 포드 중 하나로 요청을 전달합니다. 노드 간에 요청이 리다이렉트 되어 NAT가 발생하므로 클라이언트의 IP를 보존할 수 없습니다.
  2. 서비스의 externalTrafficPolicy 속성을 Local로 설정해 생성하면 포드가 위치한 노드만 랜덤한 포트를 개방합니다. 로드 밸런서는 포드가 위치한 노드로만 요청을 전달하며, 해당 노드 내의 포드에서만 요청이 분산됩니다. 따라서 네트워크 홉이 한 단계 적으며, 클라읻언트의 IP 또한 포드의 소스코드 내에서 정상적으로 확인할 수 있습니다.

externalTrafficPolicy를 Local로 설정하는 것이 무조건 좋은 것은 아닙니다. 각 노드에 포드가 고르지 않게 스케줄링 됐을 때, 요청이 고르게 분산되지 않을 수도 있기 때문입니다.

image

위 그림에서는 로드 밸런서가 두 개의 노드에 대해 트래픽을 절반씩 분배하지만 각 포드가 실제로 받는 부하의 양은 동일하지 않습니다. 즉, 늑정 노드의 포드에 부하가 집중되거나 적어질 수도 있으며, 이는 곧 utilization 측면에서 바람직하지 않을 수 있습니다.

스케줄링 기능 중 PodAntiAffinity 등을 사용하면 포드를 최대한 클러스터 노드에 고르게 배포할 수 있으며, externalTrafficPolicy: Local의 단점을 어느 정도 해결할 수도 있습니다. (나중에 학습)

둘 중 뚜렷한 정답은 없다~~

요청을 외부로 리다이렉트하는 서비스 : ExternalName

쿠버네티스를 외부 시스템과 연동해야 할 떄는 ExternalName 타입의 서비스를 사용할 수도 있습니다.

ExternalName 타입을 사용해 서비스를 생성하면 서비스가 외부 도메인을 가리키도록 설정할 수 있습니다. 예를 들면 아래의 설정은 쿠버네티스 내부의 포드들이 externalname-svc라는 이름으로 요청을 보낼 경우, 쿠버네티스의 DNS는 my.database.com으로 접근할 수 있도록 CNAME 레코드를 반환합니다. 즉, externalname-svc로 요청을 보내면 my.database.com에 접근하게 됩니다. ExternalName 타이의 서비스는 쿠버네티스와 별개로 존재하는 레거시 시스템에 연동해야 하는 상황에서 유용하게 사용할 수 있습니다.

(테스트 안했음..)

# external-sbc.yaml
apiVersion: v1
kind: Service
metadata:
  name: externalname-svc
spec:
  type: ExternalName
  externalName: my.database.com





출처
시작하세요! 도커/쿠버네티스(용찬호 저, 위키북스)
example

728x90
반응형
댓글
반응형
250x250
글 보관함
최근에 달린 댓글
«   2024/04   »
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
Total
Today
Yesterday
링크