CKA를 준비해보자 4일차 - Service(NodePort, ClusterIP, Load Balancer)

0

CKA

목록 보기
4/43

Service

kubernetes service는 app들이 cluster내부, 외부에서의 통신을 가능하게 해준다. 즉, cluster 내부의 pod들끼리의 통신이 가능하게 주고, 동시에 cluster 외부인 user와 외부 cluster 또는 data source에 연결하여 통신이 가능하게 해주는 endpoint가 되는 것이다.

    ------     ---------------   -------------
    |User|     |Other cluster|   |Data Source|
    ------     ---------------   -------------
        |             |                 |
        |             |                 |
--------------Kubernetes Cluster------------
|               ----------                 |     
|               |services|                 |
|               ----------                 |
|                   |                      |
|                   |                      |
|                 ------                   |
|                 |pod1|                   |
|                 ------                   |
|                   |                      |
|            --------------------          |
|            |                  |          |
|        ----------          ----------    |
|        |services|          |services|    |      
|        ----------          ----------    |
|            |                   |         |
|          ------             ------       |
|          |pod2|             |pod3|       |
|          ------             ------       |
--------------------------------------------

이렇게 각 pod끼리의 통신을 위해서 service중심으로 pod들이 loose하게 연결되어 통신이 가능하다. 외부와도 마찬가지인데, loose하게 연결된 service를 통해 pod가 외부의 user, cluster, data source에 접근할 수 있고, 외부에서도 cluster에 접근이 가능하다.

그럼 어떻게 외부 사용자가 service를 사용해 우리의 pod에 접속할 수 있는가?

-----user-----      ---Cluster Node(192.168.1.2)-----------
|192.168.1.10|  --> |                                     |
--------------      |            ----pod-----             |
                    |            |10.244.0.2|             |
                    |            ------------             |
                    ---------------------------------------

user host는 192.168.1.10에 있고 cluster node는 192.168.1.2라고 하자. 이들이 같은 network에 있다고 하자. pod는 내부 cluster network에 의해서 ip 10.244.0.2을 할당받는데, cluster nodeuser host가 같은 network이므로 user에서 10.244.0.2에 직접 요청을 보내면 접근이 가능하다.

curl http://10.244.0.2
hello world!

그러나 우리가 원하는 것은 이것이 아니다. pod의 ip에 직접 요청해서 응답을 받고자하는 것이 아니라는 것이다. 왜냐하면 이것이 가능한 이유는 user host가 cluster node와 같은 network라서 가능한 것이다. 만약 다르면 접근이 불가능할 것이다. 따라서, 우리가 원하는 것은 cluster node를 통해서 pod에 접근하는 방법인데, 192.168.1.2 IP를 사용하여 pod에 접근하고 싶은 것이다.

그래서 중간 매개체인 service가 이 역활을 해주는 것이다.

-----user-----      ---Cluster Node(192.168.1.2)-----------
|192.168.1.10|  --> |                                     |
--------------      |           ---service---             |
                    <---------->| 30008 port|             |
                    |           -------------             |
                    |                 |                   |
                    |            ----pod-----             |
                    |            |10.244.0.2|             |
                    |            ------------             |
                    ---------------------------------------

이제 user host는 192.168.1.230008 port를 통해서 pod에 접근이 가능하다.

curl http://192.168.1.2:30008
hello world!

service중에도 몇가지 종류가 있는데, 이렇게 cluster내의 pod를 node의 port와 연결하여 외부에서 접근할 수 있도록 만든 것이 바로 nodePort service이다.

Service Types: NodePort

  1. NodePort: 내부 pod의 port를 외부에서 접근 가능한 node의 port와 연결하여 외부에서 접근이 가능하도록 한다.
  • Nodeport
---Cluster Node(192.168.1.2)-----------
|                                     |
|                   ---service---     |
30008(Node port)<-->|           |     |
|                   -------------     |
|               80(service port, port)|
|                         |           |
|                         |           |
|                         |           |
|                         |           |
|                  80(target port)    |
|                   ----pod-----      |
|                   |10.244.0.2|      |
|                   ------------      |
---------------------------------------

NodePort service를 자세히 보도록 하자. NodePort serviec는 내부 pod를 node의 port에 연결하여 외부에서 접근이 가능하도록 해준다고 했다. 이때 필요한 port들이 몇가지 있는데, 다음과 같다.

  • target port: service를 제공하는 target이 되는 port로 pod의 port에서 application이 동작하는 port이다.
  • service port(port): 보통 port라고 부르는데, service의 port로 pod와 연결되는 port이다. 이 값은 크게 어떤 값을 쓰던지 관련이 없다. 단, 보통 target port와 동일하게 한다.
  • node port: cluster node에 뚫으려는 port를 말한다. 즉 외부에서 접근이 가능하도록 node에서 뚫은 port라는 것이다.

이 port들의 네이밍 관점은 service라고 생각하면 쉽다.

  • service-definition.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  type: NodePort
  ports:
    - targetPort: 80
      port: 80
      nodePort: 30008
  selector:
    app: myapp
    type: front-end

