<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>취미생활</title>
    <link>https://barisein.tistory.com/</link>
    <description>Windows, Kubernetes, Elasticsearch, 개발 환경에서 겪은 문제와 해결 과정을 정리하는 기술 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Sat, 20 Jun 2026 23:06:40 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>바리새인</managingEditor>
    <item>
      <title>Elasticsearch Pod Pending 원인: local PV와 taint 충돌 해결</title>
      <link>https://barisein.tistory.com/1285</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;ECK로 운영 중인 Elasticsearch에서 data pod가 &lt;code&gt;Pending&lt;/code&gt;에 머물고 custom resource는 &lt;code&gt;ApplyingChanges&lt;/code&gt; 상태를 벗어나지 못하는 경우가 있다. 이때 ECK 설정만 보면 원인을 놓칠 수 있다. 실제 원인은 local PV의 node affinity와 Kubernetes node taint가 충돌해서 scheduler가 pod를 배치하지 못하는 상황일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 이 문제를 빠르게 분리하기 위한 요약이다. 이후 본문에는 당시 확인했던 과정과 판단 변경 지점을 그대로 남겼다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;빠른 결론&lt;/h2&gt;
&lt;ul data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;ApplyingChanges&lt;/code&gt;는 ECK 관점의 상태이고, &lt;code&gt;Pending&lt;/code&gt;은 scheduler 관점의 상태다.&lt;/li&gt;
&lt;li&gt;data pod가 &lt;code&gt;Pending&lt;/code&gt;이면 먼저 pod event를 확인한다.&lt;/li&gt;
&lt;li&gt;local PV를 쓰는 경우 PV의 node affinity가 특정 노드로 pod를 묶는다.&lt;/li&gt;
&lt;li&gt;해당 노드에 taint가 있고 pod에 toleration이 없으면 scheduler는 pod를 올릴 수 없다.&lt;/li&gt;
&lt;li&gt;ECK 설정 오류와 스케줄링 불가능 상태를 분리해서 봐야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;먼저 볼 명령&lt;/h2&gt;
&lt;pre id=&quot;code_1285_first_commands&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl describe pod &amp;lt;pod-name&amp;gt; -n &amp;lt;namespace&amp;gt;
kubectl get pv &amp;lt;pv-name&amp;gt; -o yaml
kubectl describe node &amp;lt;node-name&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;describe pod&lt;/code&gt;의 event에서 taint, node affinity, volume node affinity conflict 같은 메시지가 보이면 ECK 설정보다 Kubernetes 스케줄링 조건을 먼저 확인하는 편이 빠르다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;관련 글&lt;/h2&gt;
&lt;ul data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://barisein.tistory.com/1284&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Elasticsearch DiskPressure 회고: ILM 보관 정책으로 해결한 사례&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://barisein.tistory.com/1275&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Kubernetes 노드 디스크 부족 해결: containerd 이미지와 로그 정리&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://barisein.tistory.com/1280&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Kubernetes 현황판 Root Disk가 N/A일 때: Metricbeat와 Elasticsearch 연결 문제 해결&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Kubernetes에서 ECK로 운영하던 Elasticsearch가 한동안&lt;span&gt;&amp;nbsp;&lt;/span&gt;ApplyingChanges&lt;span&gt;&amp;nbsp;&lt;/span&gt;상태에 머물렀고, 일부 data pod는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending에서 올라오지 못했다. 처음에는 ECK 설정 오류가 먼저 눈에 들어왔지만, 실제로 pod를 막고 있던 직접 원인은 따로 있었다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 당시 무엇을 오해했고, 어디서 판단을 바꿨고, 어떤 검증을 근거로 복구를 마무리했는지를 남기기 위한 기술 회고다. 공개용 문서이므로 노드명, IP, 도메인, 네임스페이스, 계정, 내부 레지스트리 주소 같은 식별 정보는 모두 일반화했다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;시작점:&lt;span&gt;&amp;nbsp;&lt;/span&gt;ApplyingChanges와&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending을 같은 층위로 보면 헷갈린다&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;장애 당시 표면 증상은 두 가지였다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Elasticsearch custom resource는&lt;span&gt;&amp;nbsp;&lt;/span&gt;ApplyingChanges에 머물렀다.&lt;/li&gt;
&lt;li&gt;일부 hot data pod는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending이었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;겉으로 보면 둘 다 &quot;Elasticsearch가 아직 정상화되지 않았다&quot;는 하나의 문장으로 묶인다. 실제로 나도 초반에는 그렇게 봤다. 그런데 이 둘은 같은 시점에 보일 수는 있어도, 같은 원인일 필요는 없다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 일에서 첫 번째로 막힌 지점이 바로 여기였다. ECK condition에 validation 오류가 보이니 그쪽을 먼저 고쳐야 한다고 생각하기 쉬웠다. 하지만&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending은 scheduler가 pod를 어디에도 올리지 못하고 있다는 뜻이고, 이 문제는 ECK 설정 validation과는 별개로 먼저 확인해야 했다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;처음 확인한 것: 리소스 부족이 아니라 스케줄링 정책 충돌인지&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우선 본 것은 pod 이벤트였다. 이유는 단순했다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending은 CPU나 메모리 부족일 수도 있지만, taint, affinity, PVC 제약처럼 scheduler 정책 문제일 때도 많기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;당시 이벤트에서 핵심적으로 보인 메시지는 이런 종류였다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot; style=&quot;background-color: #f7f1e8; color: #1f1b16; text-align: start;&quot;&gt;&lt;code&gt;FailedScheduling
had untolerated taint
Preemption is not helpful for scheduling&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 판단이 한 번 바뀌었다. 선점으로도 해결되지 않는다는 메시지가 붙어 있으면, 단순한 리소스 경쟁이 아니라 &quot;배치 규칙 자체가 맞지 않는다&quot;는 쪽으로 봐야 한다. 즉, 이 시점부터는 &quot;무슨 리소스가 부족한가&quot;보다 &quot;이 pod가 원래 어디에 올라가야 하는가&quot;를 보는 쪽이 맞았다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;전환점 1: PVC가 local PV에 묶여 있다는 사실&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음으로 확인한 것은 PVC와 PV였다. Elasticsearch data pod는 영속 볼륨에 묶여 있고, 특히 local PV를 쓰는 경우에는 storage가 곧 스케줄링 제약이 된다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;확인 결과, 문제 pod의 PVC는 특정 local PV에 바인딩되어 있었고, 그 PV에는 특정 노드만 허용하는 node affinity가 걸려 있었다. 공개용으로 일반화하면 구조는 이렇다.&lt;/p&gt;
&lt;pre class=&quot;sml&quot; style=&quot;background-color: #f7f1e8; color: #1f1b16; text-align: start;&quot;&gt;&lt;code&gt;PVC -&amp;gt; 특정 local PV에 바인딩
local PV -&amp;gt; 특정 worker node에만 연결 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 상황이 꽤 명확해졌다. 이 pod는 &quot;아무 노드에나 못 가는&quot; 상태였다. 이미 storage 때문에 목적지가 거의 고정되어 있었고, 따라서 확인 범위도 그 노드 하나로 좁혀졌다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;전환점 2: local PV가 붙은 노드에 남아 있던&lt;span&gt;&amp;nbsp;&lt;/span&gt;NoSchedule&lt;span&gt;&amp;nbsp;&lt;/span&gt;taint&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PVC가 특정 노드로 pod를 사실상 고정하고 있다는 것을 확인한 뒤, 그 노드의 taint를 봤다. 거기서 실제 직접 원인이 나왔다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문제 노드에는 불필요한&lt;span&gt;&amp;nbsp;&lt;/span&gt;NoSchedule&lt;span&gt;&amp;nbsp;&lt;/span&gt;taint가 남아 있었고, Elasticsearch pod에는 그 taint를 허용하는 toleration이 없었다. 결과는 단순했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f7f1e8; color: #1f1b16; text-align: start;&quot;&gt;&lt;code&gt;1. storage 때문에 pod는 특정 노드에 가야 함
2. 그런데 그 노드에는 NoSchedule taint가 남아 있음
3. pod는 toleration이 없음
4. 그래서 scheduler가 배치하지 못함&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 장애에서 핵심은 이 충돌이었다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending의 직접 원인은 local PV와 taint 정책이 어긋난 것이었고, ECK condition 오류는 같은 시점에 보인 별도 문제였다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;내가 초반에 오해한 부분: ECK 오류가 먼저라고 본 것&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;incident 기록에는 ECK condition에서도 두 가지가 잡혀 있었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;xpack.security.enabled가 사용자 config에 포함되어 있어 forbidden validation이 발생한 점&lt;/li&gt;
&lt;li&gt;NodeSet의 memory request와 limit이 달라 resource-aware management 조건이 깨진 점&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 정보만 보면 &quot;설정 오류부터 고치면 pod도 올라오겠지&quot;라고 생각하기 쉽다. 나도 초반에는 그쪽이 더 근본 원인처럼 보였다. 하지만 실제로는 순서가 달랐다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 validation 오류들은 분명 정리해야 했지만, 문제 pod를&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending에 묶어 둔 직접 이유는 아니었다. scheduler 이벤트, PVC 바인딩, PV node affinity, node taint를 같이 놓고 보니 우선순위가 분명해졌다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 이번 사고에서 중요한 판단 전환점은 이거였다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;ECK가 ApplyingChanges니까 operator 설정부터 고쳐야 한다&quot;가 아니라&lt;/li&gt;
&lt;li&gt;&quot;pod가 특정 노드에만 갈 수 있는데 그 노드가 정책상 막혀 있다&quot;를 먼저 해결해야 했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;실제 조치: taint 제거와 ECK spec 정상화를 분리해서 처리&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제 복구는 한 번에 끝난 것이 아니라 두 층위로 나눠서 진행됐다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째는 스케줄링을 막는 직접 원인을 제거하는 일이었다. local PV가 붙은 노드에 잘못 남아 있던 taint를 제거했다. 이 조치의 의미는 workload에 새 toleration을 추가했다기보다, 해당 노드에 남아 있던 불필요한 격리 설정을 원래 의도에 맞게 되돌린 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 번째는 ECK spec을 정상화하는 일이었다. 사용자 config에서 제거해야 하는 reserved 설정을 빼고, resource-aware management가 다시 성립하도록 NodeSet memory request와 limit을 맞췄다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 둘을 섞어 생각하지 않는 것이었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;taint 제거는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending의 직접 원인 제거&lt;/li&gt;
&lt;li&gt;ECK spec 수정은 operator reconciliation을 정상화하기 위한 정리&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;둘 다 필요했지만, 같은 문제를 두 번 푸는 조치는 아니었다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;왜 StatefulSet replica 조정과 pod 재생성을 했는가&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원인 조치 이후에는 hot StatefulSet replica를 복구하고, 오래 남아 있던 pod를 다시 생성했다. 이 부분은 단순 재시작이 목적이 아니라, 수정된 스케줄링 조건과 ECK spec이 실제 상태에 반영되도록 흐름을 정리하는 의미가 있었다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 조치를 할 때도 기준은 분명했다. &quot;설정이 바뀌었다&quot;만으로 끝내지 않고, 실제 pod가 다시 생성되어 올바른 노드에 배치되는지까지 봐야 했다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;무엇을 보고 해결됐다고 판단했나&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 단순히 pod 하나가 뜨는 것만으로 종료하지 않았다. 복구 판단은 세 층위에서 같이 확인했다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;첫째, pod 배치가 정상인지 봤다. hot pod와 warm pod가 각각 의도한 노드에&lt;span&gt;&amp;nbsp;&lt;/span&gt;Running으로 올라왔는지가 가장 직접적인 확인이었다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;둘째, Elasticsearch custom resource 상태를 봤다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;ApplyingChanges에서 벗어나&lt;span&gt;&amp;nbsp;&lt;/span&gt;Ready로 돌아왔는지, 그리고 data node 수가 기대한 값으로 복구됐는지를 확인했다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;셋째, ECK condition이 실제로 정리됐는지를 봤다. 당시 기준으로는 다음 조건들이 다시 정상이어야 했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ReconciliationComplete=True&lt;/li&gt;
&lt;li&gt;RunningDesiredVersion=True&lt;/li&gt;
&lt;li&gt;ElasticsearchIsReachable=True&lt;/li&gt;
&lt;li&gt;ResourcesAwareManagement=True&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 서로 다른 층위를 같이 봐야 &quot;scheduler 문제만 잠깐 풀렸다&quot;거나 &quot;operator 조건만 좋아 보인다&quot;는 착시를 피할 수 있었다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;이번 일에서 남긴 운영 기준&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 장애는 복잡한 버그라기보다, 서로 다른 문제를 한 덩어리로 보면 복구 순서가 흐려진다는 점을 보여줬다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음에 비슷한 상황을 다시 보면 확인 순서는 이렇게 가져가는 편이 낫다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1.&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending&lt;span&gt;&amp;nbsp;&lt;/span&gt;pod의 event부터 본다.&lt;br /&gt;2. 선점 불가 메시지가 보이면 리소스 부족보다 정책 충돌을 먼저 의심한다.&lt;br /&gt;3. Stateful workload라면 PVC와 PV binding을 확인한다.&lt;br /&gt;4. local PV라면 node affinity와 실제 대상 노드의 taint를 같이 본다.&lt;br /&gt;5. 그 다음에 ECK condition과 validation 오류를 별도 축으로 정리한다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정리하면, 이번 사고의 직접 원인은 local PV node affinity와 node taint 충돌이었다. 하지만 실제로 오래 막혔던 이유는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending과&lt;span&gt;&amp;nbsp;&lt;/span&gt;ApplyingChanges를 너무 오래 같은 문제처럼 본 데 있었다. 나중에 다시 비슷한 상황을 만나면, 그 둘을 먼저 분리해서 보는 것이 가장 큰 시간 절약 포인트가 될 것이다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Sources&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Original incident:&lt;span&gt;&amp;nbsp;&lt;/span&gt;docs/incidents/2026-04-25-elasticsearch-pending.md&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>k8s/EFK</category>
      <category>ECK</category>
      <category>Elasticsearch</category>
      <category>Kubernetes</category>
      <category>LocalPV</category>
      <category>taint</category>
      <category>운영회고</category>
      <author>바리새인</author>
      <guid isPermaLink="true">https://barisein.tistory.com/1285</guid>
      <comments>https://barisein.tistory.com/1285#entry1285comment</comments>
      <pubDate>Sat, 9 May 2026 08:38:20 +0900</pubDate>
    </item>
    <item>
      <title>Elasticsearch DiskPressure 회고: Pending 복구 뒤에도 끝나지 않았던 로그 보관 문제</title>
      <link>https://barisein.tistory.com/1284</link>
      <description>&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한 번은 Elasticsearch&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending&lt;span&gt;&amp;nbsp;&lt;/span&gt;문제를 복구한 뒤에도 cluster가 완전히 정상으로 돌아오지 않은 적이 있었다. 표면적으로는 리소스가 다시 올라왔고 custom resource도&lt;span&gt;&amp;nbsp;&lt;/span&gt;Ready로 보였는데, 실제로는 특정 노드에&lt;span&gt;&amp;nbsp;&lt;/span&gt;DiskPressure가 남아 있었고 Elasticsearch health도&lt;span&gt;&amp;nbsp;&lt;/span&gt;yellow에서 멈춰 있었다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 당시 내가 무엇을 먼저 오해했고, 어디서 판단을 바꿨고, 어떤 검증을 근거로 &quot;이제 정말 끝났다&quot;고 결론 내렸는지를 남기기 위한 기술 회고다. 공개용 문서이므로 노드명, IP, 도메인, 네임스페이스, 계정명, 내부 레지스트리 주소 같은 식별 정보는 모두 일반화했다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;시작점:&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending은 풀렸는데 왜 아직 불안했나&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;출발점은 직전 장애의 후속 확인이었다. 이전 이슈에서 Elasticsearch pod의&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending&lt;span&gt;&amp;nbsp;&lt;/span&gt;자체는 복구됐고, 그래서 처음에는 남은&lt;span&gt;&amp;nbsp;&lt;/span&gt;yellow&lt;span&gt;&amp;nbsp;&lt;/span&gt;상태도 shard 재배치가 끝나면 자연스럽게 정리될 것이라고 생각했다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 실제로는 두 가지가 같이 남아 있었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Elasticsearch는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Ready로 보였다.&lt;/li&gt;
