EMD Blog

Kubernetes Object 본문

Kubernetes

Kubernetes Object

EmaDam 2022. 9. 3. 10:53

오브젝트

개요에서 간단하게 언급했는데 쿠버네티스는 클러스터에서 파드나 서비스 등(오브젝트의 종류는 아주 다양함)을 구성할 때 이 오브젝트라는 것을 사용한다고 했다. 그리고 클러스터는 오브젝트에 기술되어 있는대로 클러스터의 상태를 유지하려 하기 때문에 이 오브젝트는 쿠버네티스 클러스터의 상태를 나타낸다고 할 수도 있다.

이 오브젝트는 그냥 무언가를 생성한다기 보다는 어떠한 의도를 담았다고 보는게 좋다. 예를 들면 아래와 같다.

  • 어떤 컨테이너화된 애플리케이션이 동작 중인지 (그리고 어느 노드에서 동작 중인지)
  • 그 애플리케이션이 이용할 수 있는 리소스
  • 그 애플리케이션이 어떻게 재구동 정책, 업그레이드, 그리고 내고장성과 같은 것에 동작해야 하는지에 대한 정책

위 내용은 문서에 있는 내용인데 대충 파드 오브젝트에 대한 내용으로 보인다. 중요한 것은 오브젝트를 생성할 때 단순하게 리소스를 추가하는 것이 아니라 것이다.

이렇게 오브젝트에 본인이 의도한 상태를 잘 기술했다면 쿠버네티스 API를 사용해서 쿠버네티스에게 전달하면 된다.

명세(spec)와 상태(status)

먼저 오브젝트 생성하려면 아래처럼 오브젝트를 기술해야 한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

대충 해석하면 apps API 그룹의 v1(버전)을 사용하고 있고 오브젝트 종류(kind)는 Deployment이다. metadata는 아직 안배웠으니 넘어가면 spec이 나오는데 내용을 잘 모르더라도 대충 보면 어떤 컨테이너를 몇 개나 만들지 적혀있는 것처럼 보인다. 즉 오브젝트의 의도한 상태를 spec에다가 기술한 것인데, 이렇게 기술해서 쿠버네티스 API를 사용해 전달하면 컨트롤 플레인은 이 spec에 기술한 의도에 맞게 지속적으로 관리한다.

그럼 컨트롤 플레인은 어떻게 의도한 상태로 유지 시킬까? 저 위 코드를 API로 전달하면 전달한 오브젝트 코드에 status필드가 추가로 생성된다. 이 status 필드에는 위 spec을 기준으로 구성된 오브젝트의 현재 상태를 나타내는데 컨트롤 플레인은 status와 spec을 비교해 의도대로 오브젝트가 구성되어 있는지 확인한 뒤 그렇지 않다면 의도에 맞게 관리한다. 간단하게 예를 들면 위 코드에는 2개의 파드를 실행(replicas: 2)하라고 되어 있는데 현재 실행되고 있는 파드가 1개(status 필드에는 1개)라면 파드를 1개 더 실행시키게 되는 것이다. (참고로 status는 실제 상태를 계속 반영한다. 위 경우 중간에 파드가 한개 중단된다면 status에도 replicas 값이 줄어든다.)

오브젝트와 API 동작방식에 대해 더 자세히 알고 싶다면 아래 문서를 참고

오브젝트 기술

위에서 봤던 코드처럼 오브젝트를 기술하면 쿠버네티스 API 요청해서 오브젝트를 생성해야 한다. API 요청은 kubectl과 같은 CLI를 사용하거나 필요에 따라 클라이언트 라이브러리 같은 것을 사용하면 된다. 위 코드는 오브젝트를 yaml 형식으로 작성했지만 사실 API에 요청하기 위해서는 json형태로 전달해야 한다. 하지만 대부분 오브젝트 생성 API 요청에 kubectl을 사용하는데 이때 kubectl은 yaml 형식의 오브젝트를 json으로 변환해서 API를 요청해준다.

위 yaml 파일(오브젝트)과 kubectl을 사용해 오브젝트를 생성하려면 아래처럼 하면 된다.

kubectl apply -f <https://k8s.io/examples/application/deployment.yaml> --record

요구되는 필드

