<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>alli-eunbi.log</title>
        <link>https://velog.io/</link>
        <description>BACKEND</description>
        <lastBuildDate>Wed, 21 May 2025 11:29:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>alli-eunbi.log</title>
            <url>https://velog.velcdn.com/images/alli-eunbi/profile/93910dba-3f18-4ddd-8e8b-57ccab160ead/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. alli-eunbi.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/alli-eunbi" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Kubernetes v1.33 Release Info]]></title>
            <link>https://velog.io/@alli-eunbi/Kubernetes-v1.33-Release-Info</link>
            <guid>https://velog.io/@alli-eunbi/Kubernetes-v1.33-Release-Info</guid>
            <pubDate>Wed, 21 May 2025 11:29:46 GMT</pubDate>
            <description><![CDATA[<p>64개의 Enhancement</p>
<ul>
<li>Alpha 24</li>
<li>Beta 20</li>
<li>Stable 18</li>
<li>Deprecated 2</li>
</ul>
<hr>
<h2 id="sig-network">SIG-Network</h2>
<h3 id="1-multiple-service-cidrs--status-stable">1. Multiple Service CIDRS  (status: stable)</h3>
<p>Service IP를 할당하는 logic을 새롭게 구현.</p>
<p>ClusterIP는 유니크한 주소를 가지며, Service CIDR라고 불리는 사전에 정의된 IP 풀에서 할당.
문제: CIDR 범위 변경 어려움, CIDR가 부족하면 서비스 생성 실패.</p>
<p>Kubernetes는 이를 해결하기 위해 <strong>두 개의 새로운 API 오브젝트</strong>를 도입했고, 현재는 stable 상태로 사용 가능:</p>
<ol>
<li><p>ServiceCIDR 오브젝트
관리자가 클러스터에 새로운 CIDR 블록을 추가 가능.
=&gt; 서비스 IP 풀을 동적으로 확장</p>
</li>
<li><p>IPAddress 오브젝트
각 서비스에 할당된 IP를 오브젝트 단위로 추적 및 관리 가능.
=&gt; 클러스터 내부에서 IP 추적 가능, 향후 IP 재사용, 모니터링, 감사 등에 유용함</p>
</li>
</ol>
<h3 id="2-nftables-backend-for-kube-proxy--status-stable">2. nftables backend for kube-proxy  (status: stable)</h3>
<p>**호환성 문제로 iptables는 linux node에서 default로 남음. </p>
<p>현재 Linux에서 공식적으로 지원되는 kube-proxy 백엔드는 iptables와 ipvs. </p>
<p>iptables 에서 아래의 문제가 존재</p>
<ol>
<li>컨트롤 플레인에서의 문제: iptables ruleset을 바꾸는 과정에서 성능 문제
단순 rule 추가 과정도 아래와 같음<pre><code>lock
kernel에서 rule 전체 다운로드
rule 파싱 및 규칙 추가
커널 업로드
lock 해제</code></pre></li>
<li>데이터 플레인에서의 문제: 서비스 수가 많아질수록 iptables 규칙도 증가
들어오는 모든 패킷이 모든 규칙을 순차적으로 거쳐야 함 -&gt; 병목 지점</li>
</ol>
<p>위 문제를 해결하기 위해 nftables를 backend로 도입:</p>
<ol>
<li>Map 구조를 활용하기 때문에 전체 rule을 조회할 필요 없어 기존 성능 문제 해결.</li>
<li>atomic 트랜잭션 적용.</li>
</ol>
<p>GA는 아직 아니나 도입 예정
kube-proxy를 라이브러리로 제공하기 위한 리팩토링이 진행될 예정</p>
<h3 id="3-topology-aware-routing-with-trafficdistribution-preferclose--status-stable">3. Topology aware routing with <em>trafficDistribution: PreferClose</em>  (status: stable)</h3>
<p>multi-zone cluster의 경우 최적화된 service traffic을 제공하는 기능이 GA 승격</p>
<p>cross-zone의 데이터 송수신의 비용 증가 및 latency 발생(kube-proxy는 서비스 트래픽을 무작위로 백엔드에 라우팅) =&gt; v1.23 에서 topology routing 기능을 제공했으나 아래와 같은 문제가 발생</p>
<ol>
<li>기존 topologyKeys는 표준이 없어 예상치 못한 결과를 낳기도 했음.</li>
<li>topology-mode: Auto는 예측 불가능성과 제어 부족.</li>
</ol>
<p>따라서 위를 해결하기 위한 새로운 필드가 도입,</p>
<ol>
<li><p><code>trafficDistribution: PreferClose</code> 필드: 
서비스 정의에 이 필드를 추가하면, kube-proxy가 클라이언트와 동일한 영역의 엔드포인트로 트래픽을 우선 라우팅 </p>
</li>
<li><p>자동 페일오버 지원: 
동일한 영역에 사용 가능한 엔드포인트가 없을 경우, Kubernetes는 자동으로 다른 영역의 엔드포인트로 트래픽을 라우팅하여 서비스 가용성을 유지</p>
</li>
</ol>
<p>** 다만, 특정 zone의 엔드포인트가 과부하되는 문제는 계속 고려해야 함. 이 경우 다른 라우팅 전략 고려하거나, 그 zone의 엔드포인트 수 증가, 오토스케일링과 같은 리소스 제어 필요</p>
<hr>
<h2 id="sig-apps">SIG-Apps</h2>
<h3 id="1-backoff-limits-per-index-for-indexed-jobs--status-stable">1. Backoff limits per index for indexed Jobs  (status: stable)</h3>
<p>현재 Kubernetes Indexed Job은 전체 인덱스가 하나의 backoff limit을 공유함.
하나의 인덱스가 반복 실패하면, 아직 실행 중인 다른 인덱스까지 모두 종료됨.</p>
<p>각 인덱스에 독립적인 backoff limit을 부여:</p>
<ul>
<li>Per-index backoff limit 도입
인덱스마다 독립적으로 재시도 횟수를 관리 가능
하나의 인덱스가 실패해도 다른 인덱스는 계속 실행됨.</li>
</ul>
<h3 id="2-job-success-policy--status-stable">2. Job success policy  (status: stable)</h3>
<p>현재 Kubernetes에서는 모든 인덱스가 성공해야만 Job을 성공으로 간주
→ 이게 너무 엄격하고, 불필요한 리소스 낭비를 일으킴.</p>
<p>SuccessPolicy 라는 새로운 필드 도입:</p>
<ul>
<li>SuccessPolicy를 정의해서 Job이 성공으로 간주되는 기준을 명시</li>
<li>정책이 충족되면 SuccessCriteriaMet 라는 새로운 Job Condition을 추가.</li>
<li>정책이 충족되면 나머지 실행 중인 Pod들은 종료 (불필요한 리소스 해소)</li>
</ul>
<hr>
<h2 id="sig-auth">Sig-Auth</h2>
<h3 id="1-bound-serviceaccount-token-security-improvements--status-stable">1. Bound ServiceAccount token security improvements  (status: stable)</h3>
<ol>
<li>unique token identifier(JTI)와 노드 정보를 토큰에 포함하여,</li>
</ol>
<p><strong>더 정확한 검증과 audit</strong>가 가능.</p>
<ol start="2">
<li>노드 전용 제약 조건을 지원하여,
특정 노드에서만 토큰을 사용할 수 있도록 제한할 수 있게 되었고,
이를 통해 토큰 오용이나 보안 사고 위험을 감소</li>
</ol>
<p>현재 GA 제공</p>
<hr>
<h2 id="sig-cli">SIG-CLI</h2>
<h3 id="1-subresource-support-in-kubectl--status-stable">1. Subresource support in kubectl  (status: stable)</h3>
<p><code>--subresource</code> 플래그가 <strong>GA(General Availability)</strong>로 승격</p>
<p>이제 kubectl 명령어에서 <code>--subresource</code> 플래그를 사용해 서브리소스에 직접 접근하고 수정할 수 있습니다.</p>
<p>🛠️ 지원되는 명령어
<code>get</code>, <code>patch</code>, <code>edit</code>, <code>apply</code>, <code>replace</code></p>
<hr>
<h2 id="sig-node">SIG-Node</h2>
<h3 id="1-sidecar-containers--status-stable">1. Sidecar containers  (status: stable)</h3>
<p>사이드카 컨테이너 기능이 Kubernetes v1.33에서 GA 승격</p>
<p>sidecar container는 restartPolicy: Always 속성을 가지며, 다음과 같은 동작을 보장:</p>
<ul>
<li>애플리케이션 컨테이너보다 먼저 시작되고,</li>
<li>Pod의 전체 수명 동안 계속 실행되며,</li>
<li>주 컨테이너들이 종료된 후 자동으로 종료됩니다.</li>
</ul>
<h3 id="2-options-to-reject-non-smt-aligned-workload--status-stable">2. Options to reject non SMT-aligned workload  (status: stable)</h3>
<p>SMT 환경에서 latency가 중요한 application의 운영 대한 예측 가능성을 높이기 위해 도입, GA 승격.</p>
<p>static CPU Manager 정책에 full-pcpus-only 옵션을 추가하면:</p>
<ul>
<li>항상 물리 코어 단위로 CPU를 할당</li>
<li>SMT가 켜져 있어도 스레드 공유를 막아 성능 간섭 방지</li>
</ul>
<p>** 참고) vcpu로 논리적으로 나눠져 있어도 메모리 버스, L1-L2 캐시, 네트워크 등 자원을 공유함.</p>
<hr>
<h2 id="sig-scheduling">SIG-Scheduling</h2>
<h3 id="1-defining-pod-affinity-or-anti-affinity-using-matchlabelkeys-and-mismatchlabelkeys--status-stable">1. Defining Pod affinity or anti-affinity using matchLabelKeys and mismatchLabelKeys  (status: stable)</h3>
<p><code>MatchLabelKeys</code> 와 <code>MismatchLabelKeys</code> 추가</p>
<p>사용 예시
<strong>1. 롤링 업데이트 시, 구버전과 신버전의 파드를 각각 버전별로 같은 존(Zone)에 배치하도록 설정</strong></p>
<pre><code>podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - database
        topologyKey: topology.kubernetes.io/zone
        matchLabelKeys: # ADDED
        - pod-template-hash</code></pre><p><strong>2. MutatingWebhookConfiguration을 이용해 tenant 라벨을 자동으로 붙이고, tenant별로 Pod가 섞이지 않도록 배치할 수 있음.</strong></p>
<pre><code>affinity:
  podAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - matchLabelKeys:
        - tenant          # 나랑 같은 tenant만 affinity 대상
      topologyKey: node-pool
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - mismatchLabelKeys:
        - tenant  </code></pre><h3 id="2-considering-taints-and-tolerations-when-calculating-pod-topology-spread-skew--status-stable">2. Considering taints and tolerations when calculating Pod topology spread skew  (status: stable)</h3>
<p>PodTopologySpread를 사용할 때,
taints와 tolerations도 스케줄링 계산에 포함할 수 있게 하자는 기능</p>
<p>예를 들어 
node가 A, B가 존재하고 node B에는 taint/toleration이 foo=bar:NoSchedule로 걸려 있음. 
deployment는 replica가 2개, topology 정책이 maxSkew:1 로 걸려있음.
scheduling시 node B를 계산에 포함하기 때문에 node b에 배치되려던 pod는 pending 상태에 빠지게 됨.
=&gt; nodeTaintsPolicy의 default 값은 Ignore로 설정되어 있기 떄문</p>
<p>nodeTaintsPolicy: Honor 옵션을 사용하면:</p>
<pre><code>topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: DoNotSchedule
          nodeTaintsPolicy: Honor</code></pre><ul>
<li>taint를 toleration 하지 못하는 노드는 계산 대상에서 아예 제외
그럼 처음부터 node b는 고려되지 않음
skew 계산 정확해지고, Unexpected Pending 방지 가능</li>
</ul>
<hr>
<h2 id="sig-storage">Sig-Storage</h2>
<h3 id="1-volume-populators--status-stable">1. Volume populators  (status: stable)</h3>
<p>Kubernetes 1.12부터 PVC에 dataSource 필드 생김.
이는 기존 PVC 또는 VolumeSnapshot을 통해 클론하거나 복원할 수 있도록 만든 기능</p>
<p>다만, </p>
<ul>
<li>현재 dataSource는 <strong>CSI(컨테이너 스토리지 인터페이스)</strong>가 지원하는 경우에만 동작하고,</li>
<li>특정한 타입만 허용하는 화이트리스트 구조(예: PVC, Snapshot만 가능).</li>
<li><strong>사용자가 새로운 데이터 소스(Custom Resource 등)</strong>를 쓰고 싶어도 API 상으로 거부되거나 무시됨. 예를 들어 VM 이미지, 백업 데이터, 외부 오브젝트 등을 볼륨에 넣고 싶어도 방법이 없음.</li>
</ul>
<p>=&gt; DataSourceRef 필드 도입, 동작 방식이 바뀜
<strong>data-source-validator가 아래와 같이 &quot;처리 가능 여부&quot;</strong>를 미리 알려줌.</p>
<pre><code>PVC 생성됨 
↓
dataSourceRef 확인: git.example.com/GitRepository
↓
등록된 VolumePopulator 목록에서 검색
↓
매치되는 VolumePopulator 발견? 
├─ YES → &quot;OK, 누군가 처리할 것임&quot; (이벤트 생성 안함)
└─ NO  → &quot;어? 이걸 처리할 populator가 없네!&quot; (경고 이벤트 생성) 
          =&gt;&quot;S3 populator가 설치되지 않았습니다&quot; 같은 명확한 메시지 전달</code></pre><h3 id="always-honor-persistentvolume-recalim-policy--status-stable">Always honor PersistentVolume recalim policy  (status: stable)</h3>
<p>PersistentVolume 누수 방지 기능이 GA(General Availability)로 승격</p>
<p>기존 문제점 (v1.33 이전)</p>
<ul>
<li><p>정상적인 삭제 순서:</p>
<pre><code>PVC → PV 순서로 삭제하면 reclaim policy가 정상 작동
외부 스토리지 자원이 올바르게 정리됨</code></pre></li>
<li><p>문제가 되는 삭제 순서:</p>
<pre><code>PV → PVC 순서로 삭제하면 reclaim policy가 무시됨
외부 인프라의 스토리지 자산이 삭제되지 않아 자원 누수 발생</code></pre></li>
</ul>
<p>개선 후:</p>
<ul>
<li>finalizer 메커니즘을 사용하여 문제 해결: finalizer 때문에 PV는 즉시 삭제되지 않고 Terminating 상태로 남음.</li>
<li>Delete reclaim policy가 올바르게 적용: PV가 완전히 삭제되기 전에 (즉, PVC가 삭제되어 finalizer가 제거된 후에) Delete reclaim policy에 따라 볼륨 삭제 프로세스가 진행.</li>
</ul>
<hr>
<p>이외 개선사항
DRA
VPA가 in-place로 downtime 없이 진행될 수 있음(Beta).</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SSO 구현 방식]]></title>
            <link>https://velog.io/@alli-eunbi/SSO-%EA%B5%AC%ED%98%84-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@alli-eunbi/SSO-%EA%B5%AC%ED%98%84-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Sun, 13 Oct 2024 15:10:05 GMT</pubDate>
            <description><![CDATA[<h3 id="sso-구현-모델-종류">SSO 구현 모델 종류</h3>
<h4 id="1-sso-delegation인증-대행-model">1. SSO Delegation(인증 대행) Model</h4>
<p>사용자가 사용하려는 서비스 인증방법을 별도의 SSO Agent가 대행해주는 방식
a. 대상 서비스의 인증 방식을 변경하기 어려울 때 사용
b. 대상 서비스의 인증 방식을 전혀 변경하지 않음
c. 서비스 인증 정보를 에이전트가 관리</p>
<pre><code>예를 들자면,
Target Server 1을 로그인 할 때 User1이 alice/alice라는 ID/ PWD가 필요하다면, 
에이전트가 이 정보를 가지고 있고, User1이 Target Service 1에 접근할 때 
에이전트가 대신 alice/alice ID/PWD 정보를 전달해서 로그인을 시켜주는 방식</code></pre><p><img src="https://velog.velcdn.com/images/alli-eunbi/post/5f7575a1-f79f-4489-8d05-acc03527e653/image.png" alt=""></p>
<h4 id="2-sso-propagation인증정보-전달-model">2. SSO Propagation(인증정보 전달) Model</h4>
<p>신뢰관계(Trust-relationship)를 토대로 사용자를 인증한 사실을 전달받아 SSO를 구현하는 방식
a. 통합 인증을 수행하는 곳에서 인증을 받음
b. 토큰(Token)을 발급 받아 사용하려는 서비스에 접근할 때 토큰을 자동으로 전달
c. 해당 서비스가 사용자를 확인할 수 있도록 하는 방식임
• 웹에서는 쿠키(Cookie)를 이용해 토큰을 자동으로 서비스에 전달할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/alli-eunbi/post/37821158-5ae5-4170-ab90-9f7755ef8ddc/image.png" alt=""></p>
<h4 id="3-delegation--propagation-방식">3. Delegation &amp; Propagation 방식</h4>
<p>모든 웹에서 Propagation 방식이 모두 적용될 수는 없으며, 특히 웹 애플리케이션의 변경이 전혀 불가능하고 사용자 통합이 어려운 경우 Delegation 방식을 사용하게 됨
• 사용하려는 서비스가 많고 애플리케이션의 특성들이 다양한 경우 Delegation 방식과 Propagation 방식을 혼용</p>
<h4 id="web-기반-one-cookie-domain-sso-vs-multi-cookie-domain-sso">Web 기반 One Cookie Domain SSO vs Multi Cookie Domain SSO</h4>
<ol>
<li>One Cookie Domain SSO</li>
</ol>
<ul>
<li>SSO 대상 서비스와 애플리케이션들이 <strong>하나의 Domain안에 존재</strong>할 때 사용되는 일반적인 기업 내부의 컴퓨팅 환경에서 사용<pre><code>a. 통합 인증을 받은 사용자는 토큰을 발급
b. 토큰은 Cookie Domain에 Cookie로 설정
c. Cookie Domain 내의 다른 서비스로 접근할 때 자동으로 토큰을 서비스에 제공
d. 서비스에서 동작되는 SSO 에이전트는 토큰으로부터 사용자 신원을 확인하고 요청된 자원에 대한 접근을 허가
=&gt; 루트 도메인에 등록된 모든 서브 도메인에서도 접근 가능</code></pre></li>
</ul>
<ol start="2">
<li>Web 기반 Multi Cookie Domain SSO</li>
</ol>
<ul>
<li>SSO 대상 서비스와 응용 애플리케이션들이 <strong>여러 도메인으로 분산</strong>돼 있을 경우</li>
</ul>
<pre><code>a. Multi Domain 환경인 경우에는 사용자 인증 및 토큰의 발행을 위한 마스터 에이전트가 존재
b. 마스터 에이전트는 각 서비스 에이전트의 사용자 인증을 위임 받아 수행
c. 인증된 사용자에게는 토큰을 발급하고 각 서비스 에이전트에게 안전하게 전달함
d. 에이전트가 해당 토큰을 자신의 Domain에서 Cookie로 저장해 사용할 수 있도록 함
=&gt; 여러 루트 도메인에 접근 가능</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[MongoDB IP대역 ]]></title>
            <link>https://velog.io/@alli-eunbi/MongoDB-IP%EB%8C%80%EC%97%AD</link>
            <guid>https://velog.io/@alli-eunbi/MongoDB-IP%EB%8C%80%EC%97%AD</guid>
            <pubDate>Fri, 01 Dec 2023 08:32:33 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.mongodb.com/docs/atlas/security-vpc-peering/#in-configure-your-network-peering-connection">https://www.mongodb.com/docs/atlas/security-vpc-peering/#in-configure-your-network-peering-connection</a></p>
<p>IP 대역은 10.0.0.0 이 제일 무난한것 같다</p>
<p><img src="blob:https://velog.io/14fe7b0b-e3fd-47ca-8756-9a372b5064fe" alt="업로드중.."></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Idle time 관련 글]]></title>
            <link>https://velog.io/@alli-eunbi/Idle-time-%EA%B4%80%EB%A0%A8-%EA%B8%80</link>
            <guid>https://velog.io/@alli-eunbi/Idle-time-%EA%B4%80%EB%A0%A8-%EA%B8%80</guid>
            <pubDate>Wed, 29 Nov 2023 02:45:30 GMT</pubDate>
            <description><![CDATA[<p><a href="https://reaperes.medium.com/aws-alb-%EC%9D%98-idle-timeout-%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC-7addb8bfb886">https://reaperes.medium.com/aws-alb-%EC%9D%98-idle-timeout-%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC-7addb8bfb886</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ArgoCD AWS Secret Manager 연동]]></title>
            <link>https://velog.io/@alli-eunbi/AWS-Secret-Manager-%EC%97%B0%EB%8F%99</link>
            <guid>https://velog.io/@alli-eunbi/AWS-Secret-Manager-%EC%97%B0%EB%8F%99</guid>
            <pubDate>Sun, 26 Nov 2023 07:14:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://engineering.lightrun.com/argocd-aws-secrets-manager-3d625aa917f7">https://engineering.lightrun.com/argocd-aws-secrets-manager-3d625aa917f7</a></p>
</blockquote>
<h3 id="추가-참조">추가 참조</h3>
<blockquote>
<p><a href="https://awstip.com/how-to-implement-argocd-vault-plugin-with-aws-secrets-manager-80c912f3a9ee">https://awstip.com/how-to-implement-argocd-vault-plugin-with-aws-secrets-manager-80c912f3a9ee</a></p>
</blockquote>
<p> 우리는 보안 설정을 저장하기 위해 Kubernetes Secret을 사용하는데, 어떤 방식으로든 이런 Secret의 변경을 추적하고, ArgoCD가 업데이트를 해야 합니다.</p>
<p> 다행스럽게도 Argo 개발자들은 이에 관심을 갖고 다양하고 멋진 플러그인을 생성할 수 있는 유연한 API를 제공했습니다. 그 중 하나는 <a href="https://argocd-vault-plugin.readthedocs.io/en/stable/">Argo Vault 플러그인</a> 입니다. 플러그인 이름에도 불구하고 <strong>AWS Secrets Manager</strong> 에서도 작동할 수 있습니다 .</p>
<ul>
<li><p>valut란?</p>
<p>  <a href="https://www.hashicorp.com/products/vault">Vault</a>는 <strong>HashiCorp</strong>에 의해서 개발된 크로스플랫폼 패스워드 및 인증 관리 시스템이다. 공개되면 안되는 비밀번호, API 키, 토큰 등을 UI, CLI, HTTP API를 사용하여 저장하고 관리한다.</p>
</li>
</ul>
<h3 id="argocd-vault-plugin의-주요-역할">ArgoCD vault plugin의 주요 역할</h3>
<ol>
<li><strong>Secret 관리</strong>: ArgoCD Vault 플러그인을 사용하면 애플리케이션의 비밀 정보를 Vault에서 중앙 관리할 수 있습니다. 이는 애플리케이션의 구성, 비밀 키, 인증 정보 등을 안전하게 저장하고 관리하는 데 도움이 됩니다.</li>
<li><strong>시크릿 주입</strong>: ArgoCD는 애플리케이션 배포 시 시크릿을 Pod에 주입할 수 있는 기능을 제공합니다. Vault 플러그인을 통해 Vault에서 관리하는 시크릿을 Pod에 주입할 수 있습니다. 이로써 보안을 강화하고 비밀 정보를 노출하지 않고도 애플리케이션을 배포할 수 있습니다.</li>
<li><strong>동적 시크릿 생성</strong>: Vault 플러그인을 사용하면 필요할 때 Vault에서 동적으로 시크릿을 생성할 수 있습니다. 이는 애플리케이션에 필요한 비밀 정보를 자동으로 생성하고 업데이트할 수 있는 유용한 기능입니다.</li>
<li><strong>인증 및 권한 관리</strong>: Vault는 강력한 인증 및 권한 관리 기능을 제공합니다. ArgoCD Vault 플러그인을 통해 Kubernetes와 Vault 간의 인증 및 권한 부여를 관리하고 조정할 수 있습니다.</li>
<li><strong>보안 강화</strong>: Vault를 사용하면 보안을 강화하고 비밀 정보를 중앙 관리하므로 보안 사고의 위험을 줄일 수 있습니다.</li>
</ol>
<h3 id="1-argocd-repo-서버-설정-수정">1. ArgoCD Repo 서버 설정 수정</h3>
<ul>
<li><p>ArgoCD Repo 서버의 역할</p>
<p>  <strong><code>argocd-repo-server</code></strong>는 ArgoCD의 구성 저장소 및 GitOps 워크플로우를 관리하는 서버 컴포넌트입니다. 이 서버는 ArgoCD 애플리케이션을 위한 배포 및 Git 저장소를 관리하고, 애플리케이션 동기화, 배포 및 로깅과 같은 GitOps 기능을 제공합니다.</p>
<p>  <strong><code>argocd-repo-server</code></strong>의 주요 역할 및 기능은 다음과 같습니다:</p>
<ol>
<li><strong>GitOps 저장소 관리</strong>: <strong><code>argocd-repo-server</code></strong>는 ArgoCD 애플리케이션 및 프로젝트 구성 정보를 저장하고 이 정보를 Git 저장소에 유지합니다. Git 저장소는 애플리케이션의 소스 코드, 구성 파일 및 배포 매니페스트를 저장하는 데 사용됩니다.</li>
<li><strong>애플리케이션 동기화</strong>: 이 서버는 GitOps 워크플로우를 지원하여 Git 저장소에서 정의된 애플리케이션 구성을 클러스터로 동기화합니다. 변경 사항이 Git 저장소에 반영되면 <strong><code>argocd-repo-server</code></strong>는 변경 사항을 감지하고 클러스터에 자동으로 배포 및 동기화합니다.</li>
<li><strong>인증 및 권한 관리</strong>: <strong><code>argocd-repo-server</code></strong>는 사용자와 인증된 Git 저장소 간의 인증 및 권한 관리를 담당합니다. 이를 통해 접근 제어 및 보안을 관리할 수 있습니다.</li>
<li><strong>애플리케이션 및 프로젝트 설정 저장</strong>: 애플리케이션 및 프로젝트에 대한 설정 정보를 저장하고 관리합니다. 이 설정 정보는 GitOps 워크플로우의 일부로 사용되며 애플리케이션을 클러스터에 배포할 때 적용됩니다.</li>
<li><strong>백업 및 복원</strong>: <strong><code>argocd-repo-server</code></strong>는 GitOps 저장소의 백업 및 복원을 지원하여 구성 정보와 애플리케이션 상태를 안전하게 보관하고 관리할 수 있습니다.</li>
</ol>
<p>  <strong><code>argocd-repo-server</code></strong>는 ArgoCD의 중요한 구성 요소 중 하나로, GitOps 워크플로우를 효과적으로 관리하고 배포 프로세스를 자동화하는 데 사용됩니다. Git 저장소와 함께 협력하여 애플리케이션을 지속적으로 전달하고 관리하는 데 필수적입니다.</p>
</li>
</ul>
<pre><code class="language-yaml"># argocd-vault-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: argocd-repo-server
spec:
  template:
    spec:
      containers:
      - name: argocd-repo-server
        env:
          - name: AWS_REGION
            value: ap-northeast-2
          - name: AVP_TYPE
            value: awssecretsmanager
          - name: AWS_ACCESS_KEY_ID_VALUE
            value: DKDKDKDKD # 필요에 맞게 수정
          - name: AWS_SECRET_ACCESS_KEY
            value: DDKDKDKDK # 필요에 맞게 수정
        volumeMounts:
        - name: custom-tools
          mountPath: /usr/local/bin/argocd-vault-plugin
          subPath: argocd-vault-plugin
      volumes:
        - name: custom-tools
          emptyDir: {}
      initContainers:
        - name: download-tools
          image: alpine:3.8
          command: [sh, -c]
# Don&#39;t forget to update this to whatever the stable release version is
        # Note the lack of the `v` prefix unlike the git tag
            env:
              - name: AVP_VERSION
                value: &quot;1.9.0&quot;
            args:
              - &gt;-
                wget -O argocd-vault-plugin
                https://github.com/argoproj-labs/argocd-vault-plugin/releases/download/v1.9.0/argocd-vault-plugin_1.9.0_linux_amd64 &amp;&amp;
                chmod +x argocd-vault-plugin &amp;&amp;
                mv argocd-vault-plugin /custom-tools/
            volumeMounts:
              - mountPath: /custom-tools
                name: custom-tools
# Not strictly necessary, but required for passing AVP configuration from a secret and for using Kubernetes auth to Hashicorp Vault
      # automountServiceAccountToken: true</code></pre>
<p>위 yaml 파일 생성 후 아래 명령어로 deployment 수정</p>
<pre><code class="language-jsx">kubectl patch deployment argocd-repo-server -n argocd --patch-file argocd-vault-patch.yaml</code></pre>
<p><strong>patch를 진행했다면, repo-server를 재가동 해주세요.</strong></p>
<pre><code class="language-bash">kubectl delete pod argocd-repo-server-</code></pre>
<h3 id="2-argocd의-config-map-수정">2. ArgoCD의 Config map 수정</h3>
<ul>
<li><p>ArgoCD CM이란?</p>
<p>  ArgoCD에서 <strong><code>argocd-cm</code></strong> (ArgoCD Configuration Management)은 ArgoCD의 구성을 관리하는 ConfigMap을 의미합니다. 이 ConfigMap은 ArgoCD 서버 및 애플리케이션에 대한 중요한 설정 정보를 저장하고 제공하는 역할을 합니다. <strong><code>argocd-cm</code></strong>의 주요 기능과 설정 항목은 다음과 같습니다:</p>
<ol>
<li><strong>서버 설정</strong>: <strong><code>argocd-cm</code></strong>는 ArgoCD 서버의 기본 설정을 포함합니다. 이 설정은 서버의 동작을 제어하고 사용자 인터페이스, 인증, 인가, 애플리케이션 동기화, 리포지토리 연동 등 다양한 측면을 구성합니다.</li>
<li><strong>디폴트 프로젝트 및 애플리케이션 설정</strong>: ConfigMap은 디폴트 프로젝트와 관련된 정보를 포함하며, 이를 통해 ArgoCD에서 애플리케이션을 어떻게 그룹화하고 구성할지를 정의할 수 있습니다.</li>
<li><strong>리포지토리 및 인증 설정</strong>: 리포지토리 연동 및 Git 저장소에 대한 인증 정보가 <strong><code>argocd-cm</code></strong>에 저장됩니다. 이를 통해 ArgoCD는 애플리케이션의 소스 코드 및 구성을 가져오고 동기화할 수 있습니다.</li>
<li><strong>스크립트 및 훅 설정</strong>: ArgoCD에서 사용할 수 있는 스크립트와 훅 (예: PreSync, PostSync)의 설정이 포함됩니다. 이를 사용하여 애플리케이션 동기화 과정 중에 사용자 정의 작업을 수행할 수 있습니다.</li>
<li><strong>애플리케이션 암호화</strong>: <strong><code>argocd-cm</code></strong>는 애플리케이션에서 사용하는 비밀 정보를 암호화하기 위한 설정을 포함할 수 있습니다. 이를 통해 보안을 강화하고 중요한 데이터를 안전하게 저장할 수 있습니다.</li>
<li><strong>그 외 설정</strong>: 다양한 기타 설정 항목도 <strong><code>argocd-cm</code></strong>에 저장될 수 있으며, 이를 통해 ArgoCD의 동작을 세부적으로 구성할 수 있습니다.</li>
</ol>
</li>
</ul>
<p>Argocd Configmap에 vault-plugin을 연동하도록 수정</p>
<pre><code class="language-jsx"># argocd-cmp-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
data:
  configManagementPlugins: |
    - name: argocd-vault-plugin
      generate:
        command: [&quot;argocd-vault-plugin&quot;]
        args: [&quot;generate&quot;, &quot;./&quot;]
    - name: argocd-vault-plugin-helm
      generate:
        command: [&quot;sh&quot;, &quot;-c&quot;]
        args: [&quot;helm template . | argocd-vault-plugin generate -&quot;]
    - name: argocd-vault-plugin-helm-with-args
      generate:
        command: [&quot;sh&quot;, &quot;-c&quot;]
        args: [&quot;helm template ${helm_args} . | argocd-vault-plugin generate -&quot;]
    - name: argocd-vault-plugin-kustomize
      generate:
        command: [&quot;sh&quot;, &quot;-c&quot;]
        args: [&quot;kustomize build . | argocd-vault-plugin generate -&quot;]</code></pre>
<p>위 yaml 파일 생성 후 아래 명령어로 configmap 수정</p>
<pre><code class="language-jsx">kubectl patch configmap argocd-cm -n argocd --patch-file argocd-cmp-config.yaml</code></pre>
<h3 id="app에-plugin을-설정하지-않아도-자동으로-vault-plugin이-붙음">app에 plugin을 설정하지 않아도 자동으로 vault plugin이 붙음</h3>
<p>기존에는 spec.source.plugin.name에 argocd-vault-plugin을 넣어야 plugin이 작동하였는데, 이 plugin 설정을 진행하면 spec.source.helm을 사용할 수가 없었습니다.</p>
<p>하지만 최근에는 plugin을 굳이 사용하지 않아도 자동으로 argocd-vault-plugin이 붙습니다. 따라서 아래의 설정은 안해도 됩니다!</p>
<p>(관련 문서는 나중에 다시.. 기록해두겠습니다)</p>
<pre><code class="language-jsx"># 안해도 되지만 기록으로 남겨둡니다.

# spec:
    # source:
        # plugin:
        # name: argocd-vault-plugin</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS EKS] Karpenter (클러스터 노드 오토스케일링)]]></title>
            <link>https://velog.io/@alli-eunbi/AWS-EKS-Karpenter-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EB%85%B8%EB%93%9C-%EC%98%A4%ED%86%A0%EC%8A%A4%EC%BC%80%EC%9D%BC%EB%A7%81</link>
            <guid>https://velog.io/@alli-eunbi/AWS-EKS-Karpenter-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EB%85%B8%EB%93%9C-%EC%98%A4%ED%86%A0%EC%8A%A4%EC%BC%80%EC%9D%BC%EB%A7%81</guid>
            <pubDate>Sun, 26 Nov 2023 07:09:16 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/alli-eunbi/post/c09857c9-13a8-42d4-950d-f991f0b20922/image.png" alt=""></p>
<p><strong>현재 요구사항 : Scale In&amp;Out이 빠르면서 비용을 절감할 수 있는 autoscaling 방침이 필요</strong></p>
<h1 id="karpenter-시작하기">Karpenter 시작하기</h1>
<blockquote>
<p>사전 준비</p>
<ul>
<li>기존 EKS 클러스터를 사용합니다.</li>
<li>기존 VPC와 서브넷을 사용합니다.</li>
<li>기존 보안 그룹을 사용합니다.</li>
<li>노드가 하나 이상의 노드 그룹에 속해 있습니다.</li>
<li><a href="https://aws.github.io/aws-eks-best-practices/karpenter/">워크로드에 EKS 모범 사례를</a> 준수하는 포드 중단 예산이 있습니다.</li>
<li>클러스터에 서비스 계정에 대한 <a href="https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html">OIDC 제공업체 가 있습니다.</a></li>
</ul>
</blockquote>
<ol>
<li><p>클러스터 이름에 대한 변수 지정</p>
<pre><code class="language-bash"> CLUSTER_NAME=&lt;your cluster name&gt;
</code></pre>
</li>
<li><p>클러스터 구성에서 다른 변수 설정 (아래 명령어 그냥 복붙하면 알아서 설정됨)</p>
<pre><code class="language-bash"> AWS_PARTITION=&quot;aws&quot;
 AWS_REGION=&quot;$(aws configure list | grep region | tr -s &quot; &quot; | cut -d&quot; &quot; -f3)&quot;
 OIDC_ENDPOINT=&quot;$(aws eks describe-cluster --name ${CLUSTER_NAME} \
     --query &quot;cluster.identity.oidc.issuer&quot; --output text)&quot;
 AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query &#39;Account&#39; \
     --output text)</code></pre>
</li>
<li><p>변수 설정 잘됐는지 아래 명령어로 확인</p>
<pre><code class="language-bash"> echo $CLUSTER_NAME $AWS_PARTITION $AWS_REGION</code></pre>
</li>
</ol>
<h3 id="iam-역할-생성">IAM 역할 생성</h3>
<p>Karpenter 및 Karpenter 컨트롤러로 프로비저닝된 노드에 대해 두 개의 새로운 IAM 역할을 생성해야 합니다.</p>
<h3 id="노드용-iam-role-생성">노드용 IAM Role 생성</h3>
<ul>
<li><p>Karpenter 노드 역할을 생성하기 위해 다음 정책과 명령을 사용합니다.</p>
<pre><code class="language-bash">  echo &#39;{
      &quot;Version&quot;: &quot;2012-10-17&quot;,
      &quot;Statement&quot;: [
          {
              &quot;Effect&quot;: &quot;Allow&quot;,
              &quot;Principal&quot;: {
                  &quot;Service&quot;: &quot;ec2.amazonaws.com&quot;
              },
              &quot;Action&quot;: &quot;sts:AssumeRole&quot;
          }
      ]
  }&#39; &gt; node-trust-policy.json</code></pre>
