Kubernetes Static Pods, 제대로 이해하기

2024. 7. 10. 23:28BACKEND/Docker & Kubernetes

 

본 포스팅에서는 Kubernetes의 Static Pod의 개념을 이해하고 생성, 수정 및 삭제할 수 있도록 학습하는 목표를 가집니다.

 

 

🔗  Kubernetes Series

모든 Kubernetes 시리즈를 확인하시려면 위를 참고해 주세요.

 

 

 


 

 

쿠버네티스의 Static Pod에 대해서 알아보도록 하겠습니다.

 

 

 

Static Pods

일반적인 Pod는 주로 Deployment, StatefulSet, DaemonSet 등의 컨트롤러를 통해 생성되고 관리됩니다.

또, 생성된 리소스는 Kubernetes API 서버를 통해 정의되고 관리됩니다.

 

정적 Pod(이하 Static Pod)는 API 서버의 관여없이 특정 노드에서 kubelet 데몬에 의해 직접 관리됩니다.

컨트롤 플레인에 의해 관리되는 일반 Pod와 달리,

kubelet이 각 Static Pod를 관찰하고 실패하면 재실행시킵니다.

 

 

Regular Pod vs. Static Pod

일반 Pod과 Static Pod의 차이점을 조금 더 살펴보도록 하겠습니다.

 

먼저 관리 주체가 다릅니다.

 

✔️ Regular Pod

일반 Pod는 API 서버에 의해 관리되고, 관리를 위한 데이터가 etcd에 저장됩니다.

따라서 kubectl 같은 명령어를 통해 쉽게 조회, 수정, 삭제가 가능합니다.

 

일반 Pod의 경우, kubelet은 kube-apiserver에 의존하여 해당 kubelet이 위치한 노드에 어떤 Pod를 배치할지 지시를 받습니다.

이 지시는 kube-scheduler의 판단에 따라 결정되고, 이후 데이터 저장소에 저장됩니다.

 

 

✔️ Static Pod
Static Pod는 API 서버와 독립적으로 동작합니다.

kubelet은 Static Pod를 감지하고 생성하지만, Static Pod의 정보를 따로 저장하지 않습니다.

 

대신, API 서버는 "Mirror Pod"라는 형태로 조회할 수 있게 합니다.

kubelet는 각 Static Pod에 해당하는 Mirror Pod를 Kubernetes API에 생성을 시도합니다.

 

그래서 Etcd에 저장되어 있지 않아도,

kube-apiserver API 나 kubectl 명령어를 통해 Static Pod 정보를 확인할 수 있습니다.

즉, kube-apiserver API에서 조회는 할 수 있어도 mirror 객체이기 때문에 그 어떤 제어는 불가능합니다.


Static Pod는 항상 특정 노드의 하나의 Kubelet에 바인딩됩니다.

Static Pod 이름은 특정한 규칙이 있는데요.

해당 ≪static_pod_name≫-≪node_name≫ 으로 생성됩니다.

자세한 내용은 마지막 섹션 Regular Pods? Mirror Pods? 을 참고해주세요.

 

 

 

Usage

독립적인 Pod를 띄우고자 할 때 Static Pod를 사용하기 때문에, 

두 종류의 Pod는 사용 목적이 다릅니다.

 

 

✔️ Regular Pod

일반 Pod는 애플리케이션 배포, 스케일링, 업데이트 등의 일반적인 Kubernetes 작업에 사용됩니다.

 

✔️ Static Pod

Static Pod는 주로 클러스터 부팅 과정에서 필수적인 컴포넌트를 실행할 때 사용됩니다.

가령, etcd 이나 kube-apiserver 등이 있죠.

 

각 노드에 필수적으로 존재해야 하는 시스템 컴포넌트를 실행하는 데 적합합니다.

 

만약, Master 없을 때 단일 Node가 독립적으로 실행될 수 있을까요? 

kube-apiserver, kube-scheduler, controllers, etcd cluster 등 리소스를 관리할 컨트롤 플레인이 없을 때, 가능할까요?

결론적으로 말씀드리면,

네, 하나의 Node가 독립적으로 실행될 수 있습니다.

 

 

 

 

