Field Selector & Finalizers

본 포스팅은 Kubernetes Official Documents를 기반으로 한다.

 

필드 셀렉터

필드 셀렉터 는 한 개 이상의 리소스 필드 값에 따라 쿠버네티스 리소스를 선택하기 위해 사용된다. 필드 셀렉터 쿼리의 예시는 다음과 같다. metadata.name=my-service metadata.namespace!=default status.phase=Pe

kubernetes.io


# Field Selector

필드 셀렉터는 한 개 이상의 리소스 필드 값에 따라 쿠버네티스 리소스를 선택하기 위해 사용된다. 필드 셀력터 쿼리의 예시는 아래와 같다.

 

  • metadata.name=my-service
  • metadata.namespace!=default
  • status.phase=Pending

다음의 kubectl 커맨드는 status.phase 필드의 값이 Running 인 모든 파드를 선택한다.

kubectl get pods --field-selector status.phase=Running
참고: 필드 셀렉터는 본질적으로 리소스 필터 이다. 기본적으로 적용되는 셀렉터나 필드는 없으며, 이는 명시된 종류의 모든 리소스가 선택된다는 것을 의미한다. 여기에 따라오는 kubectl 쿼리인 kubectl get pods 와 kubectl get pods --field-selector "" 는 동일하다.

사용 가능한 필드

사용 가능한 필드는 쿠버네티스의 리소스 종류에 따라서 다르다. 모든 리소스 종류는 metadata.name  metadata.namespace 필드 셀렉터를 사용할 수 있다. 사용할 수 없는 필드 셀렉터를 사용하면 다음과 같이 에러를 출력한다.

Error from server (BadRequest): Unable to find "ingresses" that match label selector "", field selector "foo.bar=baz": "foo.bar" is not a known field selector: only "metadata.name", "metadata.namespace"

사용 가능한 연산자

필드 셀렉터에서 =, ==, != 연산자를 사용할 수 있다 (= ==는 동일하다).

 

예를 들면, 다음의 kubectl 커맨드는 default 네임스페이스에 속해있지 않은 모든 쿠버네티스 서비스를 선택한다.

kubectl get services  --all-namespaces --field-selector metadata.namespace!=default

연계되는 셀렉터

레이블을 비롯한 다른 셀렉터처럼, 쉼표로 구분되는 목록을 통해 필드 셀렉터를 연계해서 사용할 수 있다.

 

다음의 kubectl 커맨드는 status.phase 필드가 Running 이 아니고, spec.restartPolicy 필드가 Always 인 모든 파드를 선택한다.

kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always

여러 개의 리소스 종류

필드 셀렉터를 여러 개의 리소스 종류에 걸쳐 사용할 수 있다.

 

다음의 kubectl 커맨드는 default 네임스페이스에 속해있지 않은 모든 스테이트풀셋(StatefulSet)과 서비스를 선택한다.

kubectl get statefulsets,services --all-namespaces --field-selector metadata.namespace!=default

# Finalizers(파이널라이저)

파이널라이저는 쿠버네티스가 오브젝트를 완전히 삭제하기 이전, 삭제 표시를 위해 특정 조건이 충족될 때까지 대기하도록 알려주기 위한 네임스페이스에 속한 키이다.

파이널라이저는 삭제 요청(완료)된 오브젝트가 소유한 리소스를 정리하기 위해 컨트롤러에게 알린다.

파이널라이저를 가진 특정한 오브젝트를 쿠버네티스가 삭제하도록 지시할 때, 쿠버네티스 API는 `.metadata.deletionTimestamp` 을 덧붙여 삭제하도록 오브젝트에 표시하며, 202 상태코드(HTTP "Accepted")를 리턴한다.

대상 오브젝트가 Terminating 상태를 유지하는 동안 컨트롤 플레인 또는 다른 컴포넌트는 하나의 파이널라이저에서 정의한 작업을 수행한다. 정의된 작업이 완료된 후에, 그 컨트롤러는 대상 오브젝트로부터 연관된 파이널라이저를 삭제한다.
metadata.finalizers 필드가 비어 있을 때, 쿠버네티스는 삭제가 완료된 것으로 간주하고 오브젝트를 삭제한다.

파이널라이저를 사용하면 리소스를 삭제하기 전 특정 정리 작업을 수행하도록 컨트롤러에게 경고하여 리소스의 Garbage 수집을 제어할 수 있다.

파이널라이저는 보통 실행할 코드를 지정하지 않는다. 대신 파이널라이저는 일반적으로 어노테이션과 비슷하게 특정 리소스들에 대한 키들의 목록이다. 일부 파이널라이저는 쿠버네티스가 자동으로 지정하지만, 사용자가 직접 지정할 수도 있다.


# 파이널라이저 동작 방식

매니페스트 파일을 사용해 리소스를 생성하면 metadata.finalizers 필드에 파이널라이저를 명시할 수 있다. 리소스를 삭제하려 할 때는 삭제 요청을 처리하는 API 서버가 finalizers 필드의 값을 인식하고 다음을 수행한다.

  • 삭제를 시작한 시각과 함께 metadata.deletionTimestamp 필드를 추가하도록 오브젝트를 수정한다.
  • 오브젝트의 metadata.finalizers 필드가 비워질 때까지 오브젝트가 제거되지 않도록 한다.
  • 202 상태 코드를 리턴한다(HTTP "Accepted").

이 파이널라이저를 관리하는 컨트롤러는 metadata.deletionTimestamp를 설정하는 오브젝트가 업데이트 되었음을 인지하여 오브젝트의 삭제가 요청되었음을 나타낸다.

 

