본문 바로가기

DevOps/쿠버네티스(Kubernetes)

[CKA] Kubernetes Services

Kubernetes Services

 

Kubernetes Services는 애플리케이션 내부와 외부의 다양한 구성 요소 간의 통신을 가능하게 한다. Kubernetes 서비스는 애플리케이션을 다른 애플리케이션 또는 사용자와 연결하는 데 도움이 된다.
예를 들어, 우리 애플리케이션에는 사용자에게 프론트 엔드 로드를 제공하기 위한 그룹 및 백엔드 프로세스를 실행하기 위한 다른 그룹, 외부 데이터 소스에 연결하는 세 번째 그룹과 같은 다양한 섹션을 실행하는 Pod 그룹이 있다.
이러한 Pod 서비스 그룹 간의 연결을 가능하게 한다. 최종 사용자가 사용할 수 있는 애플리케이션은 백엔드와 프런트 엔드 간의 통신을 지원한다. 외부 데이터 소스에 대한 연결을 설정하는 데 도움이 된다. 따라서 Service는 응용프로그램에서 마이크로 서비스 간의 느슨한 결합을 가능하게 한다.

 

Service 이용 사례 한 가지를 살펴보자. 지금까지 우리는 내부 네트워킹을 통해 Pod가 서로 어떻게 통신하는지에 대해 이야기했다.  외부 커뮤니케이션부터 네트워킹의 다른 측면을 살펴보면, 우리는 웹 애플리케이션을 실행하는 Pod를 배포했다. 외부 사용자로서 웹 페이지에 먼저 액세스하는 방법은 무엇일까?

 

먼저 기존 설정을 살펴보면 Kubernetes 노드에는 192.168.1.2라는 IP 주소가 있다. 내 노트북도 같은 네트워크에 있다. 따라서 IP 주소가 192.168.1.10이다. 내부 Pod 네트워크의 범위는 10.244.0.0이고 Pod의 IP는 10.244.0.2이다. 주소 10.244.0.2에 있는 Pod를 별도의 네트워크에서 ping하거나 액세스할 수 없다. 그러면 웹 페이지를 볼 수 있는 옵션은 무엇일까?

 

 첫번째, 만약 우리가 192.168.1.2에 있는 쿠버네티스 노드로 SSH를 한다면, 우리는 curl을 하거나 만약 노드가 GUI를 가지고 있다면, 우리는 http://10.244.0.2에 접근하고 이어 브라우저에서 웹 페이지를 볼 수 있을 것이다. 하지만 이것은 Kubernetes 노드 안에서 나온 것이고 그것은 정말로 원하는 것이 아니다. 노드에 SSH할 필요 없이 단순히 쿠버네티스 노드의 IP에 액세스하여 내 노트북에서 웹 서버에 액세스할 수 있기를 원한다. 따라서 중간에 노트북에서 노드를 통해 웹 컨테이너를 실행하는 Pod에 노드에 요청을 매핑하는 데 도움이 되는 무언가가 필요하다.

 

 

여기서 Kubernetes 서비스가 시작된다. Kubernetes Services는 이전에 작업했던 Pod, Replicaset 또는 Deployment와 같은 객체이다. 사용 사례 중 하나는 노드의 포트를 수신 대기하고 해당 포트의 요청을 웹 애플리케이션을 실행하는 Pod의 포트로 전달하는 것이다.

 

 

 

Service Type

서비스는 노드에서 포트를 수신하고 요청을 Pod로 전달하기 때문에 NodePort 서비스라고 한다. NodePort 는 서비스가 노드의 포트에서 내부 Pod에 액세스할 수 있도록 한다.

두번째는 ClusterIP이다. 이 경우 서비스는 클러스터 내부에 가상 IP를 생성하여 프런트 엔드 서버 집합과 같은 서로 다른 서비스 간에 백 엔드 서버 집합과 통신할 수 있도록 한다.