How Static Pod Work?

kubelet 데몬으로 실행되기 때문에 노드를 독립적으로 관리할 수 있습니다. 

 

그러나 Pod 상세 데이터를 제공해줄 API 서버가 없습니다. 

어떻게 Pod를 생성 할 수 있을까요?

 

kube-apiserver 없이 Pod 정의 파일을 전달하는 방법은,

kubelet이 Pod 상세 정보를 파일에서 읽어오게 설정할 수 있습니다.

 

https://www.simform.com/blog/kubernetes-architecture/



즉, Pod에 관한 정보를 저장하는 서버 디렉터리에 관리하고, 

kubelet을 설정하면 해당 Pod 정의 파일을 읽을 수 있습니다.


kubelet은 주기적으로 특정 디렉터리 하위의 파일을 읽고 Pod를 생성하며, Pod가 죽지 않도록 보장합니다. 

특정 디렉터리는 아래 Designated Directory 섹션에서 계속 설명합니다.

앱이 고장 나면 kubelet이 재시작을 시도합니다. 

디렉터리 내 파일이 변경되면 kubelet이 Pod를 재생성하여 변경 사항을 적용합니다. 

파일을 제거하면 해당 Static Pod가 자동으로 삭제됩니다.

API 서버나 쿠버네티스 클러스터 구성 요소의 간섭 없이,

kubelet이 스스로 만든 Pod를 정적 Pod라고 합니다. 

 

kubelet은 오직 Pod에 대한 내용만 알기 때문에, 

ReplicaSet, Deployment, Service를 통해 배치될 수 없고, 

지정된 디렉토리에 정의 파일을 배치하는 방식으로만 Pod를 만들 수 있습니다.

 

 

 

Designated Directory

그렇다면, kubelet이 Static Pod를 생성하기 위해 참조하는 폴더는 어디에 존재하며 어떻게 구성할까요?

 

Static Pod 의 Manifest (이하 매니페스트) 파일들을 관리하는 폴더는

기본적으로 /etc/kubernetes/manifests 폴더로 설정되어 있습니다.

여기서 매니페스트 파일이란, 특정 디렉터리에 있는 JSON 이나 YAML 형식의 표준 Pod 정의를 의미합니다.

 

가령, 아래와 같이 Control Plane 컴포넌트 중,

기본적으로 Static Pod 로 띄우는 파일들이 저장되어 있음을 확인할 수 있습니다.

 

controlplane ~ ➜  ll /etc/kubernetes/manifests
total 28
drwxrwxr-x 1 root root 4096 Jul 10 13:30 ./
drwxrwxr-x 1 root root 4096 Jul 10 13:30 ../
-rw------- 1 root root 2400 Jul 10 13:30 etcd.yaml
-rw------- 1 root root 3877 Jul 10 13:30 kube-apiserver.yaml
-rw------- 1 root root 3393 Jul 10 13:30 kube-controller-manager.yaml
-rw-r--r-- 1 root root    0 Apr 17 18:31 .kubelet-legacy.yaml
-rw------- 1 root root 1463 Jul 10 13:30 kube-scheduler.yaml

 

 

Kubelet은 해당 디렉터리를 정기적으로 스캔하며,

디렉터리 안의 YAML/JSON 파일이 생성되거나 삭제되었을 때 Static Pod를 생성하거나 삭제합니다.

 

참고로, Kubelet 이 특정 디렉터리를 스캔할 때 점(.)으로 시작하는 단어를 무시합니다.

예시를 위해 위에 추가 해둔 .kubelet-legacy.yaml 은 무시됩니다.

 

 

이 때, Kubelet이 참조할 디렉터리를 변경하고 싶다면 아래의 두 옵션을 통해 수정할 수 있습니다.

 

 

 

✔️ Kubelet config - staticPodPath

kubelet 을 설정할 때 kubelet 설정 파일에 staticPodPath 옵션으로 원하는 위치를 지정할 수 있습니다.

가령, kubelet이 아래와 같이 실행되었을 때, 

 

