본문 바로가기
IT/PaaS

쿠버네티스 시작하기(2) - 쿠버네티스 구성요소(1/2)

by twofootdog 2019. 12. 19.

이번 장에서는 쿠버네티스의 개념에 대해 정리를 해보고자 한다. 쿠버네티스 클러스터의 구성요소와 컨셉만 잘 이해하면 쿠버네티스를 쉽게 이해하고 사용할 수 있다. 

 

1. 마스터와 노드

쿠버네티스를 이해하기 위해서는 우선 클러스터의 구조를 이해할 필요가 있다. 클러스터 전체를 관리하는 마스터(Master)가 존재하며, 컨테이너가 배포되는 머신인 노드(Node)가 존재한다. (그런데 막상 운영을 하다보면 마스터(Master)에도 컨테이너 배포가 가능하다.)

 

 

2. 오브젝트

쿠버네티스는 가장 기본적인 구성단위가 되는 기본 오브젝트(Basic Object)와, 이 기본 오브젝트를 생성하고 관리하는 추가적인 기능을 가진 컨트롤러(Controller)로 이루어진다. 

2-1. 오브젝트 스펙(Object Spec)

오브젝트들은 모두 오브젝트의 특성(설정 정보)을 기술한 오브젝트 스펙(Object Spec)으로 정의가 되고, 커맨드 라인을 통해서 오브젝트 생성 시 인자로 전달하여 정의를 하거나 yaml이나 json 파일로 정의할 수 있다. 

 

2-2. 기본 오브젝트(Basic Object)

쿠버네티스에 의해서 배포 및 관리되는 가장 기본적인 오브젝트는 컨테이너화되어 배포되는 애플리케이션의 워크로드를 기술하는 오브젝트로 Pod, Service, Volume, Namespace 4가지가 존재한다. Pod는 컨테이너화된 애플리케이션, Volume은 디스크, Service는 로드밸런서, Namespace는 패키지명으로 생각하며 된다.

 

2-2-1. 파드(Pod)

Pod는 쿠버네티스에서 가장 기본적인 배포 단위로 컨테이너를 포함하는 단위이다. 쿠버네티스의 특징 중 하나는 컨테이너를 개별적으로 하나씩 배포하는 것이 아니라, Pod라는 단위로 배포하는데, Pod는 하나 이상의 컨테이너를 포함할 수 있다. 

아래는 간단한 Pod를 정의한 오브젝트 스펙이다.

 


apiVersion: v1
kind: Pod
metadata:
  name: prometheus
spec:
  containers:
  - name: prometheus
    image: prom/prometheus:v2.12.0
    ports:
    - containerPort: 9090

 

  • apiVersion : 이 스크립트를 실행하기 위한 쿠버네티스 API 버전
  • kind : 리소스 종류. Pod, Deployment, DaemonSet, CronJobService, PersistentVolume, PsersistentVolumeClaim, Ingress, ClusterRole, ConfigMap. Secret 등이 들어간다. 
  • metadata : 라벨이나 리소스의 이름 등 이 리소스의 각종 메타데이터를 넣음. 해당 메타데이터를 활용하여 리소스를 구분지을 수 있다. (예를 들면 메타데이터 값을 활용하여 특정 node, 특정 pod 등을 구분지어 컨트롤 할 수 있음)
  • spec : 리소스에 대한 상세한 스펙을 정의
    • Pod는 컨테이너를 가지고 있기 때문에, container를 정의함. 이름은 prometheus 로 하고 도커 이미지 prom/prometheus:v.2.12.0(docker hub에 등록된 이미지 정보)를 사용하고, 컨테이너 포트 9090을 오픈

 

