<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Node Maintenance on jyukki's Blog</title><link>https://jyukki.com/tags/node-maintenance/</link><description>Recent content in Node Maintenance on jyukki's Blog</description><generator>Hugo -- 0.147.0</generator><language>ko-KR</language><lastBuildDate>Fri, 11 Sep 2026 10:07:00 +0900</lastBuildDate><atom:link href="https://jyukki.com/tags/node-maintenance/index.xml" rel="self" type="application/rss+xml"/><item><title>2026 개발 트렌드: Kubernetes v1.37 Node Lifecycle Conditions, 노드 정비를 자동화 전에 계약으로 만들기</title><link>https://jyukki.com/posts/2026-09-11-kubernetes-node-lifecycle-conditions-trend/</link><pubDate>Fri, 11 Sep 2026 10:07:00 +0900</pubDate><guid>https://jyukki.com/posts/2026-09-11-kubernetes-node-lifecycle-conditions-trend/</guid><description>Kubernetes v1.37의 Node Lifecycle Conditions를 계기로 drain·maintenance·graceful shutdown 상태를 공통 계약으로 다루는 방법과, Alpha 신호를 자동 eviction 정책으로 오해하지 않는 운영 기준을 정리합니다.</description><content:encoded><![CDATA[<p>Kubernetes에서 node 정비는 보통 여러 신호로 흩어진다. 운영자는 <code>kubectl cordon</code>으로 새 Pod 배치를 막고, <code>drain</code>으로 기존 Pod를 옮기며, taint나 PodDisruptionBudget으로 예외를 다룬다. load balancer는 별도의 health check로 traffic을 뺀다. 이 절차 자체는 맞지만 &ldquo;이 node가 왜 비어 가는가&rdquo;, &ldquo;언제 정비가 시작되는가&rdquo;, &ldquo;drain이 끝났는가&quot;는 label·event·티켓·채팅에 따로 남기기 쉽다.</p>
<p>Kubernetes v1.37이 도입한 <strong>Node Lifecycle Conditions</strong>는 이 간극을 공통 상태 이름으로 메우려는 움직임이다. 새 이름은 <code>DrainInProgress</code>, <code>Drained</code>, <code>MaintenancePlanned</code>, <code>MaintenanceInProgress</code>, <code>GracefulNodeShutdownInProgress</code> 다섯 가지다. 중요한 제약도 분명하다. 이 기능은 Alpha이고 <code>NodeLifecycleConditions</code> feature gate는 v1.37에서 기본 비활성화다. 더구나 gate를 활성화해도 현 릴리스의 core component는 condition을 읽어 scheduling·eviction·drain 동작을 바꾸지 않는다.</p>
<p>즉, 이번 변화는 &ldquo;노드가 자동으로 안전하게 정비된다&quot;는 기능 출시가 아니다. 먼저 <strong>운영 의도를 누가, 어떤 freshness로, 어떤 실행 절차와 연결해 기록할 것인가</strong>를 정하는 계약의 출시다. 이 글은 <a href="/learning/deep-dive/deep-dive-kubernetes-rollouts/">Kubernetes Rollout 전략</a>, <a href="/learning/deep-dive/deep-dive-graceful-shutdown/">Graceful Shutdown</a>, <a href="/learning/deep-dive/deep-dive-load-balancer-healthchecks/">Load Balancer Health Check</a>, <a href="/posts/2026-09-08-kubernetes-rootless-node-components-trend/">Kubernetes v1.37 Rootless Node Components</a>를 node maintenance 관점으로 연결한다.</p>
<p>참고한 공식 자료:</p>
<ul>
<li>Kubernetes Blog, <a href="https://kubernetes.io/blog/2026/09/09/kubernetes-v1-37-node-lifecycle-conditions/">Kubernetes v1.37: Introducing Node Lifecycle Conditions</a></li>
<li>Kubernetes Docs, <a href="https://kubernetes.io/docs/reference/node/node-status/">Node Status</a></li>
<li>Kubernetes Docs, <a href="https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/">Safely Drain a Node</a></li>
<li>Kubernetes Docs, <a href="https://kubernetes.io/docs/concepts/workloads/pods/disruptions/">Pod Disruptions</a></li>
</ul>
<h2 id="이-글에서-얻는-것">이 글에서 얻는 것</h2>
<ul>
<li>Node Lifecycle Conditions가 Ready·taint·cordon·drain과 각각 무엇이 다른지 구분합니다.</li>
<li>v1.37의 Alpha 상태를 자동 조치 기능으로 오해하지 않는 이유를 이해합니다.</li>
<li>condition writer의 권한·source·freshness·clear 기준을 운영 계약으로 만드는 방법을 익힙니다.</li>
<li>기존 node maintenance runbook과 연결하되, 위험한 자동화를 shadow mode로 제한하는 기준을 세웁니다.</li>
</ul>
<h2 id="핵심-개념이슈">핵심 개념/이슈</h2>
<h3 id="1-다섯-condition은-행동-명령이-아니라-공유-상태다">1) 다섯 condition은 행동 명령이 아니라 공유 상태다</h3>
<p>각 condition은 node lifecycle의 특정 지점을 표현한다.</p>
<table>
  <thead>
      <tr>
          <th>Condition</th>
          <th>표현하는 사실</th>
          <th>대체하지 않는 기존 제어</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>DrainInProgress</code></td>
          <td>관리자가 정한 기준으로 drain이 진행 중</td>
          <td>cordon, eviction, PDB</td>
      </tr>
      <tr>
          <td><code>Drained</code></td>
          <td>선택한 drain 기준에 도달함</td>
          <td>node 재부팅·삭제 승인</td>
      </tr>
      <tr>
          <td><code>MaintenancePlanned</code></td>
          <td>앞으로 node 변경이 예정됨</td>
          <td>scheduler의 즉시 placement 변경</td>
      </tr>
      <tr>
          <td><code>MaintenanceInProgress</code></td>
          <td>정비가 실제로 시작됨</td>
          <td>traffic drain, workload 이동</td>
      </tr>
      <tr>
          <td><code>GracefulNodeShutdownInProgress</code></td>
          <td>graceful shutdown이 진행됨</td>
          <td>kubelet shutdown policy와 app 종료 처리</td>
      </tr>
  </tbody>