&lt;li&gt;한 노드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;DiskPressure=True였고 cluster health는&lt;span&gt;&amp;nbsp;&lt;/span&gt;yellow였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 이 둘을 같은 문제의 잔여 증상으로 묶어서 봤다. 즉 &quot;Pending이 길었던 여파로 아직 재배치가 덜 끝났나 보다&quot;라고 생각했다. 지금 돌아보면 이게 첫 번째 오해였다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Pending&lt;span&gt;&amp;nbsp;&lt;/span&gt;복구와 저장소 압박은 겹쳐 보일 수는 있어도 같은 원인일 필요는 없다. 그걸 너무 늦게 분리해서 본 것이 이번 회고의 시작점이다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;확인 순서를 바꾼 이유&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;판단이 바뀐 계기는 노드 루트 파일시스템 사용률이었다. 단순히 shard가 옮겨지는 중이라면 health가 잠시 흔들릴 수는 있어도, 노드 자체가&lt;span&gt;&amp;nbsp;&lt;/span&gt;DiskPressure에 들어가는 현상까지 자연스럽게 설명되지는 않았다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서부터는 시선을 스케줄링에서 저장소로 옮겼다. 순서는 의도적으로 이렇게 잡았다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 노드가 실제로 왜 압박 상태인지 본다.&lt;br /&gt;2. Elasticsearch가 어느 data node에서 디스크를 많이 쓰는지 본다.&lt;br /&gt;3. 어떤 index 또는 data stream이 비정상적으로 큰지 본다.&lt;br /&gt;4. 그다음에야 ILM과 rollover가 왜 멈췄는지 본다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 순서를 택한 이유는 간단했다. 처음부터 ILM 정책 이름이나 템플릿만 보면 &quot;설정은 있는 것 같은데 왜 안 지워지지?&quot;에서 오래 헤맬 수 있기 때문이다. 먼저 어디에 용량이 쌓였는지를 봐야, 나중에 정책 문제를 보더라도 맥락이 붙는다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;전환점 1: 문제는 재배치가 아니라 오래된 backing index 누적이었다&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;노드 상태와 Elasticsearch allocation을 같이 보니 그림이 훨씬 선명해졌다. local PV를 쓰는 data node 하나에서 사용량이 과도하게 커져 있었고, 큰 비중을 차지한 것은 로그성 data stream이었다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특히 눈에 띈 것은 두 종류였다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;metricbeat&lt;span&gt;&amp;nbsp;&lt;/span&gt;계열 data stream&lt;/li&gt;
&lt;li&gt;게임 서버 로그를 적재하던&lt;span&gt;&amp;nbsp;&lt;/span&gt;minecraft-logs&lt;span&gt;&amp;nbsp;&lt;/span&gt;data stream&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 중요한 건 단순히 &quot;인덱스가 크다&quot;가 아니었다. 오래된 backing index가 계속 남아 있었고, 새 데이터가 들어오면서 전체 크기가 누적되는 구조가 보였다. 이 시점에서 문제를 &quot;순간적인 트래픽 증가&quot;가 아니라 &quot;보관 정책 실패&quot;로 보기 시작했다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 처음에 내가 따라가던 가설은 &quot;cluster가 아직 덜 안정화됐다&quot;였지만, 실제로는 &quot;정리돼야 할 로그가 정리되지 않고 있었다&quot;가 더 정확했다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;전환점 2: 둘 다 ILM 문제였지만 고장 방식은 달랐다&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;조금 더 들어가 보니 두 data stream은 결과는 비슷했지만 실패 방식이 서로 달랐다. 이 차이를 확인한 것이 가장 큰 판단 전환점이었다.&lt;/p&gt;
&lt;h3 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;metricbeat: 정책은 있었지만 실행 권한이 깨져 있었다&lt;/h3&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음 metricbeat를 봤을 때는 더 헷갈렸다. ILM policy 자체는 존재했기 때문이다. 그래서 한동안은 &quot;정책이 있는데 아직 rollover 시점이 안 왔나&quot; 같은 쪽으로 생각이 기울었다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 ILM explain을 확인하면서 상황이 달라졌다. rollover가 반복 실패하고 있었고, 오류는 권한 문제였다. 당시 핵심 오류는 이런 형태였다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot; style=&quot;background-color: #f7f1e8; color: #1f1b16; text-align: start;&quot;&gt;&lt;code&gt;action [indices:admin/rollover] is unauthorized for API key
failed_step: check-rollover-ready&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 메시지를 보고 나서야 &quot;정책 존재&quot;와 &quot;정책이 실제로 실행 가능함&quot;은 완전히 다른 문제라는 점이 분명해졌다. metricbeat는 ILM이 없는 게 아니라, 과거 권한 상태로 저장된 실행 주체 때문에 rollover를 계속 못 하고 있었다.&lt;/p&gt;
&lt;h3 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;minecraft 로그: 참조한 정책 이름은 있었지만 실제 policy는 없었다&lt;/h3&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반대로&lt;span&gt;&amp;nbsp;&lt;/span&gt;minecraft-logs는 더 직접적인 오류였다. data stream 쪽 설정은 특정 ILM policy를 참조하고 있었지만, 정작 그 policy 객체가 클러스터에 존재하지 않았다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오류는 아주 명확했다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot; style=&quot;background-color: #f7f1e8; color: #1f1b16; text-align: start;&quot;&gt;&lt;code&gt;policy [minecraft-logs-ilm] does not exist&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 경우는 아예 정리 로직이 붙어 있지 않은 상태나 다름없었다. 데이터는 계속 쌓이는데 지워질 조건을 해석할 정책이 없으니, 오래된 backing index가 계속 남는다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 두 번째 오해도 정리됐다. 나는 처음에 둘 다 &quot;로그가 많아서 디스크가 찼다&quot; 정도로 뭉뚱그렸는데, 실제로는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나는 &quot;정책은 있으나 실행 실패&quot;&lt;/li&gt;
&lt;li&gt;다른 하나는 &quot;정책 참조만 있고 실체 없음&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이라는 전혀 다른 실패였다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;왜 노드에서 임시 정리를 먼저 했는가&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 시점에는 근본 원인이 보였지만, 노드 압박도 동시에 풀어야 했다. 다만 여기서 조심한 것은 Elasticsearch local PV 아래 파일을 직접 지우지 않는 것이었다. 디스크가 급하다고 data path를 수동으로 건드리면 장애를 더 키울 가능성이 높다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 먼저 한 일은 운영체제 레벨에서 안전하게 줄일 수 있는 항목만 정리하는 것이었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;system journal 축소&lt;/li&gt;
&lt;li&gt;사용하지 않는 container image 정리&lt;/li&gt;
&lt;li&gt;패키지 캐시 정리&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 단계의 목적은 문제를 해결하는 것이 아니라, 노드가&lt;span&gt;&amp;nbsp;&lt;/span&gt;DiskPressure에서 잠시 빠져나와 다시 운영 가능한 상태를 확보하는 것이었다. 실제로 이 조치 뒤에는 루트 파일시스템 사용률이 내려가고&lt;span&gt;&amp;nbsp;&lt;/span&gt;DiskPressure=False로 돌아왔다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;중요했던 건 여기서 멈추지 않은 것이다. 이 상태만 보면 해결된 것처럼 느껴질 수 있지만, Elasticsearch 내부에는 여전히 오래된 backing index가 남아 있었고, 그대로 두면 같은 일이 다시 반복될 구조였다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;실제 해결: 보관 정책 재정의, rollover, 그리고 non-write backing index 삭제&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;근본 해결은 두 data stream의 보관 기준을 다시 명확히 두는 것이었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;rollover:&lt;span&gt;&amp;nbsp;&lt;/span&gt;7일&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;primary shard 10GB&lt;/li&gt;
&lt;li&gt;delete:&lt;span&gt;&amp;nbsp;&lt;/span&gt;30일&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;metricbeat는 정책을 다시 써 넣은 뒤, 실패하던 ILM 단계를 재시도하고 rollover를 일으켰다. 그리고 새 write index가 생긴 것을 확인한 다음에 오래된 backing index를 삭제했다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;minecraft-logs는 누락된 policy를 만든 뒤 rollover를 수행하고, 더 이상 write index가 아닌 오래된 backing index들을 삭제했다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 내가 특히 신경 쓴 검증은 &quot;삭제가 성공하느냐&quot;가 아니라 &quot;지워도 되는 대상을 고른 게 맞느냐&quot;였다. 실제 작업 흐름은 아래와 같은 판단 순서를 따른다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f7f1e8; color: #1f1b16; text-align: start;&quot;&gt;&lt;code&gt;1. data stream의 현재 write index 확인
2. ILM policy 수정 또는 생성
3. rollover 수행
4. 새 backing index 생성 확인
5. 이전 non-write backing index 삭제&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 순서는 실제 작업 흐름을 일반화한 것이다. 환경마다 이름과 호출 방식은 다를 수 있고, 위 텍스트는 공개용 설명을 위한 예시다. 실제로 하지 않은 추가 조치나 자동화는 여기서 한 것처럼 적지 않았다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;무엇을 보고 &quot;진짜 해결됐다&quot;고 판단했나&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 단순히 에러 메시지가 줄어든 것을 종료 기준으로 잡지 않았다. 대신 서로 다른 층위의 상태가 함께 좋아지는지를 확인했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Elasticsearch cluster health가&lt;span&gt;&amp;nbsp;&lt;/span&gt;green으로 돌아오는가&lt;/li&gt;
&lt;li&gt;custom resource phase가 계속&lt;span&gt;&amp;nbsp;&lt;/span&gt;Ready인가&lt;/li&gt;
&lt;li&gt;문제 노드의&lt;span&gt;&amp;nbsp;&lt;/span&gt;DiskPressure가 해제됐는가&lt;/li&gt;
&lt;li&gt;node별 Elasticsearch disk allocation이 실제로 감소했는가&lt;/li&gt;
&lt;li&gt;새 write index가 생성됐고 오래된 대형 backing index가 사라졌는가&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 중에서 특히 중요했던 것은 node별 disk 사용률 감소였다. 정책이 &quot;설정돼 있다&quot;는 것과, 실제로 디스크 누수가 멈췄다는 것은 다르다. 이번에는 후자까지 확인하고 나서야 종료할 수 있었다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최종적으로 cluster health는&lt;span&gt;&amp;nbsp;&lt;/span&gt;green으로 복구됐고, 문제 노드의&lt;span&gt;&amp;nbsp;&lt;/span&gt;DiskPressure도 해제됐다. 그제야 직전&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending&lt;span&gt;&amp;nbsp;&lt;/span&gt;이슈와는 별개의 꼬리 문제까지 정리됐다고 볼 수 있었다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;이번 일에서 남은 교정 포인트&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 사고는 복잡한 신기능 장애라기보다, 내가 상태를 너무 빨리 낙관적으로 해석한 사건에 가까웠다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;당시 내가 놓쳤던 포인트는 세 가지였다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;첫째,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending이 풀렸다고 해서 storage pressure까지 같이 해결됐다고 보면 안 된다. 스케줄링 복구와 데이터 축적 문제는 별개의 축이다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;둘째, ILM은 정책 이름이 있느냐만 보면 안 된다. policy 존재, template 연결, rollover 조건, delete phase, 실행 권한이 모두 살아 있어야 실제 보관이 동작한다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;셋째, local PV를 쓰는 Elasticsearch에서는 인덱스 보관 정책이 곧 노드 운영 정책이다. Elasticsearch 안에서의 누적 문제가 곧 Kubernetes 노드 condition으로 드러난다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;다음에 비슷한 징후를 보면&lt;/h2&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 회고 이후로는 비슷한 상황에서 확인 순서를 이렇게 가져갈 생각이다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1.&lt;span&gt;&amp;nbsp;&lt;/span&gt;Pending이나&lt;span&gt;&amp;nbsp;&lt;/span&gt;yellow를 보더라도 바로 재배치 문제라고 단정하지 않는다.&lt;br /&gt;2. node condition과 Elasticsearch allocation을 같이 본다.&lt;br /&gt;3. 큰 index를 찾을 때는 현재 크기보다 오래된 backing index 누적 여부를 먼저 본다.&lt;br /&gt;4. ILM은 정책 존재 여부와 실행 실패 원인을 분리해서 본다.&lt;br /&gt;5. 디스크가 급해도 Elasticsearch data path는 직접 지우지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 글의 목적은 정답을 미리 써 두는 것이 아니라, 당시 내가 어디서 막혔는지 나중에 다시 복원할 수 있게 만드는 것이다. 그 기준에서 보면 이번 사건의 핵심은 &quot;명령어&quot;보다 &quot;잘못 묶어 본 가설을 언제 해체했는가&quot;에 있었다.&lt;/p&gt;
&lt;h2 style=&quot;color: #1f1b16; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Sources&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #1f1b16; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Original incident:&lt;span&gt;&amp;nbsp;&lt;/span&gt;docs/incidents/2026-04-25-elasticsearch-log-retention-disk-pressure.md&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>k8s/EFK</category>
      <category>DiskPressure</category>
      <category>Elasticsearch</category>
      <category>ILM</category>
      <category>Kubernetes</category>
      <category>Log Retention</category>
      <category>운영회고</category>
      <author>바리새인</author>
      <guid isPermaLink="true">https://barisein.tistory.com/1284</guid>
      <comments>https://barisein.tistory.com/1284#entry1284comment</comments>
      <pubDate>Sat, 9 May 2026 08:06:38 +0900</pubDate>
    </item>
    <item>
      <title>authentik OIDC를 Kubernetes에 붙일 때 반드시 알아야 할 핵심</title>
      <link>https://barisein.tistory.com/1282</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes 안에 &lt;code&gt;authentik&lt;/code&gt;을 올리고, 내부 운영 서비스에 OIDC 로그인을 붙이다 보면 처음에는 내부 HTTP로 빠르게 확인하고 나중에 외부 HTTPS 요구가 생기는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글은 그 과정에서 어떤 점을 확인했고, 왜 결국 &lt;code&gt;authentik&lt;/code&gt;도 외부 HTTPS host 기준으로 정리해야 했는지를 기록한 내용이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성 자체는 단순했다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;authentik = Identity Provider
