Service로 앱 노출하기

Service로 앱 노출하기 (Expose Your App)

출처: 문서

본문

목표 (Objectives)

  • 쿠버네티스의 Service에 대해 배운다.
  • 라벨과 선택자가 Service와 어떻게 관련되는지 이해한다.
  • 쿠버네티스 클러스터 밖에서 애플리케이션을 노출한다.

시작하기 전에

이 튜토리얼의 셸 명령은 대부분의 Linux·macOS 시스템의 기본 셸(예: bash, zsh, sh)이 지원하는 POSIX 셸 구문을 사용해요. Windows 사용자는 명령을 그대로 실행하려면 Windows Subsystem for Linux (WSL)이나 Git Bash 같은 POSIX 호환 셸을 사용해야 합니다. export, $() 같은 구조를 사용하는 명령은 PowerShell이나 Windows Command Prompt와 호환되지 않습니다.

쿠버네티스 서비스 개요

쿠버네티스 Pods는 유한해요. 파드에는 라이프사이클이 있습니다. 워커 노드가 죽으면 그 노드에서 실행 중인 파드도 손실됩니다. Replicaset은 새 파드를 만들어 클러스터를 원하는 상태로 다시 동적으로 이끌어, 애플리케이션이 계속 실행되게 할 수 있어요. 또 다른 예로 3개의 레플리카를 가진 이미지 처리 백엔드를 생각해보세요. 그 레플리카들은 교체 가능하며, 프론트엔드 시스템은 백엔드 레플리카에 신경 쓰지 않아야 하고 파드가 손실되고 재생성되는지조차 신경 쓰지 않아야 합니다. 그럼에도 쿠버네티스 클러스터의 각 파드는 같은 노드의 파드들도 고유한 IP 주소를 가지므로, 애플리케이션이 계속 기능하도록 파드 사이의 변경을 자동으로 조정하는 방법이 필요합니다.

쿠버네티스 Service는 논리적인 파드 집합을 정의하고, 그 파드에 대한 외부 트래픽 노출·로드 밸런싱·서비스 디스커버리를 가능하게 하는 추상화 계층이다.

쿠버네티스의 Service는 논리적인 파드 집합과 그에 접근하는 정책을 정의하는 추상화예요. Service는 의존하는 파드 사이에 느슨한 결합을 가능하게 합니다. Service는 모든 쿠버네티스 오브젝트 매니페스트처럼 YAML 또는 JSON으로 정의됩니다. Service가 대상으로 하는 파드 집합은 보통 _라벨 선택자(label selector)_에 의해 결정됩니다(왜 스펙에 selector를 포함하지 않는 Service를 원할 수 있는지는 아래 참고).

각 파드가 고유한 IP 주소를 가지지만, 그 IP들은 Service 없이는 클러스터 밖으로 노출되지 않아요. Service는 애플리케이션이 트래픽을 받게 해줍니다. Service는 스펙의 spec에서 type을 지정해 다양한 방식으로 노출될 수 있습니다:

  • ClusterIP (기본) - 클러스터의 내부 IP에서 Service를 노출한다. 이 유형은 Service를 클러스터 안에서만 접근 가능하게 만든다.

  • NodePort - NAT를 사용해 클러스터의 각 선택된 노드의 같은 포트에 Service를 노출한다. NodeIP:NodePort로 클러스터 밖에서 Service에 접근 가능하게 한다. ClusterIP의 상위 집합.

  • LoadBalancer - (지원된다면) 현재 클라우드에 외부 로드 밸런서를 만들고 Service에 고정된 외부 IP를 할당한다. NodePort의 상위 집합.

  • ExternalName - CNAME 레코드를 그 값으로 반환해 Service를 externalName 필드(예: foo.bar.example.com)의 내용에 매핑한다. 어떤 종류의 프록시도 설정되지 않는다.

다른 유형의 Service에 대한 더 많은 정보는 Source IP 사용 튜토리얼에서 찾을 수 있어요. Service로 애플리케이션 연결하기도 참고하세요.

또한 스펙에서 selector를 정의하지 않는 Service 사용 사례가 있다는 점을 유의하세요. selector 없이 만들어진 Service는 해당 Endpoints 오브젝트도 만들지 않습니다. 이는 사용자가 Service를 특정 엔드포인트에 수동으로 매핑하게 해줍니다. 선택자가 없는 또 다른 가능성은 type: ExternalName을 엄격히 사용하는 경우입니다.

Service와 라벨

Service는 파드 집합에 걸쳐 트래픽을 라우팅해요. Service는 애플리케이션에 영향을 주지 않고 파드가 죽고 복제될 수 있게 하는 추상화입니다. 의존하는 파드(애플리케이션의 프론트엔드·백엔드 구성 요소 같은) 사이의 디스커버리와 라우팅은 쿠버네티스 Service가 처리합니다.

Service는 라벨과 선택자를 사용해 파드 집합과 일치시키는데, 이는 쿠버네티스의 오브젝트에 논리적 연산을 허용하는 그룹화 프리미티브입니다. 라벨은 오브젝트에 붙는 키/값 쌍이며 다양한 방식으로 사용될 수 있습니다:

  • 개발·테스트·프로덕션용으로 오브젝트 지정
  • 버전 태그 임베드
  • 태그를 사용해 오브젝트 분류

