IT/Kubernetes

[Kubernetes] Basic Resource - Part 2

물통꿀꿀이 2019. 3. 30. 00:36

Service

Service는 아래의 그림 1에서 확인 할 수 있듯이 Pod를 논리적으로 묶은 것이다. 특히 Service는 Pod의 논리적인 묶음이기 때문에 여러 Node에 걸쳐서 Pod를 묶을 수 있다.

그림 1. Service

이렇게 논리적으로 묶는 이유는 k8s에서 Pod는 lifecycle (scaling out-in 등등에 의해서)이 존재하기 때문에 항상 Pod가 고정적이지 않다. 더욱이 IP를 내부적으로 공유하는 Pod의 특성상 외부에서는 항상 IP만 확인 할 수 없다. (Pod가 다시 생성되면 IP가 재할당되기 때문에) 그래서 Service라는 리소스*를 통해 Pod를 관리한다. 

* 일반적으로 Label Selector를 통해 서비스를 구분한다.

그림 2. Service Label

그림 2를 보면 각 Pod가 nginx라는 label name으로 묶인 것을 확인 할 수 있다. 만약 name이 다르다면 해당 Pod는 다른 Service에 속하게 된다. 그럼 매니페스트 파일을 사용하여 Service를 생성하고 알아보자.

apiVersion: apps/v1

kind: Deployment

metadata:

 name: echo-spring

 labels:

  app: echo

  release: spring

spec:

 ...

---

apiVersion: apps/v1

kind: Deployment

metadata:

 name: echo-summer

 labels:

  app: echo

  release: summer

spec:

 ...

먼저 위의 매니페스트 파일은 Service를 보여주기 위해 2개의 Deployment가 포함되어 있는 매니페스트 파일이다. 해당 파일을 실행시켜 보면 아래와 같다.

그림 3. Deployments

그림 3을 보면 현재 생성된 각각의 Deployment를 확인 할 수 있다. (위의 매니페이스 파일에서 언급한것 처럼 Deployment를 2개로 정의했고 각각 1개, 2개의 Pod를 생성하게 한다.) 그리고 Labels을 보면 각 Deployment의 release 이름이 다른 것을 확인 할 수 있다. 이를 바탕으로 Service 매니페스트 파일을 만들면 다음과 같다.

apiVersion: v1

kind: Service // Service 리소스 설정

metadata:

 name: echo

spec:

 selector: // Service에 정의하는 Selector 값

  app: echo

  release: summer

 ports:

 - name: http

   port: 80

해당 Service는 Pod를 app: echo와 release: summer로서 구분한다는 정의를 담고있다. 

그림 4. Service

매니페스트 파일을 실행하면 그림 4와 같이 echo 이름은 가진 Service 리소스가 생성된 것을 확인 할 수 있다. 해당 Service를 약간 더 들어가보면 Service 리소스가 정말 Deployment에서 생성한 Pod와 Label selector가 같은 것만 연결되었는지 알 수 있다.

그림 5. Service Check

생성된 Service를 확인해보면 각 Service의 앤드포인트가 10.1.0.50과 10.1.0.49를 가지고 있는 것을 알 수 있다. 그리고 각 Pod의 할당된 IP를 보면 Service의 앤드포인트와 같다는 것을 알 수 있다. 물론 k8s cluster 내부에서 임시 컨테이터를 생성하여 Service의 name을 curl로명령*을 보내고 각 Pod에서 로그만 봐도 알 수 있다.

* k8s cluster의 DNS는 Service를 서비스명.네임스페이스명.svc.local로 연결한다.

curl http://echo.default.svc.local - Full Path
curl http://echo.default - svc.locald은 생략 가능 (다른 네임스페이스 접근할 때)
curl http://echo - 가장 많이 사용 (같은 네임스페이스 접근할 때)

이렇듯 IP의 상관 없이 이름으로만 Service를 찾을 수 있는 것을 Service Discovery라고 하는데 k8s Service의 주요 특징 중 하나이다.

 

ClusterIP

ClusterIP는 위의 그림 4에서도 확인 할 수 있듯이 Service를 만들때 할당받는 IP이다.