Pod는 한개 이상의 컨테이너를 묶어서 배포도 가능하다. 컨테이너를 묶어서 배포하게 되면 아래와 같은 특징을 가지게 된다. 

  • Pod내의 컨테이너는 IP를 공유한다. 두개의 컨테이너가 하나의 Pod를 통해서 배포되었을때, localhost를 통해서 통신이 가능하다. 예를들면 컨테이너 A가 8080포트, 컨테이너 B가 9090포트로 배포되었을 때, B에서 A를 호출할 때는 localhost:8080으로 호출하면 되고, A에서 B를 호출할 때는 localhost:9090포트로 호출하면 된다.
  • Pod 내에 배포된 컨테이너간에는 디스크 볼륨을 공유할 수 있다. 요즘 애플리케이션은 애플리케이션만 단독으로 올리지 않고 Reverse proxy, 로그 수집기 등 다양한 솔루션이 같이 배포되는 경우가 많고, 특히 로그 수집기의 경우에는 애플리케이션 로그 파일을 읽어서 수집한다. 애플리케이션(tomcat, nodejs)과 로그수집기를 다른 컨테이너에 배포할 경우, 일반적인 경우에는 컨테이너에 의해서 파일 시스템이 분리되기 때문에, 로그 수집기가 애플리케이션이 배포된 컨테이너의 로그파일을 읽는 것이 불가능하지만, 하나의 Pod로 배포할 경우 Pod 내에서 컨테이너들끼리 볼륨을 공유할 수 있기 때문에 다른 컨테이너 파일을 읽을 수 있다. 

 

2-2-2. 볼륨(Volume)

Pod가 기동할 때 디폴트로, 컨테이너마다 로컬 디스크를 생성해서 기동되는데, 이 로컬디스크의 경우에는 영구적이지 못하다. 즉 컨테이너가 리스타트 되거나 새로 배포될 때마다 로컬 디스크는 Pod 설정에 따라 새롭게 정의되서 배포되기 때문에 디스크에 기록된 내용이 유실된다.

데이터베이스와 같이 영구적으로 파일을 저장해야 하는 경우에는 컨테이너 리스타트에 상관없이 파일을 영속적으로 저장해야 하는데, 이러한 형태의 스토리지를 볼륨(Volume)이라고 한다. 

볼륨은 컨테이너의 외장 디스크로 생각하면 된다. Pod가 기동할 때 컨테이너에 마운트해서 사용한다. 볼륨은 Pod에 종속되는 디스크이며(컨테이너 단위가 아님) Pod 단위이기 때문에 그 Pod에 속해있는 여러개의 컨테이너가 공유해서 사용할 수 있다.

쿠버네티스의 볼륨은 여러가지가 있는데, 로컬디스크 뿐만 아니라 NFS, ISCSI, Fiber Channel과 같은 일반적인 외장 디스크 인터페이스는 물론, GlusterFS, Ceph와 같은 오픈소스 파일시스템, AWS EBS, GCP Persistent 디스크와 같은 Public Cloud에서 제공되는 디스크, VsphereVolume과 같이 Private Cloud 솔루션에서 제공하는 디스크 볼륨까지 다양한 볼륨을 지원한다. (자세한 볼륨 리스트 : https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes)

 

2-2-3. 서비스(Service)

Pod와 볼륨을 이용하여 컨테이너를 정의한 후 Pod를 서비스로 제공할 때 일반적인 분산환경에서는 하나의 Pod로 서비스를 하는 경우는 드물고, 여러개의 Pod를 서비스하면서 이를 로드밸런서를 이용해서 하나의 IP와 Port로 묶어서 서비스를 제공한다.

Pod의 경우에는 오토스케일링으로 인해 동적으로 생성/삭제 되고, 장애가 생기면 자동으로 리스타트 되면서 그 IP가 바뀌기 때문에, 로드밸런서에서 Pod의 목록을 지정할 때는 IP주소를 이용하는 것은 어렵다.  그래서 사용하는 것이 라벨(label)과 라벨 셀렉터(label selector)라는 개념이다.

서비스를 정의할 때 어떤 Pod를 서비스로 묶을 것인지를 정의하는데, 이를 라벨 셀렉터(label selector)라고 한다. 각 Pod를 생성할 때 메타데이타 정보 부분에 라벨을 정의할 수 있다. 서비스는 라벨 셀렉터에서 특정 라벨을 가지고 있는 Pod만 선택하여 서비스에 묶게 된다. 

 

 

 

 

이를 오브젝트 스펙(Object Spec)으로 정의해보면 아래와 같다.


kind: Service
apiVersion: v1
metadata:
  name: my-svc
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  • Kind : 리소스 종류
  • apiVersion : 스크립트를 실행할 api 버전
  • 메타데이타에 서비스의 이름을 my-svc로 정의
  • spec 부분에 서비스에 대한 스펙을 정의
    • selector에서 라벨이 app:my-app인 Pod만 선택해서 서비스를 제공
    • 포트는 TCP를 이용. 서비스는 80포트로 서비스를 하며, 80포트로의 서비스 요청을 컨테이너의 8080포트로 연결해서 서비스를 제공함

 

