Fairy ' s

[K8s] Services 본문

Study/K8s

[K8s] Services

berafairy 2023. 8. 7. 16:52

 

 어떠한 애플리케이션에 다양한 섹션을 실행하는 파드의 그룹들이 있다. 예를 들어 프론트를 유저에게 서빙하는 그룹, 백엔드 프로세스를 실행하는 그룹, 외부 데이터 소스에 연결하는 그룹 등이 있을 것이다. 이러한 파드 그룹 간 연결을 가능하게 하는 것이 Service이다.

 Service를 통해 프론트 애플리케이션을 엔드 유저가 사용할 수 있게 하고, 백엔드와 프론트 파드 간의 통신을 돕고, 외부 데이터 소스 연결 설정에 도움을 준다. 즉, Service는 애플리케이션의 마이크로서비스 간에 느슨한 연결을 가능하게 한다. 


Service

 웹 애플리케이션이 실행되고 있는 파드를 배포 했을 때, 외부 사용자가 어떻게 웹 페이지에 접속할 수 있을까?

 파드와 자신의 네트워크가 다른 IP에 있으면 파드에 ping을 하거나 접속할 수 없다. 이 웹 페이지를 보기 위해서는 쿠버네티스 노드에 ssh 로 연결하면 curl을 통해 파드의 웹 페이지에 접속할 수 있다. 또는, 노드에 GUI가 있다면 브라우저를 열어서 IP주소를 입력하면 웹 페이지를 볼 수 있다. 하지만, 이것은 노드 내부에서 접근하는 방법이다.

 ssh 연결 없이 자신의 노트북에서 웹 서버에 접속하기 위해서는 노트북에서 노드로, 노드에서 파드로 중간에 요청을 매핑할 무언가가 필요한데, 이 역할을 하는 것이 Kubernetes Service이다. Pod, ReplicaSet, Deployment와 마찬가지로 Service는 오브젝트이다.


 쿠버네티스에는 3가지 타입의 Service가 있다.

1. NodePort

노드와 노드 내 파드에서 내부 파드에 접근이 가능하도록 만드는 서비스이다. 워커 노드 포트에서 외부 애플리케이션을 사용할 수 있도록 해준다.

  • TargetPort : Service가 요청을 전달할 곳, 실제 웹 서버가 있는 파드의 포트는 80이다. 
  • Port : Service 자체의 포트, 서비스는 노드 내의 가상 서버와 같다. 클러스터 내부에는 자체 IP주소가 있는데, IP 주소를 서비스의 ClusterIP라고 부른다.
  • NodePort : 노드 자체의 포트이며, 외부에서 웹 서버에 액세스하기 위해 사용할 포트이다. 노드 포트는 ' 30000 ~ 32767 ' 범위의 값을 가질 수 있다. 

Create Service

Service의 spec에는 selector와 type, ports가 있다.

  • selector : labels와 selector를 사용해 서비스를 파드에 연결해야 한다. selector에 연결할 파드의 labels 정보를 입력한다.
  • type : 서비스의 유형, ex. NodePort, ClusterIP, LoadBalancer
  • ports : 포트들을 array로 적는다. 
    • port : 80 (필수 필드) 
    • targetPort : 80 (targetPort를 적지 않으면 port와 동일하다고 간주한다.)
    • nodePort : 30008 (nodePort를 적지 않으면 30000 ~ 32767 사이 사용 가능한 포트가 자동 배정된다.)
# service_nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  type: NodePort
  ports:
  - targetPort: 80
    port: 80
    nodePort: 30008
  selector:
    # Pod Definition File을 참조하여 Pod의 labels를 Service selector로 정의한다.
    # selector를 통해 Service와 Pod를 연결한다.
    app: myapp
    type: front-end

 

# Create service with service_nodeport.yaml
$ kubectl create -f service_nodeport.yaml

# 생성한 Service 조회
$ kubectl get services
$ kubectl get svc
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
myapp-service   NodePort    10.111.202.63   <none>        80:30008/TCP   6s

# Node IP 주소를 이용해 웹 서비스에 액세스할 수 있다.
$ curl http://<Node_IP_Address>:<NodePort>

# How to view Node IP Address
# INTERNAL-IP << Node IP Address
$ kubectl get nodes -o wide
NAME       STATUS   ROLES           AGE   VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION                      CONTAINER-RUNTIME                                                                       ION
minikube   Ready    control-plane   78d   v1.26.3   192.168.49.2   <none>        Ubuntu 20.04.5 LTS   5.15.90.1-microsoft-standard-WSL2   docker://23.0.2

 

 위 내용에서는 단일 파드에 매핑된 서비스에 대해 이야기 했지만, 프로덕션 환경에서는 실행중인 웹 애플리케이션의 여러 인스턴스들과 파드들이 있을 것이다. 파드들은 모두 같은 label을 가지고 있을 것이며, Service의 selector에서 해당 label을 가지고 있는 파드를 모두 선택하게 된다. 그 중 요청을 전달할 파드는 파드 간 로드 균형을 조정하면서 랜덤 알고리즘으로 선택하게 된다. 따라서 Service는 파드 간 부하를 분산하면서 기본적으로 로드 밸런서 역할을 하게 된다.

 파드가 여러 노드에 분산되어 있다면, Service를 만들 때 쿠버네티스는 자동으로 클러스터의 모든 노드에서 대상 포트를 매핑하도록 만든다. 이 때 클러스터의 모든 노드는 동일한 포트 번호를 사용한다. 