나머지는 특별히 이전과 다를바가 없고 spectypeport에 집중해보도록 하자. 주의할 것은 nodePort는 기본적으로 한정된 값이라는 것이다. 따라서 32000보다 큰 수를 연결할 때는 조심해야한다.

마지막으로 pod를 연결하기 위한 selector이다. 해당 label을 가진 pod라면 pod와 service가 연결된다.

kubectl create -f service-definition.yaml
kubectl get services
curl http://192.168.1.2:30008

사실 service는 반드시 하나의 pod들에만 연결되는 것은 아니다. service를 사용하는 주된 목적 중 하나는 통신도 있지만 'load balancing' 기능도 있기 때문이다.

-------------Cluster Node(192.168.1.2)--------------
|                                                   |
|           ---service----                          |
<---------->| 30008 port |                          |
|           |selector:   |                          |
|           |  app: myapp|                          |
|           --------------                          |
|                  |                                |
|                  |                                |
|          --------------------------------         |
|          |              |               |         |
|   -----pod1-----  -----pod1-----  -----pod2-----  |
|   |10.244.0.2  |  |10.244.0.3  |  |10.244.0.4  |  |
|   |labels:     |  |labels:     |  |labels:     |  |
|   |  app: myapp|  |  app: myapp|  |  app: myapp|  |
|   --------------  --------------  --------------  |
|                                                   |
-----------------------------------------------------

service는 외부 사용자의 요청을 pod에 무작위 방식으로 로드를 분산한다. 이러한 로드 밸랜싱으로 부하를 나누는 것이다.

재밌게도, cluster에 여러 개의 node들이 배포된 경우에도 하나의 service로 각 node들에 있는 pod에 대한 로드밸런싱을 수행한다는 것이다.

|------Node1-----|  |------Node2-----|
|                |  |                |
| -----------------service---------- |
| |             30008 port         | |
| ---------------------------------- |
|       |        |  |       |        |
| -----pod1----- |  | -----pod1----- |
| |10.244.0.2  | |  | |10.244.0.3  | |
| |labels:     | |  | |labels:     | |
| |  app: myapp| |  | |  app: myapp| |
| -------------- |  | -------------- |
------------------  ------------------

다음과 같이 cluster가 두 개의 node인 node1, node2로 이루어져도 service한개로 이 두 노드 안에 있는 pod를 외부로 연결해주는 기능을 할 수 있는 것이다.

Service Types: ClusterIP

  1. ClusterIP: 내부에 가상의 IP를 만들어 다른 서비스간의 통신을 가능하게 해준다. 즉, 내부 pod들끼리의 통신이 가능하게 해준다.

왜 cluster내부의 pod끼리 통신을 할 때 굳이 service가 필요할까? pod끼리의 IP가 이미 cluster 내부에 있기 때문에, 이를 직접 서로 의사소통하면 되지 않을까 생각이 들 것이다.

그런데, pod의 IP는 항상 동일하지 않는다. 만약 pod가 다운되면 pod IP가 달라지게 되고 기존의 IP로는 접근할 수 없게된다. pod가 죽고 살아나는 것은 kubernetes 상에서는 정상적인 동작이기 때문에 이상한 상황이 아니다.

따라서, servcice로 ClusterIP를 받아서 service를 통해 접근하면 pod가 죽어도 변하는 IP를 service가 중간 매개체로 삼아 알려주어 연결이 가능한 것이다. service의 IP는 cluster가 다운되기 전까지 절대 바뀌지 않음으로 이를 이용하여 pod기리 통신을 하는 것이다.

또한, pod가 만약 여러 개인 경우는 어떠한가? redis같은 key-value database기능을 하는 pod들이 n개 있는 경우, 어떤 pod의 IP를 직접 물고와서 통신해야하는가? 이러한 문제는 쉽지가 않다. 그러나 service로 여러 개의 pod들을 연결할 경우, 요청을 연결된 pod 중 하나로 보내어 응답을 전송하는 것이다.

마지막으로 service는 DNS를 제공하는데, 이를 FQDN이라고 한다. 즉 service의 IP로의 직접접근이 아니라, 이름을 통해 접근할 수 있도록 하여, pod가 service의 IP를 직접 알아낼 필요가 없다. DNS이름만으로 pod가 통신이 가능한 것이다.

service의 이름이 temp이고, namespace가 default이면 다음의 DNS를 갖는다.

temp.default.svc.cluster.local
-------------------Cluster-------------
| ------     -----------              |
| |pod1|<--->|         |              |
| ------     |         |              |
|            |         |              |
| ------     |         |       ------ |
| |pod2|<--->| service | <---> |pod4| |
| ------     |         |       ------ |
|            |         |              |
| ------     |         |              |
| |pod3|<--->|         |              |
| ------     -----------              |
|                                     |
----------------------------------------

다음과 같이 cluster내부에 여러 pod들을 하나의 clusterIP를 가진 service에 연결하여 다른 pod가 이들에 접근할 수 있는 것이다.