ooo service = OIDC Client
Kubernetes Ingress = authentik 외부 진입점
cert-manager + Let's Encrypt = TLS 인증서 발급
외부 PostgreSQL = authentik DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 운영에서는 아래 세 가지가 핵심이었다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. authentik Helm chart가 existingSecret + 외부 PostgreSQL 구성을 제대로 받는가
2. OIDC discovery가 실제 host 기준으로 올바른 issuer/endpoints를 내보내는가
3. 외부 사용자가 로그인해야 한다면 authentik host도 외부 HTTPS로 열려 있어야 하는가&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 내부 HTTP로도 초기 확인은 가능하다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 점검 단계에서는 &lt;code&gt;authentik&lt;/code&gt;을 내부망 전용 HTTP로 올려도 기본 동작은 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 아래와 같은 항목이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;- Helm lint / template 통과
- existingSecret 주입 확인
- PostgreSQL 연결 확인
- initial setup / health check 응답 확인
- OIDC discovery 문서 생성 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인 URL은 이런 식으로 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;/if/flow/initial-setup/ -&amp;gt; 200
/application/o/&amp;lt;app&amp;gt;/.well-known/openid-configuration -&amp;gt; 200&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계에서는 &quot;authentik이 기본적으로 동작하는가&quot;를 빠르게 보는 데 의미가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 내부 HTTP는 초기 검증용으로는 충분하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 상태가 실제 외부 사용자 로그인까지 보장하는 것은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 외부 로그인 요구가 생기면 구조가 달라진다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 &lt;code&gt;ooo&lt;/code&gt;&amp;nbsp;서비스가 외부에서 접근 가능하고, 실제 사용자 로그인도 외부 브라우저에서 해야 하는 경우다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이런 생각을 하기 쉽다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;ooo service만 외부에 열고
authentik은 내부 주소로 둬도 되지 않을까?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 OIDC는 그렇게 단순하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OIDC는 브라우저 redirect 기반 프로토콜이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 로그인 중간에 사용자의 브라우저가 실제로 &lt;code&gt;issuer&lt;/code&gt;, &lt;code&gt;authorization_endpoint&lt;/code&gt; 주소로 이동해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조가 아래처럼 되어 있다면 문제가 생긴다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;ooo service = 외부 공개
authentik = 내부 전용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 사용자는 로그인 중에 결국 내부 주소를 따라가게 되고, 브라우저에서 해당 주소에 접근할 수 없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 운영 판단은 명확하다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;- 내부망 전용 서비스면 ooo도 내부/VPN 전용으로 유지
- 외부 사용자가 로그인해야 하면 authentik도 외부 HTTPS host로 노출&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 이렇다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;외부 사용자가 로그인해야 한다면
authentik도 사용자의 브라우저에서 접근 가능한 HTTPS 주소여야 한다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 가장 중요한 검증 지점은 OIDC Discovery다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;authentik 쪽에서 가장 중요하게 본 것은 OIDC discovery 문서였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 이 문서가 실제 로그인 흐름에 쓰이는 기준 주소를 보여주기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해야 할 값은 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;issuer
authorization_endpoint
token_endpoint
userinfo_endpoint
jwks_uri&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 기준 주소가 외부 HTTPS host라면, 이 값들도 전부 같은 기준으로 나와야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 아래와 같은 형태여야 한다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;https://auth.example.com/application/o/my-app/
https://auth.example.com/application/o/authorize/
https://auth.example.com/application/o/token/
https://auth.example.com/application/o/userinfo/
https://auth.example.com/application/o/my-app/jwks/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 중요한 것은 &lt;code&gt;issuer&lt;/code&gt;다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;issuer 값이 외부 HTTPS 주소와 다르면 로그인, 토큰 검증, 콜백 처리에서 문제가 발생할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 내부 HTTP 주소가 섞여 있으면 나중에 로그인, 콜백, 세션 처리에서 어색한 문제가 이어질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 설정 파일만 보는 것보다 discovery 결과를 직접 확인하는 것이 더 안전하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Helm chart에서 hostname 값을 억지로 찾기보다 결과를 확인한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 작업을 하면 보통 &lt;code&gt;HOST&lt;/code&gt;, &lt;code&gt;hostname&lt;/code&gt;, &lt;code&gt;public_url&lt;/code&gt; 비슷한 값을 먼저 찾게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 chart와 공식 문서를 보면 항상 기대한 이름으로 준비되어 있지는 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 임의 키를 추가하기보다 아래 순서로 확인하는 편이 안전하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. Ingress host가 원하는 host로 설정됐는가
2. Ingress가 Host / X-Forwarded-* 헤더를 정상 전달하는가
3. Application / Provider 생성 후 discovery 결과가 실제 기대 host로 보이는가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 것은 설정 이름이 그럴듯한지가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과가 실제로 원하는 외부 HTTPS 주소로 나오는지가 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Ingress 뒤에 authentik이 있을 경우 아래 헤더 흐름도 중요하다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;Host
X-Forwarded-Proto
X-Forwarded-Host&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값들이 어긋나면 authentik이 내부 HTTP 기준 URL을 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 최종 판단은 discovery 문서로 하는 것이 좋다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Secret은 values.yaml에 직접 넣지 않고 existingSecret으로 분리했다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;민감정보는 Helm values에 직접 넣지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분리한 값은 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;authentik secret key
PostgreSQL host
PostgreSQL port
PostgreSQL database
PostgreSQL user
PostgreSQL password&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값들은 Git 밖의 env 파일에서 Kubernetes Secret으로 만든 뒤, chart에서는 &lt;code&gt;existingSecret&lt;/code&gt;만 참조하도록 구성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식의 장점은 분명하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;- values.yaml에 민감값이 들어가지 않는다
- Secret 재적용과 Helm 배포를 분리할 수 있다
- preflight에서 required key 존재 여부를 따로 검증할 수 있다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 환경에서는 이런 구조가 훨씬 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 인증 시스템은 Secret 값이 많기 때문에 values 파일에 직접 넣기 시작하면 나중에 Git 관리와 배포 관리가 복잡해진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. cert-manager와 Let's Encrypt는 별도로 상태를 확인해야 한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ingress에 TLS와 cert-manager annotation을 추가했다고 바로 끝나는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 아래 순서로 봐야 한다.&lt;/p&gt;
&lt;pre class=&quot;vbnet&quot;&gt;&lt;code&gt;kubectl get ingress -n &amp;lt;namespace&amp;gt;
kubectl get certificate,order,challenge -n &amp;lt;namespace&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 이거다.&lt;/p&gt;
&lt;pre class=&quot;erlang-repl&quot;&gt;&lt;code&gt;Ingress 반영 완료 != 인증서 발급 완료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ingress는 생성됐지만 &lt;code&gt;Certificate&lt;/code&gt;가 아직 &lt;code&gt;READY=False&lt;/code&gt;일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 &lt;code&gt;Order&lt;/code&gt;, &lt;code&gt;Challenge&lt;/code&gt;가 &lt;code&gt;pending&lt;/code&gt; 상태일 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 운영 문서에는 반드시 아래 항목을 나눠서 적는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;- Ingress 적용 여부
- Certificate READY 여부
- HTTPS 응답 여부
- OIDC discovery HTTPS 응답 여부&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서가 아직 준비되지 않았는데 OIDC 오류로 착각하면 문제 원인을 잘못 잡을 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 로그인뿐 아니라 로그아웃 흐름도 같이 봐야 한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OIDC 연동은 discovery만 맞는다고 끝나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 사용자가 체감하는 부분은 보통 두 가지다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 포털에서 앱 타일을 눌렀을 때 어디로 이동하는가
2. 앱에서 logout 했을 때 authentik 세션도 같이 정리되는가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인은 되는데 로그아웃이 이상한 경우도 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 아래와 같은 상태다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- 앱에서는 로그아웃됐지만 authentik 세션은 살아 있음
- 다시 앱에 들어가면 자동 로그인됨
- logout 후 redirect URL이 기대한 곳과 다름&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 다음 값도 운영 문서에 함께 고정해두는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;- authentik Application Launch URL
- provider invalidation flow
- User Logout stage 포함 여부
- app logout -&amp;gt; authentik end-session redirect&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 문서에 남겨두지 않으면 나중에 &quot;로그인은 되는데 로그아웃이 이상하다&quot; 같은 반쪽짜리 상태가 되기 쉽다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 마지막은 groups claim과 role mapping 확인이다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OIDC가 연결됐다고 바로 모든 권한 연동이 끝난 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;discovery 문서에는 &lt;code&gt;groups&lt;/code&gt; claim 지원이 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 토큰이나 userinfo에 원하는 형태로 내려오는지는 별도로 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 체크는 아래 흐름으로 보는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. 로그인 성공
2. callback / session 흐름 확인
3. discovery issuer/endpoints 확인
4. groups claim 실제 수신 확인
5. app 내부 role mapping 기대값과 비교&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계까지 확인해야 &quot;로그인이 된다&quot;가 아니라 &quot;권한 연동도 된다&quot;로 정리할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실전 체크리스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;authentik OIDC를 Kubernetes에 붙일 때는 아래 순서로 확인하는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. Secret은 existingSecret으로 분리했는가
2. 외부 PostgreSQL 연결이 정상인가
3. initial setup / health check가 정상인가
4. Ingress host가 실제 운영 host인가
5. cert-manager Certificate가 READY=True인가
6. OIDC discovery의 issuer가 외부 HTTPS 기준인가
7. authorization_endpoint, token_endpoint, userinfo_endpoint가 외부 HTTPS 기준인가
8. 로그인 callback이 정상인가
9. logout / invalidation flow가 정상인가
10. groups claim과 app role mapping이 정상인가&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 경험을 한 줄로 줄이면 이렇다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;authentik OIDC는 내부 HTTP로 빠른 기능 확인은 가능하지만,
외부 사용자가 실제 로그인해야 하는 순간부터는 authentik도 외부 HTTPS host 기준으로 정리해야 한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영에서 특히 중요했던 포인트는 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. Secret은 existingSecret으로 분리
2. 외부 PostgreSQL 연결은 preflight에서 실제 login까지 확인
3. Host 설정은 키 이름보다 discovery 결과로 검증
4. 외부 로그인 요구가 있으면 issuer host도 외부 HTTPS로 노출
5. cert-manager는 ingress 적용과 certificate READY를 나눠서 확인
6. login뿐 아니라 logout / invalidation flow까지 문서화
7. groups claim과 role mapping까지 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 OIDC 문제는 &quot;붙었다 / 안 붙었다&quot;보다 실제 브라우저가 어디로 이동하는지와 discovery가 어떤 URL을 내보내는지를 차분히 보는 쪽이 더 빠르다.&lt;/p&gt;</description>
      <category>k8s</category>
      <category>authentik</category>
      <category>cert-manager</category>
      <category>Ingress</category>
      <category>Kubernetes</category>
      <category>let's encrypt</category>
      <category>OIDC</category>
      <category>OIDC로그인</category>
      <category>SSO</category>
      <category>인증</category>
      <category>쿠버네티스</category>
      <author>바리새인</author>
      <guid isPermaLink="true">https://barisein.tistory.com/1282</guid>
      <comments>https://barisein.tistory.com/1282#entry1282comment</comments>
      <pubDate>Sun, 26 Apr 2026 07:39:57 +0900</pubDate>
    </item>
    <item>
      <title>Kubernetes 노드 점검 직후 재부팅: busybox exec format error와 원인 분리</title>
      <link>https://barisein.tistory.com/1281</link>
      <description>&lt;p&gt;운영 중 특정 노드에서 디스크 점검 직후 재부팅이 발생했다.&lt;br&gt;처음에는 점검 명령이 원인처럼 보였지만, 실제로는 debug image 문제&lt;br&gt;가능성이 있었다.&lt;/p&gt;