세 번째 유형은 LoadBalancer 이다. 지원되는 클라우드 프로바이더에서 서비스를 위한 로드 밸런서를 프로비저닝하는 경우이다. 프런트 엔드 계층의 여러 웹 서버에 로드를 분산하는 것이 좋은 예이다.

 

 

 

1. NodePort 


우리는 애플리케이션에 대한 외부 액세스에 대해 논의했다. 우리는 서비스가 노드의 포트를 Pod의 포트에 매핑함으로써 우리를 도울 수 있다고 말했다. 서비스에 대해 좀 더 자세히 살펴보자.

세 개의 포트가 관련되어 있는데, 실제 웹 서버가 실행 중인 Pod의 포트는 80이다. 그리고 서비스의 관점에서 보면 서비스가 요청을 전송하는 대상 포트이기 때문에 targetPort라고 한다. 두 번째 포트는 서비스 자체의 포트이며 단순히 포트라고 한다. 
서비스는 사실상 클러스터 내의 노드 내부의 가상 서버와 같다. 자체 IP 주소가 있으며 이 IP 주소를 서비스의 Cluster IP(10.106.1.12)라고 한다. 마지막으로 노드 자체에 웹 서버에 외부에서 액세스하는 데 사용하는 포트를 노드 포트라고 한다. NodePort는 30008으로 정했다. NodePort가 기본적으로 30000에서 32767 사이의 유효한 범위에만 있을 수 있다. 

 

 

 

# service-definition.yml
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  type: NodePort
  ports:
  - targetPort: 80
    port: 80
    nodePort: 30008
  selector:
    app: myapp
    type: front-end
    
    
# pod-definiton.yml
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
    type: front-end
spec:
  containers:
  - name: nginx-container
    image: nginx

이제 서비스를 생성하는 방법을 살펴보자. Deployment, ReplicaSet 또는 Pod를 만든 방법과 마찬가지로 과거에는 정의 파일을 사용하여 서비스를 만들었는데, 파일의 상위 레벨 구조는 다른 객체들과 동일하게 apiVersion, kind, metadata 및 spec 섹션이 있다.  kind는 당연히  Service 가 되고 metadata에는 이름이 있을 것이고 그것이 서비스의 이름이 된다. label이 붙을 수는 있지만 지금은 필요 없다.

다음으로 spec이 있으며, 항상 그렇듯이 이 부분이 파일의 가장 중요한 부분이다. 이 부분이 실제 서비스를 정의하는 부분이다. 그리고 이것은 서비스의 spec 섹션에서 서로 다른 개체 간에 서로 다른 정의 파일의 일부이다. type 및 port 는 우리가 만들고 있는 서비스 유형을 나타낸다. 앞서 설명한 바와 같이 ClusterIP, NodePort 또는 LoadBalancer일 수 있다. 이 경우 노드 포트를 만들고 있으므로  NodePort를 갖도록 설정한다.
spec의 다음 부분은 포트이다. 첫 번째 port 유형은 targetPort이고, 다음 유형은 서비스 개체의 포트인 단순 포트이며, 이 포트도 80으로 설정한다. 세 번째는 30008 또는 유효한 범위의 임의의 숫자로 설정하는 NodePort입니다. 대상 포트를 제공하지 않는 경우 이 중 필수 필드는 포트뿐입니다. 포트와 동일한 것으로 가정할때 nodePort를 제공하지 않으면 30000에서 32767 사이의 유효한 범위의 사용 가능한 포트가 자동으로 할당된다. 또한 ports는 배열이다. 따라서 배열의 첫 번째 요소를 나타내는 ports 섹션 아래에 대시를 사용하면 안된다. 단일 서비스 내에 이러한 포트 매핑이 여러 개 있을 수 있다. 그래서 우리는 모든 정보를 가지고 있지만 무언가가 부족하다. 여기에는 서비스를 포드에 연결하는 정의 파일이 없다. 대상 포트를 간단히 지정했지만 어떤 포드의 대상 포트에 대해서인지는 언급하지 않았다.
포트 80에서 웹 서비스가 실행되는 100개의 다른 POD가 있을 수 있다.

 