pod4는 pod1, pod2, pod3의 IP를 몰라도 되며 service를 통해 통신이 가능하다. 또한, service는 pod1, pod2, pod3에 대한 로드밸런싱 기능을 제공해주고, pod4가 service의 IP가 아닌 DNS, 즉 이름을 통해 접근할 수 있도록 매개체를 지원해준다.

  • service-definition.yaml
apiVersion: v1
kind: Service
metadata:
  name: back-end

spec:
  type: ClusterIP
  ports:
    - targetPort: 80
      port: 80
  selectopr:
    app: myapp
    type: back-end

NodePort와 동일한데, typeClusterIP라는 것과 nodePort가 없다는 것이다. 사실 service의 type default가 ClusterIP이기 때문에 굳이 지명해주지 않아도 된다.

  • targetPort: 연결하려는 pod의 port이다.
  • port: service의 port이다.

앞서 말했듯이 port는 service기준으로 작성되는 것이다. 따라서 port는 service port이고 targetPort가 pod의 port가 되는 것이다.

마지막으로 selector를 통해서 label에 맞는 pod를 연결해주도록 한다.

kubectl create  -f ./service-definition.yaml
kubectl get services

Service Types: Loadbalancer

  1. LoadBalancer: 각 클라우드 공급자를 위한 loadbalancer로 nodeport와 비슷한 기능을 한다고 생각하면 된다.

좀 더 복잡한 cluster예제를 보도록 하자. 다음은 4개의 node가 있고 deployment가 2개 있는데 NodePort service가 30035, 31061로 pod에 외부로 노출되어 있다고 하자.

---Node1---  ---Node2---  ---Node3---  ---Node4---
|192.168. |  |192.168. |  |192.168. |  |192.168. |
|56.70    |  |56.71    |  |56.72    |  |56.73    |
|         |  |         |  |         |  |         |
|         |  |         |  |         |  |         |
| --------------NodePort 30035------------------ |
| |                                            | |
| ---------------------------------------------- |
|         |  |         |  |         |  |         |
|         |  |         |  |         |  |         |
| --------------NodePort 31061------------------ |
| |                                            | |
| ---------------------------------------------- |
|         |  | pod2    |  | pod4    |  | pod7  | |
| pod1    |  | pod3    |  | pod5    |  |       | |
-----------  -----------  -----------  ---------

위의 그림처럼 각 Node들은 각자의 host IP를 가지고 있고 NodePort service를 통해서 30035, 31061 port로 뚫려있다. 그렇다면 user들은 어떻게 pod에 배포된 application에 접근할 수 있는 것을까??

http://192.168.56.70:30035
http://192.168.56.71:30035
http://192.168.56.72:30035
http://192.168.56.73:30035

http://192.168.56.70:31061
http://192.168.56.71:31061
http://192.168.56.72:31061
http://192.168.56.73:31061

다음의 모든 URL을 통해서 요청이 가능하다. 그러나 user는 이렇게 다양한 URL에 관심이 없다. 오직 단일 URL하나만을 원할 뿐이다.

                    ------
                    |user|
                    ------
                      |
                      |
            ---------------------------
            |http://example-vote.com  |
            |http://example-result.com|                    
            ---------------------------
                      |
                 Load Balander
                      |
---------------------------------------------------

---Node1---  ---Node2---  ---Node3---  ---Node4---
|192.168. |  |192.168. |  |192.168. |  |192.168. |
|56.70    |  |56.71    |  |56.72    |  |56.73    |
|         |  |         |  |         |  |         |
|         |  |         |  |         |  |         |
| --------------NodePort 30035------------------ |
| |                                            | |
| ---------------------------------------------- |
|         |  |         |  |         |  |         |
|         |  |         |  |         |  |         |
| --------------NodePort 31061------------------ |
| |                                            | |
| ---------------------------------------------- |
|         |  | pod2    |  | pod4    |  | pod7  | |
| pod1    |  | pod3    |  | pod5    |  |       | |
-----------  -----------  -----------  ---------

이룰 위해서 새로운 단일 load balancer를 만들어야하는 것이다. nginx, ha proxy 등이 대표적이다. 하지만 이를 만드는 일은 매우 귀찮고 피곤한 일이다.

이를 위해서 클라우드 공급자(AWS, Azure, GCP)와 같은 cloud에서는 자체 load balancer기능을 제공한다. 이 플랫폼의 load balander를 이용하면 kubernetes service type인 load balander와 연동되어 여러 기능들을 제공받을 수 있다.

  • service-definition.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  type: LoadBalancer
  ports:
  - targetPort: 80
    port: 80
    nodePort: 30035

생긴 것 자체는 NodePort와 같다. 이렇게 typeLoadBalancer로 놓고 배포해놓으면 GCP나 AWS같은 클라우드 공급체가 자신들이 정의한 load balancer로 service를 생성하여 외부에서도 접근할 수 있도록 해준다.

단, 주의할 것은 클라우드 환경이 아닌 경우는 실행되지 않고 에러가 발생할 수 있으니 조심하도록 하자.

0개의 댓글