&lt;p&gt;  Node 재부팅이 발생했을 때는 &amp;quot;명령 때문인지, 환경 때문인지&amp;quot; 먼저&lt;br&gt;분리하는 것이 중요하다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. 증상&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Warning Rebooted 발생&lt;/li&gt;
&lt;li&gt;NodeReady 재진입&lt;/li&gt;
&lt;li&gt;동일 노드 pod 재시작&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;2. 정상 reboot 여부 확인&lt;/h2&gt;
&lt;p&gt;정상 reboot라면 systemd-shutdown 로그가 남는다.&lt;br&gt;하지만 이번에는 로그가 끊겨 있었다.&lt;/p&gt;
&lt;p&gt;  정상 reboot 아님 가능성&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. busybox debug 실패&lt;/h2&gt;
&lt;p&gt;kubectl debug node/&lt;code&gt;&amp;lt;node&amp;gt;&lt;/code&gt;{=html} --image=busybox:1.36&lt;/p&gt;
&lt;p&gt;결과:&lt;/p&gt;
&lt;p&gt;exec format error&lt;/p&gt;
&lt;p&gt;  exec format error는 보통 아키텍처 mismatch 또는 이미지 호환성&lt;br&gt;문제에서 발생한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;4. ubuntu로 재현&lt;/h2&gt;
&lt;p&gt;kubectl debug node/&lt;code&gt;&amp;lt;node&amp;gt;&lt;/code&gt;{=html} --image=ubuntu:22.04&lt;/p&gt;
&lt;p&gt;df, du 등 점검 수행 → 정상 완료&lt;/p&gt;
&lt;p&gt;  재부팅 없음&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;5. 결론&lt;/h2&gt;
&lt;p&gt;busybox → 실패&lt;br&gt;ubuntu → 정상&lt;/p&gt;
&lt;p&gt;  점검 명령 자체는 안전&lt;br&gt;  debug image 문제 가능성 높음&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. 운영 대응&lt;/h2&gt;
&lt;p&gt;기본 debug image 변경&lt;/p&gt;
&lt;p&gt;busybox ❌&lt;br&gt;ubuntu ✅&lt;/p&gt;
&lt;p&gt;  debug image는 &amp;quot;가벼움&amp;quot;보다 &amp;quot;호환성 검증&amp;quot;이 중요하다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. 실무 체크리스트&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;reboot 이벤트 확인&lt;/li&gt;
&lt;li&gt;shutdown 로그 확인&lt;/li&gt;
&lt;li&gt;debug image 최소 실행 테스트&lt;/li&gt;
&lt;li&gt;다른 이미지로 재현&lt;/li&gt;
&lt;li&gt;runbook 기본값 수정&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>k8s</category>
      <category>busybox</category>
      <category>k8s 운영</category>
      <category>kubectl debug</category>
      <category>Kubernetes</category>
      <category>node troubleshooting</category>
      <category>ubuntu</category>
      <author>바리새인</author>
      <guid isPermaLink="true">https://barisein.tistory.com/1281</guid>
      <comments>https://barisein.tistory.com/1281#entry1281comment</comments>
      <pubDate>Sat, 25 Apr 2026 23:39:54 +0900</pubDate>
    </item>
    <item>
      <title>Kubernetes 현황판 Root Disk가 N/A일 때: Metricbeat와 Elasticsearch 연결 문제 해결</title>
      <link>https://barisein.tistory.com/1280</link>
      <description>&lt;p&gt;Kubernetes 운영 현황판에서 CPU와 memory는 정상적으로 나오는데 root&lt;br&gt;disk만 &lt;code&gt;N/A&lt;/code&gt;로 표시되는 문제가 발생했다.&lt;/p&gt;