</li>
<li><p>IAM Role의 신뢰 관계는 위에서 만든 <code>node-trust-policy.json</code>의 내용을 그대로 적용합니다.</p>
<pre><code class="language-bash">  aws iam create-role --role-name &quot;KarpenterNodeRole-${CLUSTER_NAME}&quot; \
      --assume-role-policy-document file://node-trust-policy.json</code></pre>
</li>
<li><p>이제 필요한 정책을 역할에 연결합니다.</p>
<ul>
<li><p>KarpenterNodeRole에 연결한 4개의 IAM Policy는 모두 <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#aws-managed-policies">AWS 관리형 정책</a>입니다.</p>
<ul>
<li>AmazonEKSWorkerNodePolicy</li>
<li>AmazonEKS_CNI_Policy</li>
<li>AmazonEC2ContainerRegistryReadOnly</li>
<li>AmazonSSMManagedInstanceCore</li>
</ul>
<pre><code class="language-bash">aws iam attach-role-policy --role-name &quot;KarpenterNodeRole-${CLUSTER_NAME}&quot; \
  --policy-arn arn:${AWS_PARTITION}:iam::aws:policy/AmazonEKSWorkerNodePolicy

aws iam attach-role-policy --role-name &quot;KarpenterNodeRole-${CLUSTER_NAME}&quot; \
  --policy-arn arn:${AWS_PARTITION}:iam::aws:policy/AmazonEKS_CNI_Policy