라벨은 생성 시점 또는 나중에 오브젝트에 붙일 수 있어요. 언제든 수정할 수 있습니다. 이제 Service를 사용해 애플리케이션을 노출하고 몇 가지 라벨을 적용해봅시다.

Step 1: 새 Service 만들기

애플리케이션이 실행 중인지 확인해봅시다. kubectl get 명령을 사용해 기존 파드를 찾을 거예요:

kubectl get pods

실행 중인 파드가 없다면 이전 튜토리얼의 오브젝트가 정리된 것입니다. 이 경우 kubectl로 Deployment 만들기 튜토리얼로 돌아가 deployment를 다시 만드세요. 몇 초 기다렸다가 파드를 다시 나열해주세요. 파드 하나가 실행 중인 것을 확인하면 계속할 수 있습니다.

다음으로 클러스터의 현재 Service를 나열해봅시다:

kubectl get services

배포를 외부 트래픽에 노출하려면 --type=NodePort 옵션과 함께 kubectl expose 명령을 사용할 거예요:

kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080

이제 kubernetes-bootcamp라는 실행 중인 Service가 있습니다. 여기서 Service가 고유한 cluster-IP, 내부 포트, 외부-IP(노드의 IP)를 받았음을 볼 수 있어요.

외부에서 열린 포트(type: NodePort Service)를 찾으려면 describe service 하위 명령을 실행할 거예요:

kubectl describe services/kubernetes-bootcamp

할당된 Node 포트 값을 가진 NODE_PORT라는 환경 변수를 만들어요:

export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')"
echo "NODE_PORT=$NODE_PORT"

이제 curl, 노드의 IP 주소, 외부로 노출된 포트를 사용해 앱이 클러스터 밖에 노출되었는지 테스트할 수 있어요:

curl http://"$(minikube ip):$NODE_PORT"

컨테이너 드라이버로 Docker Desktop과 함께 minikube를 실행 중이라면 minikube 터널이 필요해요. Docker Desktop 안의 컨테이너가 호스트 컴퓨터와 격리되어 있기 때문입니다.

별도의 터미널 창에서 다음을 실행해요:

minikube service kubernetes-bootcamp --url

출력은 다음과 같습니다:

http://127.0.0.1:51082
!  Because you are using a Docker driver on darwin, the terminal needs to be open to run it.

그런 다음 주어진 URL로 앱에 접근해요:

curl 127.0.0.1:51082

서버로부터 응답을 받습니다. Service가 노출되었습니다.

Step 2: 라벨 사용

Deployment가 자동으로 우리 파드에 라벨을 만들었어요. describe deployment 하위 명령으로 그 라벨의 이름()을 볼 수 있습니다:

kubectl describe deployment

이 라벨을 사용해 파드 목록을 질의해봅시다. -l 파라미터와 함께 kubectl get pods 명령을 사용할 거예요:

kubectl get pods -l app=kubernetes-bootcamp

같은 방법으로 기존 Service를 나열할 수 있습니다:

kubectl get services -l app=kubernetes-bootcamp

파드 이름을 얻어 POD_NAME 환경 변수에 저장해요:

export POD_NAME="$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')"
echo "Name of the Pod: $POD_NAME"

새 라벨을 적용하려면 label 하위 명령에 오브젝트 유형, 오브젝트 이름, 새 라벨을 이어서 사용해요:

kubectl label pods "$POD_NAME" version=v1

이것은 우리 파드에 새 라벨을 적용하고(애플리케이션 버전을 파드에 고정), describe pod 명령으로 확인할 수 있어요:

kubectl describe pods "$POD_NAME"

여기서 라벨이 이제 우리 파드에 붙어 있음을 볼 수 있습니다. 그리고 새 라벨을 사용해 파드 목록을 질의할 수 있어요:

kubectl get pods -l version=v1

그리고 파드를 볼 수 있습니다.

Step 3: Service 삭제

Service를 삭제하려면 delete service 하위 명령을 사용할 수 있어요. 여기서도 라벨을 사용할 수 있습니다:

kubectl delete service -l app=kubernetes-bootcamp

Service가 사라졌는지 확인해요:

kubectl get services

이것은 Service가 제거되었음을 확인해줍니다. 경로가 더 이상 노출되지 않는지 확인하려면 이전에 노출된 IP와 포트에 curl을 실행할 수 있어요:

curl http://"$(minikube ip):$NODE_PORT"

이것은 애플리케이션이 더 이상 클러스터 밖에서 접근 불가능함을 증명합니다. 파드 안에서 curl을 실행해 앱이 여전히 실행 중인지 확인할 수 있어요:

kubectl exec -ti $POD_NAME -- curl http://localhost:8080

여기서 애플리케이션이 실행 중임을 볼 수 있습니다. Deployment가 애플리케이션을 관리하고 있기 때문입니다. 애플리케이션을 종료하려면 Deployment도 삭제해야 합니다.

더 알아보기 (Learn more)