소개
일정한 복제본 수를 유지하는 대신, 적합한 모든 노드에서 하나씩 실행되어야 하는 소프트웨어가 있습니다. 로그 수집기, 모니터링 에이전트, 네트워킹 구성 요소는 이러한 노드 중심 배치에 DaemonSet 을 흔히 사용합니다.
이 실습에서는 준비된 노드에 레이블을 지정하고, 노드 리포터를 배포한 다음, 해당 Pod 의 소유 관계를 확인하고, 노드 적합성을 변경하고, 롤링 업데이트를 수행합니다. 단일 노드 실습 환경에서는 배치 규칙을 쉽게 관찰할 수 있으며, 더 큰 클러스터에서 사용되는 동일한 컨트롤러 동작도 그대로 확인할 수 있습니다.
이 실습에서 계속해서 확인할 핵심 질문은“복제본을 몇 개 실행해야 하는가?”가 아니라“어떤 노드가 적합한가?”입니다. DaemonSet 은 이 질문에 지속적으로 답하면서 조건에 맞는 각 노드에 하나의 Pod 를 생성합니다. 이 환경에는 노드가 하나뿐이므로, 적합성의 변화가 하나에서 0 으로 또는 0 에서 하나로 바뀌는 결과를 명확하게 보여 줍니다.
적합한 노드 표시하기
이 단계에서는 노드에 특정 용도의 레이블을 추가합니다. 레이블은 컨트롤러가 선택할 수 있는 노드의 속성을 나타냅니다.
먼저 노드와 현재 설정된 레이블을 확인합니다. 출력 내용이 길기 때문에 모두 외울 필요는 없습니다. 레이블이 Node 객체에 연결된 키 - 값 형식의 정보라는 점만 확인하면 됩니다.
cd /home/labex/project/daemonset-workloads
kubectl get nodes --show-labels
새 레이블을 추가합니다. 설정 과정에서 기존 값을 제거했으므로 여기서는 --overwrite 옵션이 필요하지 않습니다. 다만 충돌하는 값이 이미 있으면 kubectl label이 이를 알려 줍니다.
kubectl label node labex-v135 practice-tier=operations
kubectl get node labex-v135 -L practice-tier
-L practice-tier는 해당 레이블을 표의 열로 추가합니다. 이제 이 노드는 실습용 운영 에이전트를 배치할 수 있는 노드임을 나타냅니다.
두 번째 명령은 매우 긴 --show-labels 출력 대신 필요한 정보만 간결하게 보여 줍니다.
NAME STATUS ROLES VERSION PRACTICE-TIER
labex-v135 Ready control-plane v1.35.5 operations
PRACTICE-TIER=operations가 필요한 근거입니다. 레이블만으로는 아무 작업도 실행되지 않으며, 다음 단계에서 워크로드가 이 레이블을 선택할 때 의미를 갖게 됩니다.
DaemonSet 배포하기
이 단계에서는 새 레이블이 지정된 노드를 대상으로 하는 DaemonSet 을 생성합니다.
매니페스트를 입력하기 전에 서로 연결된 세 가지 설정으로 나누어 살펴보세요. 선택자와 Pod 레이블이 모두 app: node-reporter를 사용하므로 컨트롤러가 자신이 관리하는 Pod 를 식별할 수 있습니다. 이어서 nodeSelector가 practice-tier=operations 레이블이 있는 노드만 배치 대상이 되도록 제한합니다. 마지막으로 Downward API 가 선택된 Pod 의 spec.nodeName을 컨테이너 내부의 NODE_NAME 환경 변수로 전달합니다.
반복 실행되는 루프는 데모 에이전트가 계속 실행되도록 합니다. 실제 DaemonSet 에서는 로그 수집기나 모니터링 에이전트가 이 역할을 대신할 수 있습니다.
cat <<'EOF' > node-reporter.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-reporter
namespace: practice-daemon
spec:
selector:
matchLabels:
app: node-reporter
template:
metadata:
labels:
app: node-reporter
spec:
nodeSelector:
practice-tier: operations
containers:
- name: reporter
image: busybox:1.36
imagePullPolicy: IfNotPresent
command: ["sh", "-c", "while true; do echo node=${NODE_NAME} version=v1; sleep 15; done"]
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
EOF
네임스페이스를 생성하고 파일을 적용한 뒤 DaemonSet 롤아웃이 완료될 때까지 기다립니다.
kubectl create namespace practice-daemon
kubectl apply -f node-reporter.yaml
kubectl rollout status daemonset/node-reporter -n practice-daemon --timeout=60s
kubectl get daemonset,pods -n practice-daemon -o wide
Downward API 의 fieldRef는 노드 이름을 매니페스트에 직접 입력하지 않고도 스케줄링된 노드의 이름을 전달합니다. 이 단일 노드 클러스터에서는 원하는 수, 현재 수, 준비된 수가 모두 1 이어야 합니다.
Pod 이름에는 자동으로 생성된 접미사가 포함되지만 NODE 열에는 labex-v135가 표시되어야 합니다. 이는 노드 적합성 규칙과 실제 스케줄링 결과가 일치한다는 직접적인 증거입니다.
단일 노드 테스트 클러스터에서 주요 열은 다음과 같이 표시됩니다.
NAME DESIRED CURRENT READY NODE SELECTOR
node-reporter 1 1 1 practice-tier=operations
NAME READY STATUS NODE
node-reporter-... 1/1 Running labex-v135
첫 번째 행을 왼쪽에서 오른쪽으로 읽어 보세요. 선택자와 일치하는 노드가 하나이고, Pod 가 하나 생성되었으며, 해당 Pod 가 준비 상태라는 뜻입니다. 더 큰 클러스터에서는 적합한 노드 수에 따라 이 값들이 증가합니다.
적합성과 소유 관계 확인하기
이 단계에서는 복제본 설정이 아니라 노드 적합성이 DaemonSet Pod 수를 결정한다는 점을 확인합니다.
먼저 정상 상태의 기준 정보를 수집합니다. 사용자 지정 열을 사용하면 자동 생성된 Pod 를 해당 DaemonSet 과 연결해 볼 수 있고, 로그를 통해 Downward API 가 노드 이름을 전달했는지도 확인할 수 있습니다.
kubectl get pods -n practice-daemon -l app=node-reporter -o custom-columns='POD:.metadata.name,NODE:.spec.nodeName,OWNER:.metadata.ownerReferences[0].kind,READY:.status.containerStatuses[0].ready'
kubectl logs -n practice-daemon -l app=node-reporter --tail=3
소유 관계 표와 로그는 다음과 비슷하게 표시되어야 합니다.
POD NODE OWNER READY
node-reporter-... labex-v135 DaemonSet true
node=labex-v135 version=v1
OWNER 필드는 이 Pod 가 컨트롤러에 의해 관리되고 있음을 보여 줍니다. 로그는 NODE_NAME이 하드코딩된 값이 아니라 Pod 의 실제 spec.nodeName에서 전달되었음을 증명합니다.
레이블을 일시적으로 제거하고 Pod 가 사라지는 모습을 확인합니다.
kubectl label node labex-v135 practice-tier-
kubectl get daemonset node-reporter -n practice-daemon
kubectl wait --for=delete pod -n practice-daemon -l app=node-reporter --timeout=60s
끝에 -를 붙이면 해당 레이블 키가 제거됩니다. 이제 적합성을 복원하고 컨트롤러가 대체 Pod 를 생성할 때까지 기다립니다.
레이블을 제거한 직후 DaemonSet 은 다음과 같은 안정적인 상태를 잠시 보여 줍니다.
NAME DESIRED CURRENT READY
node-reporter 0 0 0
이는 오류가 아니라 노드 적합성이 변경되었을 때 나타나는 정상적인 반응입니다. 현재 DaemonSet 의 nodeSelector와 일치하는 노드가 없으므로 올바른 원하는 수는 0 입니다.
kubectl label node labex-v135 practice-tier=operations
kubectl rollout status daemonset/node-reporter -n practice-daemon --timeout=60s
레이블을 변경하기 전과 후의 Pod 이름을 비교해 보세요. 대체된 Pod 는 새로운 Pod 식별자를 가지지만, DaemonSet 이름과 원하는 동작은 그대로 유지됩니다. 이것이 바로 조정 작업이 실제로 수행되는 모습입니다.
DaemonSet 업데이트 롤아웃하기
이 단계에서는 저장된 매니페스트를 업데이트하고 DaemonSet 이 기존 노드 에이전트를 교체하는 과정을 확인합니다. 파일과 실제 클러스터 객체를 일치시켜 두면 원하는 상태를 반복해서 재현할 수 있습니다.
먼저 cp를 사용해 작업용 복사본을 만듭니다. 그런 다음 Kubernetes 입문 과정에서 소개한 것과 같은 간단한 텍스트 편집 방식을 사용합니다. sed -i 's/old/new/' file은 파일에서 일치하는 텍스트를 직접 바꾸고, grep -n은 일치하는 줄을 줄 번호와 함께 출력하므로 적용하기 전에 변경 결과를 확인할 수 있습니다.
cp node-reporter.yaml node-reporter-v2.yaml
sed -i 's/version=v1/version=v2/' node-reporter-v2.yaml
grep -n 'version=' node-reporter-v2.yaml
kubectl apply -f node-reporter-v2.yaml
kubectl rollout status daemonset/node-reporter -n practice-daemon --timeout=60s
kubectl rollout history daemonset/node-reporter -n practice-daemon
kubectl logs -n practice-daemon -l app=node-reporter --tail=3
복사한 파일에는 비교를 위해 기존 v1 예제가 그대로 남아 있습니다. 한 번의 치환으로 컨테이너 명령 안에 있는 버전 텍스트만 변경됩니다. 업데이트된 파일을 적용하면 Pod 템플릿이 변경되고, 새로운 컨트롤러 리비전과 대체 Pod 가 생성됩니다. 이제 로그에는 version=v2가 포함되어야 합니다.
rollout history는 Kubernetes 가 새 리비전을 기록했음을 확인해 줍니다. 따라서 DaemonSet 도 Deployment 와 마찬가지로 템플릿을 제어된 방식으로 업데이트할 수 있습니다. 다만 대체 작업은 복제본 수가 아니라 적합한 노드를 기준으로 진행됩니다.
테스트 결과에는 두 개의 리비전과 새로운 로그 메시지가 포함되어 있었습니다.
REVISION CHANGE-CAUSE
1 <none>
2 <none>
node=labex-v135 version=v2
CHANGE-CAUSE=<none>은 사람이 읽을 수 있는 변경 사유 주석이 기록되지 않았다는 뜻일 뿐입니다. 리비전 2가 생성되었고 대체 Pod 의 로그에 version=v2가 나타난다는 사실이 롤아웃이 완료되었다는 유용한 증거입니다.
요약
노드 레이블을 사용해 DaemonSet 의 배치 적합성을 제어하고, 적합한 노드마다 하나의 Pod 가 조정되는 과정을 관찰했으며, 컨트롤러 소유 관계를 확인하고 노드 에이전트 업데이트를 롤아웃했습니다. 이제 DaemonSet 을 복제본 중심의 Deployment 및 작업 완료 중심의 Job 과 비교해 이해할 수 있습니다.