그러면 이전에 ReplicaSet를 사용한 것처럼 어떻게 해야 할까? Kubernetes에서 매우 자주 볼 수 있는 기술인 label과 selector을 사용하여 이것들을 연결할 것이다. 우리는 Pod가 라벨로 생성되었다는 것을 알고 있다. 해당 라벨을 서비스 정의 파일에 가져와서 spec 섹션에 selector라는 새 속성이 있어야 한다. ReplicaSet 및 deployment 정의 파일도 이에 대한 포드를 식별할 수 있는 레이블 목록을 제공한다. Pod 정의 파일에서 레이블을 가져와 selector 아래에 배치한다. 이렇게 하면 서비스가 Pod에 연결된다.

 

$ kubectl create -f service-definition.yml
service "myapp-service" created

 

완료되면 kubectlcreate 명령을 사용하여 서비스를 생성하고 서비스 정의 파일을 입력하면 서비스가 생성된다.

 

 

$ kubectl get services
NAME            TYPE         CLUSTER-IP        EXTERNAL-IP         PORT(S)
kubernetes     ClusterIP    10.96.0.1             <none>            443/TCP
myapp-service  NodePort     10.106.127.123        <none>            80:30008/TCP

생성된 서비스를 보려면 서비스 클러스터 IP 및 맵 포트를 나열하는 kubectl get services 명령을 실행한다. type은 우리가 만든 NodePort이고 NodePort는 정의 파일에서 지정한 포트이기 때문에 30008로 설정된다.

 

 

$ curl http://192.168.1.2:30008

이제 이 포트를 사용하여 curl 또는 웹 브라우저를 사용하여 웹 서비스에 액세스할 수 있다. 따라서 노드의 IP인 192.168.1.2로 컬링한 다음 포트 30008을 사용한다. 

 

 


단일 포드에 매핑된 서비스에 대해 지금까지 설명했다. 하지만 항상 그런 것은 아니다. Pod가 여러 개일 때는 어떻게 할까? 프로덕션 환경에서 이 경우 고가용성 및 로드 밸런싱을 위해 실행 중인 웹 응용 프로그램의 여러 인스턴스가 있다. 웹 애플리케이션을 실행하는 유사한 Pod가 여러 개 있으며, 모두 key가 myapp인 동일한 label을 가지고 있으며, 서비스를 생성하는 동안 동일한 label이 selector로 사용된다. 따라서 서비스가 생성되면 label과 일치하는 Pod를 찾고 그 중 세 개를 찾는다.
그러면 서비스는 자동으로 세 개의 포드를 엔드포인트로 선택하여 사용자로부터 오는 외부 요청을 전달한다. 이 작업을 수행하기 위해 추가 구성을 수행할 필요가 없다.

 

 


또한 세 개의 서로 다른 Pod 간에 부하분산을 위해 어떤 알고리즘을 사용하는지 알고 싶다면, 이 서비스는 다양한 포드에 로드를 분산하는 내장 로드 밸런서 역할을 하며 마지막으로 포드가 여러 노드에 분산될 때 어떤 일이 발생하는지 살펴보자.
이 경우 클러스터의 개별 노드에 있는 포드에 웹 애플리케이션이 있다. 추가 구성 작업 없이 서비스를 생성하면 클러스터의 모든 노드에 걸쳐 있는 서비스가 자동으로 생성되고 클러스터 내의 모든 노드의 동일한 NodePort 에 대상 포트를 매핑하여 클러스터에 있는 모든 노드의 IP를 사용하여 동일한 포트를 사용하여 애플리케이션에 액세스할 수 있다. 이 경우 30008 포트번호이다. 이러한 노드중 하나의 IP를 사용하여 볼 수 있다. 그리고 동일한 포트로 컬링을 시도하고 있으며 클러스터의 모든 노드에서 동일한 포트를 사용할 수 있다.

 