&lt;p&gt;처음에는 Metricbeat가 디스크 정보를 수집하지 못하는 문제처럼 보였지만,&lt;br&gt;실제 원인은 &lt;strong&gt;대시보드 API pod에 Elasticsearch 접속 환경변수가 주입되지&lt;br&gt;않은 것&lt;/strong&gt;이었다.&lt;/p&gt;
&lt;p&gt;이 글은 Kubernetes API, Metricbeat, Elasticsearch, application runtime&lt;br&gt;env를 단계별로 점검하며 원인을 좁힌 과정을 정리한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  핵심 포인트 (먼저 정리)&lt;/h2&gt;
&lt;p&gt;  N/A는 데이터 없음이 아니라 &lt;strong&gt;조회 실패일 가능성이 높다&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  CPU / Memory와 Disk는 데이터 경로가 다르다&lt;/p&gt;
&lt;p&gt;CPU / Memory → metrics-server → Kubernetes API&lt;br&gt;Root Disk → Metricbeat → Elasticsearch → Dashboard API&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. 증상&lt;/h2&gt;
&lt;p&gt;NODE CPU MEMORY ROOT DISK READY STATUS&lt;br&gt;worker-1 1% 74% N/A True OK&lt;br&gt;worker-2 7% 72% N/A True OK&lt;br&gt;worker-3 4% 83% N/A True Watch&lt;br&gt;worker-4 31% 64% N/A True OK&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. Metricbeat 확인&lt;/h2&gt;
&lt;p&gt;kubectl get pods -n &lt;code&gt;&amp;lt;monitoring-namespace&amp;gt;&lt;/code&gt;{=html} | grep metricbeat&lt;/p&gt;
&lt;p&gt;  모든 노드에 Running이면 정상&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. Elasticsearch 확인&lt;/h2&gt;
&lt;p&gt;kubectl port-forward svc/&lt;code&gt;&amp;lt;es-service&amp;gt;&lt;/code&gt;{=html} 9200:9200 -n&lt;br&gt;&lt;code&gt;&amp;lt;namespace&amp;gt;&lt;/code&gt;{=html}&lt;/p&gt;
&lt;p&gt;curl &amp;#39;&lt;a href=&quot;http://localhost:9200/%5C_cat/indices/metricbeat-%5C*&amp;#39;&quot;&gt;http://localhost:9200/\_cat/indices/metricbeat-\*&amp;#39;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;  데이터 있으면 정상&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;4. Root filesystem 확인&lt;/h2&gt;
&lt;p&gt;curl &amp;#39;&lt;a href=&quot;http://localhost:9200/metricbeat-%5C*/%5C_search&amp;#39;&quot;&gt;http://localhost:9200/metricbeat-\*/\_search&amp;#39;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;  &lt;code&gt;/&lt;/code&gt; 또는 &lt;code&gt;/hostfs&lt;/code&gt; mount 확인&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;5. 핵심 원인&lt;/h2&gt;
&lt;p&gt;Metricbeat 정상&lt;br&gt;Elasticsearch 정상&lt;br&gt;BUT Application → Elasticsearch 연결 실패&lt;/p&gt;
&lt;p&gt;  즉, env 문제&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. env 확인&lt;/h2&gt;
&lt;p&gt;kubectl exec deploy/&lt;code&gt;&amp;lt;app&amp;gt;&lt;/code&gt;{=html} -- env | grep ES&lt;/p&gt;
&lt;p&gt;  값 없으면 문제&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. 해결&lt;/h2&gt;
&lt;p&gt;APP_ES_URL=http://...&lt;br&gt;APP_ES_USERNAME=elastic&lt;br&gt;APP_ES_PASSWORD=...&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;8. rollout restart&lt;/h2&gt;
&lt;p&gt;kubectl rollout restart deploy/&lt;code&gt;&amp;lt;app&amp;gt;&lt;/code&gt;{=html}&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;9. 검증&lt;/h2&gt;
&lt;p&gt;  root disk % 정상 표시&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  실무 체크리스트&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Metricbeat pod 존재 여부\&lt;/li&gt;
&lt;li&gt;ES 데이터 존재 여부\&lt;/li&gt;
&lt;li&gt;mount point 확인\&lt;/li&gt;
&lt;li&gt;env 존재 여부\&lt;/li&gt;
&lt;li&gt;rollout 여부&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;  결론&lt;/h2&gt;
&lt;p&gt;  Disk N/A는 Metricbeat 문제가 아니다&lt;/p&gt;
&lt;p&gt;  대부분은 Elasticsearch 연결 문제(env)&lt;/p&gt;
&lt;p&gt;  데이터 경로를 나눠서 확인하면 빠르게 해결된다&lt;/p&gt;</description>
      <category>k8s/EFK</category>
      <category>Dashboard</category>
      <category>Elasticsearch</category>
      <category>k8s monitoring</category>
      <category>k8s observability</category>
      <category>Kubernetes</category>
      <category>metricbeat</category>
      <category>secret</category>
      <author>바리새인</author>
      <guid isPermaLink="true">https://barisein.tistory.com/1280</guid>
      <comments>https://barisein.tistory.com/1280#entry1280comment</comments>
      <pubDate>Sat, 25 Apr 2026 22:15:09 +0900</pubDate>
    </item>
    <item>
      <title>Elasticsearch DiskPressure 원인 분석: ILM 보관 정책으로 해결한 사례</title>
      <link>https://barisein.tistory.com/1279</link>
      <description>&lt;p&gt;Elasticsearch Pod Pending 문제를 해결한 이후에도 cluster 상태가&lt;br&gt;&lt;code&gt;yellow&lt;/code&gt;로 유지되는 현상이 발생했다.&lt;br&gt;확인 결과 스케줄링 문제가 아니라 &lt;strong&gt;로그성 data stream 증가로 인한 디스크&lt;br&gt;압박(DiskPressure)&lt;/strong&gt; 이 원인이었다.&lt;/p&gt;