2-2-4. 네임스페이스(Namespace)

Namespace란 쿠버네티스 클러스터 내의 논리적인 분리단위이다. Pod, Service 등은 Namspace 별로 생성 및 관리가 될 수 있고, 사용자의 권한 역시 Namespace별로 나눠서 부여할 수 있다. 즉 하나의 클러스터 내에, 개발/운영/테스트 환경이 있을 경우 클러스터를 개발/운영/테스트 3개의 Namespace로 나눠서 운영할 수 있다. Namespace로 할 수 있는 것은

  • 사용자별로 Namespace별 접근권한을 다르게 운영할 수 있다.
  • Namespace 별로 리소스의 할당량을 지정할 수 있다. 개발계는 CPU 100, 운영계는 CPU 400과 GPU 100개 식으로, 사용 가능한 리소스의 수를 지정할 수 있다.
  • Namespace별로 리소스를 나눠서 관리할 수 있다.(Pod, Service 등)

주의할점은 Namespace는 논리적인 분리라는 것이다(물리적인 환경분리가 아니다). 때문에 다른 Namespace간의 Pod라도 통신은 가능하다. 물론 네트워크 정책을 이용하여 Namespace간 통신을 막을 수는 있지만 높은 수준의 분리정책을 원하는 경우에는 쿠버네티스 클러스터 자체를 분리하는 것이 좋다. (네임스페이스 참고자료 : https://cloudplatform.googleblog.com/2018/04/Kubernetes-best-practices-Organizing-with-Namespaces.html

https://kubernetes.io/blog/2016/08/kubernetes-namespaces-use-cases-insights/)

 

2-3. 라벨(label) & 라벨 셀렉터(label selector)

라벨은 쿠버네티스의 리소스를 선택하는데 사용된다. 각 리소스는 라벨을 가질 수 있고, 라벨 검색조건으로 특정 라벨을 가지고 있는 리소스만 선택 가능하다. 라벨을 선택하여 특정 리소스만 배포하거나 업데이트 할수 있고, 라벨로 선택된 리소스만 Service에 연결하거나 특정 라벨로 선택된 리소스만 네트워크 접근 권한을 부여하는 행위 등을 할 수 있다. 라벨은 metadata 섹션에 키/값 쌍으로 정의가 가능하며, 하나의 리소스에는 하나의 라벨이 아니라 여러 라벨을 동시에 적용할 수 있다.

"metadata": {
 "labels": {
   "key1": "value1",
   "key2": "value2",
   "key3": "value3"
 }
}

라벨을 선택하는 방법은 라벨 셀럭터를 이용한다. 오브젝트 스펙(Object Spec)에서 selector라고 정의하고 라벨 조건을 정의하면 된다. 셀렉터의 종류에는 Eqaulity based selector와 Set based selector가 있으며, Eqaulity based selector는 같냐 다르냐와 같은 조건을 이용하여 리소스를 선택하며(예를들면 env=dev, env!=prod), Set based selector는 집합의 개념을 사용한다(예를들면 env in (dev, prod), env notin (test, dev))

 

 

2-4. 컨트롤러(Controller)

위에서 소개한 4개의 기본 오브젝트로 애플리케이션을 설정하고 배포하는 것이 가능하지만, 이를 좀 더 편리하게 관리하기 위해서 쿠버네티스는 컨트롤러라는 개념을 이용한다. 

컨트롤러는 기본 오브젝트들을 생성하고 이를 관리하는 역할을 해준다. 컨트롤러는 Replication Controller(aka RC), Replication Set, DaemonSet, Job, StatefulSet, Deployment 등이 있다. 

 

컨트롤러에 대한 자세한 설명은 다음 장에서 진행하겠습니다.

 

 

참고

https://bcho.tistory.com/1256

 

쿠버네티스 #2 - 개념 이해 (1/2)

쿠버네티스 #2 개념 이해 (1/2) 조대협 (http://bcho.tistory.com) 쿠버네티스를 공부하면서 가장 헷갈리는 부분이 용어와 컨셉이다. 이 컨셉만 잘 이해하면 쿠버네티스를 쉽게 이해하고 사용할 수 있지만, 적어도..

bcho.tistory.com

 

댓글