단일 노드의 단일 포드 또는 단일 노드의 여러 포드가 되든, 여러 노드의 여러 포드가 되든, 포드가 제거되거나 추가될 때 서비스 생성 중에 추가 단계를 수행할 필요 없이 서비스가 정확히 동일하게 생성된다. 서비스는 자동으로 업데이트되므로 일단 생성되면 매우 유연하고 적응력이 뛰어나다.

 

 

2. ClusterIP

풀 스택 웹 애플리케이션은 일반적으로 애플리케이션의 다른 부분을 호스팅하는 다른 종류의 Pod를 갖는다. front-end 웹 서버를 실행하는 여러 개의 Pod와 back-end 서버를 실행하는 Pod, Redis와 같은 키 값 저장소를 실행하는 Pod 세트, MySQL과 같은 영구 데이터베이스를 실행하는 또 다른 Pod 세트가 있을 수 있다.
웹 프런트 엔드 서버는 백엔드 서버와 통신해야 하며 백엔드 작업자는 데이터베이스와 Redis 서비스 등에 연결해야 한다. 그렇다면 애플리케이션의 이러한 서비스 또는 계층 간에 연결을 설정하는 올바른 방법은 무엇일까?
Pod에는 모두 IP 주소가 할당되어 있지만, 우리가 알고 있는 이 IP는 고정적이지 않다. 이 Pod들은 언제든지 내려갈 수 있고 새로운 Pod들이 항상 만들어진다. 따라서 애플리케이션 간의 내부 통신을 위해 이러한 IP 주소에 의존할 수 없다. 또한 10.244.0.3의 첫 번째 프런트 엔드 Pod를 백엔드 서비스에 연결해야 하는 경우에는 어떻게 해야 할까? 3개의 백엔드 서버중 어디로 갈까? 그리고 어떤것이 그 결정을 내릴까?

kubernetes 서비스를 통해 이러한 Pod를 함께 그룹화하고 그룹의 Pod에 액세스할 수 있는 단일 인터페이스를 제공할 수 있다.
예를 들어, 백엔드 Pod에 대해 생성된 서비스는 모든 백엔드 Pod 를 함께 그룹화하고 다른 Pod 가 이 서비스에 액세스할 수 있도록 단일 인터페이스를 제공하는 데 도움이 된다. 요청은 서비스 아래의 Pod 중 하나로 무작위로 전달된다. 마찬가지로 Redis에 대한 추가 서비스를 생성하고 백엔드 pod가 서비스를 통해 Redis 시스템에 접근할 수 있도록 한다. 이를 통해 우리는 Kubernetes 클러스터에 마이크로 서비스 기반 애플리케이션을 쉽고 효과적으로 배포할 수 있다. 이제 각 계층은 다양한 서비스 간의 통신에 영향을 주지 않고 필요에 따라 확장하거나 이동할 수 있다.

 

# service-definition.yaml
apiVersion: v1
kind: Service
metadata:
  name: back-end
spec:
  type: ClusterIP
  ports:
  - targetPort:80
    port: 80
    
  selector:
    labels:
      app: myapp
      type: back-end

각 서비스는 클러스터 내에서 해당 서비스에 할당된 IP 이름을 얻으며, 이 IP 이름은 서비스에 액세스하는 데 다른 포드에서 사용해야 하는 이름이다. targetPort가 있고 targetPort는 백엔드가 노출되는 포트(이 경우 80)이며,서비스가 노출되는 포트도 80이다.
이제 kubectl create 명령을 사용하여 서비스를 생성한 다음 kubectl get services 명령을 사용하여 상태를 확인할 수 있다.


 

 

 

 

 

출처:

https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/learn/lecture/14295512#overview

https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/learn/lecture/14295514#overview