&lt;p&gt;이 글에서는 실제 운영 환경에서 발생한 문제를 기준으로&lt;br&gt;인덱스 용량 확인 → ILM 설정 → 정리 과정까지 정리한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  1. 증상&lt;/h2&gt;
&lt;p&gt;노드 상태 확인:&lt;/p&gt;
&lt;p&gt;kubectl describe node &lt;code&gt;&amp;lt;node-name&amp;gt;&lt;/code&gt;{=html}&lt;/p&gt;
&lt;p&gt;  DiskPressure 발생&lt;/p&gt;
&lt;p&gt;Elasticsearch 상태:&lt;/p&gt;
&lt;p&gt;kubectl get elasticsearch -n &lt;code&gt;&amp;lt;namespace&amp;gt;&lt;/code&gt;{=html}&lt;/p&gt;
&lt;p&gt;  Ready 상태지만 내부적으로 shard allocation 제한 발생&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;⚠️ 2. 원인 확인 (인덱스 크기)&lt;/h2&gt;
&lt;p&gt;Elasticsearch 접근:&lt;/p&gt;
&lt;p&gt;kubectl port-forward svc/&lt;code&gt;&amp;lt;es-http-service&amp;gt;&lt;/code&gt;{=html} 9200:9200 -n&lt;br&gt;&lt;code&gt;&amp;lt;namespace&amp;gt;&lt;/code&gt;{=html}&lt;/p&gt;
&lt;p&gt;인덱스 크기 확인:&lt;/p&gt;
&lt;p&gt;curl -s &amp;#39;&lt;a href=&quot;http://localhost:9200/%5C_cat/indices?v&amp;amp;s=store.size:desc&amp;#39;&quot;&gt;http://localhost:9200/\_cat/indices?v&amp;amp;s=store.size:desc&amp;#39;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;디스크 사용량:&lt;/p&gt;
&lt;p&gt;curl -s &amp;#39;&lt;a href=&quot;http://localhost:9200/%5C_cat/allocation?v&amp;#39;&quot;&gt;http://localhost:9200/\_cat/allocation?v&amp;#39;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;  문제 인덱스&lt;/h3&gt;
&lt;p&gt;metricbeat data stream (대용량) minecraft logs (중간 규모)&lt;/p&gt;
&lt;p&gt;  로그성 데이터가 계속 누적됨&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  3. 위험한 행동 (하지 말 것)&lt;/h2&gt;
&lt;p&gt;❌ local PV 내부 파일 직접 삭제&lt;br&gt;→ Elasticsearch index 깨짐&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt; ️ 4. 임시 대응 (디스크 확보)&lt;/h2&gt;
&lt;p&gt;journalctl --vacuum-size=500M crictl rmi --prune apt clean&lt;/p&gt;
&lt;p&gt;  OS/컨테이너 레벨 정리만 수행&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  5. ILM 정책 문제&lt;/h2&gt;
&lt;p&gt;문제 1:&lt;/p&gt;
&lt;p&gt;rollover 실패 (권한 문제)&lt;/p&gt;
&lt;p&gt;문제 2:&lt;/p&gt;
&lt;p&gt;policy 자체 없음&lt;/p&gt;
&lt;p&gt;  로그는 계속 쌓이지만 삭제 안 됨&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 6. 해결: ILM 정책 설정&lt;/h2&gt;
&lt;p&gt;정책 기준:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rollover: 7일 또는 10GB&lt;/li&gt;
&lt;li&gt;delete: 30일&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예시:&lt;/p&gt;
&lt;p&gt;curl -X PUT &amp;#39;&lt;a href=&quot;http://localhost:9200/%5C_ilm/policy/log-policy&amp;#39;%5C&quot;&gt;http://localhost:9200/\_ilm/policy/log-policy&amp;#39;\&lt;/a&gt;&lt;br&gt;-d &amp;#39;{ &amp;quot;policy&amp;quot;: { &amp;quot;phases&amp;quot;: { &amp;quot;hot&amp;quot;: { &amp;quot;actions&amp;quot;: { &amp;quot;rollover&amp;quot;: {&lt;br&gt;&amp;quot;max_age&amp;quot;: &amp;quot;7d&amp;quot;, &amp;quot;max_primary_shard_size&amp;quot;: &amp;quot;10gb&amp;quot; } } }, &amp;quot;delete&amp;quot;: {&lt;br&gt;&amp;quot;min_age&amp;quot;: &amp;quot;30d&amp;quot;, &amp;quot;actions&amp;quot;: { &amp;quot;delete&amp;quot;: {} } } } } }&amp;#39;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  7. Rollover + Index 정리&lt;/h2&gt;
&lt;p&gt;rollover 실행:&lt;/p&gt;
&lt;p&gt;curl -X POST &amp;#39;&lt;a href=&quot;http://localhost:9200/%60&quot;&gt;http://localhost:9200/`&lt;/a&gt;&lt;data-stream&gt;`{=html}/_rollover&amp;#39;&lt;/p&gt;
&lt;p&gt;오래된 index 삭제:&lt;/p&gt;
&lt;p&gt;curl -X DELETE &amp;#39;&lt;a href=&quot;http://localhost:9200/%60&quot;&gt;http://localhost:9200/`&lt;/a&gt;&lt;old-index&gt;`{=html}&amp;#39;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;⚠️ 주의&lt;/h2&gt;
&lt;p&gt;삭제 전 반드시 확인:&lt;/p&gt;
&lt;p&gt;curl &amp;#39;&lt;a href=&quot;http://localhost:9200/%5C_data_stream/%60&quot;&gt;http://localhost:9200/\_data_stream/`&lt;/a&gt;&lt;name&gt;`{=html}&amp;#39;&lt;/p&gt;
&lt;p&gt;  write index 삭제하면 장애 발생&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 8. 결과&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;DiskPressure 해소&lt;/li&gt;
&lt;li&gt;Elasticsearch health → green&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  실무 체크리스트&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;ILM policy 존재 여부&lt;/li&gt;
&lt;li&gt;template 연결 여부&lt;/li&gt;
&lt;li&gt;rollover 설정 여부&lt;/li&gt;
&lt;li&gt;delete phase 존재 여부&lt;/li&gt;
&lt;li&gt;권한 정상 여부&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;  결론&lt;/h2&gt;
&lt;p&gt;Elasticsearch DiskPressure는 단순 디스크 문제가 아니라&lt;br&gt;  &lt;strong&gt;ILM 정책 미설정 문제&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  local PV 환경에서는 반드시 ILM을 설정해야 한다&lt;/p&gt;</description>
      <category>k8s/EFK</category>
      <category>DiskPressure</category>
      <category>Elasticsearch</category>
      <category>elasticsearch 관리</category>
      <category>ILM</category>
      <category>k8s storage</category>
      <category>k8s 운영</category>
      <category>Kubernetes</category>
      <category>metricbeat</category>
      <category>minecraft</category>
      <author>바리새인</author>
      <guid isPermaLink="true">https://barisein.tistory.com/1279</guid>
      <comments>https://barisein.tistory.com/1279#entry1279comment</comments>
      <pubDate>Sat, 25 Apr 2026 16:06:12 +0900</pubDate>
    </item>
    <item>
      <title>Elasticsearch Pod Pending 원인 분석: local PV와 taint 충돌 해결</title>
      <link>https://barisein.tistory.com/1278</link>
      <description>&lt;p&gt;Kubernetes 환경에서 ECK로 운영 중인 Elasticsearch Pod가 장시간 &lt;code&gt;Pending&lt;/code&gt;&lt;br&gt;상태에 머무르는 문제가 발생했다.&lt;br&gt;처음에는 리소스 부족을 의심했지만, 실제 원인은 &lt;strong&gt;local PV의 node&lt;br&gt;affinity와 node taint 정책 충돌&lt;/strong&gt;이었다.&lt;/p&gt;