위에서 봤던 코드 중 metadata는 잘 몰라서 그냥 넘어간 필드도 있었는데 다시 정리해보면 아래와 같다.

  • apiVersion: 쿠버네티스 API Version (API 그룹/Version 형태로 작성하지만 핵심 API의 경우 그룹 없이 v1으로 표기)
  • kind: 오브젝트 종류
  • metadata: 이름, UID, 네임스페이스와 같이 오브젝트를 유일하게 구분해줄 수 있는 필드
  • spec: 오브젝트에 대해 어떤 상태를 의도하는지 기술하는 필드

spec 필드에 기술되어야 하는 포맷이나 값은 오브젝트마다 다르기 때문에 API Reference를 확인해가면서 작성하면 된다.

관리

쿠버네티스 오브젝트를 관리하는 방법은 아래처럼 3가지가 존재한다.

  • 명령형 커맨드
  • 명령형 오브젝트 구성
  • 선언형 오브젝트 구성

여러 방법을 혼용하면 예상치 못한 동작을 초래할 수 있기 때문에 이중 한 가지 방법만을 사용해서 관리해야 한다.

명령형 커맨드

이 방식은 kubectl을 사용해서 오브젝트를 직접 활성화 시키거나 관리한다. 무슨 말이냐 하면 위에서는 오브젝트를 yaml 파일로 만들어서 파일을 기반으로 오브젝트를 활성화 시키거나 관리했는데 이 방식을 사용하면 아래처럼 명령어로 오브젝트를 기술한다.

kubectl create deployment nginx --image nginx

위 명령어는 deployment 오브젝트를 생성하는데 nginx 이미지를 사용해서 컨테이너를 띄우겠다는 뜻이다.

보통은 저렇게 create 명령어로 생성을 하지만 일반적인 오브젝트는 아래처럼 동사 형태로 심플하게 생성할 수 있다.

  • run : 파드 오브젝트 생성
  • expose: 서비스 오브젝트 생성
  • Autoscaler 오브젝트 생성

일반적인 create 명령어의 형태는 아래와 같다.

kubectl create <오브젝트 타입(kind)> [<서브 타입>] <인스턴스 명>

오브젝트 업데이트는 어떻게 할까? 보통은 명령어로 업데이트 할 수 있다.

set <field>

말 그대로 원하는 오브젝트의 원하는 field를 설정하는 명령어이다. 또한 오브젝트를 생성할 때와 마찬가지로 일반적인 필드는 동산형태의 명령어를 제공하고 있다.

  • scale: 레플리카 수 업데이트
  • annotate: 어노테이션 추가/제거
  • label: 라벨 추가/제거

만약 오브젝트 스키마에 대한 이해도가 조금 있다면(위에서 본 yaml 파일과 같은 구성 파일을 작성할 줄 안다면) 명령어 대신 구성을 직접 편집 할 수도 있다.( 당연한 거지만 명령어로 생성했더라도 구성은 존재한다. 명령어 방식은 굳이 구성을 만지지 않아도 생성이 가능한 방식일 뿐이다.)

  • edit: 편집기에서 구성을 바로 편집
  • patch: 편집기를 사용하지 않고 여러 필드를 수정(reference)
    • 변경할 필드를 yaml/json 형식으로 전달 받는데 중요한 것은 병합한다는 것이다. 이 말은 일부 필드를 바꾼답시고 전체 구성을 지정할 필요 없이 변경할 필드 구성만 전달하면 된다. 사용 예시는 여기

오브젝트 삭제는 아래 명령어를 사용하면 된다.

kubectl delete <오브젝트 타입>/<인스턴스 명>

타입까지 지정해주는 이유는 오브젝트의 이름은 타입이 다르면 중복이 가능하기 떄문이다.

오브젝트를 확인하려면 아래와 같은 명령어를 사용한다.

  • get : 일치하는 오브젝트에 대한 기본 정보 출력
  • describe : 오브젝트 상세 정보 출력
  • logs : 실행 중인 컨테이너의 log 출력

create 명령어로만 지정할 수 없는 몇가지 필드가 존재한다. 이 필드를 지정하고 싶다면 create와 set을 조합해서 사용 가능하다. 대략적인 순서는 create 구성생성 → 파이프(|)로 받아서 구성 변경 → 변경된 구성을 기반으로 오브젝트 생성이다.

kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client | kubectl set selector --local -f - 'environment=qa' -o yaml | kubectl create -f -

