DaemonSet

DaemonSet (daemonset)

출처: 쿠버네티스 공식 문서 — DaemonSet

DaemonSet은 노드-로컬(node-local) 기능을 제공하는 Pod를 정의해요. 이런 Pod는 네트워킹 도우미 도구처럼 클러스터 운영에 기본이 될 수도 있고, 애드온(add-on)의 일부일 수도 있어요.

DaemonSet은 모든(또는 일부) Node가 Pod의 복사본을 하나씩 실행하도록 보장해요. 노드가 클러스터에 추가되면 그 노드에도 Pod가 추가돼요. 노드가 클러스터에서 제거되면 그 Pod들은 가비지 컬렉션돼요. DaemonSet을 삭제하면 DaemonSet이 만든 Pod들도 정리돼요.

DaemonSet의 몇 가지 전형적인 용도는 다음과 같아요:

  • 모든 노드에서 클러스터 스토리지 데몬 실행
  • 모든 노드에서 로그 수집 데몬 실행
  • 모든 노드에서 노드 모니터링 데몬 실행

단순한 경우, 모든 노드를 덮는 하나의 DaemonSet이 데몬 유형 하나당 사용돼요. 더 복잡한 설정은 데몬 유형 하나에 여러 DaemonSet을 사용할 수도 있는데, 하드웨어 유형에 따라 서로 다른 플래그나 서로 다른 메모리·CPU 요청을 갖도록 하는 식이에요.

DaemonSet 스펙 작성하기 (Writing a DaemonSet Spec)

DaemonSet 생성 (Create a DaemonSet)

DaemonSet은 YAML 파일로 설명할 수 있어요. 예를 들어 아래 daemonset.yaml 파일은 fluentd-elasticsearch Docker 이미지를 실행하는 DaemonSet을 설명해요:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # these tolerations are to have the daemonset runnable on control plane nodes
      # remove them if your control plane nodes should not run pods
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v5.0.1
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
      # it may be desirable to set a high priority class to ensure that a DaemonSet Pod
      # preempts running Pods
      # priorityClassName: important
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log

YAML 파일을 기반으로 DaemonSet을 생성하려면:

kubectl apply -f https://k8s.io/examples/controllers/daemonset.yaml

필수 필드 (Required Fields)

다른 모든 쿠버네티스 구성과 마찬가지로 DaemonSet도 apiVersion, kind, metadata 필드가 필요해요. 구성 파일 작업에 대한 일반적인 정보는 stateless 애플리케이션 실행kubectl을 사용한 오브젝트 관리를 참고하세요.

DaemonSet 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 해요.

DaemonSet은 .spec 섹션도 필요해요.

Pod 템플릿 (Pod Template)

.spec.template.spec의 필수 필드 중 하나예요.

.spec.templatepod 템플릿이에요. Pod와 정확히 같은 스키마를 가지는데, 중첩되어 있고 apiVersion이나 kind가 없다는 점만 달라요.

Pod의 필수 필드에 더해, DaemonSet의 Pod 템플릿은 적절한 라벨을 지정해야 해요(pod selector 참고).

DaemonSet의 Pod 템플릿은 RestartPolicyAlways와 같아야 하며, 아니면 미지정(기본값 Always)이어야 해요.

Pod Selector

.spec.selector 필드는 pod selector예요. Job.spec.selector와 같은 방식으로 동작해요.

.spec.template의 라벨과 매칭되는 pod selector를 지정해야 해요. 또한 DaemonSet이 생성되면 .spec.selector는 변경(변형)될 수 없어요. Pod selector를 변경하면 의도치 않게 Pod가 고아(orphan)가 될 수 있고, 사용자에게 혼란을 준다는 것이 밝혀졌어요.

.spec.selector는 두 필드로 구성된 오브젝트예요:

  • matchLabelsReplicationController.spec.selector와 같은 방식으로 동작.
  • matchExpressions — 키, 값 목록, 키와 값을 연관짓는 연산자(operator)를 지정해서 더 정교한 selector를 만들 수 있게 함.

둘 다 지정하면 결과는 AND 연산돼요.

.spec.selector.spec.template.metadata.labels와 매칭되어야 해요. 이 둘이 매칭되지 않는 구성은 API가 거부해요.

선택된 노드에서 Pod 실행 (Running Pods on select Nodes)

.spec.template.spec.nodeSelector를 지정하면 DaemonSet 컨트롤러는 그 노드 selector와 매칭되는 노드에 Pod를 생성해요. 마찬가지로 .spec.template.spec.affinity를 지정하면 DaemonSet 컨트롤러는 그 노드 어피니티와 매칭되는 노드에 Pod를 생성해요. 둘 다 지정하지 않으면 DaemonSet 컨트롤러는 모든 노드에 Pod를 생성해요.