aws iam attach-role-policy --role-name &quot;KarpenterNodeRole-${CLUSTER_NAME}&quot; \
  --policy-arn arn:${AWS_PARTITION}:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly

aws iam attach-role-policy --role-name &quot;KarpenterNodeRole-${CLUSTER_NAME}&quot; \
  --policy-arn arn:${AWS_PARTITION}:iam::aws:policy/AmazonSSMManagedInstanceCore</code></pre>
</li>
</ul>
</li>
<li><p>IAM 역할을 EC2 인스턴스 프로필에 연결합니다.</p>
<pre><code class="language-bash">  aws iam create-instance-profile \
      --instance-profile-name &quot;KarpenterNodeInstanceProfile-${CLUSTER_NAME}&quot;

  aws iam add-role-to-instance-profile \
      --instance-profile-name &quot;KarpenterNodeInstanceProfile-${CLUSTER_NAME}&quot; \
      --role-name &quot;KarpenterNodeRole-${CLUSTER_NAME}&quot;</code></pre>
</li>
</ul>
<h3 id="컨트롤러용-iam-role-생성">컨트롤러용 IAM Role 생성</h3>
<ul>
<li><p>이제 Karpenter 컨트롤러가 새 인스턴스를 프로비저닝하는 데 사용할 IAM 역할을 생성해야 합니다. Karpenter 컨트롤러는 IRSAIAM Role for Service Accout 방식으로 IAM 권한을 얻어 EC2 생성, 삭제를 수행합니다.</p>
<ol>
<li><p>IAM Role의 신뢰관계를 생성합니다.</p>
<pre><code class="language-bash"> cat &lt;&lt; EOF &gt; controller-trust-policy.json
 {
     &quot;Version&quot;: &quot;2012-10-17&quot;,
     &quot;Statement&quot;: [
         {
             &quot;Effect&quot;: &quot;Allow&quot;,
             &quot;Principal&quot;: {
                 &quot;Federated&quot;: &quot;arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:oidc-provider/${OIDC_ENDPOINT#*//}&quot;
             },
             &quot;Action&quot;: &quot;sts:AssumeRoleWithWebIdentity&quot;,
             &quot;Condition&quot;: {
                 &quot;StringEquals&quot;: {
                     &quot;${OIDC_ENDPOINT#*//}:aud&quot;: &quot;sts.amazonaws.com&quot;,
                     &quot;${OIDC_ENDPOINT#*//}:sub&quot;: &quot;system:serviceaccount:karpenter:karpenter&quot;
                 }
             }
         }
     ]
 }
 EOF</code></pre>
</li>
<li><p>Karpenter 컨트롤러에서 사용할 IAM Role을 생성합니다.</p>
<pre><code class="language-bash"> aws iam create-role --role-name KarpenterControllerRole-${CLUSTER_NAME} \
     --assume-role-policy-document file://controller-trust-policy.json</code></pre>
</li>
</ol>
</li>
</ul>
<ol>
<li><p>Karpenter 컨트롤러용 IAM Policy를 생성합니다.</p>
<pre><code class="language-bash">
 cat &lt;&lt; EOF &gt; controller-policy.json
 {
     &quot;Statement&quot;: [
         {
             &quot;Action&quot;: [
                 &quot;ssm:GetParameter&quot;,
                 &quot;ec2:DescribeImages&quot;,
                 &quot;ec2:RunInstances&quot;,
                 &quot;ec2:DescribeSubnets&quot;,
                 &quot;ec2:DescribeSecurityGroups&quot;,
                 &quot;ec2:DescribeLaunchTemplates&quot;,
                 &quot;ec2:DescribeInstances&quot;,
                 &quot;ec2:DescribeInstanceTypes&quot;,
                 &quot;ec2:DescribeInstanceTypeOfferings&quot;,
                 &quot;ec2:DescribeAvailabilityZones&quot;,
                 &quot;ec2:DeleteLaunchTemplate&quot;,
                 &quot;ec2:CreateTags&quot;,
                 &quot;ec2:CreateLaunchTemplate&quot;,
                 &quot;ec2:CreateFleet&quot;,
                 &quot;ec2:DescribeSpotPriceHistory&quot;,
                 &quot;pricing:GetProducts&quot;
             ],
             &quot;Effect&quot;: &quot;Allow&quot;,
             &quot;Resource&quot;: &quot;*&quot;,
             &quot;Sid&quot;: &quot;Karpenter&quot;
         },
         {
             &quot;Action&quot;: &quot;ec2:TerminateInstances&quot;,
             &quot;Condition&quot;: {
                 &quot;StringLike&quot;: {
                     &quot;ec2:ResourceTag/karpenter.sh/provisioner-name&quot;: &quot;*&quot;
                 }
             },
             &quot;Effect&quot;: &quot;Allow&quot;,
             &quot;Resource&quot;: &quot;*&quot;,
             &quot;Sid&quot;: &quot;ConditionalEC2Termination&quot;
         },
         {
             &quot;Effect&quot;: &quot;Allow&quot;,
             &quot;Action&quot;: &quot;iam:PassRole&quot;,
             &quot;Resource&quot;: &quot;arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}&quot;,
             &quot;Sid&quot;: &quot;PassNodeIAMRole&quot;
         },
         {
             &quot;Effect&quot;: &quot;Allow&quot;,
             &quot;Action&quot;: &quot;eks:DescribeCluster&quot;,
             &quot;Resource&quot;: &quot;arn:${AWS_PARTITION}:eks:${AWS_REGION}:${AWS_ACCOUNT_ID}:cluster/${CLUSTER_NAME}&quot;,
             &quot;Sid&quot;: &quot;EKSClusterEndpointLookup&quot;
         }
     ],
     &quot;Version&quot;: &quot;2012-10-17&quot;
 }
 EOF</code></pre>
<ol>
<li><p><a href="https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#inline-policies">인라인 정책</a>Inline Policy를 Karpenter Controller용 IAM Role에 연결합니다.</p>
<pre><code class="language-bash">
aws iam put-role-policy --role-name KarpenterControllerRole-${CLUSTER_NAME} \
 --policy-name KarpenterControllerPolicy-${CLUSTER_NAME} \
 --policy-document file://controller-policy.json
</code></pre>
</li>
</ol>
</li>
</ol>
<h3 id="서브넷-및-보안-그룹에-태그-추가"><strong>서브넷 및 보안 그룹에 태그 추가</strong></h3>
<p><img src="https://velog.velcdn.com/images/alli-eunbi/post/bb63928a-a3dc-4d48-87fd-2df8888ed3dd/image.png" alt=""></p>
<ul>
<li>Karpenter가 사용할 서브넷을 알 수 있도록 노드 그룹 서브넷에 태그를 추가해야 합니다.</li>
</ul>
<ol>
<li><p>for 반복문 형태로 여러 서브넷에 한 번에 태그가 추가됩니다.</p>
<pre><code class="language-bash"> for NODEGROUP in $(aws eks list-nodegroups --cluster-name ${CLUSTER_NAME} \
     --query &#39;nodegroups&#39; --output text); do aws ec2 create-tags \
     --tags &quot;Key=karpenter.sh/discovery,Value=${CLUSTER_NAME}&quot; \
     --resources $(aws eks describe-nodegroup --cluster-name ${CLUSTER_NAME} \
     --nodegroup-name $NODEGROUP --query &#39;nodegroup.subnets&#39; --output text )
 done</code></pre>
<p> 추가되는 태그 정보는 다음과 같습니다.</p>
<ul>
<li><strong>Key</strong> : <code>karpenter.sh/discovery</code></li>
<li><strong>Value</strong> : 현재 사용중인 자신의 EKS 클러스터 이름을 찾아 자동 입력됨</li>
</ul>
</li>
<li><p>보안 그룹에도 태그를 추가해야합니다. </p>
<p> 이 명령은 클러스터의 첫 번째 노드 그룹에 대한 보안 그룹에만 태그를 지정합니다. </p>
<p> 만약, 여러 보안 그룹을 사용하고 있는 경우 Karpenter가 노드별로 사용해야 할 보안 그룹을 결정해야 합니다.</p>
<pre><code class="language-bash"> NODEGROUP=$(aws eks list-nodegroups --cluster-name ${CLUSTER_NAME} \
     --query &#39;nodegroups[0]&#39; --output text)LAUNCH_TEMPLATE=$(aws eks describe-nodegroup --cluster-name ${CLUSTER_NAME} \
     --nodegroup-name ${NODEGROUP} --query &#39;nodegroup.launchTemplate.{id:id,version:version}&#39; \
     --output text | tr -s &quot;\t&quot; &quot;,&quot;)

 # If your EKS setup is configured to use only Cluster security group, then please execute -
 # 클러스터에서 하나의 보안그룹만 사용한다면 아래 명령어 사용
 SECURITY_GROUPS=$(aws eks describe-cluster \
     --name ${CLUSTER_NAME} --query &quot;cluster.resourcesVpcConfig.clusterSecurityGroupId&quot; --output text)

 # If your setup uses the security groups in the Launch template of a managed node group, then :
 # 노드그룹의 시작 템플릿에 있는 보안그룹을 사용한다면 아래 명령어 사용
 SECURITY_GROUPS=$(aws ec2 describe-launch-template-versions \
     --launch-template-id ${LAUNCH_TEMPLATE%,*} --versions ${LAUNCH_TEMPLATE#*,} \
     --query &#39;LaunchTemplateVersions[0].LaunchTemplateData.[NetworkInterfaces[0].Groups||SecurityGroupIds]&#39; \
     --output text)

 aws ec2 create-tags \
     --tags &quot;Key=karpenter.sh/discovery,Value=${CLUSTER_NAME}&quot; \
     --resources ${SECURITY_GROUPS}
</code></pre>
</li>
</ol>
<h3 id="aws-auth-configmap-업데이트"><strong>aws-auth ConfigMap 업데이트</strong></h3>
<ul>
<li><p>방금 생성한 노드 IAM 역할을 사용하는 노드가 클러스터에 조인하도록 허용해야 합니다. <code>aws-auth</code>이를 위해서는 클러스터에서 ConfigMap을 수정해야 합니다 .</p>
<pre><code class="language-bash">  kubectl edit configmap aws-auth -n kube-system</code></pre>
</li>
<li><p>변경전 aws-auth configmap의 내용은 아래와 같습니다.</p>
<pre><code class="language-yaml">  apiVersion: v1
  data:
    mapRoles: |
      - groups:
        - system:bootstrappers
        - system:nodes
        rolearn: arn:aws:iam::111122223333:role/dev-global-eks-node-iam-role
        username: system:node:{{EC2PrivateDNSName}}    
  kind: ConfigMap
  metadata:
    ...</code></pre>
<p>  mapRoles를 보면 기존 ASG로 관리되는 노드그룹이 등록되어 있습니다.</p>
</li>
<li><p>mapRoles에 Karpenter의 노드용 Role을 추가해줘야 합니다.</p>
<pre><code class="language-yaml">  apiVersion: v1
  data:
    mapRoles: |
      - groups:
        - system:bootstrappers
        - system:nodes
        rolearn: arn:aws:iam::111122223333:role/dev-global-eks-node-iam-role
        username: system:node:{{EC2PrivateDNSName}}
  +   - groups:
  +     - system:bootstrappers
  +     - system:nodes
  +     rolearn: arn:aws:iam::111122223333:role/KarpenterNodeRole-YOUR_CLUSTER_NAME_HERE
  +     username: system:node:{{EC2PrivateDNSName}}
  kind: ConfigMap
  metadata:
    ...</code></pre>
<p>  위와 같이 추가되면 전체 aws-auth configmap에는 두개의 그룹이 있게 됩니다. 하나는 Karpenter 노드 역할용이고 다른 하나는 기존 노드 그룹용입니다.</p>
</li>
</ul>
<h1 id="karpenter-배포">Karpenter 배포</h1>
<p>카펜터의 버전을 지정해줍니다.</p>
<ul>
<li>2023.10 기준 v0.31.0 이 제일 최신 (v0.31.1 로 갱신)</li>
</ul>
<pre><code class="language-bash">export KARPENTER_VERSION=v0.31.0</code></pre>
<p>helm 차트 배포 명령어를 수행해줍니다.</p>
<pre><code class="language-bash">helm template karpenter oci://public.ecr.aws/karpenter/karpenter --version ${KARPENTER_VERSION} --namespace karpenter \
    --set settings.aws.defaultInstanceProfile=KarpenterNodeInstanceProfile-${CLUSTER_NAME} \
    --set settings.aws.clusterName=${CLUSTER_NAME} \
    --set serviceAccount.annotations.&quot;eks\.amazonaws\.com/role-arn&quot;=&quot;arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:role/KarpenterControllerRole-${CLUSTER_NAME}&quot; \
    --set controller.resources.requests.cpu=1 \
    --set controller.resources.requests.memory=1Gi \
    --set controller.resources.limits.cpu=1 \
    --set controller.resources.limits.memory=1Gi &gt; karpenter.yaml</code></pre>
<h3 id="karpenter-controller-pod의-배치-설정">Karpenter Controller Pod의 배치 설정</h3>
<ul>
<li><p>Karpenter Controller 파드들은 <strong>Karpenter가 스스로 생성한 워커노드</strong>에 배치되면 안됩니다. 운 나쁘게 자기 자신이 위치한 EC2 노드를 스스로 Terminate 하게 될 경우, 클러스터 전체의 노드 프로비저닝이 멈출 수 있기 때문입니다. 이러한 이유로 Karpenter Controller Pod는 기존 Auto Scaling Group 기반에서 운영되는 노드그룹에 배치되어야 합니다.</p>
</li>
<li><p>위와 같은 이유로 클러스터에 Karpenter를 설치해서 사용하더라도 <strong>최소 1개의 노드그룹</strong>은 반드시 필요합니다. 저도 처음엔 “Karpenter를 쓰면 노드그룹(ASG)은 하나도 필요 없겠네?“라고 생각했지만 현재로서는 불가능합니다. 처음 Karpenter를 쓸 때 쉽게 혼동할 수 있는 부분입니다.</p>
</li>
<li><p><strong>적절한 파드 분배</strong> (선택사항): <a href="https://karpenter.sh/v0.30/getting-started/migrating-from-cas/#set-nodeaffinity-for-critical-workloads-optional">Karpenter 공식문서</a>에서는 클러스터 운영 및 유지에 필요한 핵심 파드들은 <code>nodeAffinity</code>를 사용해 기존 ASG로 운영되는 노드그룹에 배치하는 걸 권장하고 있습니다. 파드 배치의 예시는 다음과 같습니다.</p>
<ul>
<li><p><strong>기존 노드그룹</strong>에 배치</p>
<p>  ASG</p>
<ul>
<li>karpenter</li>
<li>coredns</li>
<li>metrics-server</li>
<li>prometheus</li>
<li>grafana</li>
</ul>
</li>
<li><p><strong>Karpenter 노드</strong>에 배치</p>
<ul>
<li>Backend Application Pod</li>
<li>Frontend Application Pod</li>
<li>Data Application Pod</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>위와 같은 배포 원칙으로 인해 karpenter.yaml 파일을 편집하여 karpenter node affinity를 변경할 겁니다. 변경되면 karpenter는 기존 노드 그룹 노드 중 하나에서 실행될겁니다.</p>
<p>규칙은 다음과 같아야 합니다. <code>$NODEGROUP</code> 은 기존에 클러스터에서 사용하던 노드그룹 중 하나의 이름을 넣으면 됩니다.</p>
<pre><code class="language-yaml">affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: karpenter.sh/provisioner-name
                operator: DoesNotExist
+           - matchExpressions:
+             - key: eks.amazonaws.com/nodegroup
+               operator: In
+               values:
+               - YOUR_NODE_GROUP_NAME  # 노드그룹 이름은 현재 사용중인 노드 그룹으로 수정하기</code></pre>
<p>이제 Karpenter 배포가 준비되었습니다.</p>
<ol>
<li><p>Karpenter 네임스페이스를 생성합니다.</p>
<pre><code class="language-bash"> kubectl create namespace karpenter</code></pre>
</li>
</ol>
<p>Karpenter가 새 노드를 프로비저닝할 때 사용하는 CRD(Custom Resource Definition)인 <code>provisioners</code>와 <code>awsnodetemplates</code>을 생성합니다.</p>
<pre><code class="language-bash">kubectl create -f \
    https://raw.githubusercontent.com/aws/karpenter/${KARPENTER_VERSION}/pkg/apis/crds/karpenter.sh_provisioners.yaml
kubectl create -f \
    https://raw.githubusercontent.com/aws/karpenter/${KARPENTER_VERSION}/pkg/apis/crds/karpenter.k8s.aws_awsnodetemplates.yaml
kubectl create -f \
    https://raw.githubusercontent.com/aws/karpenter/${KARPENTER_VERSION}/pkg/apis/crds/karpenter.sh_machines.yaml
kubectl apply -f karpenter.yaml</code></pre>
<p>생성 후에는 <code>kubectl api-resources</code> 명령어로 CRD 목록을 확인합니다.</p>
<pre><code class="language-bash">kubectl api-resources \
    --categories karpenter \
    -o wide</code></pre>
<p><code>awsnodetemplates</code>과 <code>provisioners</code> 리소스가 새로 추가된 걸 확인할 수 있습니다.</p>
<pre><code class="language-bash">NAME               SHORTNAMES   APIVERSION                   NAMESPACED   KIND              VERBS                                                        CATEGORIES
awsnodetemplates                karpenter.k8s.aws/v1alpha1   false        AWSNodeTemplate   delete,deletecollection,get,list,patch,create,update,watch   karpenter
provisioners                    karpenter.sh/v1alpha5        false        Provisioner       delete,deletecollection,get,list,patch,create,update,watch   karpenter</code></pre>
<h1 id="karpenter-node-설정">Karpenter Node 설정</h1>
<p>Worker Node를 생성하려면 Provisioner와 AWS Node template, 두개가 필요합니다.</p>
<h2 id="1-provisioner">1. Provisioner</h2>
<pre><code class="language-yaml">apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: &lt;&lt;&lt;provisioner name&gt;&gt;&gt;
spec:
    providerRef:
      name: &lt;&lt;node template name&gt;&gt;
# taint 설정 및 labels 설정을 추가할 수 있다.
#    taints:
#    - key: purpose
#      value: monitoring
#      effect: NoExecute
#  labels:
#    purpose: monitoring
  requirements:
      # instance type을 정의 여러개를 한번에 정의할 수 있습니다.
    - key: node.kubernetes.io/instance-type
      operator: In
      values: [ c6i.8xlarge, c6i.4xlarge ]
      # WorkerNode를 생성할 Zone
    - key: topology.kubernetes.io/zone
      operator: In
      values: [ ap-northeast-2a, ap-northeast-2c ]
      # on-demand, spot 중 원하는 인스턴스를 선언, 둘다 정의할 수 있습니다.
    - key: karpenter.sh/capacity-type
      operator: In
      values: [ on-demand ]
    - key: kubernetes.io/os
      operator: In
      values:
        - linux
    - key: kubernetes.io/arch
      operator: In
      values:
        - amd64 # or arm64 
# pod를 적극적으로 이동하고 노드를 삭제하거나 더 저렴한 인스턴스 타입으로 교체하는 노드 통합 기능
  consolidation:
    enabled: true

# launch Template 사용시(권장하지 않음)
 # providerRef:
 #   name: &lt;&lt;&lt;AWSNodeTemplasteName&gt;&gt;&gt;

# consolidation.enabled 가 false인 경우 사용
#  ttlSecondsAfterEmpty: 30
  ttlSecondsUntilExpired: 2592000</code></pre>
<ul>
<li><p>예시</p>
<pre><code class="language-bash">  apiVersion: karpenter.sh/v1alpha5
  kind: Provisioner
  metadata:
    name: prometheus-provisioner
  spec:
    labels:
      purpose: prometheus
      nodegroup: prometheus

    requirements:
        # instance type을 정의 여러개를 한번에 정의할 수 있습니다.
      - key: node.kubernetes.io/instance-type
        operator: In
        values: [ t4g.medium ]
        # WorkerNode를 생성할 Zone
      - key: topology.kubernetes.io/zone
        operator: In
        values: [ ap-northeast-2a, ap-northeast-2c ]
        # on-demand, spot 중 원하는 인스턴스를 선언, 둘다 정의할 수 있습니다.
      - key: karpenter.sh/capacity-type
        operator: In
        values: [ on-demand ]
      - key: kubernetes.io/os
        operator: In
        values:
          - linux
      - key: kubernetes.io/arch
        operator: In
        values:
          - arm64
    consolidation:
      enabled: true
    providerRef:
      name: prometheus-node-template
    ttlSecondsUntilExpired: 2592000</code></pre>
</li>
</ul>
<p>설정 방식)</p>
<p>ttlSecondsAfterEmpty는 현재 Karpenter에 의해 생성된 WorkerNode에 DaemonSet 이외에 모든 POD들이 삭제되어 있는 상태라면 해당 WorkerNode를 해당 시간만큼 기다렸다가 삭제하라는 의미 입니다. 즉 불필요해진 WorkerNode로 판단되었기 때문에 해당 시간만큼 기다렸다가 삭제하는 것입니다.</p>
<p>ttlSecondsUntilExpired는 Karpenter에 의해 WorkerNode가 생성되었을 때 해당 WorkerNode의 수명을 의미합니다. 즉, 위에서 설정한 시간만큼 WorkerNode가 살아 있었다면 신규 WorkerNode로 교체하라는 것을 의미합니다. 시간은 “초”로 설정할 수 있으며 위의 값이 의미하는 것은 다음과 같습니다. <em>60sec * 60min * 24hour * 30days = 2592000</em></p>
<p>마지막으로 <strong>consolidation</strong>인데 이 옵션이 아주 중요합니다. consolidation은 WorkerNode의 상태를 감시하며 Node에 현재 배포되어 있는 POD들의 spec을 확인하여 해당 POD를 이동했을 때 WorkerNode를 삭제할 수 있다면 POD를 이동시킵니다. 결국 자원효율성을 최대한으로 사용하기 위한 옵션입니다. <strong>주의 사항으로는 원하지 않는 POD의 이동이 자주 발생할 수 있으며 위에서 설명한 ttlSecondsAfterEmpty 옵션과 동시에 사용이 불가능하다는 점</strong>이 있습니다. 2개의 옵션 중 1개만 사용해야 합니다. 제가 위에서 사용한 Sample에도 consolidation을 false로 설정해 놓은 것을 보실 수 있을 겁니다.</p>
<h2 id="2-node-template">2. Node Template</h2>
<pre><code class="language-yaml">apiVersion: karpenter.k8s.aws/v1alpha1
kind: AWSNodeTemplate
metadata:
  name: &lt;&lt;&lt;AWSNodeTemplateName&gt;&gt;&gt;
spec:
  # subnet, securityGroup tag
  subnetSelector:
    karpenter.sh/discovery: &lt;&lt;&lt;subnet_tag_value&gt;&gt;&gt;
  securityGroupSelector:
    karpenter.sh/discovery: &lt;&lt;&lt;securityGroup_tag_value&gt;&gt;&gt;
  # WorkerNode Role Name, 미설정시 karpenter에서 생성한 instance role로 자동 할당
  # eksctl로 자동생성한 IAM은 설정시 에러 발생할 가능성이 있음, 추가 생성한 IAM 으로는 가능
  instanceProfile: &lt;&lt;&lt;Instance_Role_Name&gt;&gt;&gt;
  # WorkerNode AMI
  # amiFamily 사용 시 자동으로 최신버전의 AmazonLinux2 WorkerNode image 사용
  # AL2, Bottlerocket, Ubuntu, Windows2019, Windows2022 등을 사용할 수 있습니다.
  amiFamily: AL2
  # 주석 처리 되어 있지만 amiSelector를 사용하여 특정 AMI 를 지정해줄 수도 있습니다.
#  amiSelector:
#    aws-ids: &lt;&lt;&lt;AMI ID&gt;&gt;&gt;

# 노드그룹 사용시, instance에 연결된 볼륨을 확인 후 값을 맞춰줘야 합니다.
# 아래 blockDeviceMapping 찾는법 토글 참조
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 10G
        volumeType: gp3
        iops: 3000
        throughput: 125
        deleteOnTermination: true
  # WorkerNode tag
  tags:
    Name : &lt;&lt;&lt;instance_name_tag&gt;&gt;&gt;

# 아래는 필요시 사용
  # WorkerNode userdata
  # userData: &lt;&lt;&lt;instance_userdata&gt;&gt;&gt;</code></pre>
<ul>
<li><p>예시</p>
<pre><code class="language-bash">  apiVersion: karpenter.k8s.aws/v1alpha1
  kind: AWSNodeTemplate
  metadata:
    name: prometheus-node-template
  spec:
    # subnet, securityGroup tag
    subnetSelector:
      Name: &quot;subnet-private-a,subnet-private-c&quot;
    securityGroupSelector:
      karpenter.sh/discovery: &quot;devops&quot;
    amiFamily: AL2
    blockDeviceMappings:
      - deviceName: /dev/xvda
        ebs:
          volumeSize: 50G
          volumeType: gp3
          iops: 3000
          throughput: 125
          deleteOnTermination: true
    tags:
      Name: prometheus
      nodegroup-role: worker
      Team: devops</code></pre>
</li>
</ul>
<h1 id="karpenter의-statefulset-배포-처리">Karpenter의 Statefulset 배포 처리</h1>
<pre><code class="language-yaml">// 앱의 yaml 파일이나 helm에서 annotation을 수정하면 됨.
spec:
  template:
    metadata:
      labels:
           annotations:
           # 아래가 추가되어야 함
           karpenter.sh/do-not-evict: &quot;true&quot;</code></pre>
<h1 id="주의할-점">주의할 점.</h1>
<h3 id="1-pdb-설정혹시라도-운영에서-consolidation-혹은-spot-인스턴스-사용하는-경우-참조">1. PDB 설정(혹시라도 운영에서 consolidation 혹은 spot 인스턴스 사용하는 경우 참조)</h3>
<p><img src="https://velog.velcdn.com/images/alli-eunbi/post/eab4c0b4-ed9f-4dc6-a44d-b73ddb059ca8/image.png" alt=""></p>
<p>Pod Disruption Budget(PDB)은 애플리케이션의 복제본 수가 선언된 임계값 미만으로 떨어지면 퇴거 프로세스를 일시적으로 중단할 수 있습니다. 사용 가능한 복제본 수가 임계값을 초과하면 제거 프로세스가 계속됩니다. <code>minAvailable</code>, <code>maxUnavailable</code> 를 사용하여 PDB의 복제본 수 를 선언할 수 있습니다. 예를 들어 앱 복사본을 3개 이상 사용하려면 PDB를 만들면 됩니다.</p>
<pre><code class="language-yaml">apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: my-svc-pdb
spec:
  minAvailable: 3
  selector:
    matchLabels:
      app: my-svc</code></pre>
<p>위의 PDB 정책은 3개 이상의 복제본을 사용할 수 있을 때까지 제거 프로세스를 중단하도록 Kubernetes에 지시합니다. <code>PodDisruptionBudgets</code> 은 노드의 draining을 존중합니다. EKS 관리형 노드 그룹 업그레이드 중에 <a href="https://docs.aws.amazon.com/eks/latest/userguide/managed-node-update-behavior.html">노드는 15분의 시간 초과로 드레이닝됩니다</a> . 15분 후 업데이트가 강제 실행되지 않으면(EKS 콘솔에서는 롤링 업데이트 옵션이라고 함) 업데이트가 실패합니다. 업데이트가 강제 실행되면 pod 삭제됩니다.</p>
<p><a href="https://github.com/aws/aws-node-termination-handler">자체 관리형 노드의 경우 AWS 노드 종료 핸들러</a> 와 같은 도구를 사용할 수도 있습니다 . 이 도구는 <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-instances-status-check_sched.html">EC2 유지 관리</a> 이벤트 및 <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-interruptions.html">EC2 스팟 중단</a> 과 같이 EC2 인스턴스를 사용할 수 없게 만드는 이벤트에 Kubernetes 제어 플레인이 적절하게 응답하도록 보장합니다 . Kubernetes API를 사용하여 노드를 차단하여 새 포드가 예약되지 않았는지 확인한 다음 노드를 비워 실행 중인 모든 포드를 종료합니다.</p>
<p>pod anti-affinity를 사용하여 여러 노드에 배포 포드를 예약하고 노드 업그레이드 중에 PDB 관련 지연을 방지할 수 있습니다. </p>
<h3 id="2-anti-affinity-활용">2. anti-affinity 활용</h3>
<ul>
<li>노드 당 포드 하나 예약.</li>
</ul>
<h3 id="3-readiness-probe--liveness-probe-활용">3. Readiness probe / liveness probe 활용</h3>
<ul>
<li><p>Readiness probe 비가용성</p>
<p>  Liveness 프로브는 Pod를 종료하여(따라서 앱을 다시 시작하여) 해결되는 앱의 오류를 감지하는 반면, Readiness Probe는 앱을 <em>일시적으로</em> 사용할 수 없는 조건을 감지합니다. 이러한 상황에서는 앱이 일시적으로 응답하지 않을 수 있습니다. 그러나 이 작업이 완료되면 다시 정상 상태가 될 것으로 예상됩니다.</p>
<p>  예를 들어, 집중적인 디스크 I/O 작업 중에는 요청을 처리하기 위해 애플리케이션을 일시적으로 사용하지 못할 수 있습니다. 여기서 애플리케이션의 Pod를 종료하는 것은 해결 방법이 아닙니다. 동시에 Pod로 전송된 추가 요청이 실패할 수 있습니다.</p>
<p>  준비 프로브를 사용하여 앱의 일시적인 사용 불가를 감지하고 앱이 다시 작동할 때까지 Pod에 대한 요청 전송을 중지할 수 있습니다. <em>실패로 인해 Pod가 재생성되는 Liveness Probe와 달리, Readiness Probe가 실패하면 Pod가 Kubernetes Service로부터 트래픽을 수신하지 못하게 됩니다</em> . 준비 프로브가 성공하면 포드는 서비스에서 트래픽 수신을 재개합니다.</p>
<p>  Liveness Probe와 마찬가지로 Pod 외부 리소스(예: 데이터베이스)에 의존하는 Readiness Probe를 구성하지 마세요. 다음은 잘못 구성된 준비로 인해 애플리케이션이 작동하지 않을 수 있는 시나리오입니다. 앱의 데이터베이스에 연결할 수 없을 때 포드의 준비 프로브가 실패하면 동일한 상태 확인 기준을 공유하기 때문에 다른 포드 복제본도 동시에 실패합니다. 이러한 방식으로 프로브를 설정하면 데이터베이스를 사용할 수 없을 때마다 포드의 준비 프로브가 실패하고 Kubernetes가 <em>모든</em> 포드에 트래픽 전송을 중지합니다.</p>
<p>  준비 프로브 사용의 부작용은 배포를 업데이트하는 데 걸리는 시간이 늘어날 수 있다는 것입니다. 준비 프로브가 성공하지 않으면 새 복제본은 트래픽을 수신하지 않습니다. 그때까지는 이전 복제본이 계속해서 트래픽을 수신합니다.</p>
</li>
<li><p>Liveness Probe를 사용하여 비정상 포드 제거<a href="https://aws.github.io/aws-eks-best-practices/reliability/docs/application/#use-liveness-probe-to-remove-unhealthy-pods">¶</a></p>
<p>  활동성 프로브는 프로세스가 계속 실행되지만 애플리케이션이 응답하지 않는 <em>교착 상태 조건을 감지할 수 있습니다.</em> 예를 들어 포트 80에서 수신 대기하는 웹 서비스를 실행하는 경우 Pod의 포트 80에서 HTTP GET 요청을 보내도록 Liveness 프로브를 구성할 수 있습니다. Kubelet은 정기적으로 Pod에 GET 요청을 보내고 응답을 기다립니다. 포드가 200-399 사이에서 응답하면 kubelet은 포드가 정상이라고 간주합니다. 그렇지 않으면 포드가 비정상으로 표시됩니다. Pod가 상태 확인에 지속적으로 실패하면 kubelet은 Pod를 종료합니다.</p>
<p>  <code>initialDelaySeconds</code>첫 번째 프로브를 지연하는 데 사용할 수 있습니다 .</p>
<p>  Liveness Probe를 사용할 때 Kubernetes가 모든 Pod를 교체하려고 시도하여 애플리케이션을 오프라인으로 렌더링하므로 모든 Pod가 동시에 Liveness Probe에 실패하는 상황이 발생하지 않도록 해야 합니다. 또한 Kubernetes는 Liveness Probe에도 실패하는 새로운 Pod를 계속 생성하여 제어 플레인에 불필요한 부담을 줄 것입니다. Pod 외부 요소(예: 외부 데이터베이스)에 의존하도록 Liveness Probe를 구성하지 마세요. 즉, 포드 외부의 응답하지 않는 데이터베이스로 인해 포드가 활성 프로브에 실패하게 해서는 안 됩니다.</p>
<p>  Sandor Szücs의 게시물 <a href="https://srcco.de/posts/kubernetes-liveness-probes-are-dangerous.html">LIVENESS PROBES ARE DANGEROUS에서는</a> 잘못 구성된 프로브로 인해 발생할 수 있는 문제에 대해 설명합니다.</p>
</li>
</ul>
<h3 id="4--시작하는-데-시간이-오래-걸리는-응용-프로그램에는-시작-프로브를-사용하세요-자바-관련-고려사항">4.  시작하는 데 시간이 오래 걸리는 응용 프로그램에는 시작 프로브를 사용하세요. (자바 관련 고려사항)</h3>
<p>• 컨테이너가 보통 <code>초기 지연 시간 + 실패 임계 값 * 대기 초(initialDelaySeconds + failureThreshold * periodSeconds)</code> 이후에 기동된다면, 스타트업 프로브가 활성화 프로브와 같은 엔드포인트를 체크하도록 명시해야 한다.</p>
<p>앱을 시작하는 데 추가 시간이 필요한 경우 시작 프로브를 사용하여 활성 및 준비 프로브를 지연할 수 있습니다. 예를 들어, 데이터베이스에서 캐시를 하이드레이션해야 하는 Java 앱이 완전히 작동하려면 최대 2분이 걸릴 수 있습니다. 완전히 작동할 때까지 모든 활성 또는 준비 프로브는 실패할 수 있습니다. 시작 프로브를 구성하면 활성 또는 준비 프로브가 실행되기 전에 Java 앱이 <em>정상 상태가</em> 될 수 있습니다 .</p>
<p>시작 프로브가 성공할 때까지 다른 모든 프로브는 비활성화됩니다. Kubernetes가 애플리케이션 시작을 기다려야 하는 최대 시간을 정의할 수 있습니다. 구성된 최대 시간 후에도 포드가 여전히 시작 프로브에 실패하면 포드가 종료되고 새 포드가 생성됩니다.</p>
<p>Startup Probe는 Liveness Probe와 유사합니다. 실패하면 Pod가 다시 생성됩니다. <a href="https://medium.com/swlh/fantastic-probes-and-how-to-configure-them-fef7e030bd2f">Ricardo A.가 자신의 게시물 인 환상적인 프로브 및 구성 방법</a> 에서 설명했듯이 , 시작 프로브는 애플리케이션의 시작 시간을 예측할 수 없을 때 사용해야 합니다. 애플리케이션을 시작하는 데 10초가 걸린다는 것을 알고 있다면 대신 Liveness/Readiness Probe를 사용해야 합니다 <code>initialDelaySeconds</code>.</p>
<h3 id="5-requestlimit-걸어두기">5. Request/Limit 걸어두기</h3>
<p>Limit을 걸지 않으면 노드에 다중 포드가 예약 되어 OOM 에러가 발생할 수 있습니다.</p>
<p>설정 귀찮을땐 limit range를 namespace 별로 걸수도 있으니 확인</p>
<p><a href="https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/memory-default-namespace/">https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/memory-default-namespace/</a></p>
<p>주의사항)</p>
<p>동일한 태그를 사용하는 리소스들은 두개 이상 정의 금지</p>
<p>Karpenter provisioner yaml 수정 후 다시 apply 해도 적용이 되지 않기 때문에, 바로 적용하려면 별도의 롤링 업데이트를 해야함.</p>
<p><a href="https://stackoverflow.com/questions/74231427/can-karpenter-target-only-specific-node-groups-in-a-cluster">https://stackoverflow.com/questions/74231427/can-karpenter-target-only-specific-node-groups-in-a-cluster</a></p>
<p><a href="https://younsl.github.io/blog/k8s/karpenter/">https://younsl.github.io/blog/k8s/karpenter/</a></p>
<p>무신사 유튜브 참조: <a href="https://www.youtube.com/watch?v=FPlCVVrCD64">https://www.youtube.com/watch?v=FPlCVVrCD64</a></p>
<p>카카오 스타일 블로그 참조 : <a href="https://devblog.kakaostyle.com/ko/2022-10-13-1-karpenter-on-eks/">https://devblog.kakaostyle.com/ko/2022-10-13-1-karpenter-on-eks/</a></p>
<p><a href="https://techblog.gccompany.co.kr/karpenter-7170ae9fb677">https://techblog.gccompany.co.kr/karpenter-7170ae9fb677</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jmeter에서 TPS 올리는 법]]></title>
            <link>https://velog.io/@alli-eunbi/Jmeter%EC%97%90%EC%84%9C-TPS-%EC%98%AC%EB%A6%AC%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@alli-eunbi/Jmeter%EC%97%90%EC%84%9C-TPS-%EC%98%AC%EB%A6%AC%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Sat, 18 Nov 2023 05:33:49 GMT</pubDate>
            <description><![CDATA[<p>국가 성능 시험을 통과하기 위해 TPS를 올려야 하는 상황이 있었다.
Jmeter를 사용하여 throuput을 올리려고 요청을 아무리 늘려도 TPS가 올라가지 않았다.</p>
<p>경험상 tps를 올리는 방법은 ramp up time을 5~10초로 설정해 각 thread 시작 전에 Jmeter가 thread를 생성할 시간을 주고, loop를 천단위로 올린 다음 thread 숫자를 50까지 낮추는 거였다.</p>
<p>어차피 tps는 요청수/요청이 걸린 시간 임으로, 한 요청이 응답을 받는 시간을 단축시키면 된다. 그렇기 때문에 thread의 숫자가 많은 것보단, loop를 많이 생성해 요청수를 늘리고 응답시간을 줄일 수 있도록 해야한다.</p>
<p>ramp up time은 꼭 1초가 아니라 최소 5초까지 설정하여 thread 생성후 시작때까지 Jmeter가 준비할 시간을 주도록 한다. 그래야 요청을 실제로 보내는 시간이 느려지지 않아 응답 속도를 더 빠르게 맞출 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[테스팅 환경 구축(feat. artillery)]]></title>
            <link>https://velog.io/@alli-eunbi/%ED%85%8C%EC%8A%A4%ED%8C%85-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95feat.-artillery</link>
            <guid>https://velog.io/@alli-eunbi/%ED%85%8C%EC%8A%A4%ED%8C%85-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95feat.-artillery</guid>
            <pubDate>Tue, 14 Nov 2023 08:07:41 GMT</pubDate>
            <description><![CDATA[<p>회사에서 국가 성능 테스트에 관한 업무가 있었다.
내가 맡은 부분은 socket의 동접자를 10000명이상 감당한 서버인가 테스트 해보는 것이었다.</p>
<p>동접자를 테스트해본 결과 5000명까지는 로컬(맥)에서 vuser(가상의 user)를 생성 가능했지만, 이게 5000명이 넘어가는 순간부터는 계속해서 socket error를 냈다.
오토 스케일링도 충분히 걸어놨음에도 불구하고 socket이 에러가 나자, 이게 과연 socket 서버의 문제인지 로컬 맥북으로만 테스트를 감당할거라고 생각한 나의 판단의 문제인지 헷갈리기 시작했다.</p>
<p>그래서 팀장님께 자문을 구해 artillery distributed test 툴을 도입하였다.</p>
<blockquote>
<p><a href="https://www.artillery.io/docs/load-testing-at-scale/aws-fargate">https://www.artillery.io/docs/load-testing-at-scale/aws-fargate</a></p>
</blockquote>
<h2 id="distributed-load-testing-on-aws-fargate">Distributed Load Testing on AWS Fargate</h2>
<p>Artillery는 artillery cli로 관련 명령을 넣으면 aws-sdk를 사용해 부하테스트용 Fargate 서버들을 생성해 클라이언트의 역할을 수행한다.</p>
<h3 id="준비물">준비물</h3>
<p>AWS SDK</p>
<h3 id="작동원리">작동원리</h3>
<p>AWS Fargate를 사용하여 부하 테스트를 실행할 때 Artillery는 이를 최대한 원활하게 만들려고 노력합니다.</p>
<ol>
<li><p>부하 테스트 번들링
Artillery는 테스트 스크립트를 분석하고 모든 종속성을 번들로 패키징하여 테스트를 실행하는 Fargate 컨테이너에서 사용할 수 있도록 합니다.</p>
</li>
<li><p>AWS 리소스 생성
필요한 경우 Artillery는 테스트를 실행할 Fargate 컨테이너와 Fargate 클러스터에 대한 IAM 역할을 생성합니다.</p>
</li>
<li><p>부하 generator 생성
Artillery는 다수의 Fargate 컨테이너( --count 플래그로 직접 사용자가 몇개의 부하를 전송할 서버를 띄울지 지정)를 가동하고 모든 컨테이너에 테스트 번들을 배포한 후 모든 컨테이너가 동기화되어 사용 가능해질 때까지 기다립니다.</p>
</li>
<li><p>부하 생성기의 결과 집계
로드 생성기가 실행되면 Artillery는 개별 컨테이너의 보고서를 집계하고 이를 통계적으로 올바른 방식으로 집계하여 사용자에게 제공합니다.</p>
</li>
<li><p>AWS 리소스 정리
테스트가 완료되면 Artillery는 더 이상 필요하지 않은 AWS 리소스를 제거합니다.</p>
<ul>
<li>테스트 번들을 저장한 s3 버킷</li>
<li>테스트 실행하는 Fargate 컨테이너와 Artillery CLI 간의 통신을 위한 SQS 대기열</li>
</ul>
</li>
</ol>
<h3 id="iam-권한">IAM 권한</h3>
<pre><code>CreateOrGetECSRole
CreateECSPolicy
iam:CreateServiceLinkedRole
iam:PassRole
SQSPermissions
SQSListQueues
ECSPermissionsGeneral
ECSPermissionsScopedToCluster
ECSPermissionsScopedWithCondition
S3Permissions
secretsmanager:GetSecretValue
# ssm 권한
ssm:PutParameter
ssm:GetParameter
ssm:GetParameters
ssm:DeleteParameter
ssm:DescribeParameters
ssm:GetParametersByPath
# ec2 describe 권한
ec2:DescribeRouteTables
ec2:DescribeVpcs
ec2:DescribeSubnets</code></pre><h3 id="제한-사항">제한 사항</h3>
<p><strong>* 실행 중인 테스트는 AWS 콘솔에서만 중지할 수 있습니다. *</strong>
AWS Fargate 테스트 실행이 시작되면 AWS 콘솔 또는 AWS CLI를 통해 Fargate 작업을 중지해야만 테스트를 중지할 수 있습니다.</p>
<p>향후에는 Artillery CLI에 현재 실행 중인 테스트를 중지하는 기능을 추가할 예정입니다.</p>
<h3 id="실행-명령어">실행 명령어</h3>
<pre><code>artillery run-fargate --region {region} --count 3 ./test.yaml --output report.json</code></pre><p>필자는 npm으로 artillery를 다운받아 실행했는데 앞에 npx 붙여서 실행해도 잘 된다.</p>
<h3 id="error">error</h3>
<p>default VPC에서 퍼블릭 서브넷을 찾을 수 없습니다.</p>
<ol>
<li>default vpc가 없는 경우
<img src="https://velog.velcdn.com/images/alli-eunbi/post/4149b11a-9592-40df-9251-5f78434cf1c1/image.png" alt=""></li>
</ol>
<p>aws 콘솔에서 vpc -&gt; 기본 vpc 설정여부를 확인해서 default vpc로 설정이 되어있는지 확인해야한다. aws에서 이미 생성된 vpc를 기본 vpc로 변경하는 방법은 없는것 같다.
(필자는 성능 테스트 용도라 빠르게 지울 생각으로 그냥 하나 생성했다.)
그리고 혹여 vpc에 public subnet이 없을 경우 테스트 실패하니 public subnet 설정을 해준다.</p>
<p>&lt;기타 잡담&gt;</p>
<ol>
<li><p>think를 활용한 실제 동접자 만들기</p>
<pre><code>scenarios:
 -engine: socketio-v3
  flow:
      - emit:
         namespace: &quot;/&quot;
         channel: &quot;connection&quot;
     # think 설정하면 소켓이 바로 끊어지지 않고 160초 동안 connection을 유지
     # 이 설정을 하면 동접자를 만들 수 있음.
     - think: 160</code></pre></li>
<li><p>socket artillery는 절대.. v2 사용 금지.... 무족권 socket-v3이상에서 사용하기..</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Karpenter의 webhook	http: TLS handshake error]]></title>
            <link>https://velog.io/@alli-eunbi/Karpenter%EC%9D%98-webhookhttp-TLS-handshake-error</link>
            <guid>https://velog.io/@alli-eunbi/Karpenter%EC%9D%98-webhookhttp-TLS-handshake-error</guid>
            <pubDate>Mon, 06 Nov 2023 03:19:03 GMT</pubDate>
            <description><![CDATA[<p>Karpenter에서 이따금씩 위와 같은 에러를 방출하는 경우가 있다.
찾아보니 해당 에러는 유구한 역사같다.</p>
<p>Secret의 karpenter-cert를 삭제하고 다시 배포하는 것이 에러 해결 방법이 되었다.</p>
<pre><code># 적용 명령어 k apply -f secret.yaml
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: karpenter-cert
  namespace: karpenter
  labels:
    helm.sh/chart: karpenter-v0.31.1
    app.kubernetes.io/name: karpenter
    app.kubernetes.io/instance: karpenter
    app.kubernetes.io/version: &quot;0.31.1&quot;
    app.kubernetes.io/managed-by: Helm</code></pre><p>karpenter의 배포가 꼬이면 또 다시 발생할 가능성이 높은것 같다.</p>
<p><a href="https://github.com/aws/karpenter-core/issues/718">https://github.com/aws/karpenter-core/issues/718</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[no space left on device ]]></title>
            <link>https://velog.io/@alli-eunbi/no-space-left-on-device</link>
            <guid>https://velog.io/@alli-eunbi/no-space-left-on-device</guid>
            <pubDate>Wed, 25 Oct 2023 06:36:50 GMT</pubDate>
            <description><![CDATA[<pre><code>no space left on device </code></pre><p>prometheus를 배포하고 10gi 정도를 storageClass로 할당했으나 위와 같은 에러가 떴다.</p>
<p>개발 클러스터라 많은 양의 메트릭 정보가 들어갈거라고 생각하지 않았는데, 의외로 10Gi 로도 부족했다.</p>
<p>이럴때, 임의로 스토리지 클래스를 수정하면 에러가 발생할 수 있으니 아래 절차를 따라 보는 것을 추천한다.</p>
<h2 id="1-스토리지-클래스-수정을-위한-config-변경">1. 스토리지 클래스 수정을 위한 config 변경</h2>
<pre><code># 스토리지 클래스 확인
k get sc</code></pre><p>ebs csi driver가 잘 작동되고 있는걸 확인하면 변경을 위해 추가해야할 내용을 넣어야 한다.</p>
<pre><code># 스토리지 클래스를 수정하기 위한 명령어
k edit sc

# 아래 내용을 추가
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
reclaimPolicy: Retain

allowVolumeExpansion: true   &lt;---- 이거!

mountOptions:
  - debug
volumeBindingMode: Immediate</code></pre><h2 id="2-용량-변경">2. 용량 변경</h2>
<pre><code>k edit pvc {pvc 이름}

spec
  resources:
    requests:
      storage: 30gi  &lt;-- 이 storage를 변경</code></pre><p>이렇게 변경하고 나면 prometheus helm의 values의 내용도 함께 변경해야한다.</p>
<pre><code>prometheus:
  prometheusSpec:
    storageSpec:
    # Using PersistentVolumeClaim
    #
      volumeClaimTemplate:
        spec:
          storageClassName: ebs-sc
          accessModes: [&quot;ReadWriteOnce&quot;]
          resources:
            requests:
              storage: 30Gi</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Daemonset을 taint/toleration 상관없이 배포하기]]></title>
            <link>https://velog.io/@alli-eunbi/Daemonset%EC%9D%84-toleration-%EC%83%81%EA%B4%80%EC%97%86%EC%9D%B4-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@alli-eunbi/Daemonset%EC%9D%84-toleration-%EC%83%81%EA%B4%80%EC%97%86%EC%9D%B4-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 24 Oct 2023 02:26:33 GMT</pubDate>
            <description><![CDATA[<p>Daemonset은 노드에 하나씩 꼭 붙어야 하는 파드를 의미한다. 예를 들어 promtail이나 fluent-bit와 같이 로그를 수집하는 daemonset은 노드마다 붙어야 각 서버의 로그를 받을 수 있다.</p>
<p>이런 daemonset이 많아지거나, 노드가 많아지게 되어 일일이 toleration 설정을 맞춰주는것은 상당히 귀찮은 작업이다.</p>
<p>이런 작업을 줄이려면 daemonset에 아래와 같은 tolerations를 추가 해주면 된다.</p>
<pre><code>tolerations:
  - effect: NoSchedule
    operator: Exists
  - key: CriticalAddonsOnly
    operator: Exists
  - effect: NoExecute
    operator: Exists</code></pre><p>참조) <a href="https://stackoverflow.com/questions/57006394/kubernetes-daemonsets-in-the-presence-of-tolerations">https://stackoverflow.com/questions/57006394/kubernetes-daemonsets-in-the-presence-of-tolerations</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Karpenter Best practice 요약]]></title>
            <link>https://velog.io/@alli-eunbi/Karpenter-Best-practice-%EC%9A%94%EC%95%BD</link>
            <guid>https://velog.io/@alli-eunbi/Karpenter-Best-practice-%EC%9A%94%EC%95%BD</guid>
            <pubDate>Mon, 23 Oct 2023 14:36:30 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/alli-eunbi/post/7d0f4ae3-8242-4e04-a8bc-01c9ce73d22e/image.png" alt="">
카펜터에는 지켜야하는 여러 규칙이 존재합니다.
아래는 요약으로 정리하였으니 개발시에 확인하면 좋습니다.</p>
<h2 id="karpenter에서-지켜야하는-수칙">Karpenter에서 지켜야하는 수칙</h2>
<hr>
<h3 id="용량에-대한-요구사항이-변화하는-작업workload에-카펜터를-사용하세요">용량에 대한 요구사항이 변화하는 작업(workload)에 카펜터를 사용하세요</h3>
<p>카펜터는 ASG, MNG와 다르게 Kubernetes 기본 API에 더 가까운 scaling management를 사용합니다.
ASG와 MNG는 EC2 CPU 로드와 같은 AWS의 지표를 기반으로 scaling이 트리거 됩니다. Cluster Autoscaler는 kubernetes 추상화를 AWS 추상화로 연결하지만, 가용영역에 대한 scheduling과 같은 유연성이 일부 손실 됩니다. </p>
<p>카펜터는 이러한 AWS의 추상화 계층을 제거하였습니다. karpetner는 수요가 급증하거나 다양한 컴퓨팅 사이즈에 대한 요구사항이 있는 경우 사용하기 적합합니다. MNG와 ASG는 보다 정적인 작업을 실행하는 경우 적합하며, 필요에 따라서는 karpenter와 혼용해서 쓰는 것도 가능합니다.</p>
<hr>
<h3 id="eks-fargate-또는-노드-그룹mng에-속한-worker-node에서-karpenter-컨트롤러를-실행해주세요">EKS Fargate 또는 노드 그룹(MNG)에 속한 worker node에서 Karpenter 컨트롤러를 실행해주세요</h3>
<p>Karpenter는 helm 차트로 배포됩니다. Helm 차트는 클러스터 확장에 컨트롤러를 사용하기 위해 Karpenter controller와 webhook 포드를 먼저 실행합니다. 적어도 하나의 worker node가 있는 하나의 노드 그룹에 띄우기를 권장합니다.
대안으로는, 위의 선행되야할 포드들을 karpenter라는 네임스페이스에 Fargate profile을 생성 후 EKS fargate에 띄울 수 있습니다. 이렇게 되면 모든 pod들은 karpenter라는 네임스페이스 아래에 EKS Fargate로 배포 됩니다. 
<strong>절대 karpenter를 Karpenter가 관리하는 노드 위에 띄우지 마세요</strong></p>
<hr>
<h3 id="사용자-지정-시작-템플릿을-카펜터와-함께-사용하는-것을-피해주세요">사용자 지정 시작 템플릿을 카펜터와 함께 사용하는 것을 피해주세요</h3>
<p>카펜터는 사용자 지정 시작 템플릿을 사용하는 것을 권하지 않습니다. 사용자 지정 템플릿을 사용하게 되면 다중 아키텍처 지원, 노드 자동 업그레이드 기능 및 securityGroup 검색을 막습니다. 시작 템플릿을 사용하면 Karpenter의 provisioner에서 특정 필드가 중복되며, 서브넷 및 인스턴스 유형과 같은 다른 필드는 Karpenter에서 무시되기 때문에 혼란이 발생할 수도 있습니다.
사용자 지정 사용자 데이터를 사용하거나 AWS 노드 템플릿에서 사용자 지정 AMI를 직접 지정하면 시작 템플릿 사용을 피할 수 있는 경우가 많습니다. 이를 수행하는 방법에 대한 자세한 내용은 노드 템플릿 에서 확인할 수 있습니다.</p>
<hr>
<h3 id="작업에-맞지-않는-instance-유형은-제외-시켜주세요">작업에 맞지 않는 instance 유형은 제외 시켜주세요.</h3>
<p>클러스터에서 실행되는 워크로드에 필요하지 않은 경우 node.kubernetes.io/instance-type 키를 사용하여 특정 인스턴스 유형을 제외하는 것이 좋습니다 .</p>
<p>다음 예에서는 대규모 Graviton 인스턴스 프로비저닝을 방지하는 방법을 보여줍니다.</p>
<pre><code>- key: node.kubernetes.io/instance-type
    operator: NotIn
    values:
      &#39;m6g.16xlarge&#39;
      &#39;m6gd.16xlarge&#39;
      &#39;r6g.16xlarge&#39;
      &#39;r6gd.16xlarge&#39;
      &#39;c6g.16xlarge&#39;</code></pre><hr>
<h3 id="spot-사용시-중단-처리interruption-handling-활성화해주세요">Spot 사용시 중단 처리(Interruption Handling) 활성화해주세요</h3>
<p>Karpenter는 aws.interruptionQueue의 값을 통해 활성화되는 기본 중단 처리를 지원합니다. 다음과 같이 워크로드를 중단시킬 수 있는 비자발적 중단 이벤트에 대한 중단 처리를 감시입니다.</p>
<ul>
<li>스팟 중단 경고</li>
<li>예약된 변경 상태 이벤트(유지 관리 이벤트)</li>
<li>인스턴스 종료 이벤트</li>
<li>인스턴스 중지 이벤트</li>
</ul>
<p>Karpenter는 이러한 이벤트 중 하나가 노드에 발생할 것을 감지하면 중단 이벤트가 발생하기 전에 자동으로 노드를 차단, 배수 및 종료하여 중단 전 워크로드 정리를 위한 최대 시간을 제공합니다. 아래에 설명된 대로 Karpenter와 함께 AWS 노드 종료 핸들러를 사용하는 것은 권장되지 않습니다.</p>
<blockquote>
<p><a href="https://karpenter.sh/docs/faq/#interruption-handling">https://karpenter.sh/docs/faq/#interruption-handling</a></p>
</blockquote>
<p>체크포인트 또는 기타 형태의 정상적인 배수가 필요한 포드(종료 전 2분의 시간 필요)는 클러스터에서 Karpenter 중단 처리를 활성화해야 합니다.</p>
<h3 id="internet-access가-되지-않는-private-cluster의-경우">Internet access가 되지 않는 private cluster의 경우</h3>
<p>인터넷 경로가 없는 VPC에 EKS 클러스터를 프로비저닝하는 경우 EKS 설명서에 표시된 프라이빗 클러스터 요구 사항 에 따라 환경을 구성했는지 확인해야 합니다 . 또한 VPC에 STS VPC 지역 엔드포인트를 생성했는지 확인해야 합니다. 그렇지 않은 경우 아래에 나타나는 것과 유사한 오류가 표시됩니다.</p>
<pre><code>ERROR controller.controller.metrics Reconciler error {&quot;commit&quot;: &quot;5047f3c&quot;, &quot;reconciler group&quot;: &quot;karpenter.sh&quot;, &quot;reconciler kind&quot;: &quot;Provisioner&quot;, &quot;name&quot;: &quot;default&quot;, &quot;namespace&quot;: &quot;&quot;, &quot;error&quot;: &quot;fetching instance types using ec2.DescribeInstanceTypes, WebIdentityErr: failed to retrieve credentials\ncaused by: RequestError: send request failed\ncaused by: Post \&quot;https://sts.&lt;region&gt;.amazonaws.com/\&quot;: dial tcp x.x.x.x:443: i/o timeout&quot;}</code></pre><p>Karpenter 컨트롤러는 서비스 계정에 대한 IAM 역할(IRSA)을 사용하기 때문에 비공개 클러스터에서는 이러한 변경이 필요합니다. IRSA로 구성된 포드는 AWS Security Token Service(AWS STS) API를 호출하여 자격 증명을 획득합니다. 아웃바운드 인터넷 액세스가 없는 경우 VPC에서 AWS STS VPC 엔드포인트를 생성하고 사용해야 합니다 .</p>
<p>또한 프라이빗 클러스터에서는 SSM용 VPC 엔드포인트를 생성해야 합니다 . Karpenter는 새 노드를 프로비저닝하려고 시도할 때 시작 템플릿 구성과 SSM 매개변수를 쿼리합니다. VPC에 SSM VPC 엔드포인트가 없으면 다음 오류가 발생합니다.</p>
<pre><code>INFO    controller.provisioning Waiting for unschedulable pods  {&quot;commit&quot;: &quot;5047f3c&quot;, &quot;provisioner&quot;: &quot;default&quot;}
INFO    controller.provisioning Batched 3 pods in 1.000572709s  {&quot;commit&quot;: &quot;5047f3c&quot;, &quot;provisioner&quot;: &quot;default&quot;}
INFO    controller.provisioning Computed packing of 1 node(s) for 3 pod(s) with instance type option(s) [c4.xlarge c6i.xlarge c5.xlarge c5d.xlarge c5a.xlarge c5n.xlarge m6i.xlarge m4.xlarge m6a.xlarge m5ad.xlarge m5d.xlarge t3.xlarge m5a.xlarge t3a.xlarge m5.xlarge r4.xlarge r3.xlarge r5ad.xlarge r6i.xlarge r5a.xlarge]        {&quot;commit&quot;: &quot;5047f3c&quot;, &quot;provisioner&quot;: &quot;default&quot;}
ERROR   controller.provisioning Could not launch node, launching instances, getting launch template configs, getting launch templates, getting ssm parameter, RequestError: send request failed
caused by: Post &quot;https://ssm.&lt;region&gt;.amazonaws.com/&quot;: dial tcp x.x.x.x:443: i/o timeout  {&quot;commit&quot;: &quot;5047f3c&quot;, &quot;provisioner&quot;: &quot;default&quot;}</code></pre><p>Price List Query API 에는 VPC 엔드포인트 가 없습니다 . 결과적으로 가격 데이터는 시간이 지나면서 더 이상 유효하지 않게 됩니다. Karpenter는 바이너리에 주문형 가격 데이터를 포함하여 이 문제를 해결하지만 Karpenter가 업그레이드될 때만 해당 데이터를 업데이트합니다. 가격 데이터 요청이 실패하면 다음과 같은 오류 메시지가 표시됩니다.</p>
<pre><code>ERROR   controller.aws.pricing  updating on-demand pricing, RequestError: send request failed
caused by: Post &quot;https://api.pricing.us-east-1.amazonaws.com/&quot;: dial tcp 52.94.231.236:443: i/o timeout; RequestError: send request failed
caused by: Post &quot;https://api.pricing.us-east-1.amazonaws.com/&quot;: dial tcp 52.94.231.236:443: i/o timeout, using existing pricing data from 2022-08-17T00:19:52Z  {&quot;commit&quot;: &quot;4b5f953&quot;}</code></pre><p><strong><em>요약하자면, 완전한 프라이빗 EKS 클러스터에서 Karpenter를 사용하려면 다음 VPC 엔드포인트를 생성해야 합니다.</em></strong></p>
<p>com.amazonaws.<region>.ec2
com.amazonaws.<region>.ecr.api
com.amazonaws.<region>.ecr.dkr
com.amazonaws.<region>.s3 – For pulling container images
com.amazonaws.<region>.sts – For IAM roles for service accounts
com.amazonaws.<region>.ssm - If using Karpenter
메모</p>
<blockquote>
<p>Karpenter(컨트롤러 및 웹훅 배포) 컨테이너 이미지는 Amazon ECR 프라이빗 또는 VPC 내부에서 액세스할 수 있는 다른 프라이빗 레지스트리에 있거나 복사되어야 합니다. 그 이유는 Karpenter 컨트롤러와 웹훅 포드가 현재 공용 ECR 이미지를 사용하기 때문입니다. VPC 내에서 또는 VPC와 피어링된 네트워크에서 이러한 이미지를 사용할 수 없는 경우 Kubernetes가 ECR 공개에서 이러한 이미지를 가져오려고 하면 이미지 가져오기 오류가 발생합니다.</p>
</blockquote>
<h2 id="provisioner에서-지켜야하는-수칙">Provisioner에서 지켜야하는 수칙</h2>
<hr>
<h3 id="다양한-작업을-다양한-워커노드-타입에서-진행하려고-할때는-provisioner를-여러개-생성하세요">다양한 작업을 다양한 워커노드 타입에서 진행하려고 할때는 provisioner를 여러개 생성하세요.</h3>
<p>  여러팀이 하나의 클러스터를 공유할 경우, 각 팀의 요구사항에 따라 여러 타입의 워커노드를 운영해야 할때가 있습니다. 예를 들어 어떤 팀은 ARM을 쓰고 다른팀은 Bottlerocket을 쓰는 등, OS나 instance type이 다를 수 있죠. 이런 경우에는 여러 provisoner를 생성하여 각 팀에 필요한 워커노드를 생성하면 됩니다.</p>
<hr>
<h3 id="상호배타적이거나-가중치를-가진-provisoner를-생성해주세요">상호배타적이거나 가중치를 가진 provisoner를 생성해주세요.</h3>
<p>이렇게 하면 일관된 스케줄링 동작이 제공됩니다. 여러 Provisioners가 일치하는 경우 Karpenter는 어떤 Provisioner를 사용할지 무작위로 선택하여 예상치 못한 결과를 초래할 수 있습니다. 여러 Provisioners를 만드는 유용한 예는 다음과 같습니다:</p>
<p>GPU를 가진 Provisioner를 만들고 이러한 (비용이 많이 드는) 노드에서만 특수 워크로드를 실행할 수 있도록 하는 provisoner:</p>
<pre><code># Provisioner for GPU Instances with Taints
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: gpu
spec:
  requirements:
  - key: node.kubernetes.io/instance-type
    operator: In
    values:
    - p3.8xlarge
    - p3.16xlarge
  taints:
  - effect: NoSchedule
    key: nvidia.com/gpu
    value: &quot;true&quot;
  ttlSecondsAfterEmpty: 60
</code></pre><p>Deployment에 taint를 위한 toleration을 걸어주세요:</p>
<pre><code># Deployment of GPU Workload will have tolerations defined
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate-gpu
spec:
  ...
    spec:
      tolerations:
      - key: &quot;nvidia.com/gpu&quot;
        operator: &quot;Exists&quot;
        effect: &quot;NoSchedule&quot;</code></pre><p>다른 팀을 위한 일반적인 배포를 진행할때는, provisioner에 nodeAffinity를 사용하여주세요. 그러면 Deployment는 nodeSelectorTerms를 사용하여 billing-team에 맞는 node를 선택합니다.</p>
<pre><code># Provisioner for regular EC2 instances
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: generalcompute
spec:
  labels:
    billing-team: my-team
  requirements:
  - key: node.kubernetes.io/instance-type
    operator: In
    values:
    - m5.large
    - m5.xlarge
    - m5.2xlarge
    - c5.large
    - c5.xlarge
    - c5a.large
    - c5a.xlarge
    - r5.large
    - r5.xlarge</code></pre><p>Deployment에 사용하는 nodeAffinity:</p>
<pre><code># Deployment will have spec.affinity.nodeAffinity defined
kind: Deployment
metadata:
  name: workload-my-team
spec:
  replicas: 200
  ...
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                - key: &quot;billing-team&quot;
                  operator: &quot;In&quot;
                  values: [&quot;my-team&quot;]</code></pre><hr>
<h3 id="타이머ttl을-사용하여-cluster에서-node를-자동-삭제-가능하도록-하세요">타이머(TTL)을 사용하여 cluster에서 node를 자동 삭제 가능하도록 하세요.</h3>
<p>노드의 제공 시간이나 만료 시간에 도달한 워크로드 포드가 없는 노드를 삭제할 때 타이머를 사용할 수 있습니다. 노드 만료는 노드를 업그레이드하기 위한 수단으로 사용될 수 있으므로 노드가 은퇴되고 업데이트된 버전으로 교체됩니다. 노드 제공 해제에 관한 정보는 Karpenter 문서의 &quot;<a href="https://karpenter.sh/docs/concepts/deprovisioning/">Karpenter 노드가 어떻게 해제되는가</a>&quot;를 참조하여 ttlSecondsUntilExpired 및 ttlSecondsAfterEmpty를 사용하여 노드를 해제하는 방법을 확인하십시오.</p>
<hr>
<h3 id="karpenter를-사용할-때-특히-spot을-활용할-때-karpenter가-생성할-수-있는-인스턴스-유형을-지나치게-제한하지-마십시오">Karpenter를 사용할 때 특히 Spot을 활용할 때, Karpenter가 생성할 수 있는 인스턴스 유형을 지나치게 제한하지 마십시오.</h3>
<p>  Spot을 사용할 때 Karpenter는 EC2 인스턴스를 프로비저닝하기 위해 Price Capacity Optimized 할당 전략을 사용합니다. 
  이 전략은 EC2에게 귀하가 시작하려는 인스턴스 수에 대한 가장 깊은 풀에서 인스턴스를 프로비저닝하도록 지시하며 중단 위험이 가장 낮은 풀에서 인스턴스를 프로비저닝합니다. 
  그런 다음 EC2 Fleet은 이러한 풀 중에서 가장 저렴한 가격의 Spot 인스턴스를 요청합니다. Karpenter가 사용할 수 있는 인스턴스 유형이 더 많을수록 EC2는 Spot 인스턴스의 실행 시간을 최적화하는 데 더 효과적일 것입니다. </p>
<p>  기본적으로 Karpenter는 귀하의 클러스터가 배포된 지역 및 가용 영역에서 EC2가 제공하는 모든 인스턴스 유형을 사용합니다. 
  Karpenter는 보류 중인 pod을 기반으로 모든 인스턴스 유형 세트 중에서 지능적으로 선택하여 pod이 적절한 크기와 구성의 인스턴스에 예약되도록 합니다. 
  예를 들어, 만약 귀하의 pod이 GPU를 필요로 하지 않는다면, Karpenter는 pod을 GPU를 지원하는 EC2 인스턴스 유형에 예약하지 않을 것입니다. </p>
<p>  어떤 인스턴스 유형을 사용해야 하는지 확신이 없을 때는 Amazon ec2-instance-selector를 실행하여 계산 요구 사항과 일치하는 인스턴스 유형 목록을 생성할 수 있습니다. 
  예를 들어, CLI는 메모리 vCPU, 아키텍처 및 지역을 입력 매개변수로 사용하고 이러한 제약 조건을 충족시키는 EC2 인스턴스 목록을 제공합니다.</p>
<pre><code>$ ec2-instance-selector --memory 4 --vcpus 2 --cpu-architecture x86_64 -r ap-southeast-1
c5.large
c5a.large
c5ad.large
c5d.large
c6i.large
t2.medium
t3.medium
t3a.medium
</code></pre><p>Spot 인스턴스를 사용할 때 Karpenter에 너무 많은 제약 조건을 두지 않는 것이 중요합니다. 귀하의 응용 프로그램의 가용성에 영향을 미칠 수 있습니다. 예를 들어, 특정 유형의 모든 인스턴스가 회수되고 대체할 수 있는 적절한 대안이 없는 경우가 발생할 수 있습니다. 그런 경우에 pod은 구성된 인스턴스 유형의 Spot 용량이 보충될 때까지 대기 상태로 남을 것입니다. 
  서로 다른 가용 영역에 인스턴스를 분산 배치하여 부족한 용량 오류의 위험을 줄일 수 있습니다. 왜냐하면 가용 영역마다 Spot 풀이 다르기 때문입니다. 이에 따라 일반적으로 가장 좋은 방법은 Spot을 사용할 때 Karpenter가 다양한 인스턴스 유형 집합을 사용할 수 있도록 하는 것입니다.</p>
<h2 id="pod-스케쥴링">Pod 스케쥴링</h2>
<hr>
<h3 id="고가용성-애플리케이션을-실행해야-하는-경우-일반적인-eks-best-practice를-따르세요">고가용성 애플리케이션을 실행해야 하는 경우, 일반적인 EKS <a href="https://aws.github.io/aws-eks-best-practices/reliability/docs/application/#recommendations">Best practice</a>를 따르세요.</h3>
<p>  Karpenter 문서의 &quot;<a href="https://karpenter.sh/docs/concepts/scheduling/#topology-spread">Topology Spread</a>&quot;를 참조하여 팟을 노드와 가용 영역에 분산 배치하는 방법에 대한 자세한 내용을 확인하십시오. Pod를 추방하거나 삭제하려는 시도가 있는 경우 유지해야 하는 최소한의 사용 가능한 pod을 설정하는 <a href="https://karpenter.sh/docs/troubleshooting/#disruption-budgets">Disruption Budgets</a>를 사용하십시오.</p>
<hr>
<h3 id="클라우드-제공업체에서-제공하는-컴퓨팅-기능을-제한하려면-layered-제약-조건을-사용하세요">클라우드 제공업체에서 제공하는 컴퓨팅 기능을 제한하려면 layered 제약 조건을 사용하세요.</h3>
<p>Karpenter의 layered 제약 조건 모델을 사용하면 pod 스케줄링에 최상의 노드를 배치할 수 있는 복합적인 provisioner 및 deployment 세트를 만들 수 있습니다. pod 사양으로 요청할 수 있는 제약 조건 예시는 다음과 같습니다:</p>
<ul>
<li><p>특정 application만 사용 가능한 가용 영역에서 실행해야 하는 경우. 예를 들어, 특정 가용 영역에 있는 EC2 인스턴스에서 실행되는 다른 application과 통신해야 하는 pod이 있다고 가정해보십시오. 
VPC에서 가용 영역 간 트래픽을 줄이려면 pod을 해당 EC2 인스턴스가 위치한 가용 영역에 공존시키고 싶을 것입니다. 이러한 대상 설정은 종종 node selctor를 사용하여 수행됩니다. <a href="https://karpenter.sh/docs/concepts/scheduling/#selecting-nodes">node selector</a>에 대한 자세한 내용은 Kubernetes 문서를 참조하십시오.</p>
</li>
<li><p>특정 종류의 프로세서 또는 다른 하드웨어가 필요한 경우. 예를들어 GPU에서 pod을 실행하는 법은 Karpenter 문서의 &quot;<a href="https://karpenter.sh/docs/concepts/scheduling/#acceleratorsgpu-resources">Accelerators</a>&quot; 섹션을 참조하십시오.</p>
</li>
</ul>
<hr>
<h3 id="compute-지출에-대한-billing-alarm을-설정하세요">Compute 지출에 대한 Billing alarm을 설정하세요.</h3>
<p>  자동 스케일을 구성한 클러스터의 경우, 지출이 임계값을 초과했을 때 경고하는 청구 알람을 생성해야 합니다. 또한 Karpenter 구성에 리소스 제한을 추가해야 합니다. 
 Karpenter를 사용하여 리소스 제한을 설정하는 방법은 AWS Auto Scaling 그룹의 최대 용량을 설정하는 것과 유사하며, Karpenter 프로비저너에서 인스턴스화할 수 있는 최대 컴퓨팅 리소스를 나타냅니다.</p>
<blockquote>
<p>참고: 
전체 클러스터에 대한 전역 제한을 설정하는 것은 불가능합니다. 제한은 특정 프로비저너에 적용됩니다.</p>
</blockquote>
<p>아래 스니펫은 Karpenter에게 최대 1000 CPU 코어 및 1000Gi 메모리만 프로비저닝하도록 지시합니다. 제한이 충족되거나 초과되었을 때만 Karpenter는 용량을 추가하지 않습니다. </p>
<p>  제한을 초과하면 Karpenter 컨트롤러는 메모리 리소스 사용량이 1000 제한을 초과했음을 나타내는 메시지를 컨트롤러 로그에 작성합니다. 
  컨테이너 로그를 CloudWatch 로그로 라우팅하는 경우 로그에서 특정 패턴 또는 용어를 검색하고 구성된 메트릭 임계값이 위반될 때 경고를 알리는 CloudWatch 알람을 생성할 수 있습니다.</p>
<pre><code>spec:
  limits:
    resources:
      cpu: 1000
      memory: 1000Gi
</code></pre><p>Karpenter와 함께 제한을 사용하는 자세한 내용은 Karpenter 문서의 &quot;Setting Resource Limits&quot;를 참조하십시오.</p>
<p>만약 제한을 사용하지 않거나 Karpenter가 프로비저닝할 수 있는 인스턴스 유형을 제한하지 않는다면, Karpenter는 필요에 따라 클러스터에 계속해서 컴퓨팅 용량을 추가할 것입니다. 
  Karpenter를 이러한 방식으로 구성하면 클러스터가 자유롭게 확장될 수 있지만 비용에 중대한 영향을 가질 수 있습니다. 이러한 이유로 청구 알람을 구성하는 것을 권장합니다. 
  청구 알람을 사용하면 계정에서 계산된 예상 비용이 정의된 임계값을 초과할 때 경고 및 예방적인 알림을 받을 수 있습니다. 추가 정보는 &quot;<a href="https://aws.amazon.com/ko/blogs/mt/setting-up-an-amazon-cloudwatch-billing-alarm-to-proactively-monitor-estimated-charges/">Amazon CloudWatch Billing Alarm 설정</a>&quot;을 참조하십시오.</p>
<p>또한 비용 이상 탐지(Cost Anomaly Detection)를 활성화하려면 AWS 비용 관리 기능을 사용하여 비용과 사용량을 계속 모니터링하고 비정상적인 지출을 감지합니다. 자세한 정보는 &quot;<a href="https://docs.aws.amazon.com/cost-management/latest/userguide/getting-started-ad.html">AWS 비용 이상 탐지 시작 가이드</a>&quot;에서 찾을 수 있습니다.
  AWS Budgets에서 예산을 만든 경우 특정 임계값을 초과했을 때 알림을 구성할 수도 있습니다. 예산 액션을 사용하면 이메일을 보내거나 SNS 주제에 메시지를 게시하거나 Slack과 같은 채팅 봇에 메시지를 보낼 수 있습니다. 자세한 내용은 &quot;<a href="https://docs.aws.amazon.com/cost-management/latest/userguide/budgets-controls.html">AWS Budgets 액션 구성</a>&quot;을 참조하십시오.</p>
<hr>
<h3 id="karpenter에서-노드의-프로비저닝을-방지하기-위해-do-not-evict-주석을-사용하세요">Karpenter에서 노드의 프로비저닝을 방지하기 위해 do-not-evict 주석을 사용하세요.</h3>
<p>Karpenter가 프로비저닝한 노드에서 중요한 응용 프로그램을 실행 중인 경우, 예를 들어 긴 실행 배치 작업이나 상태를 유지해야 하는 응용 프로그램인 경우, 노드의 TTL이 만료되면 해당 인스턴스가 종료될 때 응용 프로그램이 중단될 수 있습니다. Pod에 karpenter.sh/do-not-evict 주석을 추가하면 Karpenter에게 해당 노드를 해당 Pod가 종료되거나 do-not-evict 주석이 제거될 때까지 유지하도록 지시합니다. 자세한 정보는 &quot;<a href="https://karpenter.sh/docs/concepts/deprovisioning/">Deprovisioning documentation</a>&quot;를 참조하십시오.</p>
<p>  만약, deprovisioning 작업 중인 노드에 non-daemonset pod만 남아있다면 카펜터는 deprovisiong이 성공하거나 실패할때까지 해당 노드를  targeting할 것입니다.</p>
<hr>
<h3 id="non-cpu-자원들에-consolidation을-사용한다면-request와-limit을-동일하게-구성하세요">Non-CPU 자원들에 consolidation을 사용한다면, request와 limit을 동일하게 구성하세요.</h3>
<p>일반적으로 consolidation 및 스케줄링은 pod의 리소스 요청과 노드의 할당 가능한 리소스 양을 비교하여 작동합니다. 리소스 제한은 고려되지 않습니다. 그렇지 않으면 Karpenter는 노드에 가능한 한 많은 포드를 배치하므로 노드에 메모리 압박이 발생할 수 있습니다. 이는 통합 모드에서 특히 중요합니다.
 동일한 노드의 여러 팟이 동시에 터지는 경우 메모리 부족 (OOM) 상태로 인해 일부 팟이 종료될 수 있습니다. consolidation은 요청만을 고려하여 팟을 노드에 패킹하기 때문에 이러한 상황이 발생할 가능성이 있습니다.</p>
<p>  <a href="https://kyverno.io/policies/karpenter/set-karpenter-non-cpu-limits/set-karpenter-non-cpu-limits/">https://kyverno.io/policies/karpenter/set-karpenter-non-cpu-limits/set-karpenter-non-cpu-limits/</a></p>
<hr>
<h3 id="리소스-request-및-limit에-대한-기본값을-구성하기-위해-limitranges를-사용하세요">리소스 request 및 limit에 대한 기본값을 구성하기 위해 LimitRanges를 사용하세요.</h3>
<p>Kubernetes는 기본 요청(requests) 또는 제한(limits)을 설정하지 않기 때문에, 컨테이너가 기본 호스트의 CPU 및 메모리 리소스를 사용하는 것에 제한이 없습니다. Kubernetes 스케줄러는 팟의 총 요청을 보고 (팟의 컨테이너 또는 Init 컨테이너에서의 총 요청 중 더 큰 값) pod을 스케줄할 작업자 노드를 결정합니다. 마찬가지로 Karpenter는 pod의 request를 고려하여 프로비저닝할 인스턴스 유형을 결정합니다. 
  어떤 pod에서는 리소스 request가 지정되지 않은 경우, 네임스페이스에 합리적인 기본값을 적용하기 위해 Limit Range를 사용할 수 있습니다.</p>
<hr>
<h3 id="모든-작업에-정확한-리소스-request를-적용하세요">모든 작업에 정확한 리소스 request를 적용하세요.</h3>
<p>Karpenter는 귀하의 작업 요구 사항에 대한 정보가 정확한 경우에 가장 적합한 노드를 시작할 수 있습니다. 특히 Karpenter의 consolidation 기능을 사용하는 경우에 중요합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Karpenter와 CA의 동작 방식 비교]]></title>
            <link>https://velog.io/@alli-eunbi/Karpenter%EC%99%80-CA%EC%9D%98-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@alli-eunbi/Karpenter%EC%99%80-CA%EC%9D%98-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Thu, 19 Oct 2023 02:25:48 GMT</pubDate>
            <description><![CDATA[<p><a href="https://imgnew.megazone.com/2022/02/attachment-220211-8.pdf">https://imgnew.megazone.com/2022/02/attachment-220211-8.pdf</a></p>
<p>관리형 노드 그룹에 배포되지 않는 문제
<a href="https://repost.aws/questions/QUkSinVy1dSMa2IbkFOAbsrg/eks-node-capacity-settings-with-karpenter">https://repost.aws/questions/QUkSinVy1dSMa2IbkFOAbsrg/eks-node-capacity-settings-with-karpenter</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS 비용 관리를 위한 메서드]]></title>
            <link>https://velog.io/@alli-eunbi/AWS-%EB%B9%84%EC%9A%A9-%EA%B4%80%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-%EB%A9%94%EC%84%9C%EB%93%9C</link>
            <guid>https://velog.io/@alli-eunbi/AWS-%EB%B9%84%EC%9A%A9-%EA%B4%80%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-%EB%A9%94%EC%84%9C%EB%93%9C</guid>
            <pubDate>Wed, 04 Oct 2023 15:11:28 GMT</pubDate>
            <description><![CDATA[<h2 id="1-non-tagged-resources를-찾아내기">1. non-tagged resources를 찾아내기</h2>
<p><a href="https://stackoverflow.com/questions/34595579/can-i-programmatically-find-all-untagged-resources">https://stackoverflow.com/questions/34595579/can-i-programmatically-find-all-untagged-resources</a></p>
<p>리스트로 뽑기</p>
<pre><code class="language-aws">--query &#39;Reservations[].Instances[?!contains(Tags[].Key, `Name`)][].InstanceId&#39;</code></pre>
<p>Json으로 관련 정보까지 추출</p>
<pre><code>aws ec2 describe-instances \
  --query &#39;Reservations[].Instances[?!not_null(Tags[?Key == `Name`].Value)] | []&#39; &gt; test.json</code></pre><p>해당 resources 만든 사람 찾아내기
<a href="https://gist.github.com/sudharsans/39d5eaf8a82b7ccdf8b3230d13ba7d81">https://gist.github.com/sudharsans/39d5eaf8a82b7ccdf8b3230d13ba7d81</a></p>
<h2 id="2-iam에-aws의-리소스-생성시-무조건-태그-지정하는-role을-부여">2. IAM에 AWS의 리소스 생성시 무조건 태그 지정하는 role을 부여</h2>
<p><a href="https://youtube.com/watch?v=qjwRNlEBRdk&amp;si=qYtz0dalMbWHedPO">https://youtube.com/watch?v=qjwRNlEBRdk&amp;si=qYtz0dalMbWHedPO</a></p>
<h2 id="3-태그-안넣은-리소스-발견시-슬랙-알람">3. 태그 안넣은 리소스 발견시 슬랙 알람</h2>
<p><a href="https://stackoverflow.com/questions/67019664/send-notification-if-any-newly-launched-aws-resource-doesnt-have-specific-tags">https://stackoverflow.com/questions/67019664/send-notification-if-any-newly-launched-aws-resource-doesnt-have-specific-tags</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[loki context-canceled 에러]]></title>
            <link>https://velog.io/@alli-eunbi/loki-context-canceled-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@alli-eunbi/loki-context-canceled-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Wed, 13 Sep 2023 05:33:53 GMT</pubDate>
            <description><![CDATA[<pre><code>frontend_processor.go:69 msg=&quot;error processing requests&quot; address={address}:9095 err=&quot;rpc error: code = Canceled desc = context canceled&quot;</code></pre><pre><code> level=error ts=2023-09-12T05:35:52.956282515Z caller=retry.go:73 org_id=fake msg=&quot;error processing request&quot; try=0 query=&quot;{query} |= \&quot;\&quot;&quot; err=&quot;context canceled&quot;</code></pre><p>로키에서 위와 같은 context canceled 에러가 뜰때가 있다.
여러가지 이유가 있을 수 있는데, 필자의 경우 grpc로 수신 받을 수 있는 메시지 크기가 작아 에러가 생겼다.</p>
<p>보통 query-frontend에서 에러가 나는 이유는 querier에서 에러가 나기 때문인데, 아래 링크와 같이 명렁어를 추가해주면 에러가 발생하지 않는다.</p>
<p>아래 링크는 loki 쪽에서 받을 수 있는 로그의 크기를 늘려 놓은 것을 정리해두었다.</p>
<blockquote>
<p><a href="https://velog.io/@alli-eunbi/Loki-promtail-%EC%82%AC%EC%9D%B4%EC%A6%88-%ED%81%AC%EA%B8%B0-%EA%B4%80%EB%A0%A8">https://velog.io/@alli-eunbi/Loki-promtail-%EC%82%AC%EC%9D%B4%EC%A6%88-%ED%81%AC%EA%B8%B0-%EA%B4%80%EB%A0%A8</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Loki-promtail 사이즈 크기 관련]]></title>
            <link>https://velog.io/@alli-eunbi/Loki-promtail-%EC%82%AC%EC%9D%B4%EC%A6%88-%ED%81%AC%EA%B8%B0-%EA%B4%80%EB%A0%A8</link>
            <guid>https://velog.io/@alli-eunbi/Loki-promtail-%EC%82%AC%EC%9D%B4%EC%A6%88-%ED%81%AC%EA%B8%B0-%EA%B4%80%EB%A0%A8</guid>
            <pubDate>Tue, 12 Sep 2023 06:02:47 GMT</pubDate>
            <description><![CDATA[<pre><code>rpc error: code = ResourceExhausted desc = grpc: received message larger than max (4375193 vs. 4194304)</code></pre><p>로키로 로그를 받아오면서 한 팀이 유독 request body 와 header 값을 로그에 남겨, 메시지 크기 에러를 불러오는 경우가 있었다.</p>
<p><img src="https://velog.velcdn.com/images/alli-eunbi/post/dbb24f46-5a93-4519-9fa7-6f7cc2331452/image.png" alt=""></p>
<p>grpc는 기본으로 송신하는 쪽에 메시지 사이즈 제한이 없지만, 수신하는 쪽에는 4mb라는 제한이 있다.</p>
<p>해당 팀에서 로그에 해당 내용들은 제거할 수 없다고 하여, loki 서버에서 로그를 받을때 6mb 까지 제한을 풀도록 수정하도록 하였다.</p>
<p>loki-distributed 에 loki의 각 서버마다 존재하는 extraArgs: [] 에 아래 명령어를 추가하면 된다.</p>
<p>단위는 bytes로 되어 있다.</p>
<pre><code>queryScheduler:
    extraArgs:
    - grpc-max-recv-msg-size = 6291456
</code></pre><p>예시는 queryScheduler이지만 필자는 사용하고 있는 아래 목록에 추가하였다.</p>
<ul>
<li>frontend</li>
<li>ingester</li>
<li>querier</li>
<li>query-scheduler</li>
<li>ruler</li>
</ul>
<blockquote>
<p><a href="https://grafana.com/docs/loki/latest/configuration/#server_config">https://grafana.com/docs/loki/latest/configuration/#server_config</a>
<a href="https://grafana.com/docs/loki/latest/configuration/#grpc_client_config">https://grafana.com/docs/loki/latest/configuration/#grpc_client_config</a></p>
</blockquote>
<p>필자가 로키 쪽에만 message limit을 올렸지만, 만약 promtail이 scraping 하는 모든 서버가 4mb 이상의 크기의 로그를 전송할 것 같아 promtail의 max log size를 올리고 싶다면, 아래 명령어를 사용하면 된다.</p>
<p>아래는 5mb로 올릴때의 예시이다.</p>
<pre><code>server:
  grpc_server_max_recv_msg_size: 5242880</code></pre><blockquote>
<p><a href="https://grafana.com/docs/loki/latest/send-data/promtail/configuration/#server">https://grafana.com/docs/loki/latest/send-data/promtail/configuration/#server</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Tempo에서 Service Graph 그리는 법]]></title>
            <link>https://velog.io/@alli-eunbi/Tempo%EC%97%90%EC%84%9C-Service-Graph-%EA%B7%B8%EB%A6%AC%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@alli-eunbi/Tempo%EC%97%90%EC%84%9C-Service-Graph-%EA%B7%B8%EB%A6%AC%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Thu, 31 Aug 2023 11:57:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/alli-eunbi/post/a98a0af4-eac4-463f-ac76-0c6817deffc1/image.png" alt=""></p>
<hr>
<h1 id="tempo의-metric-generator">Tempo의 Metric Generator</h1>
<p>메트릭 생성기(metrics-generator)는 Tempo의 선택적인 구성 요소입니다. 
기본 helm 차트에 추가해야 합니다.</p>
<ul>
<li>작동 방식</li>
</ul>
<ol>
<li>Metric Generator는 수집한 trace에서 metric data를 생성합니다. </li>
<li>Distributor는 수신된 Span을 Ingester와 metric-generator 양쪽에 모두 기록합니다. </li>
<li>metric-generator는 Span을 처리하고 Prometheus remote-write 프로토콜을 사용하여 Prometheus 데이터 소스에 메트릭을 기록합니다.</li>
</ol>
<hr>
<h2 id="개요">개요</h2>
<p>Metrics-generator는 Tempo의 ingest 경로에서 사용 가능한 데이터를 활용하여 trace에서 메트릭을 생성하여 추가 가치를 제공합니다.</p>
<p>메트릭 생성기는 내부적으로 일련의 process들을 실행합니다. 각 프로세서는 Span을 수집하고 메트릭을 생성합니다. 각 프로세서는 각각 다른 메트릭 정보들을 생성합니다. </p>
<p>현재 다음과 같은 프로세서를 사용할 수 있습니다:</p>
<ul>
<li>서비스 그래프</li>
<li>Span 메트릭</li>
</ul>
<h3 id="서비스-그래프">서비스 그래프</h3>
<p>서비스 그래프는 분산 시스템 내의 서비스 간 관계를 나타내는 것입니다.</p>
<p>이 서비스 그래프 프로세서는 trace를 분석하여 서비스 간의 관계를 나타내는 map을 작성하며 엣지(edge)를 찾는 것을 목표로 합니다. </p>
<p>edge란 부모-자식 관계인 Span으로, 두 서비스 간의 점프(예: 요청)를 나타냅니다. 요청의 양과 지속 시간은 메트릭으로 기록되며, 이를 사용하여 그래프를 나타냅니다.</p>
<h3 id="span-메트릭">Span 메트릭</h3>
<p>Span 메트릭 프로세서는 Span에서 RED(Request, Error, Duration) 메트릭을 유도합니다.</p>
<p>Span 메트릭 프로세서는 각 차원 조합별로 Span의 총 개수와 지속 시간을 계산합니다. 차원은 서비스 이름, 작업, Span 종류, 상태 코드 및 Span에 있는 태그 또는 속성일 수 있습니다. 사용 가능한 차원이 많을수록 생성된 메트릭의 카디널리티가 높아집니다.</p>
<hr>
<h2 id="remote-writing-metrics"><strong><strong>Remote writing metrics</strong></strong></h2>
<p>메트릭 생성기는 주기적으로 메트릭을 <code>remote_write</code> 엔드포인트로 보내는 Prometheus 에이전트를 실행합니다. <code>remote_write</code> 엔드포인트는 구성 가능하며, Prometheus 호환 엔드포인트일 수 있습니다. 쓰기 간격은 metrics_generator.registry.collection_interval을 통해 제어할 수 있습니다.</p>
<p>멀티 테넌시가 활성화된 경우 메트릭 생성기는 remote-write 엔드포인트로 원본 요청의 X-Scope-OrgID 헤더를 전달합니다.</p>
<hr>
<h2 id="metric-generator-설정">Metric Generator 설정</h2>
<p>tempo의 values.yaml 에서 아래와 같이 설정을 진행합니다.</p>
<pre><code class="language-jsx">metricsGenerator:
  enabled: true
  config:
    storage:
      remote_write:
        - url: http://prom-kube-prometheus-stack-prometheus.monitoring:9090/api/v1/write
          send_exemplars: false
global_overrides:
  metrics_generator_processors:
    - service-graphs</code></pre>
<h2 id="prometheus-설정">Prometheus 설정</h2>
<pre><code class="language-jsx">prometheus:
    enableFeatures:
     - remote-write-receiver
    enableRemoteWriteReceiver: true</code></pre>
<h2 id="grafana-datasource-설정">Grafana Datasource 설정</h2>
<pre><code class="language-jsx">grafana:
additional_datasource:
- name: Tempo
      type: tempo
      uid:
      url: http://tempo-query-frontend-discovery.tempo:3100
      access: proxy
      basicAuth: false
      jsonData:
        serviceMap:
          datasourceUid: &#39;prometheus&#39;

// 여기 아래의 nodeGraph 설정이 true로 되어 있어야 함.
        nodeGraph:
          enabled: true</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[VPC 세팅 방법]]></title>
            <link>https://velog.io/@alli-eunbi/VPC-%EC%84%B8%ED%8C%85-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@alli-eunbi/VPC-%EC%84%B8%ED%8C%85-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sun, 20 Aug 2023 08:11:51 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>defualt vpc는 항상 있음.</p>
<ul>
<li><a href="http://172.31.XXX">172.31.</a>0.0/16 구성</li>
<li>private, public 구분이 없음</li>
<li>서로 다른 vpc 간에 peering 안됨.(ip 대역이 같음)</li>
<li>처음부터 삭제하고 시작하기 위해 vpc+라우팅 테이블 삭제 함.</li>
</ul>
</li>
<li><p>사용중인 vpc ip 대역의 리스트를 모으고 그 다음 ip 대역이 다르게 설정 해줘야 함.
<img src="https://velog.velcdn.com/images/alli-eunbi/post/33ee11b0-2d78-4f5b-8e28-2311fb6259bd/image.png" alt=""></p>
</li>
<li><p>vpc 설정 → DNS 설정 → DNS 확인 활성화</p>
</li>
<li><p><em>(필수)*</em></p>
<ul>
<li>DNS 확인 활성화 : RDS나 로드밸런서와 같이 dns로 접근하는 서비스에서 매번 배포때마다 바뀌는 ip 주소 대신 dns를 할당시켜 주기 때문에 무조건 할당되야 함.</li>
</ul>
</li>
</ul>
<p><strong>* 다음 할일 *</strong></p>
<ul>
<li>서브넷 만들기</li>
<li>인터넷 게이트웨이</li>
<li>라우팅 테이블</li>
</ul>
<h2 id="서브넷">서브넷</h2>
<h3 id="기본-지식">기본 지식</h3>
<ul>
<li><p>보안을 위해 private과 public을 구분</p>
</li>
<li><p>private</p>
<ul>
<li><p>물리적으로 라우팅 테이블이 인터넷이 차단 되어 있기 때문에 외부에서 접근 불가</p>
</li>
<li><p>private 서브넷 내부에서도 서버용 / DB용 / Lambda 를 분리</p>
<ul>
<li>Lambda를 분리한 이유 : 떴다 죽었다를 반복하다보니 문제 발생시 ip 추적이 어려움<ul>
<li>이 대역으로 생긴 문제는  lambda인지를 확인</li>
<li>private로 물리는 이유는 trigger나 gateway가 붙기 때문에 굳이 public일 필요가 없음.</li>
<li>람다 같은 경우에는 함수가 여러개 수행되거나, 동접자가 많아 띄워지는 함수가 많아질 경우를 대비하여 대역을 많이 줘야 함.</li>
</ul>
</li>
</ul>
</li>
<li><p>internet-gateway : private 서브넷을 외부와의 연결 시키기 위함(vpc 외부에 존재) → 퍼블릭 ip 주소 같은 것을 할당해주지 않음.</p>
</li>
<li><p>nat-gateway : vpc 내부에서 외부로 나가기 위함. nat-gateway는 public subnet에 존재, private vpc에 외부 ip 할당 하기 위함.
 <img src="https://velog.velcdn.com/images/alli-eunbi/post/43cf232f-da15-4e9f-abcf-4ff67868ad85/image.png" alt=""></p>
</li>
<li><p>vpn</p>
<ul>
<li>private vpc에 존재하는 서비스들을 개발시 접근하기 위해 생성</li>
<li>vpn을 할당하는 ec2를 띄워서 마치 터미널 처럼 해당 vpn을 이용
<img src="https://velog.velcdn.com/images/alli-eunbi/post/d2645af1-e951-4de0-8c8d-4d4af24c16d4/image.png" alt=""></li>
</ul>
</li>
</ul>
</li>
</ul>
<ul>
<li>public<ul>
<li>외부에서 접근해야하는 것</li>
</ul>
</li>
<li>서브넷은 가용영역에 묶임. 만약 가용영역이 두군데라고 하면 idc 센터 장애 대비 두군데로 생성</li>
</ul>
<h3 id="서브넷-세팅">서브넷 세팅</h3>
<p>예시)</p>
<ul>
<li>public
  a. 가용 영역 A
  b.  가용 영역 C</li>
<li>private
  a. 가용 영역 A
  b.  가용 영역 C</li>
<li>RDS
  a. 가용 영역 A
  b.  가용 영역 C</li>
</ul>
<ol>
<li><p>인터넷 게이트웨이 생성
인터넷 게이트웨이 생성 이후에는 vpc 연결해야함
 <img src="https://velog.velcdn.com/images/alli-eunbi/post/394fadbb-edfa-4c15-a4d2-36f7d816bb60/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/alli-eunbi/post/0c91e5ce-57f1-41f7-9c84-4921088cc539/image.png" alt=""></p>
</li>
<li><p>서브넷 생성</p>
<ol>
<li>이유 : Nat-gateway가 public subnet에 생성되야 함</li>
<li>Cidr로 계산한 대역으로 블록 설정
ex) ip주소/22
<img src="https://velog.velcdn.com/images/alli-eunbi/post/684b4c82-d077-4509-97f5-96f85cb9ddf8/image.png" alt=""></li>
</ol>
</li>
</ol>
<ol start="3">
<li><p>라우팅 테이블 생성
 a. public
<img src="https://velog.velcdn.com/images/alli-eunbi/post/a007e8fe-9e49-41e5-8990-cd64510a6cbc/image.png" alt="">
b. public 서브넷의 외부와의 접근과 같은 설정들을 진행 해줘야함.</p>
<pre><code>     1. 모든 로컬 허용(기본값)
     2. 인터넷 게이트웨이 연결</code></pre><p><img src="https://velog.velcdn.com/images/alli-eunbi/post/f46321c5-77ff-4d75-8119-6a35081f0a17/image.png" alt=""></p>
<ol start="2">
<li>외부에서 접근 가능</li>
<li>peering 연결</li>
</ol>
</li>
<li><p>NAT gateway 생성</p>
<ol>
<li><p>비용</p>
<ol>
<li><p>외부 인터넷이 가장 비쌈</p>
</li>
<li><p>내부, 다른 가용 영역이 중간</p>
</li>
<li><p>내부, 동일한 가용 영역 저렴</p>
</li>
<li><p>AWS 관리형 서비스</p>
<p>따라서, NAT gateway는 두개 만들어주는게 좋음.
추후에 붙힐수 있으니 nat gateway는 일단 하나만 만들어 둠.</p>
</li>
</ol>
</li>
<li><p>elastic ip 생성 해줘야함.</p>
<ol>
<li>탄력적 IP 할당 버튼 눌러서 하나 할당 해주기</li>
<li>aws에서 알아서 할당 해줌</li>
</ol>
</li>
</ol>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/alli-eunbi/post/8c840dab-50a3-4aca-89ed-bc90f0b46c4a/image.png" alt=""></p>
<ol start="5">
<li>private subnet 생성<ol>
<li>subnet 생성 자체는 public과 동일</li>
<li>라우팅 테이블 생성 후 아래와 같이 nat-gateway 연결</li>
</ol>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/alli-eunbi/post/bed8ba95-a13a-42fd-82c5-76beb70028f8/image.png" alt=""></p>
<ol start="6">
<li>peering 연결
 <img src="https://velog.velcdn.com/images/alli-eunbi/post/c0433c3b-342d-454f-b949-afbb22f63976/image.png" alt=""></li>
</ol>
<pre><code>1. (다른 계정일 경우에만) 계정 id가 있어야 함
2. (같은 계정인 경우)
    1. vpc id를 복사 (받는 쪽에서 요청 해야 함.)
           ![](https://velog.velcdn.com/images/alli-eunbi/post/7d807c98-c292-40f6-97cd-fbf2438e09ce/image.png)


    b. vpc id 넣어준 후 해당 vpc로 가서 수락해줌.
    ![](https://velog.velcdn.com/images/alli-eunbi/post/2a284356-8d92-420c-a6f0-1b64e54e7120/image.png)


3. 라우팅 테이블에서 해당 vpc ip 허용
    1. 아래와 같이 pcx 피어링 연결
    ![](https://velog.velcdn.com/images/alli-eunbi/post/43ac10f6-fbed-4f49-acf7-20ea24226f92/image.png)</code></pre><ol start="7">
<li>ACL 생성 → rds를 위해 활용 : 물리적 방화벽 역할<ol>
<li>내부 vpc의 3306 포트만 허용</li>
<li>빡세게 관리할때 사용</li>
</ol>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[EKS Ingress 설정]]></title>
            <link>https://velog.io/@alli-eunbi/EKS-Ingress-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@alli-eunbi/EKS-Ingress-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Thu, 17 Aug 2023 05:53:52 GMT</pubDate>
            <description><![CDATA[<p>ingress 설정과 관련된 설명은 아래 공식문서가 제일 잘 나와있다.</p>
<p><a href="https://awskocaptain.gitbook.io/aws-builders-eks/6.-aws-load-balancer-controller">https://awskocaptain.gitbook.io/aws-builders-eks/6.-aws-load-balancer-controller</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[vpc 세팅에러]]></title>
            <link>https://velog.io/@alli-eunbi/vpc-%EC%84%B8%ED%8C%85%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@alli-eunbi/vpc-%EC%84%B8%ED%8C%85%EC%97%90%EB%9F%AC</guid>
            <pubDate>Thu, 17 Aug 2023 01:27:48 GMT</pubDate>
            <description><![CDATA[<p>사내 모니터링 플랫폼을 개발하다가 여러가지 문제에 봉착했다.
EKS helm으로 띄운 loki 및 tempo의 서비스는 gossip protocol을 사용해 하나의 gossip ring을 만들고 그 안의 각각의 서비스들이 memberlist를 조회하며 trace 및 로그 데이터를 넘겨주는데 이때 meberlist를 제대로 찾지 못하는 에러 및 서비스들이 돌아가는 node의 private ip 주소를 찾지 못하는 에러를 발견했다.</p>
<p>이유는 잘못된 vpc 세팅으로 인해 발생한 에러였다.
<a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-cidr-blocks.html">https://docs.aws.amazon.com/vpc/latest/userguide/vpc-cidr-blocks.html</a></p>
<p>위를 확인해보면 aws에서  private subnet을  지원하는 ip CIDR이 있다.
<img src="https://velog.velcdn.com/images/alli-eunbi/post/6544fdec-fac4-4a1e-9e36-f5e199f3e200/image.png" alt=""></p>
<p>이중에서도 확인해야할 부분이 있다.</p>
<p>IPv4 CIDR 블록 연결 제한
다음 표에서는 허용 및 제한되는 VPC CIDR 블록 연결에 대한 개요를 제공합니다. 제한이 적용되는 이유는 일부 AWS 서비스가 AWS 서비스 측에서 충돌하지 않는 CIDR 블록을 요구하는 교차 VPC 및 교차 계정 기능을 사용하기 때문입니다.</p>
<h3 id="제한된-ip-주소-범위">제한된 IP 주소 범위</h3>
<p>172.17.0.0/16
10.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.168.0.0/16
198.19.0.0/16
공개적으로 라우팅 가능한 기타 IPv4 CIDR 블록(비RFC 1918) 또는 100.64.0.0/10 범위의 CIDR 블록.</p>
<h3 id="inter-region-vpc-peering-가능한-리전">Inter-region vpc peering 가능한 리전</h3>
<p>미국 동부(버지니아 북부)
미국 동부(오하이오)
미국 서부(캘리포니아 북부)
미국 서부(오레곤)
AWS EU(런던)
EU(아일랜드)
EU(파리)
아시아 태평양(뭄바이)
아시아 태평양(시드니)
아시아 태평양(싱가포르)
아시아 태평양(도쿄)
캐나다(중부)
남아메리카(상파울루)
서울</p>
<p>예를 들어, 오사카와 같은 경우는 해외 region과 vpc peering이 되지 않습니다.</p>
]]></description>
        </item>
    </channel>
</rss>