</table>
<p>condition이 <code>True</code>라는 사실만으로 Kubernetes scheduler가 새 Pod를 막거나 controller가 기존 Pod를 evict하지 않는다. 그 동작은 여전히 cordon, drain, taint/toleration, PodDisruptionBudget, deployment rollout 같은 기존 API와 runbook이 담당한다. 운영 중 <code>MaintenancePlanned=True</code>만 보고 cordon을 생략하거나, 반대로 <code>Drained=True</code>만 보고 node를 삭제하면 의도가 실행 결과로 바뀌었다고 잘못 가정하게 된다.</p>
<p>이 구분은 load balancer readiness와도 같다. health check에서 traffic이 빠졌다고 모든 in-flight 요청이 끝난 것이 아니듯, lifecycle condition은 사람이 공유할 상태를 말할 뿐 물리적·논리적 정비의 완료를 보장하지 않는다. application traffic, batch job, local data, storage detach의 종료 기준은 별도 증거를 가져야 한다.</p>
<h3 id="2-alpha-feature-gate와-신호-소비는-별개다">2) Alpha feature gate와 신호 소비는 별개다</h3>
<p>v1.37에서 새 이름은 well-known <code>NodeConditionType</code>으로 예약됐고, 관리자가 또는 관리자가 권한을 준 controller가 설정·해제한다. <code>NodeLifecycleConditions</code> gate는 기본으로 꺼져 있으며, 켜더라도 현재 release에서는 실질적으로 no-op에 가깝다. core workload controller가 이 상태에 맞춰 행동하지 않는다는 뜻이다.</p>
<p>이 사실은 두 가지 설계 원칙으로 이어진다.</p>
<ol>
<li>production availability action의 단일 입력으로 쓰지 않는다.</li>
<li>미래 release에서 controller가 소비할 수 있음을 전제로, 지금부터 상태의 author와 의미를 느슨하게 만들지 않는다.</li>
</ol>
<p>&ldquo;Alpha라서 아무것도 하지 않는다&quot;도 아쉽고, &ldquo;Alpha지만 우리 automation이 알아서 처리한다&quot;도 위험하다. 지금 할 수 있는 좋은 작업은 상태 어휘를 dashboard·change ticket·runbook에 일관되게 넣는 일이다. 미래 기능을 기다리는 동안에도 on-call이 node의 의도와 실제 drain 진행을 빠르게 맞출 수 있다.</p>
<h3 id="3-condition-writer는-작은-control-plane이다">3) condition writer는 작은 control plane이다</h3>
<p>Node condition은 보통 node object를 patch할 수 있는 주체가 쓴다. 이 권한을 maintenance bot, cluster autoscaler, hardware controller, 사람이 모두 가지면 같은 node에 상충하는 상태를 만들 수 있다. 예를 들어 한 controller가 <code>MaintenanceInProgress=True</code>를 기록한 직후 다른 도구가 작업을 취소했는데 clear하지 못하면, dashboard의 상태는 진실이 아니다.</p>
<p>최소 계약은 아래 네 항목이다.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>writer: 서비스 계정 하나 또는 명시적 handoff 규칙
</span></span><span style="display:flex;"><span>source: controller 이름 + change ticket/maintenance window
</span></span><span style="display:flex;"><span>freshness: lastTransitionTime 및 heartbeat/재확인 시간
</span></span><span style="display:flex;"><span>clear: 성공, 취소, timeout 각각의 해제 owner와 절차
</span></span></code></pre></div><p>source는 message에 자유 텍스트만 남기는 것보다 annotation이나 event의 change ID로 참조 가능한 편이 좋다. 단, condition 정의 바깥의 annotation schema는 조직이 정하는 정책이지 Kubernetes가 보장하는 표준은 아니다. 정책을 만들 때는 정비 시작·종료 시간이 기록되는 system of record를 하나 정하고, condition은 그 상태를 cluster에 전달하는 projection으로 보는 편이 안전하다.</p>
<h3 id="4-stale-상태를-정상-상태보다-먼저-다룬다">4) stale 상태를 정상 상태보다 먼저 다룬다</h3>
<p><code>MaintenancePlanned=True</code>가 일주일 전 예정을 아직 가리키는지, 실제로 정비가 막힌 것인지, 이미 취소됐는데 clear가 누락된 것인지는 condition 값만으로 알기 어렵다. 그래서 첫 대시보드는 &ldquo;현재 maintenance node 수&quot;보다 <strong>stale signal</strong>을 먼저 보여줘야 한다.</p>
<p>시작 기준의 예시는 다음과 같다.</p>
<table>
  <thead>
      <tr>
          <th>신호</th>
          <th>사람이 확인할 조건</th>
          <th>자동 행동 금지 이유</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>MaintenancePlanned=True</code></td>
          <td>예정 시각이 지났거나 24시간 이상 재확인 없음</td>
          <td>계획 취소·연기 여부는 외부 change record에 있음</td>
      </tr>
      <tr>
          <td><code>DrainInProgress=True</code></td>
          <td>30분 안에 eviction/event 진행이 없음</td>
          <td>PDB, finalizer, local storage 때문에 멈췄을 수 있음</td>
      </tr>
      <tr>
          <td><code>Drained=True</code></td>
          <td>cordon과 workload 잔존 수가 기준과 맞지 않음</td>
          <td>팀마다 &ldquo;drained&quot;의 정의가 다를 수 있음</td>
      </tr>
      <tr>
          <td>shutdown condition</td>
          <td>node heartbeat·graceful shutdown event가 모순</td>
          <td>controller와 kubelet 관측 시간이 다를 수 있음</td>
      </tr>
  </tbody>