$ ps -ef |  grep /usr/bin/kubelet
root        4345  0.0  0.0 4357904 94820 ?       Ssl  13:30   0:11 /usr/bin/kubelet \
--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf \
--kubeconfig=/etc/kubernetes/kubelet.conf \
--config=/var/lib/kubelet/config.yaml \
--container-runtime-endpoint=unix:///var/run/containerd/containerd.sock \
--pod-infra-container-image=registry.k8s.io/pause:3.9
...

 

설정 파일인 --config=/var/lib/kubelet/config.yaml 을 살펴보면, 

 

$ cat /var/lib/kubelet/config.yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
...
staticPodPath: /etc/kubernetes/manifests
...

 

위와 같이 설정된 staticPodPath 를 확인할 수 있습니다.

Kubelet의 구성 파일은 🔗 Kubelet Config 에서 자세히 확인할 수 있습니다.

 

 

 

 

✔️ --pod-manifest-path option

혹은, 권장하지는 않지만, 명령행의 인자로 넘겨줄 수 있습니다.

 

--pod-manifest-path=/etc/kubernetes/manifests

 

호스트의 어떤 디렉터리든 될 수 있으며,

디렉터리 위치는 서비스를 실행하는 동안 kubelet에 옵션으로 전달됩니다.

 

이처럼 Kubelet이 특정 위치에서 Static Pod를 불러오기 때문에,

Static Pod을 생성할 때는 가장 먼저 kubelet 옵션을 살펴볼 필요가 있습니다.

 

 

 

 

 

Create a Static Pod

가장 간단하게 Static Pod를 생성하는 방법은

pod 실행 yaml 를 Kubelet이 참조하는 staticPodPath에 저장하면 됩니다.

 

가령, 아래와 같은 단일 명령어로 말이죠.

 

$ kubectl run --restart=Never \
    --image=busybox static-busybox \
    --dry-run=client -o yaml \
    --command -- sleep 1000 \
    > /etc/kubernetes/manifests/static-busybox.yaml

 

 

해당 명령어를 입력한 후, 아래와 같이 저장된 것을 확인할 수 있습니다.

 

controlplane ~ ➜  cat /etc/kubernetes/manifests/static-busybox.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: static-busybox
  name: static-busybox
spec:
  containers:
  - command:
    - sleep
    - "1000"
    image: busybox
    name: static-busybox
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Never
status: {}

 

 

두 번째 방법은 아래와 같이 일반 Pod 파일을 생성하는 것입니다.

 

원하는 Static Pod Yaml Spec을 작성해서 위 위치에 저장해주면,

Kubelet이 주기적으로 읽어서 업데이트 할 때 Pod가 올라갑니다.

 

# /etc/kubernetes/manifests/static-web.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    role: myrole
spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
          protocol: TCP

 

 

그럼, 이번엔 생성한 Static Pod를 확인해보겠습니다.

 

 

 

 

Observe Static Pods

먼저, Static Pod를 확인하기 위해서 Container Runtime의 명령어를 사용할 수 있습니다.

Kubectl 명령어나 kube-serverapi 가 없이도 확인할 수 있다는 의미입니다.

 

아래와 같이 확인합니다.

 

✔️ Docker: docker ps 
✔️ cri-o: crictl ps
✔️ containerd: nerdctl ps

 

controlplane ~ ➜  crictl ps
CONTAINER           IMAGE               CREATED             STATE               NAME                      ATTEMPT             POD ID              POD
7bfd3f585b3a9       fffffc90d343c       2 minutes ago       Running             web                       0                   cb6e2e86b0ccf       static-web-controlplane
8255080953cff       65ad0d468eb1c       10 minutes ago      Running             static-busybox            0                   a6edc266133b3       static-busybox-controlplane
...

 

 

 

 

Modify / Delete a Static Pod

그럼, Static Pod 를 수정 하거나 삭제하려면 어떻게 해야할까요?

kubectl edit 명령어나 그 밖에 떠 있는 Pod에서는 수정하거나 삭제할 수 없습니다.

Mirror 객체이기 때문이죠.

 

반드시 Kubelet이 읽어오는 Static Pod 파일 자체를 수정해야합니다.