그런 다음 컨트롤러는 그 리소스에 지정된 파이널라이저의 요구사항을 충족하려 시도한다. 컨트롤러는 파이널라이저 조건이 충족될 때 마다 리소스의 finalizers 필드에서 해당 키(key)를 제거한다. finalizers 필드가 비워지면 deletionTimestamp 필드가 설정된 오브젝트는 자동으로 삭제된다.

 

또한 파이널라이저를 사용하여 관리되지 않는 리소스가 삭제되지 않도록 할 수 있다.

 

파이널라이저의 일반적인 예로는 퍼시스턴트 볼륨(Persistent Volume) 오브젝트가 실수로 삭제되는 것을 방지하는 kubernetes.io/pv-protection가 있다.

 

파드가 퍼시스턴트 볼륨 오브젝트를 사용 중일 때 쿠버네티스는 pv-protection 파이널라이저를 추가한다. 퍼시스턴트 볼륨을 삭제하려 하면 Terminating 상태가 되지만 파이널라이저가 존재하기 때문에 컨트롤러가 삭제할 수 없다.

 

파드가 퍼시스턴트 볼륨의 사용을 중지하면 쿠버네티스가 pv-protection 파이널라이저를 해제하고 컨트롤러는 볼륨을 삭제한다.


# 소유자 참조, 레이블, 파이널라이저

레이블(Label)와 마찬가지로 쿠버네티스에서 소유자 참조(Owner reference)는 오브젝트 간의 관계를 설명하지만 다른 목적으로 사용된다. 

 

컨트롤러(Controller)가 파드와 같은 오브젝트를 관리할 때 레이블을 사용하여 관련 오브젝트의 그룹에 대한 변경 사항을 추적한다.

 

예를 들어 잡(Job)이 하나 이상의 파드를 생성하면 잡 컨트롤러는 해당 파드에 레이블을 적용하고 클러스터 내 동일한 레이블을 갖는 파드에 대한 변경 사항을 추적한다.

 

또한, 잡 컨트롤러는 이러한 파드에 소유자 참조도 추가하여 파드를 생성한 잡을 가리킨다.

 

이 파드가 실행될 때 잡을 삭제하면 쿠버네티스는 사용자 참조(레이블 대신)를 사용하여 클러스터 내 어떤 파드가 정리되어야 하는지 결정한다.

 

쿠버네티스는 또한 삭제 대상 리소스에 대한 소유자 참조를 식별할 때 파이널라이저를 처리한다.

 

경우에 따라 파이널라이저는 종속 오브젝트의 삭제를 차단할 수 있으며 이로 인해 대상 소유자 오브젝트가 완전히 삭제되지 않고 예상보다 오래 유지될 수 있다.

 

이 경우 대상 소유자 및 종속 객체에 대한 파이널라이저와 소유자 참조를 확인해 원인을 해결해야 한다.

침고: 오브젝트가 삭제 상태에 있는 경우, 삭제를 계속하려면 파이널라이저를 수동으로 제거해서는 안 된다. 일반적으로 파이널라이저는 특정한 목적으로 가지고 리소스에 추가되므로, 강제로 제거하면 클러스터에 문제가 발생할 수 있다. 이는 파이널라이저의 목적을 이해하고 다른 방법(예를 들어, 일부 종속 객체를 수동으로 정리하는 것)으로 수행될 때만 수행해야 한다.

첨언.

라벨 셀렉터를 통해서, 특정 리소스들의 필터링이 가능하기 때문에, kubectl {verbs} -l 명령어를 통해서 특정 디플로이에 속한 파드의 로그를 모두 가져오거나, 별도의 라벨을 부여해서 쿠버네티스 객체들을 일괄적으로 관리할 수 있다.

 

실제로, logs를 볼 때 매번 특정 파드 명을 지정해서 로그를 확인했었는데, 라벨 셀렉터를 사용하여 특정 디플로이먼트의 레플리카에 속하 파드의 로그를 한번에 확인할 수 있다.


특정 파드를 롤링 업데이트를 수행할 때, Terminating 상태를 간간히 마주할 수 있었다.

 

이때 파이널라이저 조건을 바탕으로, 해당 파드가 종료 요청을 받았을 때 수행해야할 작업을 진행 중인 상태라는 것을 이해할 수 있었다.

 

파드나 다른 객체들이 파이널라이저를 통해서 Terminating 상태에서 수행하는 일을 지정하거나, 함부로 삭제되지 못하게 만들 수 있고, 이러한 방식은 Linux System Call을 지정해서 처리할 작업을 하고 프로세스를 종료하는 것과 유사하다고 느껴졌다.

 

카카오 시니어 분께서 오셔서, 현업에서는 특정 프로세스를 종료시킬 때 해당 시스템 콜을 사용하여, 종료되기 전에 특정 로직을 수행해야하는 상황이 존재하고, 이런 처리가 필수적이라고 말씀해 주셨다.

 

데이터베이스를 예시로 들어주셨다. 만약 데이터베이스 프로세스의 삭제를 요청할 경우, 현재 처리중이었던 트랜잭션들을 모두 마무리를 해야 데이터의 일관성이 유지된다. 이러한 로직을 데이터베이스 프로세스에 특정 시스템 콜이 들어올 경우 수행하도록 동작하게 만들어야한다.

 

시스템 콜에 대해서는 따로 공부를 해야겠다.

728x90
반응형