그림 6. Service ClusterIP

해당 IP는 Service에 의존적이기 때문에 Service의 lifecycle과 생사를 같이한다. 즉, Service가 만들어질 때 같이 할당 받고 Service가 내려가면 IP 또한 다른 곳에서 재사용 될 수 있다. (또한 할당된 IP는 Service가 running 하는 동안에는 변경 할 수 없다.)

사실 위의 Service 매니페스트 파일을 보면 따로 적어준 부분은 없는데 Service는 따로 지정해주지 않으면 default type을 ClusterIP로 사용한다. 

이와 같이 ClusterIP를 통해서 k8s Cluster의 내부 IP 주소에 Service를 공개 할 수 있다. ClusterIP는 k8s Cluster 내부에서만 사용하는 주소로 Service에 접근 할 때 Service Name으로 접근을 가능하도록 하게 한다. (내부 DNS를 통해 ClusterIP와 Service Name을 매핑)

그림 7. ClusterIP Application

따라서 ClusterIP를 사용하면 그림 7과 같이 여러 가지 방식으로 사용할 수 있다. (그렇지만 해당 그림에 대한 설명은 이후 포스팅에서 할 것이다.)

 

NodePort

NodePort는 ClusterIP와는 다르게 외부에서 접근이 가능하다. (IP를 할당받는 것은 ClusterIP와 같다.)

apiVersion: v1

kind: Service

metadata:

 name: echo

spec:

 type: NodePort // NodePort 설정

 selector:

  app: echo

  release: summer

 ports:

 - name: http

   port: 80

그래서 위의 Service 매니페스트 파일을 보면 type: NodePort만 추가된 것을 확인 할 수 있다. 

그림 8. NodePort

그림 8을 확인해보면 NodePort가 설정되었고 추가적으로 31879 포트가 생성된 것을 확인 할 수 있다. (이제 외부에서 접근이 가능하다.)

 

LoadBalancer

LoadBalancer는 일반적으로 Cloud Provider(주요 3사 : GCP, AWS, Azure)를 위한 것이다. 즉, Cloud Provider가 제공하는 Load Balancer와 연동하기 위함이다.

apiVersion: v1

kind: Service

metadata:

  name: my-service

spec:

  selector:

    app: MyApp

  ports:

  - protocol: TCP

    port: 80

    targetPort: 9376

  clusterIP: 10.0.171.239

  loadBalancerIP: 78.11.24.19

  type: LoadBalancer // LoadBalancer 설정

status:

  loadBalancer:

    ...

위의 매니페스트 파일을 보면 알 수 있듯이 type: LoadBalancer가 추가되었다. (나머지 값은 나중에 알아보도록 하자.)

그림 9. LoadBalancer with Azure

그림 9을 확인해보면 Azure Load Balancer에 type이 LoadBalancer인 각각의 Service가 붙어있는 것을 확인 할 수 있다.

이렇듯 External Load Balancer (Cloud Provider의 Load Balancer)에서 발생하는 트래픽을 바로 Pod에서 받을 수 있다. (물론 해당 트래픽은 Cloud Provider에 의존적이다.)

그림 10. ClusterIP vs LoadBalancer vs NodePort

각각의 개념을 간단하게 표현한 그림 10을 확인해보면 Service의 타입인 ClusterIP, LoadBalancer, NodePort의 차이를 쉽게 알 수 있다.

 

ExternalName

ExternalName은 Service를 DNS에 매핑하는 것이다. 

apiVersion: v1

kind: Service

metadata:

  name: my-service // 참조 할 Service Name

  namespace: prod

spec:

  type: ExternalName // ExternalName 설정

  externalName: my.database.example.com // 매핑 할 ExternalName

위의 매니페스트 파일처럼 설정을 하면 my.database.example.com을 my-service로도 접근이 가능하다.

 

Reference

https://www.ianlewis.org/en/performing-maintenance-pods

https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/

https://kubernetes.io/docs/concepts/services-networking/service/

https://twitter.com/bibryam/status/977570896725663744

https://blog.jreypo.io/containers/microsoft/azure/cloud/cloud-native/how-to-expose-your-kubernetes-workloads-on-azure/