위 명령어를 파이프 단위로 나눠서 보면 아래와 같다.

  1. kubectl create service clusterip my-svc —clusterip=”None” -o yaml —dry-run=client는 service에 대한 구성을 생성한다. 하지만 --dry-run 옵션을 사용해서 구성을 API로 요청하는 것이 아닌 출력시킨다.
  2. 위에서 파이프(|)를 통해 service의 구성을 전달 받았다. kubectl set selector —local -f - ‘environment=qa’ -o yaml 은 전달 받은 구성에서 label로 environment=qa을 설정한다.
  3. 위에서 설정한 구성을 다시 파이프로 전달 받아서 오브젝트를 생성한다.

위와 비슷한 느낌으로 —edit 옵션도 활용 가능하다.

kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client > /tmp/srv.yaml
kubectl create --edit -f /tmp/srv.yaml
  1. service 구성을 생성하는데 똑같이 —dry-run을 사용해서 구성을 /tmp/srv.yaml로 내보낸다(>).
  2. ceate에 option 명령어를 사용해서 오브젝트 생성전 구성을 편집한다.

명령형 오브젝트 구성

위랑 똑같이 명령형이지만 이 방식은 구성이 들어간다. 이 말은 똑같이 명령어를 사용하지만 구성 파일을 사용해서 오브젝트를 관리한다는 뜻이다. 예를 들어 오브젝트를 생성한다고 하면

kubectl create -f <파일명|url>

이런 형식이다.

사실 명령형 커맨드 방식과 사용 방법이 거의 동일하기 때문에 명령형 커맨드와 비슷한 방식으로 사용하면 된다.

명령형 오브젝트 구성 방식을 사용할 경우 오브젝트는 구성 파일에 맞게 생성되고 업데이트 된다. 때문에 오브젝트가 생성된 후 구성 파일에 의해 업데이트 된 것이 아닌 외부 요소(ex HorizontalPodAutoscaler)에 의해 오브젝트가 업데이트 될 경우 해당 변경 사항을 구성 파일에 적용하지 않으면 이후 구성 파일을 적용할 때 기존 업데이트 내용(외부 요소에 의한 업데이트 내용)은 삭제된다.

참고로 구성파일은 URL로도 지정할 수 있다. 만약 구성 파일 내용을 편집하고 싶다면 —edit 옵션을 사용하면 된다.

kubectl create -f <url> --edit

만약 명령형 커맨드로 생성한 오브젝트를 구성 파일로 관리하고 싶다면 아래 절차를 따르면 된다.

  1. 생성된 오브젝트를 구성 파일로 내보낸다.
  2. 생성된 구성 파일에서 status 필드를 제거한다.
  3. 오브젝트를 replace 한다.

위 과정을 코드로 작성하면 아래처럼 작성할 수 있다.

# 생성된 오브젝트를 구성 파일로 내보내기
kubectl get <kind>/<name> -o yaml > <type>_<name>.yaml

# 구성 파일에서 status 필드 제거
vi <type>_<name>.yaml

# 오브젝트 replace 
kubectl replace -f <type>_<name>.yaml

선언적 오브젝트 구성

선언적 오브젝트 구성 파일로 오브젝트를 관리하기는 하나 구성을 어떻게 할 것인지 사용자가 지정하지는 않는다. 위 두 명령어 방식의 경우 오브젝트를 create, replace, delete 등의 명령어로 동작을 직접 지정해준데 반에 선언적 오브젝트 구성 방식은 kubectl로 오브젝트를 자동으로 감지한다.

선언형 오브젝트 구성방식의 가장 큰 특징 중 하나는 오브젝트 구성 변경으로 replace API가 아닌 patch API를 사용한다는 점이다. 이는 오브젝트를 새로 생성하지 않고 활성 오브젝트의 인지되는 부분만 변경하기 때문에 외부에서 활성 오브젝트를 변경 했더라도 그 변경 사항이 유지된다.

특정 디렉터리 내 오브젝트 구성 파일들을 기준으로 활성 오브젝트를 생성하고나 패치하고 싶다면 아래 명령어를 사용한다.

# configs 디렉토리 내 오브젝트 구성 파일 apply
# -R 옵션을 추가하면 재귀적(하위 디렉터리 까지)으로 처리
kubectl apply -f configs/

apply 전에 어떠한 변경사항이 발생하는 지 확인하고 싶다면 아래 명령어를 사용한다.

# 재귀적으로 처리하고 싶다면 -R 옵션 추가
kubectl diff -f configs/