Daemon Pod가 스케줄되는 방식 (How Daemon Pods are scheduled)

DaemonSet은 모든 적격 노드가 Pod의 복사본을 실행하도록 보장하는 데 사용될 수 있어요. DaemonSet 컨트롤러는 각 적격 노드에 대해 Pod를 하나씩 만들고, 대상 호스트와 매칭되도록 Pod의 spec.affinity.nodeAffinity 필드를 추가해요. Pod가 생성된 후에는 보통 기본 스케줄러가 이를 인계받아 .spec.nodeName 필드를 설정함으로써 Pod를 대상 호스트에 바인딩해요. 새 Pod가 노드에 들어갈 수 없으면, 기본 스케줄러는 새 Pod의 우선순위에 따라 기존 Pod 일부를 선점(축출)할 수 있어요.

참고 (Note)

DaemonSet pod가 각 노드에서 실행되는 것이 중요하다면, .spec.template.spec.priorityClassName을 더 높은 우선순위의 PriorityClass로 설정해서 이런 축출이 발생하도록 보장하는 것이 바람직해요.

DaemonSet의 .spec.template.spec.schedulerName 필드를 설정해서 DaemonSet Pod에 다른 스케줄러를 지정할 수 있어요.

.spec.template.spec.affinity.nodeAffinity 필드(지정된 경우)에 원래 지정된 노드 어피니티는, DaemonSet 컨트롤러가 적격 노드를 평가할 때 고려되지만, 생성된 Pod에서는 적격 노드의 이름과 매칭되는 노드 어피니티로 대체돼요.

nodeAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
    - matchFields:
      - key: metadata.name
        operator: In
        values:
        - target-host-name

테인트와 톨러레이션 (Taints and tolerations)

DaemonSet 컨트롤러는 DaemonSet Pod에 일련의 톨러레이션을 자동으로 추가해요:

톨러레이션 키 효과 세부사항
node.kubernetes.io/not-ready NoExecute DaemonSet Pod는 건강하지 않거나 Pod를 받아들일 준비가 안 된 노드에도 스케줄될 수 있음. 그런 노드에서 실행 중인 DaemonSet Pod는 축출되지 않음.
node.kubernetes.io/unreachable NoExecute DaemonSet Pod는 노드 컨트롤러에서 도달할 수 없는 노드에도 스케줄될 수 있음. 그런 노드에서 실행 중인 DaemonSet Pod는 축출되지 않음.
node.kubernetes.io/disk-pressure NoSchedule DaemonSet Pod는 디스크 압력 문제가 있는 노드에도 스케줄될 수 있음.
node.kubernetes.io/memory-pressure NoSchedule DaemonSet Pod는 메모리 압력 문제가 있는 노드에도 스케줄될 수 있음.
node.kubernetes.io/pid-pressure NoSchedule DaemonSet Pod는 프로세스 압력 문제가 있는 노드에도 스케줄될 수 있음.
node.kubernetes.io/unschedulable NoSchedule DaemonSet Pod는 스케줄 불가능으로 표시된 노드에도 스케줄될 수 있음.
node.kubernetes.io/network-unavailable NoSchedule 호스트 네트워킹을 요청하는 DaemonSet Pod에만 추가됨, 즉 spec.hostNetwork: true를 가진 Pod. 그런 DaemonSet Pod는 네트워크가 사용 불가능한 노드에도 스케줄될 수 있음.

DaemonSet의 Pod 템플릿에 정의해서 DaemonSet Pod에 자신만의 톨러레이션을 추가할 수도 있어요.

DaemonSet 컨트롤러가 node.kubernetes.io/unschedulable:NoSchedule 톨러레이션을 자동으로 설정하기 때문에, 쿠버네티스는 unschedulable로 표시된 노드에도 DaemonSet Pod를 실행할 수 있어요.

클러스터 네트워킹 같은 중요한 노드 수준 기능을 DaemonSet으로 제공한다면, 쿠버네티스가 노드가 준비되기 전에 DaemonSet Pod를 노드에 배치하는 것이 도움이 돼요. 예를 들어 그 특별한 톨러레이션이 없다면, 네트워크 플러그인이 그 노드에서 실행되지 않아 노드가 ready로 표시되지 않는 상황과, 동시에 노드가 아직 ready가 아니어서 네트워크 플러그인이 그 노드에서 실행되지 않는 상황의 교착 상태(deadlock)에 빠질 수 있어요.

Daemon Pod와 통신하기 (Communicating with Daemon Pods)