</table>
<p>10분, 30분, 24시간은 예시다. 짧은 maintenance window와 수 시간 걸리는 stateful workload를 같은 임계값으로 다루면 alert noise가 커진다. 중요한 것은 condition을 자동 action trigger보다 <strong>investigation trigger</strong>로 시작하는 순서다.</p>
<h2 id="실무-적용">실무 적용</h2>
<h3 id="1-기존-drain-runbook을-바꾸지-않고-상태를-병행-기록한다">1) 기존 drain runbook을 바꾸지 않고 상태를 병행 기록한다</h3>
<p>처음 도입하는 cluster는 flow를 새로 만들지 않는다. 다음처럼 기존의 검증된 절차에 lifecycle 상태를 붙인다.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>change 승인
</span></span><span style="display:flex;"><span>  -&gt; MaintenancePlanned 기록 (owner·window·ticket 연결)
</span></span><span style="display:flex;"><span>  -&gt; load balancer에서 traffic 제거 확인
</span></span><span style="display:flex;"><span>  -&gt; cordon
</span></span><span style="display:flex;"><span>  -&gt; DrainInProgress 기록
</span></span><span style="display:flex;"><span>  -&gt; drain + PDB/eviction 관찰
</span></span><span style="display:flex;"><span>  -&gt; Drained 기록 (조직의 완료 정의 충족 시)
</span></span><span style="display:flex;"><span>  -&gt; MaintenanceInProgress 기록
</span></span><span style="display:flex;"><span>  -&gt; 정비·reboot·검증
</span></span><span style="display:flex;"><span>  -&gt; condition clear + uncordon + traffic 복귀 증거
</span></span></code></pre></div><p>여기서 <code>Drained</code>의 완료 정의는 팀마다 명시해야 한다. 예를 들어 &ldquo;DaemonSet과 mirror Pod는 제외하고, 일반 workload Pod 0개, termination 중 Pod 0개, unschedulable=true&quot;처럼 적는다. stateful workload에 local PV가 있다면 volume detach와 application health까지 완료 조건에 넣을지 정해야 한다. 이름이 표준이어도 종료 기준까지 표준인 것은 아니다.</p>
<h3 id="2-writer-권한과-감사-흔적을-분리한다">2) writer 권한과 감사 흔적을 분리한다</h3>
<p>condition을 쓰는 service account에는 필요 최소의 node patch 권한만 주고, 일반 배포 도구나 개발자 role에 넓은 Node write 권한을 주지 않는다. 변경 이벤트에는 writer, node, condition, 이전/이후 값, ticket, 만료 시각을 남긴다. audit log가 없는 cluster라면 처음에는 사람이 수동 입력하는 횟수를 최소화하는 편이 낫다.</p>
<p>권한 검토에서 확인할 질문은 세 가지다.</p>
<ul>
<li>이 writer가 모든 node를 patch해야 하는가, 아니면 관리하는 node pool만 다루면 되는가?</li>
<li>두 writer가 같은 condition을 소유할 때 handoff와 conflict resolution은 무엇인가?</li>
<li>change ticket이 취소됐을 때 누가 condition을 clear하고, 일정 시간 뒤 stale alert를 받는가?</li>
</ul>
<p>condition을 &ldquo;대시보드용 메타데이터&quot;라며 권한을 느슨하게 주면, 나중에 automation이 붙는 순간 신뢰 경계가 이미 무너진다. condition writer를 control plane의 작은 구성요소로 취급해야 하는 이유다.</p>
<h3 id="3-12-node-48시간-shadow-consumer로-검증한다">3) 1~2 node, 48시간, shadow consumer로 검증한다</h3>
<p>첫 canary는 maintenance가 예정된 worker 1~2대에서 시작한다. 48시간 동안 기존 runbook의 action과 lifecycle condition의 순서를 비교한다. 평가 지표는 feature gate가 작동했는지가 아니라 다음의 운영 일치도다.</p>
<table>
  <thead>
      <tr>
          <th>검증 항목</th>
          <th>통과 기준 예시</th>
          <th>abort/보류 신호</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>기록 일치</td>
          <td>ticket·event·condition의 owner와 time window가 100% 일치</td>
          <td>source 또는 완료 시각이 1건이라도 모순</td>
      </tr>
      <tr>
          <td>drain 추적</td>
          <td><code>DrainInProgress</code> 후 drain event가 5분 내 관측</td>
          <td>event 없이 상태만 True로 지속</td>
      </tr>
      <tr>
          <td>상태 정리</td>
          <td>완료·취소 뒤 condition이 10분 안에 clear</td>
          <td>stale condition 1건 발생</td>
      </tr>
      <tr>
          <td>안전성</td>
          <td>PDB 위반 0건, 예상 밖 workload eviction 0건</td>
          <td>availability impact 또는 rollback 필요</td>
      </tr>
      <tr>
          <td>관측</td>
          <td>dashboard/event/audit에서 동일 node 상태 확인</td>
          <td>writer를 추적할 수 없음</td>
      </tr>
  </tbody>