&lt;p&gt;이 글에서는 해당 문제의 원인과 해결 과정을 실무 기준으로 정리한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  1. 증상&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl get pods -n &amp;lt;namespace&amp;gt; -o wide
kubectl get elasticsearch,kibana -n &amp;lt;namespace&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;es-es-hot-ssd-0   0/1   Pending&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  Kibana와 Operator는 정상&lt;br&gt;  Elasticsearch만 ApplyingChanges 상태 유지&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;⚠️ 2. 스케줄링 이벤트&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl describe pod &amp;lt;pod-name&amp;gt; -n &amp;lt;namespace&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;FailedScheduling
had untolerated taint
Preemption is not helpful&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  리소스 부족 ❌&lt;br&gt;  스케줄링 정책 충돌 ✅&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  3. PVC / PV 확인&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl describe pv &amp;lt;pv-name&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Node Affinity:
  kubernetes.io/hostname in [node-a]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  local PV = 특정 노드 강제 배치&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  4. 핵심 원인&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl describe node &amp;lt;node-name&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;tier=frontend:NoSchedule&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  Pod에는 toleration 없음&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  정리&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;PVC → local PV 바인딩&lt;/li&gt;
&lt;li&gt;PV → 특정 노드 고정&lt;/li&gt;
&lt;li&gt;노드 → taint 존재&lt;/li&gt;
&lt;li&gt;Pod → toleration 없음&lt;/li&gt;
&lt;li&gt;결과 → Pending&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;  5. 해결 방법&lt;/h2&gt;
&lt;h3&gt;방법 1: taint 제거 (권장 - 잘못된 설정일 경우)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl taint node &amp;lt;node-name&amp;gt; tier=frontend:NoSchedule-&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3&gt;방법 2: toleration 추가&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;tolerations:
  - key: tier
    operator: Equal
    value: frontend
    effect: NoSchedule&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  정책 유지가 목적이라면 이 방법&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;⚙️ 6. ECK 추가 체크&lt;/h2&gt;
&lt;h3&gt;메모리 설정&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;resources:
  requests:
    memory: 2Gi
  limits:
    memory: 2Gi&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  request = limit 맞춰야 함&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;상태 확인&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl get elasticsearch &amp;lt;name&amp;gt; -n &amp;lt;namespace&amp;gt; \
-o jsonpath=&amp;#39;{range .status.conditions[*]}{.type}{&amp;quot;\t&amp;quot;}{.status}{&amp;quot;\n&amp;quot;}{end}&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 7. 최종 확인&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl get pods -n &amp;lt;namespace&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Running 상태 확인&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  8. 실무 체크리스트&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1. pod event 확인
2. toleration 확인
3. PVC/PV binding 확인
4. local PV node affinity 확인
5. node taint 확인&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  결론&lt;/h2&gt;
&lt;p&gt;Elasticsearch Pending 문제는 단순 리소스 문제가 아니라&lt;br&gt;  storage + scheduling 정책 충돌 문제였다.&lt;/p&gt;
&lt;p&gt;  local PV 사용 시 반드시 taint까지 함께 확인해야 한다.&lt;/p&gt;</description>
      <category>k8s/EFK</category>
      <category>ECK</category>
      <category>Elasticsearch</category>
      <category>k8s troubleshooting</category>
      <category>Kubernetes</category>
      <category>kubernetes storage</category>
      <category>LocalPV</category>
      <category>pending</category>
      <category>taint</category>
      <author>바리새인</author>
      <guid isPermaLink="true">https://barisein.tistory.com/1278</guid>
      <comments>https://barisein.tistory.com/1278#entry1278comment</comments>
      <pubDate>Sat, 25 Apr 2026 16:02:26 +0900</pubDate>
    </item>
    <item>
      <title>Kubernetes에서 Slack Webhook을 안전하게 관리하는 방법 (Fluentd +  Minecraft 알림)</title>
      <link>https://barisein.tistory.com/1277</link>
      <description>&lt;p&gt;Minecraft 서버 접속 로그를 기반으로 Slack 알림을 보내는 환경을 운영하던&lt;br&gt;중, Slack Webhook URL이 Kubernetes manifest에 평문으로 포함된 문제를&lt;br&gt;발견했다.&lt;/p&gt;
&lt;p&gt;처음에는 단순히 동작 여부만 확인했지만, 운영 환경에서는 &lt;strong&gt;보안과 관리&lt;br&gt;방식이 더 중요&lt;/strong&gt;하다.&lt;/p&gt;
&lt;p&gt;이 글에서는 실제 운영 환경에서 Webhook을 Git과 분리하고 안전하게 관리한&lt;br&gt;방법을 정리한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. 기존 구조&lt;/h2&gt;
&lt;p&gt;전체 흐름:&lt;/p&gt;
&lt;p&gt;Minecraft 로그 → Fluentd → 로그 필터 → Slack Webhook&lt;/p&gt;
&lt;p&gt;Fluentd 설정 예시:&lt;/p&gt;
&lt;p&gt;&amp;lt;match minecraft.alert&amp;gt; @type slack webhook_url&lt;br&gt;&amp;quot;#{ENV[&amp;#39;SLACK_WEBHOOK_URL&amp;#39;]}&amp;quot; &lt;code&gt;&amp;lt;/match&amp;gt;&lt;/code&gt;{=html}&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. 문제점&lt;/h2&gt;
&lt;p&gt;기존 방식:&lt;/p&gt;
&lt;p&gt;kind: Secret stringData: webhook_url: &amp;quot;&lt;code&gt;&amp;lt;slack-webhook-url&amp;gt;&lt;/code&gt;{=html}&amp;quot;&lt;/p&gt;
&lt;p&gt;문제:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Git 커밋 시 노출 위험&lt;/li&gt;
&lt;li&gt;코드 리뷰/백업에서 유출 가능&lt;/li&gt;
&lt;li&gt;블로그 작성 시 실수 복사 가능&lt;/li&gt;
&lt;li&gt;Webhook 교체(rotate) 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;3. 개선 전략&lt;/h2&gt;
&lt;p&gt;핵심 원칙:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Secret 값은 Git에 넣지 않는다&lt;/li&gt;
&lt;li&gt;Secret은 외부에서 생성한다&lt;/li&gt;
&lt;li&gt;애플리케이션은 Secret을 참조만 한다&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;4. Secret 외부 관리&lt;/h2&gt;
&lt;p&gt;파일 위치:&lt;/p&gt;
&lt;p&gt;/secure/path/monitoring.env&lt;/p&gt;
&lt;p&gt;내용:&lt;/p&gt;
&lt;p&gt;SLACK_WEBHOOK_URL=...&lt;/p&gt;
&lt;p&gt;권한 설정:&lt;/p&gt;
&lt;p&gt;chmod 600 monitoring.env&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;5. Secret 생성&lt;/h2&gt;
&lt;p&gt;kubectl create secret generic slack-webhook&lt;br&gt;--from-literal=webhook_url=&amp;quot;$SLACK_WEBHOOK_URL&amp;quot;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. Fluentd 연동&lt;/h2&gt;
&lt;p&gt;env: - name: SLACK_WEBHOOK_URL valueFrom: secretKeyRef: name:&lt;br&gt;slack-webhook key: webhook_url&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. 운영 팁 (중요)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Kubernetes Secret은 base64 → 암호화 아님&lt;/li&gt;
&lt;li&gt;webhook 노출 시 즉시 재발급&lt;/li&gt;
&lt;li&gt;Fluentd 장애 시 알림 중단됨&lt;/li&gt;
&lt;li&gt;중요한 알림은 Grafana/Prometheus와 병행 추천&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;8. 실무 체크리스트&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Git에 Secret 포함 여부 확인&lt;/li&gt;
&lt;li&gt;Secret 외부 관리 여부 확인&lt;/li&gt;
&lt;li&gt;환경변수 주입 정상 여부 확인&lt;/li&gt;
&lt;li&gt;로그 필터 정상 동작 확인&lt;/li&gt;
&lt;li&gt;Slack 전송 로그 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;9. 결론&lt;/h2&gt;
&lt;p&gt;Webhook 같은 민감 정보는 반드시 Git에서 분리해야 한다.&lt;/p&gt;
&lt;p&gt;운영 환경에서는&lt;/p&gt;
&lt;p&gt;Secret + 외부 관리 구조&lt;/p&gt;
&lt;p&gt;를 기본으로 사용하는 것이 안전하다.&lt;/p&gt;</description>
      <category>k8s/Kafka</category>
      <category>fluentd</category>
      <category>fluentd 설정</category>
      <category>k8s logging</category>
      <category>k8s 보안</category>
      <category>Kubernetes</category>
      <category>kubernetes secret 관리</category>
      <category>minecraft</category>
      <category>secret</category>
      <category>slack</category>
      <category>slack webhook</category>
      <author>바리새인</author>
      <guid isPermaLink="true">https://barisein.tistory.com/1277</guid>
      <comments>https://barisein.tistory.com/1277#entry1277comment</comments>
      <pubDate>Sat, 25 Apr 2026 15:51:55 +0900</pubDate>
    </item>
    <item>
      <title>Bitnami Kafka KRaft Controller ImagePullBackOff 원인 및 복구 방법</title>
      <link>https://barisein.tistory.com/1276</link>
      <description>&lt;h1&gt;Bitnami Kafka KRaft Controller ImagePullBackOff 원인 및 복구 방법&lt;/h1&gt;
