[Kubernetes] 쿠버네티스 파드(pod)와 레이블(label), 네임스페이스(namespace)에 대해

<쿠버네티스 인 액션>(마르코 룩샤, 에이콘 출판)의 "3장 파드: 쿠버네티스에서 컨테이너 실행" 을 읽고 정리해봤다.

파드

파드는 쿠버네티스의 가장 작은 기본 단위이다. 파드는 하나 또는 여러 개의 컨테이너를 가지고 있을 수 있다. 하나의 파드는 하나의 워커 노드에서 실행되므로, 해당 파드에 속한 모든 컨테이너는 같은 워커 노드에서 실행된다.

파드와 컨테이너

단일 컨테이너에서 여러 프로세스를 실행하면 각 프로세스의 로그를 관리하는 것이 복잡하다. 개별 프로세스가 실패할 경우 각각 재시작하는 것도 까다롭다. 또, 어떤 프로세스가 어떤 로그를 남겼는지 확인하기도 곤란하다.

각 프로세스를 개별 컨테이너로 실행해야 이런 불편을 해결할 수 있다. 그리고 이 컨테이너를 묶어 하나의 단위로 관리할 상위 구조가 필요한데, 파드가 그것이다.

각 프로세스가 개별 컨테이너로 뜨게 되므로 여러 프로세스가 리소스를 공유하기 위해서는 각 컨테이너가 완벽히 격리되어서는 안 된다. 파드 내의 모든 컨테이너는 동일한 리눅스 네임스페이스를 공유하도록 도커 설정이 된다.

파드 내부의 모든 컨테이너는 동일한 네트워크 네임스페이스와 Unix Timesharing System 네임스페이스 내에서 실행되므로 같은 호스트 이름과 네트워크 인터페이스를 공유한다.

파드 내 모든 컨테이너는 IPC 통신도 가능하다.

다만 파일시스템은 각 컨테이너가 기본적으로 서로 격리된다. 다만, 설정을 통해 공유하도록 할 수 있다.

한 파드 내에서는 각 컨테이너 내의 실행 중인 프로세스가 같은 포트 번호를 사용하지 않게 주의해야 한다. 같은 포트 번호를 사용할 경우 충돌이 일어난다.

모든 파드는 하나의 플랫 네트워크 주소 공간에 존재하므로, 각 파드는 서로의 IP 주소를 사용해 서로에게 접근할 수 있다.

파드 내 컨테이너를 적절히 구성하기

모든 컨테이너를 하나의 파드에 넣는 것은 좋은 선택이 아니다. 프로세스 별로 서로 다르게 스케일링 해야할 수도 있다. 따라서 반드시 함께 구성해야 하는 경우에만 하나의 파드에 여러 컨테이너를 두는 것이 맞다.

컴퓨팅 리소스를 효율적으로 사용하기 위해서도 되도록이면 관련되지 않은 서비스는 별도의 파드로 분리하는 것이 좋다.

다만 사이드카 컨테이너와 같은 경우 하나의 파드에 두어야 하는데, 주 컨테이너와 리소스를 공유할 수 있어야 하기 때문이다.

파드 생성

yaml이나 JSON 메니페스트를 쿠버네티스 REST API 엔드포인트로 전송해 파드를 생성하는 것이 일반적이다.

yaml이나 JSON 디스크립터는 3가지로 이루어진다.

  • Metadata: 이름, 네임스페이스, 레이블 및 파드에 대한 기타 정보
  • Spec: 파드 컨테이너, 볼륨 등 파드 자체에 대한 명세
  • Status: 실행 중인 파드에 관한 현재 정보

leonk.yml


apiVersion: v1
kind: Pod
metadata:
  name: leonk
spec:
  containers:
    - image: chaewonkong/simple:server
      name: leonk
      ports:
        - containerPort: 8081
          protocol: TCP

간단한 yaml 파일이다. 하지만 파드를 띄우는 데는 충분하다.

kubectl create -f leonk.yml

위 명령어로 파드를 생성할 수 있다.

어플리케이션 로그 조회

보통 컨테이너화된 애플리케이션은 표준 출력, 표준 에러 로그를 남기는게 일반적이다. 쿠버네티스는 이런 로그를 쉽게 조회할 수 있도록 API를 제공한다.

kubectl logs leonk 명령어를 통해 로그를 조회할 수 있다.

컨테이너 로그는 10MB 단위로 로테이션 된다. logs 명령은 마지막으로 순환된 로그만 조회한다.

파드에 요청 보내기

별도로 서비스를 두지 않아도 포트 포워딩을 통해 파드에 연결할 수 있다.

kubectl port-forward leonk 8888:8081

localhost:8888로 접속하면 "hello world"를 확인할 수 있다.

레이블

파드는 쿠버네티스의 원자 단위지만, 파드를 묶어 관리하는 것이 편리한 경우도 있다. 이 경우 레이블을 이용할 수 있다.

사실 레이블은 파드에만 붙일 수 있는 것은 아니고 쿠버네티스의 모든 객체에 key:value 쌍으로 붙일 수 있다.

하나의 리소스는 고유한 key라는 전제 하에 여러 레이블을 가질 수 있다.

레이블을 이용하면 리소스를 쉽게 조직화할 수 있고, 조직화된 리소스 단위로 정책을 적용하거나 명령을 실행할 수 있어 편리하다.