</table>
<p>내부 automation을 시험하고 싶다면 <code>MaintenancePlanned</code>를 받았을 때 &ldquo;cordon했을 것&quot;이라는 예상 action만 로그에 적는 shadow mode로 시작한다. 실제 <code>kubectl cordon</code>은 수행하지 않는다. 48시간 이상, 또는 여러 maintenance event에서 shadow 결과가 기존 runbook과 100% 맞고 stale·rollback 사례도 경험한 뒤에야 human approval이 있는 제안 모드로 올릴 수 있다.</p>
<h3 id="4-node-pool-capability-변화와-함께-읽는다">4) node pool capability 변화와 함께 읽는다</h3>
<p>최근 Kubernetes v1.37의 rootless node component처럼 node 자체의 capability가 달라지는 변화는 재부팅·drain·rollback을 더 자주 요구한다. 이런 때 lifecycle condition은 rootless, GPU, storage, OS image 같은 capability label의 대체재가 아니다. 반대로 node를 어떤 pool로 되돌리고 어떤 workload가 대기 중인지 설명하는 맥락을 제공한다.</p>
<p>예를 들어 rootless node pool을 upgrade할 때 <code>MaintenancePlanned</code>는 계획과 owner를 전달할 수 있다. 실제 placement는 label·taint·affinity가, 작업 중 안전한 축소는 PDB와 capacity budget이, traffic 손실 방지는 readiness와 load balancer deregistration이 맡는다. 한 상태 필드로 모든 control plane 문제를 해결하려 하면 오히려 책임 경계가 흐려진다.</p>
<h2 id="트레이드오프주의점">트레이드오프/주의점</h2>
<p>첫째, condition은 명세화된 단어이지 강제된 workflow가 아니다. <code>Drained=True</code>의 기준을 통일하지 않으면 다른 팀의 controller가 서로 다른 완료 상태를 같은 이름으로 기록한다. 처음부터 owner와 종료 정의를 짧게라도 남겨야 한다.</p>
<p>둘째, Alpha feature의 API·소비 방식은 바뀔 수 있다. platform dashboard나 data model을 만들더라도 condition 이름의 미래 동작을 확정적으로 가정하지 말고, raw condition·writer·version을 함께 저장한다. gate를 production 기본값으로 바꾸는 결정도 Kubernetes upgrade와 별도로 review한다.</p>
<p>셋째, maintenance signal을 받자마자 자동 eviction·autoscaling·capacity 감소를 수행하면 false positive가 availability incident로 바뀐다. condition은 외부 ticket·Node Ready·cordon·drain event·PDB 상태와 함께 평가해야 한다. 특히 stateful workload는 event ordering이 맞아도 data path가 종료되지 않았을 수 있다.</p>
<p>넷째, 너무 많은 custom annotation은 공통 어휘의 장점을 잃게 한다. Kubernetes가 제공하는 다섯 condition은 공통으로 쓰고, 조직 고유 메타데이터는 owner·ticket·window·expiry처럼 최소로 유지한다. 운영자가 한 화면에서 30초 안에 &ldquo;누가 왜 이 node를 비우는가&quot;를 읽지 못하면 계약이 과해진 것이다.</p>
<h2 id="체크리스트-또는-연습">체크리스트 또는 연습</h2>
<ul>
<li><input disabled="" type="checkbox"> <code>DrainInProgress</code>, <code>Drained</code>, <code>MaintenancePlanned</code>, <code>MaintenanceInProgress</code>, <code>GracefulNodeShutdownInProgress</code>의 팀별 완료 정의를 적었다.</li>
<li><input disabled="" type="checkbox"> condition writer, Node patch RBAC, owner handoff, audit log와 clear 절차를 정했다.</li>
<li><input disabled="" type="checkbox"> v1.37에서는 기존 cordon·drain·taint·PDB·traffic drain을 대체하지 않는다는 원칙을 runbook에 명시했다.</li>
<li><input disabled="" type="checkbox"> maintenance ticket, condition, node event, load balancer 상태를 한 화면 또는 한 runbook에서 대조한다.</li>
<li><input disabled="" type="checkbox"> stale maintenance, stalled drain, 완료 뒤 미해제 상태에 대한 사람 확인 임계값을 정했다.</li>
<li><input disabled="" type="checkbox"> 1~2 node에서 48시간 canary하고, consumer automation은 action 없는 shadow mode로 검증했다.</li>
<li><input disabled="" type="checkbox"> 자동 cordon/eviction으로 승격하기 전에 PDB·capacity·rollback·human approval을 별도 게이트로 둔다.</li>
</ul>
<p>연습으로 최근 node maintenance 한 건을 골라 보자. change ticket의 예정 시각, traffic 제거 시각, cordon, drain 시작/완료, reboot, workload 복귀, uncordon을 타임라인으로 적는다. 그다음 어느 지점에 다섯 lifecycle condition을 기록할지와 각 writer를 정한다. 마지막으로 ticket이 취소되거나 drain이 PDB에서 멈춘 상황을 넣어 condition을 누가 언제 clear할지 써 보면, 이 기능의 핵심이 자동화 문법이 아니라 운영 책임의 명확화라는 점이 드러난다.</p>
<h2 id="관련-글">관련 글</h2>
<ul>
<li><a href="/learning/deep-dive/deep-dive-kubernetes-rollouts/">Kubernetes Rollout 전략</a></li>
<li><a href="/learning/deep-dive/deep-dive-graceful-shutdown/">Graceful Shutdown</a></li>
<li><a href="/learning/deep-dive/deep-dive-load-balancer-healthchecks/">Load Balancer Health Check</a></li>
<li><a href="/posts/2026-09-08-kubernetes-rootless-node-components-trend/">Kubernetes v1.37 Rootless Node Components</a></li>
</ul>
]]></content:encoded></item></channel></rss>