&lt;p&gt;Kubernetes 환경에서 Kafka(KRaft 모드) controller Pod 중 하나가 장시간 ImagePullBackOff 상태에 머무는 문제가 발생했다.&lt;br&gt;초기에는 노드 문제나 네트워크 이슈로 보였지만, 실제 원인은 이미지 repository 변경이었다.&lt;/p&gt;
&lt;p&gt;이 글에서는 문제 원인과 복구 과정을 순서대로 정리한다.&lt;br&gt;(※ 네임스페이스, 노드명, 내부 주소 등은 모두 일반화된 값이다)&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. 증상&lt;/h2&gt;
&lt;p&gt;Kafka controller Pod 하나가 정상적으로 올라오지 않았다.&lt;/p&gt;
&lt;p&gt;kubectl get pods -n &lt;namespace&gt; -o wide&lt;/p&gt;
&lt;p&gt;kafka-kraft-controller-2  0/1  Init:ImagePullBackOff&lt;/p&gt;
&lt;p&gt;이벤트를 확인했다.&lt;/p&gt;
&lt;p&gt;kubectl describe pod -n &lt;namespace&gt; kafka-kraft-controller-2&lt;/p&gt;
&lt;p&gt;Pulling image &amp;quot;docker.io/bitnami/kafka:4.0.0-debian-12-r10&amp;quot;&lt;br&gt;Back-off pulling image&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. 배포 방식 확인 (Helm)&lt;/h2&gt;
&lt;p&gt;helm list -n &lt;namespace&gt;&lt;br&gt;helm get values -n &lt;release-name&gt;&lt;br&gt;kubectl get sts -n &lt;namespace&gt; &lt;statefulset-name&gt; -o yaml&lt;/p&gt;
&lt;p&gt;Helm chart: bitnami/kafka&lt;br&gt;Chart version: 32.4.2&lt;br&gt;App version: 4.0.0&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. 실제 이미지 Pull 테스트&lt;/h2&gt;
&lt;p&gt;crictl pull docker.io/bitnami/kafka:4.0.0-debian-12-r10&lt;/p&gt;
&lt;p&gt;결과: not found&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;4. 원인&lt;/h2&gt;
&lt;p&gt;docker.io/bitnami/kafka:4.0.0-debian-12-r10 → 삭제됨&lt;br&gt;docker.io/bitnamilegacy/kafka:4.0.0-debian-12-r10 → 사용 가능&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;5. 해결&lt;/h2&gt;
&lt;p&gt;helm upgrade --install &lt;release-name&gt; bitnami/kafka &lt;br&gt;--version 32.4.2 &lt;br&gt;--namespace &lt;namespace&gt; &lt;br&gt;--set image.registry=docker.io &lt;br&gt;--set image.repository=bitnamilegacy/kafka &lt;br&gt;--set image.tag=4.0.0-debian-12-r10&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. 확인&lt;/h2&gt;
&lt;p&gt;kubectl rollout status sts/&lt;statefulset-name&gt; -n &lt;namespace&gt;&lt;br&gt;kubectl get pods -n &lt;namespace&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. 결론&lt;/h2&gt;
&lt;p&gt;ImagePullBackOff 발생 시 이미지 존재 여부부터 확인해야 한다.&lt;/p&gt;</description>
      <category>k8s/Kafka</category>
      <category>bitnami</category>
      <category>Helm</category>
      <category>imagepullbackoff</category>
      <category>K8S</category>
      <category>Kafka</category>
      <category>kafka error</category>
      <category>Kraft</category>
      <category>Kubernetes</category>
      <category>kubernetes troubleshooting</category>
      <author>바리새인</author>
      <guid isPermaLink="true">https://barisein.tistory.com/1276</guid>
      <comments>https://barisein.tistory.com/1276#entry1276comment</comments>
      <pubDate>Sat, 25 Apr 2026 11:22:09 +0900</pubDate>
    </item>
    <item>
      <title>Kubernetes 노드 디스크 부족 해결: containerd 이미지와 로그 정리</title>
      <link>https://barisein.tistory.com/1275</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes 노드에서 root filesystem 사용량이 높아지면 아직 &lt;code&gt;DiskPressure=False&lt;/code&gt; 상태라도 이미지 pull 실패, pod eviction, kubelet 이상 동작으로 이어질 수 있다. 이럴 때는 무작정 파일을 지우기보다 containerd 이미지, journald 로그, 실제 사용량을 순서대로 확인하는 것이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 Kubernetes 노드 디스크 사용량이 높을 때 확인한 순서와 정리 방법을 정리한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 노드 상태 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 노드가 실제로 압박 상태인지 확인한다.&lt;/p&gt;
&lt;pre id=&quot;code_1275_describe_node&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl describe node &amp;lt;node-name&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인할 항목은 아래와 같다.&lt;/p&gt;
&lt;ul data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;DiskPressure&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MemoryPressure&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PIDPressure&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ready&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;event에 eviction 관련 메시지가 있는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;DiskPressure=False&lt;/code&gt;라도 root filesystem 사용률이 계속 올라가고 있다면 미리 정리하는 편이 낫다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 디스크 사용량 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드에 직접 접속할 수 있다면 &lt;code&gt;df&lt;/code&gt;와 &lt;code&gt;du&lt;/code&gt;로 확인한다.&lt;/p&gt;
&lt;pre id=&quot;code_1275_df_du&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df -h
du -xh /var/lib/containerd 2&amp;gt;/dev/null | sort -h | tail
du -xh /var/log 2&amp;gt;/dev/null | sort -h | tail&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 SSH가 어렵다면 &lt;code&gt;kubectl debug node&lt;/code&gt;로 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1275_debug_node&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl debug node/&amp;lt;node-name&amp;gt; -q --image=busybox:1.36 -- sleep 600&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 자주 커지는 위치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes 노드에서 자주 커지는 위치는 보통 아래다.&lt;/p&gt;
&lt;ul data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;/var/lib/containerd&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/log&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/log/journal&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;pod 로그 경로&lt;/li&gt;
&lt;li&gt;오래된 이미지와 사용하지 않는 snapshot&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;containerd를 쓰는 환경에서는 Docker 명령이 아니라 &lt;code&gt;crictl&lt;/code&gt; 또는 &lt;code&gt;nerdctl&lt;/code&gt; 기준으로 확인해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. journald 로그 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;journald 로그가 크면 보관 크기를 제한해 정리할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1275_journalctl&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;journalctl --disk-usage
journalctl --vacuum-size=1G&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 환경에서는 일회성 정리만 하지 말고 &lt;code&gt;/etc/systemd/journald.conf&lt;/code&gt;에 보관 제한을 두는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1275_journald_conf&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SystemMaxUse=1G
SystemKeepFree=2G&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 변경 후에는 journald를 재시작한다.&lt;/p&gt;
&lt;pre id=&quot;code_1275_journald_restart&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;systemctl restart systemd-journald&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. containerd 이미지 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하지 않는 이미지는 &lt;code&gt;crictl&lt;/code&gt;로 정리할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1275_crictl_prune&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;crictl images
crictl rmi --prune&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리 전에는 현재 실행 중인 pod가 사용하는 이미지를 지우지 않는지 확인해야 한다. &lt;code&gt;--prune&lt;/code&gt;은 사용하지 않는 이미지를 대상으로 하지만, 운영 중에는 작업 전후 상태를 반드시 확인하는 것이 좋다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 정리 후 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리 후에는 다시 사용량과 노드 상태를 확인한다.&lt;/p&gt;
&lt;pre id=&quot;code_1275_after_check&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df -h
kubectl describe node &amp;lt;node-name&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인할 것은 아래다.&lt;/p&gt;
&lt;ul data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;root filesystem 사용률이 내려갔는지&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DiskPressure&lt;/code&gt;가 &lt;code&gt;False&lt;/code&gt;인지&lt;/li&gt;
&lt;li&gt;pod 재시작이나 eviction이 새로 발생하지 않았는지&lt;/li&gt;
&lt;li&gt;kubelet/containerd 로그에 오류가 없는지&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 재발 방지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복되는 문제라면 아래 설정을 같이 봐야 한다.&lt;/p&gt;
&lt;ul data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;journald 보관 크기 제한&lt;/li&gt;
&lt;li&gt;logrotate 설정&lt;/li&gt;
&lt;li&gt;kubelet image GC 설정&lt;/li&gt;
&lt;li&gt;containerd snapshot 정리 정책&lt;/li&gt;
&lt;li&gt;모니터링에서 root filesystem 사용률 알림&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 한 번 찬 노드는 시간이 지나면 다시 찰 가능성이 높다. 임계치 알림을 걸고, 어떤 디렉터리가 커지는지 주기적으로 보는 편이 안전하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;같이 보면 좋은 글&lt;/h2&gt;
&lt;ul data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://barisein.tistory.com/1274&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Kubernetes 노드 CPU 높아 보일 때 점검 순서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://barisein.tistory.com/1273&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rootful containerd에서 sudo 없이 nerdctl 쓰려다 실패한 기록&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://barisein.tistory.com/1285&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Elasticsearch Pod Pending 원인: local PV와 taint 충돌 해결&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes 노드 디스크가 부족할 때는 &lt;code&gt;/var/lib/containerd&lt;/code&gt;와 &lt;code&gt;/var/log&lt;/code&gt;를 먼저 확인한다. &lt;code&gt;journalctl --vacuum-size&lt;/code&gt;, &lt;code&gt;crictl rmi --prune&lt;/code&gt;으로 급한 사용량을 줄일 수 있지만, 재발 방지를 위해 journald 제한, logrotate, image GC 설정까지 같이 봐야 한다.&lt;/p&gt;</description>
      <category>k8s</category>
      <category>containerd</category>
      <category>crictl</category>
      <category>Disk Cleanup</category>
      <category>journalctl</category>
      <category>Kubernetes</category>
      <author>바리새인</author>
      <guid isPermaLink="true">https://barisein.tistory.com/1275</guid>
      <comments>https://barisein.tistory.com/1275#entry1275comment</comments>
      <pubDate>Sat, 25 Apr 2026 10:55:20 +0900</pubDate>
    </item>
  </channel>
</rss>