가령, 위의 예시 중 static-busybox 의 sleep 시간을 500으로 변경하고 싶다면 아래와 같이 수정합니다.

 

$ kubectl run --restart=Never \
    --image=busybox static-busybox \
    --dry-run=client -o yaml \
    --command -- sleep 500 \
    > /etc/kubernetes/manifests/static-busybox.yaml

 

 

단순히 오버라이딩하는 것으로 수정이 완료 됩니다. 

삭제할 때도 동일합니다.

해당 Static Pod 정의 파일 자체를 삭제하면 됩니다.

 

$ rm /etc/kubernetes/manifests/static-busybox.yaml

 

 

 

 

 

General Pods ? Mirror Pods?

일반 Pod와 Static Pod 로 생성된 인스턴스인 Mirror Pod는 어떻게 구분할까요?

두 가지 방법이 있습니다.

 

 

✔️ 1. 이름을 통한 추측

Static Pod는 생성 시, 이름 뒤에 노드 이름이 붙습니다.

위에서 언급했다시피, ≪static_pod_name≫-≪node_name≫ 의 포맷으로 생성됩니다.

 

Ex: etcd-controlplane, kube-apiserver-controlplane, kube-controller-manager-controlplane, kube-scheduler-controlplane

 

컨트롤 플레인에 존재하는 Static Pod 를 살펴보면 쉽게 이해할 수 있을 텐데요.

 

❯ k get nodes
NAME                 STATUS   ROLES           AGE   VERSION
kind-control-plane   Ready    control-plane   59d   v1.29.2

❯ kubectl get pods -A
NAMESPACE            NAME                                         READY   STATUS    RESTARTS      AGE
kube-system          coredns-76f75df574-l6tsh                     0/1     Running   1 (11s ago)   59d
kube-system          coredns-76f75df574-s787j                     0/1     Running   1 (11s ago)   59d
kube-system          kube-apiserver-kind-control-plane            0/1     Running   1 (11s ago)   59d
kube-system          kube-controller-manager-kind-control-plane   0/1     Running   1 (11s ago)   59d
kube-system          kube-proxy-8v42q                             1/1     Running   1 (11s ago)   59d
kube-system          kube-scheduler-kind-control-plane            0/1     Running   1 (11s ago)   59d
...

 

 

먼저, kube-scheduler 나 kube-apiserver 등의 경우, 하이픈과 노드 이름 kind-control-plane 이 붙은 것을 확인할 수 있습니다.

kube-scheduler-kind-control-plane, kube-apiserver-kind-control-plane

→ Static Pod

 

반면, kube-proxy나 coredns의 경우, 하이픈 뒤 랜덤 문자열이 붙은 것을 확인할 수 있습니다.

kube-proxy-8v42q, coredns-76f75df574-l6tsh, coredns-76f75df574-s787j

→ 일반 Pod

 

 

 

 

✔️ 2. Pod 상세 조회

 

Static Pod의 경우네는 .metadata.ownerReferences.kindNode 로 표기됩니다.

가령, 아래 kube-controller-manager 를 확인해보면,

 

controlplane ~ ➜  kubectl get pods kube-controller-manager-controlplane -n kube-system -o yaml
apiVersion: v1
kind: Pod
metadata:
  ...
  name: kube-controller-manager-controlplane
  namespace: kube-system
  ownerReferences:
    - apiVersion: v1
      controller: true
      kind: Node
      name: controlplane
      uid: 07cf24ac-9926-4125-9f64-f501d99ddd95

 

kind가 Node 인 것을 확인할 수 있습니다.

→ Static Pod

 


반면, Control Plane을 통해 관리되는 Pod는 .metadata.ownerReferences.kindReplicaSet 와 같이 Node가 아닙니다.

 

controlplane ~ ➜  kubectl get pods coredns-69f9c977-8v7km -n kube-system -o yaml 
apiVersion: v1
kind: Pod
metadata:
  ...
  name: coredns-69f9c977-8v7km
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: coredns-69f9c977
    uid: 2d54c431-233c-4fdc-9734-f90bb46dcdc5

 

 

위와 같이 kind가 ReplicaSet인 것을 확인할 수 있습니다.

→ 일반 Pod