DaemonSet의 Pod와 통신하는 몇 가지 가능한 패턴은 다음과 같아요:

  • Push(Push): DaemonSet의 Pod는 통계 데이터베이스 같은 다른 서비스에 업데이트를 보내도록 구성됨. 클라이언트는 가지지 않음.
  • NodeIP와 Known Port: DaemonSet의 Pod는 hostPort를 사용할 수 있어서, 노드 IP를 통해 pod에 도달할 수 있음. 클라이언트는 노드 IP 목록을 어떤 식으로든 알고, 포트는 관례상 앎.
  • DNS: 같은 pod selector를 가진 헤드리스 서비스(headless service)를 만들고, endpoints 리소스로 DaemonSet을 발견하거나 DNS에서 여러 A 레코드를 가져옴.
  • Service: 같은 Pod selector를 가진 서비스를 만들고, 그 서비스를 사용해 무작위 노드의 데몬에 도달함. Service 내부 트래픽 정책(Internal Traffic Policy)을 사용해 같은 노드의 pod로 제한할 수 있음.

DaemonSet 업데이트 (Updating a DaemonSet)

노드 라벨이 변경되면 DaemonSet은 새로 매칭되는 노드에 Pod를 신속히 추가하고, 더 이상 매칭되지 않는 노드에서는 Pod를 삭제해요.

DaemonSet이 만드는 Pod를 수정할 수는 있어요. 그러나 Pod가 모든 필드의 업데이트를 허용하는 것은 아니에요. 또 DaemonSet 컨트롤러는 (같은 이름의 노드라도) 다음에 노드가 생성될 때 원래 템플릿을 사용해요.

DaemonSet을 삭제할 수 있어요. kubectl에서 --cascade=orphan을 지정하면 Pod는 노드에 남아 있어요. 이후 같은 selector로 새 DaemonSet을 만들면, 새 DaemonSet이 기존 Pod를 입양(adopt)해요. 교체할 Pod가 있으면 DaemonSet은 updateStrategy에 따라 그 Pod들을 교체해요.

DaemonSet에 대해 롤링 업데이트를 수행할 수 있어요.

DaemonSet의 대안 (Alternatives to DaemonSet)

Init 스크립트 (Init scripts)

노드에서 데몬 프로세스를 직접 시작하는 것(예: init, upstartd, systemd 사용)도 확실히 가능해요. 전혀 문제없어요. 하지만 그런 프로세스를 DaemonSet으로 실행하면 몇 가지 장점이 있어요:

  • 애플리케이션과 같은 방식으로 데몬의 로그를 모니터링하고 관리할 수 있음.
  • 데몬과 애플리케이션에 같은 구성 언어와 도구(예: Pod 템플릿, kubectl)를 사용할 수 있음.
  • 자원 제한이 있는 컨테이너에서 데몬을 실행하면 데몬과 앱 컨테이너 사이의 격리가 높아짐. 다만 이것은 데몬을 컨테이너에서 실행하되 Pod 안이 아니라 실행해도 이룰 수 있음.

베어 Pod (Bare Pods)

특정 노드에서 실행되도록 지정한 Pod를 직접 만들 수도 있어요. 그러나 DaemonSet은 노드 실패나 커널 업그레이드 같은 파괴적인 노드 유지보수 같은 어떤 이유로든 삭제되거나 종료된 Pod를 교체해요. 이런 이유로 개별 Pod를 만드는 것보다 DaemonSet을 사용해야 해요.

스태틱 Pod (Static Pods)

kubelet이 주시하는 특정 디렉터리에 파일을 작성해서 Pod를 만들 수도 있는데, 이것을 스태틱 pod(static pods)라고 불러요. DaemonSet과 달리, 스태틱 Pod는 kubectl이나 다른 쿠버네티스 API 클라이언트로 관리할 수 없어요. 스태틱 Pod는 apiserver에 의존하지 않아서 클러스터 부트스트래핑 경우에 유용해요. 또한 스태틱 Pod는 미래에 deprecated될 수 있어요.

Deployments

DaemonSet은 Deployments와 비슷해요. 둘 다 Pod를 만들고, 그 Pod들은 종료될 것으로 기대되지 않는 프로세스(예: 웹 서버, 스토리지 서버)를 가져요.

리플리카 수를 늘리거나 줄이고 롤아웃 업데이트하는 것이 Pod가 정확히 어느 호스트에서 실행되는지 제어하는 것보다 중요한 프론트엔드 같은 무상태 서비스에는 Deployment를 사용해요. Pod의 복사본이 항상 모든 호스트 또는 특정 호스트에서 실행되는 것이 중요하다면 DaemonSet을 사용해요. DaemonSet이 그 특정 노드에서 다른 Pod가 올바르게 실행될 수 있게 해주는 노드 수준 기능을 제공하는 경우죠.

예를 들어 네트워크 플러그인은 흔히 DaemonSet으로 실행되는 컴포넌트를 포함해요. 그 DaemonSet 컴포넌트는 실행 중인 노드의 클러스터 네트워킹이 동작하도록 보장해요.

더 알아보기 (What's next)