<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>DevOps Archives -</title>
	<atom:link href="https://blog.kwt.co.kr/tag/devops/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.kwt.co.kr/tag/devops/</link>
	<description>여러분의 돈과 시간을 낭비하지마세요.</description>
	<lastBuildDate>Tue, 24 Feb 2026 00:32:44 +0000</lastBuildDate>
	<language>ko-KR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.2</generator>

<image>
	<url>https://blog.kwt.co.kr/wp-content/uploads/2022/07/cropped-logo_bg-32x32.jpg</url>
	<title>DevOps Archives -</title>
	<link>https://blog.kwt.co.kr/tag/devops/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Kubernetes 온프레미스 클러스터 업그레이드하기</title>
		<link>https://blog.kwt.co.kr/kubernetes-%ec%98%a8%ed%94%84%eb%a0%88%eb%af%b8%ec%8a%a4-%ed%81%b4%eb%9f%ac%ec%8a%a4%ed%84%b0-%ec%97%85%ea%b7%b8%eb%a0%88%ec%9d%b4%eb%93%9c%ed%95%98%ea%b8%b0/</link>
					<comments>https://blog.kwt.co.kr/kubernetes-%ec%98%a8%ed%94%84%eb%a0%88%eb%af%b8%ec%8a%a4-%ed%81%b4%eb%9f%ac%ec%8a%a4%ed%84%b0-%ec%97%85%ea%b7%b8%eb%a0%88%ec%9d%b4%eb%93%9c%ed%95%98%ea%b8%b0/#respond</comments>
		
		<dc:creator><![CDATA[시간 조절자]]></dc:creator>
		<pubDate>Sun, 22 Feb 2026 14:58:08 +0000</pubDate>
				<category><![CDATA[기술]]></category>
		<category><![CDATA[쿠버네티스]]></category>
		<category><![CDATA[CKA]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[etcd]]></category>
		<category><![CDATA[kubeadm]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[온프레미스]]></category>
		<category><![CDATA[클러스터 업그레이드]]></category>
		<guid isPermaLink="false">https://blog.kwt.co.kr/?p=1619</guid>

					<description><![CDATA[<p>6노드 온프레미스 Kubernetes 클러스터를 v1.30.4에서 v1.31.14로 업그레이드한 과정을 정리한다. etcd 백업부터 control-plane, worker 노드 순차 업그레이드까지 실전에서 주의할 점을 공유한다.</p>
<p>The post <a href="https://blog.kwt.co.kr/kubernetes-%ec%98%a8%ed%94%84%eb%a0%88%eb%af%b8%ec%8a%a4-%ed%81%b4%eb%9f%ac%ec%8a%a4%ed%84%b0-%ec%97%85%ea%b7%b8%eb%a0%88%ec%9d%b4%eb%93%9c%ed%95%98%ea%b8%b0/">Kubernetes 온프레미스 클러스터 업그레이드하기</a> appeared first on <a href="https://blog.kwt.co.kr"></a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">들어가며</h2>



<p><a href="https://blog.kwt.co.kr/?p=744">이전 포스팅</a>에서 집에 굴러다니는 미니PC들로 쿠버네티스 클러스터를 구축한 이야기를 했었다.</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-black-color"><a href="https://kwt.co.kr/kubernetes">쿠버네티스 클러스터 둘러보기</a></mark></p>



<p>그때 설치한 버전이 v1.30.4였는데, 그 뒤로 Jenkins, Kafka, MySQL InnoDB Cluster, Redis 같은 것들을 하나씩 올리면서 &#8220;잘 돌아가는데 굳이 건드려야 하나&#8221; 싶어서 업그레이드를 계속 미뤄왔다. 근데 v1.30 지원 종료(EOL)도 되었고(on-prem은 2025년에 만료), CKA 시험 준비를 하면서 kubeadm 업그레이드를 공부하다 보니 이참에 직접 해보자 싶었다.</p>



<p>실제 프로덕션 워크로드가 돌아가는 환경에서 하는 거라 우려했는데, 막상 절차대로 하니까 생각보다 어렵지 않았다. 그 과정을 기록해둔다.</p>



<h2 class="wp-block-heading">3줄 요약</h2>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<ul class="wp-block-list">
<li>Kubernetes는 <strong>한 번에 1 마이너 버전씩만</strong> 업그레이드할 수 있다 (v1.30 → v1.31 OK, v1.30 → v1.32 불가. <s>도대체 왜 이렇게 만든건가?</s>)</li>



<li>반드시 <strong>control-plane 먼저, worker 나중에</strong> 순서를 지켜야 한다</li>



<li>업그레이드 전 <strong>etcd 백업은 필수</strong> &#8211; 실패 시 복구할 수 있는 유일한 보험</li>
</ul>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Kubernetes 클러스터 구성</h2>



<p>업그레이드 대상 클러스터 구성은 이렇게 생겼다:</p>



<p>Kubernetes Cluster v1.30.4</p>



<p>Control Plane (3대)</p>



<ul class="wp-block-list">
<li>luckys-worker0
<ul class="wp-block-list">
<li>ingress-nginx-controller</li>



<li>redis-node-0</li>



<li>mysql-operator</li>
</ul>
</li>



<li>luckys-worker1
<ul class="wp-block-list">
<li>ingress-nginx-controller</li>



<li>dev-mysql-cluster-0</li>
</ul>
</li>



<li>luckys-worker2
<ul class="wp-block-list">
<li>kafka-controller-2</li>



<li>redis-node-2</li>
</ul>
</li>
</ul>



<p>Worker (3대)</p>



<ul class="wp-block-list">
<li><s>luckys-worker3 &#8211; 사망</s></li>



<li>luckys-worker4
<ul class="wp-block-list">
<li>kafka-controller-1</li>



<li>prod-mysql-cluster-1</li>



<li>loki (로그 수집)</li>
</ul>
</li>



<li>luckys-worker5
<ul class="wp-block-list">
<li>kafka-controller-0</li>



<li>prod-mysql-cluster-2</li>



<li>prometheus, alertmanager</li>



<li>redis-dev-master (standalone)</li>
</ul>
</li>



<li>luckys-worker6
<ul class="wp-block-list">
<li>prod-mysql-cluster-0</li>



<li>ingress-nginx-controller</li>



<li>jenkins</li>



<li>nexus, openclaw, grafana</li>
</ul>
</li>
</ul>



<p>주요 워크로드: Jenkins, Kafka, MySQL InnoDB Cluster, Redis Sentinel, Longhorn, MetalLB, Ingress-Nginx, 이외 다수 Application</p>



<p>OS는 Ubuntu 24.04 LTS, 컨테이너 런타임은 containerd.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">사전 준비</h2>



<h3 class="wp-block-heading">1. 현재 버전 확인</h3>



<pre class="wp-block-code"><code>kubeadm version
# kubeadm version: v1.30.4

kubelet --version
# Kubernetes v1.30.4

kubectl version</code></pre>



<h3 class="wp-block-heading">2. 업그레이드 가능한 버전 확인</h3>



<p>v1.31 저장소를 추가하고 사용 가능한 버전을 확인한다:</p>



<pre class="wp-block-code"><code># v1.31 저장소 추가
echo 'deb &#91;signed-by=/etc/apt/keyrings/kubernetes-apt-keyring-v1.31.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' \
  | sudo tee /etc/apt/sources.list.d/kubernetes-v1.31.list

# GPG 키 등록
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key \
  | sudo gpg --dearmor --yes -o /etc/apt/keyrings/kubernetes-apt-keyring-v1.31.gpg

sudo apt update
sudo apt-cache madison kubeadm | head -5</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>참고:</strong> 기존 v1.30 저장소의 GPG 키가 만료되어 <code>apt update</code> 시 에러가 발생할 수 있다. v1.31 저장소만 정상이면 업그레이드 진행에 문제없다.</p>
</blockquote>



<h3 class="wp-block-heading">3. etcd 백업 (필수!)</h3>



<p>업그레이드 전 반드시 etcd를 백업한다. 문제가 생기면 이 백업으로 클러스터를 복구할 수 있다.</p>



<pre class="wp-block-code"><code># 인증서 경로 확인
kubectl describe pod etcd-luckys-worker0 -n kube-system

# etcd 백업 실행
export ETCDCTL_API=3
etcdctl snapshot save /opt/etcd-backup-before-upgrade.db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# 백업 확인
etcdctl snapshot status /opt/etcd-backup-before-upgrade.db --write-out=table</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>etcdctl이 설치되어 있지 않다면 <code>kubectl exec</code>으로 etcd Pod 안에서 실행하거나, etcd 바이너리를 직접 설치하면 된다.</p>
</blockquote>



<h3 class="wp-block-heading">4. 노드별 워크로드 분포 확인</h3>



<p>drain 하면 영향받는 워크로드를 미리 파악해야 한다:</p>



<pre class="wp-block-code"><code># 각 노드에서 돌아가는 Pod 확인
kubectl get pods -A -o wide --field-selector spec.nodeName=luckys-worker0 | grep -v kube-system

# Taint 확인 (control-plane에 Taint가 없으면 일반 워크로드도 올라가 있을 수 있음)
kubectl describe node luckys-worker0 | grep -i taint</code></pre>



<p>내 클러스터는 control-plane에 Taint가 설정되어 있지 않아서(마스터도 예외 없다) 일반 워크로드도 control-plane 노드에서 실행되고 있었다. MySQL InnoDB Cluster, Redis, Ingress 등의 분포를 확인하고 drain해도 프로덕션에 영향이 없는지 검증한 후 진행했다.</p>



<h3 class="wp-block-heading">업그레이드 시 안전도</h3>



<p>비교적 안전</p>



<ul class="wp-block-list">
<li>luckys-worker0 (prod 1개뿐, MySQL/Kafka 없음)</li>



<li>luckys-worker2 (워크로드 적음)</li>
</ul>



<p>우려됨</p>



<ul class="wp-block-list">
<li>luckys-worker1 (prod 앱 + ingress)</li>



<li>luckys-worker5 (모니터링 + MySQL + Kafka)</li>
</ul>



<p>매우 우려됨</p>



<ul class="wp-block-list">
<li>luckys-worker4 (워크로드 최다 + MySQL + Kafka)</li>



<li>luckys-worker6 (prod 앱 많음 + MySQL + Jenkins + Ingress)</li>
</ul>



<p>사실 다른 것도 그렇지만, Longhorn 으로 데이터가 서로 다른 노드에 동기화 되어야 하는데, upgrade로 인한 중단 시 쓰기 지연이 발생할 경우 클러스터 전체의 성능이 대폭 하락하는 문제가 있어서 이 지점이 가장 골머리 아프다.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">업그레이드 순서</h2>



<p><strong>반드시 control-plane → worker 순서로 해야한다.</strong> kubelet은 apiserver보다 높은 버전일 수 없기 때문이다.</p>



<pre class="wp-block-code"><code>1. Control Plane #1 (luckys-worker0) ← 첫 번째는 kubeadm upgrade apply
2. Control Plane #2 (luckys-worker1) ← 이후는 kubeadm upgrade node
3. Control Plane #3 (luckys-worker2)
4. Worker #1 (luckys-worker4)        ← 전부 kubeadm upgrade node
5. Worker #2 (luckys-worker5)
6. Worker #3 (luckys-worker6)</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Control Plane 첫 번째 노드 업그레이드</h2>



<p>첫 번째 control-plane 노드만 <code>kubeadm upgrade apply</code>를 사용한다. 나머지는 전부 <code>kubeadm upgrade node</code>를 쓴다.</p>



<h3 class="wp-block-heading">Step 1: kubeadm 업그레이드</h3>



<pre class="wp-block-code"><code># kubeadm 패키지 잠금 해제 → 설치 → 다시 잠금
apt-mark unhold kubeadm &amp;&amp; \
apt-get update &amp;&amp; apt-get install -y kubeadm=1.31.14-1.1 &amp;&amp; \
apt-mark hold kubeadm</code></pre>



<h3 class="wp-block-heading">Step 2: 업그레이드 계획 확인 및 실행</h3>



<pre class="wp-block-code"><code># 버전 확인
kubeadm version

# 업그레이드 계획 확인
sudo kubeadm upgrade plan

# 업그레이드 실행
sudo kubeadm upgrade apply v1.31.14</code></pre>



<p><code>kubeadm upgrade plan</code>은 현재 상태를 분석해서 업그레이드 가능 여부를 보여준다. 문제가 없으면 <code>apply</code>로 실제 업그레이드를 진행한다.</p>



<h3 class="wp-block-heading">Step 3: 노드에서 Pod 퇴거 (drain)</h3>



<p>kubeadm upgrade 후, kubelet 업그레이드 전에 drain한다.</p>



<pre class="wp-block-code"><code>kubectl drain luckys-worker0 --ignore-daemonsets</code></pre>



<ul class="wp-block-list">
<li><code>--ignore-daemonsets</code>: DaemonSet Pod(모니터링, 네트워크 등)은 무시</li>



<li>emptyDir 사용하는 Pod 때문에 실패하면 <code>--delete-emptydir-data</code> 추가</li>
</ul>



<h3 class="wp-block-heading">Step 4: kubelet &amp; kubectl 업그레이드</h3>



<pre class="wp-block-code"><code># 패키지 잠금 해제 → 설치 → 다시 잠금
apt-mark unhold kubelet kubectl &amp;&amp; \
apt-get update &amp;&amp; apt-get install -y kubelet=1.31.14-1.1 kubectl=1.31.14-1.1 &amp;&amp; \
apt-mark hold kubelet kubectl

# kubelet 재시작
sudo systemctl daemon-reload
sudo systemctl restart kubelet</code></pre>



<h3 class="wp-block-heading">Step 5: 노드 복귀 (uncordon)</h3>



<pre class="wp-block-code"><code>kubectl uncordon luckys-worker0</code></pre>



<h3 class="wp-block-heading">Step 6: 업그레이드 확인</h3>



<pre class="wp-block-code"><code>kubectl get nodes
# luckys-worker0의 VERSION이 v1.31.14로 변경되었는지 확인</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">나머지 Control Plane 노드 업그레이드</h2>



<p>두 번째, 세 번째 control-plane 노드는 <strong><code>kubeadm upgrade node</code></strong>를 사용한다. <code>apply</code>가 아닌 점에 주의. <code>kubeadm upgrade plan</code>도 불필요하다.</p>



<p>각 노드에 SSH 접속 후:</p>



<pre class="wp-block-code"><code># 1. kubeadm 업그레이드
apt-mark unhold kubeadm &amp;&amp; \
apt-get update &amp;&amp; apt-get install -y kubeadm=1.31.14-1.1 &amp;&amp; \
apt-mark hold kubeadm

# 2. 노드 업그레이드
sudo kubeadm upgrade node          # ← apply가 아닌 node!

# 3. drain (다른 control-plane 노드에서 실행)
kubectl drain luckys-worker1 --ignore-daemonsets

# 4. kubelet &amp; kubectl 업그레이드
apt-mark unhold kubelet kubectl &amp;&amp; \
apt-get update &amp;&amp; apt-get install -y kubelet=1.31.14-1.1 kubectl=1.31.14-1.1 &amp;&amp; \
apt-mark hold kubelet kubectl

sudo systemctl daemon-reload
sudo systemctl restart kubelet

# 5. uncordon (다른 노드에서 실행)
kubectl uncordon luckys-worker1</code></pre>



<p>luckys-worker2도 동일하게 진행한다.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Worker 노드 업그레이드</h2>



<p>Worker 노드도 거의 동일하다. <code>kubeadm upgrade node</code>를 사용한다.</p>



<pre class="wp-block-code"><code># 1. kubeadm 업그레이드
apt-mark unhold kubeadm &amp;&amp; \
apt-get update &amp;&amp; apt-get install -y kubeadm=1.31.14-1.1 &amp;&amp; \
apt-mark hold kubeadm

# 2. 노드 업그레이드
sudo kubeadm upgrade node

# 3. drain (control-plane에서 실행)
kubectl drain luckys-worker4 --ignore-daemonsets

# 4. kubelet &amp; kubectl
apt-mark unhold kubelet kubectl &amp;&amp; \
apt-get update &amp;&amp; apt-get install -y kubelet=1.31.14-1.1 kubectl=1.31.14-1.1 &amp;&amp; \
apt-mark hold kubelet kubectl

sudo systemctl daemon-reload
sudo systemctl restart kubelet

# 5. uncordon (control-plane에서 실행)
kubectl uncordon luckys-worker4</code></pre>



<p>luckys-worker5, luckys-worker6도 동일하게 진행한다.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">전체 업그레이드 완료 확인</h2>



<pre class="wp-block-code"><code>kubectl get nodes -o wide</code></pre>



<pre class="wp-block-code"><code>NAME             STATUS   ROLES           VERSION    OS-IMAGE
luckys-worker0   Ready    control-plane   v1.31.14   Ubuntu 24.04 LTS
luckys-worker1   Ready    control-plane   v1.31.14   Ubuntu 24.04 LTS
luckys-worker2   Ready    control-plane   v1.31.14   Ubuntu 24.04 LTS
luckys-worker4   Ready    &lt;none&gt;          v1.31.14   Ubuntu 24.04 LTS
luckys-worker5   Ready    &lt;none&gt;          v1.31.14   Ubuntu 24.04 LTS
luckys-worker6   Ready    &lt;none&gt;          v1.31.14   Ubuntu 24.04 LTS</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">주의사항 &amp; 삽질 기록</h2>



<h3 class="wp-block-heading">GPG 키 만료 문제</h3>



<p>v1.30 저장소의 GPG 키가 만료되어 <code>apt update</code> 시 에러가 났다:</p>



<pre class="wp-block-code"><code>EXPKEYSIG 234654DA9A296436 isv:kubernetes OBS Project</code></pre>



<p>v1.31 저장소를 새로 추가하고 키를 등록하면 해결된다. 기존 v1.30 저장소 에러는 무시해도 된다.</p>



<p><strong>1. v1.31 GPG 키 다운로드 및 등록</strong></p>



<pre class="wp-block-code"><code>curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring-v1.31.gpg</code></pre>



<p><strong>2. v1.31 저장소 추가</strong></p>



<pre class="wp-block-code"><code>echo 'deb &#91;signed-by=/etc/apt/keyrings/kubernetes-apt-keyring-v1.31.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes-v1.31.list</code></pre>



<h3 class="wp-block-heading">control-plane에 Taint가 없는 경우</h3>



<p>일반적으로 control-plane 노드에는 <code>NoSchedule</code> Taint가 설정되어 있어서 일반 워크로드가 배치되지 않는다. 근데 내 클러스터처럼 Taint가 없으면 MySQL, Redis, Ingress 등이 control-plane에서도 실행된다.(Taint를 설정하기엔 비용 이슈가..)</p>



<p>drain 전에 반드시 해당 노드의 워크로드를 확인하고, 프로덕션 영향을 검토해야 한다:</p>



<pre class="wp-block-code"><code>kubectl get pods -A -o wide --field-selector spec.nodeName=&lt;노드명&gt; | grep -v kube-system</code></pre>



<h3 class="wp-block-heading">drain vs cordon</h3>



<ul class="wp-block-list">
<li><code>kubectl drain</code>: 기존 Pod를 다른 노드로 퇴거시키고 스케줄 차단</li>



<li><code>kubectl cordon</code>: 새 Pod 스케줄만 차단, 기존 Pod는 그대로</li>
</ul>



<p>프로덕션 영향이 걱정되면 <code>cordon</code>만 하고 업그레이드를 진행하는 방법도 있다. kubelet 재시작 시 잠깐 중단되지만 Pod가 다른 노드로 이동하지는 않는다.</p>



<h3 class="wp-block-heading">MySQL InnoDB Cluster 고려</h3>



<p>MySQL InnoDB Cluster는 3개 인스턴스가 서로 다른 노드에 분산되어 있어서, 한 노드를 drain해도 나머지 2개가 쿼럼을 유지한다. drain 전에 어떤 노드에 어떤 인스턴스가 있는지 확인하자:</p>



<pre class="wp-block-code"><code>kubectl get pods -A -o wide | grep mysql
kubectl get innodbcluster -A</code></pre>



<h3 class="wp-block-heading">한 대씩, 확인하면서</h3>



<p>절대 여러 노드를 동시에 drain하지 말자. 특히 control-plane은 etcd 쿼럼(과반수) 유지가 필수다. 3대 중 2대가 동시에 내려가면 클러스터가 멈춘다.</p>



<pre class="wp-block-code"><code>한 대 업그레이드 완료 → kubectl get nodes로 Ready 확인 → 다음 노드</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">업그레이드 절차 요약</h2>



<pre class="wp-block-code"><code>&#91;사전 준비]
  etcd 백업 → 저장소 추가 → 워크로드 분포 확인

&#91;Control Plane 첫 번째 노드]
  kubeadm 설치 → kubeadm upgrade apply → drain → kubelet kubectl 설치 → restart → uncordon

&#91;Control Plane 나머지 + Worker 전체]
  kubeadm 설치 → kubeadm upgrade node → drain → kubelet kubectl 설치 → restart → uncordon

&#91;완료]
  kubectl get nodes로 전체 버전 확인</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">마치며</h2>



<p>막상 해보니 절차만 지키면 크게 어렵지 않아서 이걸 왜이리 미뤄왔나 싶다.</p>



<p>핵심은 세 가지다:</p>



<ol class="wp-block-list">
<li><strong>etcd 백업</strong>: 만약을 위한 보험</li>



<li><strong>순서 준수</strong>: control-plane 먼저, worker 나중에</li>



<li><strong>한 대씩</strong>: 확인하고 넘어가기</li>
</ol>



<p>CKA 시험에서도 kubeadm 업그레이드는 거의 매번 출제되는 문제라고 한다. 실제 클러스터에서 한 번 해보면 시험에서도 별 문제 없이 풀 수 있을 것 같다.</p>



<h3 class="wp-block-heading">참고 링크</h3>



<ul class="wp-block-list">
<li><a href="https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/">Kubernetes 공식 문서 &#8211; Upgrading kubeadm clusters</a></li>



<li><a href="https://kubernetes.io/releases/version-skew-policy/">Kubernetes Version Skew Policy</a></li>



<li><a href="https://training.linuxfoundation.org/certification/certified-kubernetes-administrator-cka/">CKA 시험 공식 페이지</a></li>



<li><a href="https://blog.kwt.co.kr/?p=744">홈 서버 쿠버네티스 클러스터 구축기</a></li>
</ul>
		<div class="wpulike wpulike-robeen " ><div class="wp_ulike_general_class wp_ulike_is_restricted"><button type="button"
					aria-label="Like Button"
					data-ulike-id="1619"
					data-ulike-nonce="3ddfbdb81c"
					data-ulike-type="post"
					data-ulike-template="wpulike-robeen"
					data-ulike-display-likers=""
					data-ulike-likers-style="popover"
					class="wp_ulike_btn wp_ulike_put_image wp_post_btn_1619"></button><span class="count-box wp_ulike_counter_up" data-ulike-counter-value="0"></span>			</div></div>
	<p>The post <a href="https://blog.kwt.co.kr/kubernetes-%ec%98%a8%ed%94%84%eb%a0%88%eb%af%b8%ec%8a%a4-%ed%81%b4%eb%9f%ac%ec%8a%a4%ed%84%b0-%ec%97%85%ea%b7%b8%eb%a0%88%ec%9d%b4%eb%93%9c%ed%95%98%ea%b8%b0/">Kubernetes 온프레미스 클러스터 업그레이드하기</a> appeared first on <a href="https://blog.kwt.co.kr"></a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kwt.co.kr/kubernetes-%ec%98%a8%ed%94%84%eb%a0%88%eb%af%b8%ec%8a%a4-%ed%81%b4%eb%9f%ac%ec%8a%a4%ed%84%b0-%ec%97%85%ea%b7%b8%eb%a0%88%ec%9d%b4%eb%93%9c%ed%95%98%ea%b8%b0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>OpenClaw &#8211; Kubernetes 클러스터에 구축기 with Claude Code</title>
		<link>https://blog.kwt.co.kr/openclaw-kubernetes-%ed%81%b4%eb%9f%ac%ec%8a%a4%ed%84%b0%ec%97%90-%ea%b5%ac%ec%b6%95%ed%95%98%ea%b8%b0/</link>
					<comments>https://blog.kwt.co.kr/openclaw-kubernetes-%ed%81%b4%eb%9f%ac%ec%8a%a4%ed%84%b0%ec%97%90-%ea%b5%ac%ec%b6%95%ed%95%98%ea%b8%b0/#respond</comments>
		
		<dc:creator><![CDATA[시간 조절자]]></dc:creator>
		<pubDate>Sun, 08 Feb 2026 03:56:38 +0000</pubDate>
				<category><![CDATA[기술]]></category>
		<category><![CDATA[AI Agent]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[Claude Code]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Helm]]></category>
		<category><![CDATA[Jenkins]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[OpenClaw]]></category>
		<guid isPermaLink="false">https://blog.kwt.co.kr/?p=1461</guid>

					<description><![CDATA[<p>오픈소스 AI 자동화 프레임워크 OpenClaw을 Kubernetes 클러스터에 설치하고, kubectl/Jenkins/GitHub과 연동하여 자율적으로 코드 작성부터 배포까지 수행하는 DevOps 에이전트를 구축한 과정을 공유합니다.</p>
<p>The post <a href="https://blog.kwt.co.kr/openclaw-kubernetes-%ed%81%b4%eb%9f%ac%ec%8a%a4%ed%84%b0%ec%97%90-%ea%b5%ac%ec%b6%95%ed%95%98%ea%b8%b0/">OpenClaw &#8211; Kubernetes 클러스터에 구축기 with Claude Code</a> appeared first on <a href="https://blog.kwt.co.kr"></a>.</p>
]]></description>
										<content:encoded><![CDATA[


<h2 class="wp-block-heading">들어가며</h2>



<p>&#8220;AI에게 서버 관리를 맡길 수 있을까?&#8221;</p>



<p>최근 AI 에이전트 기술이 빠르게 발전하면서, 단순한 챗봇을 넘어 <strong>실제 인프라를 관리하는 AI</strong>에 대한 관심이 높아지고 있습니다. 이번 글에서는 오픈소스 AI 자동화 프레임워크인 <strong>OpenClaw</strong>을 Kubernetes 클러스터에 설치하고, <strong>코드 작성 → GitHub 푸시 → Jenkins 빌드 → K8s 배포</strong>까지 자율적으로 수행하는 DevOps 에이전트를 구축한 과정을 공유합니다.</p>



<p>전체 구현 과정은 <strong>Claude Code</strong>(Anthropic의 CLI 기반 AI 코딩 도구)와 함께 진행했으며, 설정 파일 작성부터 트러블슈팅까지 실시간으로 협업하며 완성했습니다.</p>



<figure class="wp-block-image size-full is-resized"><img fetchpriority="high" decoding="async" width="1516" height="1398" src="https://blog.kwt.co.kr/wp-content/uploads/2026/02/스크린샷-2026-02-08-오후-12.27.19.png" alt="openclaw 가재상 열일" class="wp-image-1464" style="width:582px;height:auto"/></figure>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">핵심요약</h2>



<ul class="wp-block-list">
<li>쿠버네티스 클러스터 내에 pod 로 실행중</li>



<li>슬랙과 연동해서 대화형으로 구성</li>



<li>GLM-4.7 (영균매니저님이 소개해준 Z.AI)을 main model 로 사용</li>



<li>pod 에서 상위 레벨인 node 상태와 클러스터 제어를 위해 권한 부여(service account의 RBAC 설정)</li>



<li>GLM 모델 특성(원래 느림)도 있겠지만 상호작용이 개느림, 답답</li>
</ul>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">OpenClaw ?</h2>



<p><strong>OpenClaw</strong>은 2026년 초 등장한 오픈소스 AI 자동화 프레임워크입니다. 주요 특징은 다음과 같습니다:</p>



<ul class="wp-block-list">
<li><strong>다양한 LLM 지원</strong>: Anthropic Claude, OpenAI GPT, Google Gemini, Z.AI GLM 등</li>



<li><strong>멀티 채널</strong>: 웹 UI, Slack, Discord, WhatsApp 등으로 대화 가능</li>



<li><strong>도구 실행</strong>: 셸 명령어, 파일 조작, 웹 브라우저 자동화 기능 내장</li>



<li><strong>Helm Chart 제공</strong>: Kubernetes 배포를 위한 공식 Helm 차트 지원</li>
</ul>



<p>핵심은 AI가 단순히 텍스트를 생성하는 것을 넘어, <strong>실제 명령어를 실행하고 시스템을 제어</strong>할 수 있다는 점입니다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">최종 아키텍처</h2>



<p>구축 완료 후의 전체 아키텍처는 다음과 같습니다:</p>



<pre class="wp-block-code"><code>┌─────────────────────────────────────────────────────────┐
│  Kubernetes Cluster (v1.30.4, 6 nodes)                  │
│                                                         │
│  ┌─────────── openclaw namespace ──────────┐            │
│  │  ┌──────────────────────────────────┐   │            │
│  │  │  OpenClaw Pod (2 containers)     │   │            │
│  │  │  ├─ main: OpenClaw Agent         │   │            │
│  │  │  │   ├─ kubectl (RBAC)    ──────────────→ K8s API │
│  │  │  │   ├─ helm              ──────────────→ K8s API │
│  │  │  │   ├─ git               ──────────────→ GitHub  │
│  │  │  │   └─ curl              ──────────────→ Jenkins │
│  │  │  └─ chromium: Browser Sidecar    │   │            │
│  │  └──────────────────────────────────┘   │            │
│  │  ServiceAccount: openclaw-sa            │            │
│  │  ClusterRole: Full K8s Access           │            │
│  └─────────────────────────────────────────┘            │
│                                                         │
│  ┌─── corpbreak-com-ingress ns ───┐                     │
│  │  ExternalName Svc + Ingress    │                     │
│  │  → openclaw.corpbreak.com      │                     │
│  └────────────────────────────────┘                     │
└─────────────────────────────────────────────────────────┘
         &#x2195;                    &#x2195;                &#x2195;
    &#091;Slack Bot]         &#091;Web UI HTTPS]    &#091;Jenkins API]
</code></pre>



<h3 class="wp-block-heading">주요 구성 요소</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>구성 요소</th><th>역할</th></tr></thead><tbody><tr><td>OpenClaw Main Container</td><td>AI 에이전트 엔진, LLM 호출, 도구 실행</td></tr><tr><td>Chromium Sidecar</td><td>브라우저 자동화 (웹 스크래핑, 스크린샷)</td></tr><tr><td>ServiceAccount + RBAC</td><td>Pod에서 K8s API 접근 권한 부여</td></tr><tr><td>Init Containers</td><td>kubectl, helm 바이너리 설치 + 시스템 프롬프트 주입</td></tr><tr><td>Ingress</td><td>HTTPS 외부 접근 (WebSocket 지원)</td></tr></tbody></table></figure>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">구현 과정</h2>



<h3 class="wp-block-heading">Step 1: Helm으로 기본 설치</h3>



<p>먼저 OpenClaw의 공식 Helm 차트를 이용하여 기본 설치를 진행했습니다.</p>



<pre class="wp-block-code"><code># Helm 레포지토리 추가
helm repo add openclaw https://serhanekicii.github.io/openclaw-helm
helm repo update

# 네임스페이스 생성 및 설치
kubectl create namespace openclaw
helm install openclaw openclaw/openclaw -n openclaw -f values.yaml
</code></pre>



<p>LLM은 Z.AI의 <strong>GLM 4.7</strong> 모델을 선택했습니다. OpenClaw은 <code>zai</code>를 빌트인 프로바이더로 지원하기 때문에, API 키만 환경변수로 설정하면 됩니다.</p>



<h3 class="wp-block-heading">Step 2: 첫 번째 장애 &#8211; Gateway Token</h3>



<p>설치 직후 Pod가 <strong>CrashLoopBackOff</strong> 상태에 빠졌습니다.</p>



<pre class="wp-block-code"><code>Config invalid
Problem: Gateway auth is set to token, but no token is configured
</code></pre>



<p>OpenClaw의 Gateway는 인증 토큰이 필수입니다. <code>openssl rand -hex 32</code>로 토큰을 생성하고 Secret에 추가한 후, 설정 파일에서 환경변수로 참조하도록 수정했습니다.</p>



<pre class="wp-block-code"><code>"gateway": {
  "auth": {
    "token": "${GATEWAY_TOKEN}"
  }
}
</code></pre>



<h3 class="wp-block-heading">Step 3: HTTPS Ingress 구성</h3>



<p>기존 인프라의 패턴에 맞춰 <strong>ExternalName Service + Ingress</strong> 조합으로 외부 접근을 구성했습니다.</p>



<pre class="wp-block-code"><code># ExternalName Service (cross-namespace routing)
apiVersion: v1
kind: Service
metadata:
  name: openclaw-external
  namespace: corpbreak-com-ingress
spec:
  type: ExternalName
  externalName: openclaw.openclaw.svc.cluster.local
</code></pre>



<p>OpenClaw의 WebUI는 <strong>WebSocket</strong>을 사용하므로, Ingress에 다음 annotation이 필수입니다:</p>



<pre class="wp-block-code"><code>nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
</code></pre>



<h3 class="wp-block-heading">Step 4: WebSocket 연결 오류 해결</h3>



<p>접속 시도 시 여러 WebSocket 에러가 연이어 발생했습니다:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>에러</th><th>원인</th><th>해결</th></tr></thead><tbody><tr><td><code>origin not allowed</code></td><td>CORS 설정 누락</td><td><code>gateway.controlUi.allowedOrigins</code> 추가</td></tr><tr><td><code>gateway token missing</code></td><td>토큰 전달 방식</td><td>URL 쿼리 파라미터로 토큰 전달</td></tr><tr><td><code>pairing required</code></td><td>디바이스 승인 필요</td><td><code>kubectl exec</code>로 디바이스 승인</td></tr></tbody></table></figure>



<p>각 오류를 하나씩 해결한 끝에 웹 UI 접속에 성공했습니다.</p>



<figure class="wp-block-image size-full"><img decoding="async" width="1718" height="800" src="https://blog.kwt.co.kr/wp-content/uploads/2026/02/스크린샷-2026-02-08-오후-12.36.34.png" alt="openclaw ui 첫대화" class="wp-image-1466"/></figure>



<h3 class="wp-block-heading">Step 5: Slack 연동</h3>



<p>OpenClaw은 Slack Socket Mode를 지원합니다. Slack App을 생성하고 다음 두 토큰을 Secret에 추가하면 됩니다:</p>



<ul class="wp-block-list">
<li><strong>App-Level Token</strong> (<code>xapp-</code>): Socket Mode 연결용</li>



<li><strong>Bot Token</strong> (<code>xoxb-</code>): 메시지 송수신용</li>
</ul>



<pre class="wp-block-code"><code>"channels": {
  "slack": {
    "enabled": true,
    "appToken": "${SLACK_APP_TOKEN}",
    "botToken": "${SLACK_BOT_TOKEN}"
  }
}
</code></pre>



<p>설정 후 OpenClaw 로그에서 <code>socket mode connected</code> 메시지를 확인할 수 있습니다.</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="442" height="436" src="https://blog.kwt.co.kr/wp-content/uploads/2026/02/image-2.png" alt="openclaw, 가재상 첫 대답" class="wp-image-1467" style="width:305px;height:auto"/></figure>



<h3 class="wp-block-heading">Step 6: 브라우저 자동화 설정</h3>



<p>OpenClaw의 브라우저 기능을 활성화하려면 두 가지가 필요합니다:</p>



<ol class="wp-block-list">
<li><strong>Chromium Sidecar Container</strong>: CDP(Chrome DevTools Protocol) 서버 제공</li>



<li><strong>Playwright 브라우저 바이너리</strong>: Main 컨테이너에서 직접 브라우저 실행</li>
</ol>



<p>Chromium Sidecar는 Helm Chart에서 자동 생성되지만, Playwright 바이너리는 <strong>Init Container</strong>로 별도 설치가 필요했습니다.</p>



<pre class="wp-block-code"><code>initContainers:
  install-browser:
    image:
      repository: ghcr.io/openclaw/openclaw
      tag: "2026.2.3"
    command:
      - sh
      - -c
      - |
        PLAYWRIGHT_BROWSERS_PATH=/home/node/.openclaw/browsers \
        node /app/node_modules/playwright-core/cli.js install chromium
</code></pre>



<h3 class="wp-block-heading">Step 7: DevOps 도구 설치 (kubectl, helm)</h3>



<p>OpenClaw Pod 안에서 클러스터를 관리하려면 kubectl과 helm이 필요합니다. 이들도 Init Container로 설치했습니다.</p>



<pre class="wp-block-code"><code>initContainers:
  install-tools:
    image:
      repository: alpine
      tag: "3.21"
    command:
      - sh
      - -c
      - |
        # kubectl 설치
        wget -q "https://dl.k8s.io/release/v1.30.4/bin/linux/amd64/kubectl" \
          -O /home/node/.openclaw/bin/kubectl
        chmod +x /home/node/.openclaw/bin/kubectl

        # helm 설치
        wget -q "https://get.helm.sh/helm-v3.17.1-linux-amd64.tar.gz" \
          -O /tmp/helm.tar.gz
        tar -xzf /tmp/helm.tar.gz -C /tmp
        mv /tmp/linux-amd64/helm /home/node/.openclaw/bin/helm
</code></pre>



<p>이 바이너리들은 PVC에 저장되므로, Pod가 재시작되어도 다시 다운로드할 필요가 없습니다.</p>



<h3 class="wp-block-heading">Step 8: RBAC 설정 &#8211; Pod에서 클러스터 제어하기</h3>



<p>Kubernetes에서 Pod가 클러스터 API에 접근하려면 <strong>ServiceAccount + ClusterRole + ClusterRoleBinding</strong>이 필요합니다.</p>



<pre class="wp-block-code"><code>apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: openclaw-cluster-role
rules:
  - apiGroups: &#091;""]
    resources: &#091;"nodes", "pods", "pods/log", "services",
                "configmaps", "secrets", "namespaces"]
    verbs: &#091;"get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: &#091;"apps"]
    resources: &#091;"deployments", "replicasets", "statefulsets"]
    verbs: &#091;"get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: &#091;"networking.k8s.io"]
    resources: &#091;"ingresses"]
    verbs: &#091;"get", "list", "watch", "create", "update", "patch", "delete"]
</code></pre>



<p>이렇게 설정하면 OpenClaw이 Pod 내부에서 <code>kubectl get nodes</code>, <code>kubectl get pods -A</code> 등을 자유롭게 실행할 수 있습니다.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="880" height="1136" src="https://blog.kwt.co.kr/wp-content/uploads/2026/02/image-3.png" alt="쿠버네티스를 점령한 openclaw 가재상" class="wp-image-1468" style="width:391px;height:auto"/></figure>



<h3 class="wp-block-heading">Step 9: 시스템 프롬프트 주입 (Bootstrap File)</h3>



<p>OpenClaw이 DevOps 작업을 정확히 수행하려면, 프로젝트 구조와 CI/CD 파이프라인에 대한 지식이 필요합니다.</p>



<p>처음에는 에이전트 설정에 <code>systemPrompt</code> 필드를 직접 추가했으나, <strong>&#8220;Unrecognized key&#8221; 오류로 Pod가 크래시</strong>했습니다. OpenClaw은 시스템 프롬프트를 설정 파일이 아닌 <strong>Bootstrap 파일</strong>로 주입하는 방식을 사용합니다.</p>



<p>Workspace 디렉토리에 <code>AGENTS.md</code> 파일을 생성하면, OpenClaw이 자동으로 이를 감지하여 시스템 프롬프트에 포함시킵니다.</p>



<pre class="wp-block-code"><code>/home/node/.openclaw/workspace/AGENTS.md
</code></pre>



<p>이 파일에 다음 정보를 포함했습니다:</p>



<ul class="wp-block-list">
<li>도구 경로 (kubectl, helm, git)</li>



<li>클러스터 정보 (노드 구성, StorageClass, Registry)</li>



<li>GitHub 인증 방법</li>



<li>전체 서비스 목록과 도메인 매핑</li>



<li>Jenkins API 사용법 (빌드 트리거, 상태 확인, Job 생성)</li>



<li>Blue-Green 배포 파이프라인 구조</li>



<li>deploy-config YAML 템플릿</li>
</ul>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">주요 설정 파일 구조</h2>



<h3 class="wp-block-heading">values.yaml (Helm Values)</h3>



<p>최종 <code>values.yaml</code>의 핵심 구조는 다음과 같습니다:</p>



<pre class="wp-block-code"><code>configMode: overwrite

app-template:
  controllers:
    main:
      serviceAccount:
        name: openclaw-sa          # RBAC 연결
      containers:
        main:
          envFrom:
            - secretRef:
                name: openclaw-env-secret  # API 키, 토큰
          env:
            PATH: /home/node/.openclaw/bin:...  # kubectl, helm 경로

      initContainers:
        install-browser: ...       # Playwright Chromium
        install-tools: ...         # kubectl, helm + AGENTS.md

  configMaps:
    config:
      data:
        openclaw.json: |
          {
            "gateway": { ... },
            "browser": { ... },
            "agents": {
              "defaults": {
                "workspace": "/home/node/.openclaw/workspace",
                "model": { "primary": "zai/glm-4.7" }
              }
            },
            "channels": {
              "slack": { "enabled": true, ... }
            }
          }

  persistence:
    data:
      type: persistentVolumeClaim
      size: 5Gi
      storageClass: longhorn
</code></pre>



<h3 class="wp-block-heading">Secret 구성</h3>



<pre class="wp-block-code"><code># openclaw-env-secret에 포함된 키들
ZAI_API_KEY: ...          # GLM 4.7 API 키
GATEWAY_TOKEN: ...        # Gateway 인증 토큰
SLACK_APP_TOKEN: ...      # Slack Socket Mode
SLACK_BOT_TOKEN: ...      # Slack Bot
GITHUB_TOKEN: ...         # GitHub PAT (코드 push)
JENKINS_URL: ...          # Jenkins API URL
JENKINS_USER: ...         # Jenkins 사용자
JENKINS_TOKEN: ...        # Jenkins API 토큰
</code></pre>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">OpenClaw이 할 수 있는 일</h2>



<p>구축이 완료된 후, OpenClaw은 Slack 메시지 하나로 다음 작업들을 수행할 수 있습니다:</p>



<h3 class="wp-block-heading">1. 클러스터 모니터링</h3>



<pre class="wp-block-code"><code>사용자: "현재 corpbreak-dev 네임스페이스 Pod 상태 확인해줘"
OpenClaw: kubectl get pods -n corpbreak-dev 실행 → 결과 보고
</code></pre>



<h3 class="wp-block-heading">2. 코드 작성 및 GitHub Push</h3>



<pre class="wp-block-code"><code>사용자: "exchange-service에 새 API 엔드포인트 추가해줘"
OpenClaw: git clone → 코드 수정 → git commit → git push
</code></pre>



<h3 class="wp-block-heading">3. Jenkins 빌드 트리거</h3>



<pre class="wp-block-code"><code>사용자: "checklist 서비스 dev 환경에 배포해줘"
OpenClaw: Jenkins API 호출 → 빌드 트리거 → 상태 모니터링
</code></pre>



<h3 class="wp-block-heading">4. 새 서비스 생성 (E2E)</h3>



<pre class="wp-block-code"><code>사용자: "새로운 survey-service를 만들어줘"
OpenClaw:
  1. 스켈레톤 프로젝트 클론
  2. 패키지명/설정 변경
  3. deploy-config YAML 작성
  4. GitHub에 Push
  5. Jenkins Job 생성
  6. 빌드 트리거 및 배포 확인
</code></pre>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Claude Code와 함께한 구현 과정</h2>



<p>이번 구축의 특별한 점은, 전체 과정을 <strong>Claude Code</strong>와 실시간으로 협업하며 진행했다는 것입니다.</p>



<p>Claude Code는 Anthropic이 만든 CLI 기반 AI 코딩 도구로, 터미널에서 직접 파일을 읽고, 수정하고, 명령어를 실행할 수 있습니다. 이번 작업에서 Claude Code가 수행한 역할:</p>



<ul class="wp-block-list">
<li><strong>리서치</strong>: OpenClaw 설치 방법 웹 검색 및 문서 분석</li>



<li><strong>설정 파일 작성</strong>: values.yaml, RBAC, Ingress 등 모든 K8s 매니페스트 생성</li>



<li><strong>Helm 명령 실행</strong>: <code>helm install</code>, <code>helm upgrade</code> 직접 실행</li>



<li><strong>트러블슈팅</strong>: Pod 로그 분석, 에러 원인 파악, 설정 수정</li>



<li><strong>검증</strong>: <code>kubectl exec</code>로 Pod 내부 확인, API 테스트</li>
</ul>



<p><strong>사실상 Claude Code가 모든 것을 만들었다</strong>..!</p>



<p>총 <strong>10번의 Helm revision</strong>을 거치며, 각 단계에서 발생한 오류를 Claude Code가 실시간으로 진단하고 수정했습니다. 특히 &#8220;systemPrompt 필드가 인식되지 않는 문제&#8221;처럼 공식 문서에도 명확히 나와있지 않은 이슈를 웹 검색과 문서 분석을 통해 해결한 과정이 인상적이었습니다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">삽질 기록: 이것만은 알고 시작하세요</h2>



<p>구축 과정에서 겪은 주요 실수들을 정리합니다:</p>



<h3 class="wp-block-heading">1. bjw-s app-template의 ServiceAccount 설정</h3>



<p>OpenClaw Helm 차트는 내부적으로 <strong>bjw-s app-template</strong> 차트를 사용합니다. ServiceAccount를 연결할 때 일반적인 <code>spec.serviceAccountName</code>이 아닌, 차트 고유의 경로를 사용해야 합니다.</p>



<pre class="wp-block-code"><code># 틀린 방법들
defaultPodOptions:
  serviceAccountName: openclaw-sa  # Error: additional properties not allowed

serviceAccount:
  name: openclaw-sa  # Error: got string, want object

# 정답
controllers:
  main:
    serviceAccount:
      name: openclaw-sa
</code></pre>



<h3 class="wp-block-heading">2. OpenClaw 에이전트 설정</h3>



<p>에이전트 설정을 위해서는 workspace의 Bootstrap 파일(<code>AGENTS.md</code>, <code>SOUL.md</code> 등)을 사용해야 합니다.</p>



<h3 class="wp-block-heading">3. nginx-ingress의 configuration-snippet 차단</h3>



<p>nginx-ingress v1.11.2부터 보안상 <code>configuration-snippet</code> annotation이 기본 차단됩니다. WebSocket 지원은 <code>proxy-http-version: "1.1"</code> annotation만으로 충분합니다.</p>



<h3 class="wp-block-heading">4. Alpine에서 git 바이너리 복사 불가</h3>



<p>Alpine Linux에서 설치한 git은 공유 라이브러리에 의존하므로, 바이너리만 복사하면 동작하지 않습니다. 다행히 OpenClaw 이미지에는 <code>/usr/bin/git</code>이 이미 포함되어 있었습니다.</p>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">보안 고려사항</h2>



<p>AI 에이전트에게 클러스터 접근 권한을 부여할 때는 보안에 특별히 주의해야 합니다:</p>



<ul class="wp-block-list">
<li><strong>최소 권한 원칙</strong>: 필요한 리소스와 동작만 RBAC으로 허용</li>



<li><strong>프로덕션 보호</strong>: 시스템 프롬프트에 &#8220;prod 배포는 반드시 사용자 확인 후 진행&#8221; 명시</li>



<li><strong>Secret 관리</strong>: API 키와 토큰은 모두 Kubernetes Secret으로 관리</li>



<li><strong>감사 추적</strong>: OpenClaw의 로깅 설정으로 모든 도구 실행 기록 보관</li>



<li><strong>네트워크 격리</strong>: OpenClaw이 접근할 수 있는 외부 엔드포인트 제한 고려</li>
</ul>



<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">마치며</h2>



<p>AI 에이전트가 실제 인프라를 관리하는 시대가 이미 시작되었습니다. OpenClaw은 아직 초기 단계의 프로젝트이지만, Kubernetes 클러스터 위에서 실제 DevOps 작업을 수행할 수 있다는 가능성을 보여주었습니다.</p>



<p>물론 아직 개선할 점이 있습니다. 브라우저 스크린샷 기능이 완벽하지 않고, 복잡한 멀티스텝 작업에서 간혹 컨텍스트를 놓치기도 합니다. 하지만 &#8220;Slack으로 메시지 하나 보내면 서비스가 배포된다&#8221;는 경험은 충분히 인상적입니다.</p>



<p>단, 이미 충분히 지식이 있고, Claude Code 를 이용한 자동화를 구축해둔 상황이라면, 굳이 OpenClaw 를 설치해서 사용할 필요가 있나? 싶은 생각도 들었습니다. (아직 사용을 제대로 해보지 못해서 그런 것인지.. 맥미니가 다시 중고로 쏟아지지 않을까.!?)</p>



<p>이번 구축 과정에서 <strong>Claude Code</strong>의 역할도 매우 컸습니다. Helm 차트 분석, YAML 설정 작성, 실시간 트러블슈팅까지 &#8211; AI와 함께 AI를 설치하는 재미있는 경험이었습니다.</p>



<p>혹시 비슷한 환경을 구축해보고 싶으신 분이 계시다면, 이 글이 시행착오를 줄이는 데 도움이 되길 바랍니다.</p>



<h3 class="wp-block-heading">참고 링크</h3>



<ul class="wp-block-list">
<li>OpenClaw GitHub: https://github.com/openclaw/openclaw</li>



<li>OpenClaw Helm Chart: https://github.com/serhanekicii/openclaw-helm</li>



<li>OpenClaw 공식 문서: https://docs.openclaw.ai</li>



<li>Claude Code: https://claude.com/claude-code</li>
</ul>



<p></p>
		<div class="wpulike wpulike-robeen " ><div class="wp_ulike_general_class wp_ulike_is_restricted"><button type="button"
					aria-label="Like Button"
					data-ulike-id="1461"
					data-ulike-nonce="c8f6b0b9c5"
					data-ulike-type="post"
					data-ulike-template="wpulike-robeen"
					data-ulike-display-likers=""
					data-ulike-likers-style="popover"
					class="wp_ulike_btn wp_ulike_put_image wp_post_btn_1461"></button><span class="count-box wp_ulike_counter_up" data-ulike-counter-value="0"></span>			</div></div>
	<p>The post <a href="https://blog.kwt.co.kr/openclaw-kubernetes-%ed%81%b4%eb%9f%ac%ec%8a%a4%ed%84%b0%ec%97%90-%ea%b5%ac%ec%b6%95%ed%95%98%ea%b8%b0/">OpenClaw &#8211; Kubernetes 클러스터에 구축기 with Claude Code</a> appeared first on <a href="https://blog.kwt.co.kr"></a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kwt.co.kr/openclaw-kubernetes-%ed%81%b4%eb%9f%ac%ec%8a%a4%ed%84%b0%ec%97%90-%ea%b5%ac%ec%b6%95%ed%95%98%ea%b8%b0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