예를들어 env=dev, env=stage, env=prod 와 같이 레이블이 존재한다면, env=dev만을 대상으로 파드를 제거한다거나 할 수 있다.

레이블 셀렉터

레이블이 강력한 이유는 레이블 셀렉터를 통해 특정 레이블에 대해 명령을 실행할 수 있기 때문이다.

레이블 셀렉터는 특정 레이블로 태그된 집합을 선택해 원하는 작업을 수행한다.

레이블 셀렉터는 다음 기준으로 리소스를 선택한다.

  • 특정 키 포함 여부
  • 특정 키와 값을 가지는지 여부
  • 특정 키와 다른 값을 가지는 경우

말로는 어려우나 명령문은 쉽다.

kubectl get po -l env

위 명령은 env라는 key를 가진 모든 파드를 조회한다.

kubectl get po env=debug

위 명령은 debug를 env로 가지는 파드를 조회한다.

kubectl get po 'env!=debug'

위 명령은 env가 debug가 아닌 파드를 조회한다.

그밖에도 in (debug,prod), notin (debug,prod) 와 같은 명령어도 존재한다.

레이블 셀렉터에는 여러 조건을 사용할 수 있다. env=prod,rel=true 와 같이 조건을 추가해 조회하거나 명령할 수 있다.

하드웨어 인프라가 동일하지 않아 특정 파드를 특수한 하드웨어들에 스케줄링하고 싶은 경우에도 레이블 셀렉터를 이용할 수 있다. 예를 들어 ML 관련된 파드를 GPU가 있는 하드웨어로 스케줄링 되도록 할 수 있다. 단, 구체적으로 노드를 지정하기 보다는 스케줄링되야할 노드의 요구사항을 기술하고 레이블 셀렉터로 해당하는 노드에 매칭되게 하는 것이다.

어노테이션

어노테이션은 키:값 쌍으로 된 정보이나, 레이블과는 달리 식별 정보를 갖지는 않는다. 어노테이션은 정보를 전달하거나, 쿠버네티스에 정식으로 존재하지 않는 새로운 기능을 제공할 때 사용한다.

예를들어, 쿠버네티스에 새 기능을 추가할 때 알파, 베타 버전은 어노테이션을 사용하다가 정식 릴리즈에서는 새로운 필드로 도입하는 식이다.

혹은 AWS EKS에 배포할 떄 로드 밸런서 컨테이너를 어노테이션 기반으로 세팅하도록 하는 경우처럼 쿠버네티스를 사용할 때 도움을 주는 툴이나 도구에서 활용되기도 한다.

어노테이션 추가

kubectl annotate pod leonk leonkong.cc/someannotation="hi there"

어노테이션 조회

kubectl describe pod leonk

어노테이션 부분으로 스크롤해 조회 가능하다.

네임스페이스를 활용한 리소스 그룹화

네임스페이스를 이용하면 복잡한 시스템을 개별 그룹으로 분리할 수 있다. 혹은 멀티 테넌트 환경처럼 리소스를 분리하는 데도 사용된다.

당연히 서로 다른 네임스페이스는 동일한 이름의 리소스를 가질 수 있다.

단, 노드 리소스는 전역(global)이라 네임스페이스로 묶여있지 않다.

조회

kubectl get ns

some_namespace에 속한 파드들을 조회하고 싶다면, --namespace 또는 -n flag를 이용해 조회한다.

kubectl get po --namespace some_namespace

네임스페이스 생성

먼저 yaml을 이용하는 방식을 알아본다.

some_namespace.yml

apiVersion: v1
kind: Namespace
metadata:
  name: some_namespace

다음 명령어를 통해 yaml을 읽어 생성한다.

kubectl create -f some_namespace.yml

혹은 kubectl 명령을 이용해서 생성하는 것도 가능하다.

kubectl create namespace some_namespace

네임스페이스가 제공하는 격리에 대한 이해

네임스페이스를 통해 오브젝드들을 그룹화할 수 있고, 특정 네임스페이스에 속한 리소스를 대상으로 작업할 수 있지만, 실행 중인 오브젝트에 대해 격리를 제공하지는 않는다.

예를 들면, 서로 다른 네임스페이스의 파드들은 반드시 격리된 것은 아니라서 네트워킹 솔루션에 따라 통신이 가능할 수도 있다.

리소스 제거

파드 중지 및 제거

kubectl delete po leonk

파드를 삭제하면 쿠버네티스는 파드 안의 컨테이너를 종료하는 데, SIGTERM 신호를 프로세스에 보내고 지정된 시간(default 30s)을 대기한다. 시간 내 종료되지 않을 경우 SIGKILL 신호를 통해 프로세스를 종료한다.

당연히 레이블 셀렉터를 이용해 파드를 삭제하는 것도 가능하다.

네임스페이스 제거

파드는 네임스페이스를 제거하면 자동으로 삭제된다.

kubectl delete ns some_namespace

네임스페이스 내부의 모든 리소스 제거

아래 명령으로 네임스페이스 내부 모든 리소스를 제거할 수 있다.

kubectl delete all --all

이 명령은 kubernetes 서비스도 삭제하지만 자동으로 재시작된다.

마치며

3장을 공부하며 쿠버네티스에서 매우 중요한 개념인 파드, 레이블, 네임스페이스를 알 수 있었다.