2. ClusterIP

 풀 스택 웹 애플리케이션에는 다양한 종류의 파드가 있다. 프론트엔드 웹 서버를 실행하는 파드 세트, 백엔드 웹 서버를 실행하는 파드 세트, Redis와 같은 key-value store를 실행하는 파드 세트, MYSQL과 같은 영구 데이터베이스를 실행중인 파드 세트들이 있을 수 있다. 웹 프론트엔드 서버는 백엔드 서버와 통신해야 하며, 백엔드 서버는 데이터베이스, Redis 서버 등과 통신해야 한다.

 모든 파드에는 할당된 IP 주소가 있고, 파드는 죽을 수도, 새로운 파드를 생성할 수도 있다. 따라서 파드에 할당된 IP 주소는 정적 주소가 아니다. 그렇기 때문에, 애플리케이션 간 통신을 한다고 했을 때, 이 파드 IP에 의존할 수 없다.

 이러한 문제를 해결하기 위해 애플리케이션 간의 연결을 설정하기 위해 Pod를 그룹화하여 Pod에 액세스할 수 있는 단일 인터페이스(Virtual IP)를 제공한다. 다른 파드가 서비스에 액세스하면 요청이 파드 중 하나로 전달된다. 이를 통해 우리는 쿠버네티스 클러스터에서 쉽게 마이크로서비스 기반 애플리케이션을 효과적으로 배포할 수 있다.

 또한, 다양한 서비스 간의 커뮤니케이션에 영향을 주지 않고 각 레이어의 크기를 조정할 수 있다. 각 서비스에는 IP와 이름이 할당되는데, 클러스터 내부에서 사용하는 이름으로 이러한 타입의 서비스를 ClusterIP라고 한다.

Create ClusterIP

만드는 방식은 NodePort랑 같다.

# service_clusterip.yaml

apiVersion: v1
kind: Service
metadata:
  name: back-end
spec:
  type: ClusterIP
  ports:
  - targetPort: 80
    port: 80
  selector:
    # Pod Definition File을 참조하여 Pod의 labels를 Service selector로 정의한다.
    # selector를 통해 Service와 Pod를 연결한다.
    app: myapp
    type: back-end

 

# Create Service
$ kubectl create -f service_clusterip.yaml

# 생성한 서비스 조회
$ kubectl get svc
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
back-end        ClusterIP   10.106.221.163   <none>        80/TCP         26s

3. LoadBalancer

 시나리오 .

 투표앱과 결과앱이 있고, 이런 파드는 클러스터의 워커 노드에서 호스팅된다. 클러스터에 4개의 워커 노드가 있다고 가정하고, NodePort 타입의 서비스를 생성해 외부 유저가 애플리케이션에 액세스할 수 있도록 한다. NodePort 타입의 서비스는 노드로 들어오는 트래픽을 수신하고 각 노드에 트래픽을 라우팅하는 데에 도움을 주는데, 애플리케이션에 액세스 할 때 유저에게 어떤 URL을 제공해야 할까?

 IP와 포트를 통해 두 애플리케이션 모두 어디든 액세스할 수 있으므로, 제공할 수 있는 조합은 투표앱 4개 조합(IP:port), 결과앱 4개 조합(IP:port)이다. 만약 두 개의 노드에서만 파드가 호스팅 되는 경우에서도 여전히 클러스터에 있는 모든 노드의 IP에 여전히 액세스할 수도록 구성 되어있다. 따라서 이 URL을 공유하고, 유저가 애플리케이션에 액세스할 수 있도록 한다.

 하지만,엔드 유저가 원하는 방향은 IP주소로 액세스 하지 않고, 단일 URL로 액세스하는 것이다. 그러기 위해서는 로드 밸런서용 VM을 생성하고 로드 밸런서(AJ/Proxy, nginx)를 설치하고 구성해야 한다. 그리고 노드들에 트래픽을 라우팅하도록 로드 밸런서를 구성한다.

 이렇게 외부 로드 밸런싱에 대한 설정을 모두 마치면 유지 관리를 해야 한다. 유지 관리는 Google Cloud, AWS, Azure 같은 클라우드 플랫폼에서 네이티브 로드 밸런서를 활용할 수 있다. 쿠버네티스는 클라우드 공급자의 네이티브 로드 밸런서를 사용하는 것을 지원하며, 우리는 서비스 타입을 설정하기만 하면 된다. GCP, AWS, Azure는 확실히 지원이 되지만, VirtualBox 같은 지원되지 않는 환경에서 서비스 타입을 로드 밸런서로 설정하면 nodePort로 설정한 것과 같은 결과가 나타난다. 즉, 로드 밸런서로 부하 분한을 하지 않게 된다.

service_loadbalancer.yaml

apiVersion: v1
kind: Service
metadate:
  name: myapp-service
spec:
  type: LoadBalancer
  ports:
  - targetPort: 80
    port: 80
    nodePort: 30008

 

 

 

'Study > K8s' 카테고리의 다른 글

[K8s] Imperative vs Declarative  (0) 2023.08.08
[K8s] Namespaces  (0) 2023.08.08
[K8s] kubectl command  (0) 2023.08.07
[K8s] Deployment  (0) 2023.08.06
[K8s] Replication controller & ReplicaSets  (0) 2023.08.05
Comments