<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>만능 코드공방</title>
    <link>https://custom-li.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 21 May 2026 01:36:15 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>커스텀 리</managingEditor>
    <image>
      <title>만능 코드공방</title>
      <url>https://tistory1.daumcdn.net/tistory/3297427/attach/5e5d8063f49547489f5299ca832b5bd8</url>
      <link>https://custom-li.tistory.com</link>
    </image>
    <item>
      <title>CKA 취득 후기(renew 2025.02.18)</title>
      <link>https://custom-li.tistory.com/223</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;153&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duP72Q/btsMM4AEFxO/QM8teSsKIZeWfj11d61FQ0/tfile.svg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duP72Q/btsMM4AEFxO/QM8teSsKIZeWfj11d61FQ0/tfile.svg&quot; data-alt=&quot;Certified Kubernetes Administrator (CKA)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duP72Q/btsMM4AEFxO/QM8teSsKIZeWfj11d61FQ0/tfile.svg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduP72Q%2FbtsMM4AEFxO%2FQM8teSsKIZeWfj11d61FQ0%2Ftfile.svg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;153&quot; height=&quot;150&quot; data-origin-width=&quot;153&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Certified Kubernetes Administrator (CKA)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 구입했던 CKA 시험 바우처의 만료일이 한 달도 남지 않아 급하게 공부를 시작하게 되었고, 최종적으로 CKA 자격증을 취득하게 되었습니다. 특히, 25년 2월 18일부터 CKA 시험이 리뉴얼되어 변경된 시험 유형이나 취득 후기가 존재하지 않았습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이번 후기에서는 저와 같이 리뉴얼된 시험을 준비하는 분들에게 대략적인 학습 방법과 문제 유형을 공유하기 위해 게시글을 작성하게 되었습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;CKA 변경 사항&lt;/h2&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;Storage&lt;/b&gt;: dynamic volume provisioning, volume types, access modes, reclaim policies&lt;/li&gt;&lt;li&gt;&lt;b&gt;Troubleshooting&lt;/b&gt;: cluster and node diagnostics, network services and connectivity&lt;/li&gt;&lt;li&gt;&lt;b&gt;Workloads &amp;amp; Scheduling&lt;/b&gt;: Pod admission, node affinity, autoscaling&lt;/li&gt;&lt;li&gt;&lt;b&gt;Networking&lt;/b&gt;: Gateway API, coreDNS&lt;/li&gt;&lt;li&gt;&lt;b&gt;Cluster Architecture&lt;/b&gt;: Helm, Kustomize, extension interfaces, CRDs, Operators&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://training.linuxfoundation.org/certified-kubernetes-administrator-cka-program-changes/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;CKA 변경 사항&lt;/span&gt;&lt;/a&gt;은 25년 2월 18일 이후 시험부터 반영되었습니다. 주요 변경 사항을 본다면 Helm, CRD, CoreDNS, Gateway API가 존재합니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;학습 방법&lt;/h2&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;Udemy 뭄샤드 CKA 강의 (2회독)&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;처음 강의를 수강할 당시에는 Gateway API, Admission Controller, Helm, Kustomize 등 리뉴얼된 시험 유형은 존재하지 않았으나, 25년 2월 이후 추가 업데이트가 이루어져 Udemy 강의만으로 최신 내용을 충분히 커버할 수 있었습니다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;Killer shell CKA-A, CKA-B (각각 2회 반복)&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;Killer Shell 문제는 실제 시험보다 다소 난이도가 높았기 때문에 문제 유형을 파악하고, 제한된 시간(2시간) 내에 풀 수 있도록 반복하면서 연습하였습니다. 또한, 시험 환경에서 사용하는 가상환경의 변경된 커맨드에 익숙해지기 위해 많은 시간을 소요했었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;저는 기존 회사 업무를 통해 Kubernetes를 다룬 경험이 있어 기본 개념은 익숙했지만, 리뉴얼된 시험 영역 중 Gateway API는 처음 접하는 개념이었습니다. 따라서 이 부분을 집중적으로 공부하였습니다. Helm과 Kustomize의 경우 실무에서 경험이 있었기에 어렵지 않게 준비할 수 있었습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;1차 시험&lt;/h2&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzOsg7/btsMMBr1WGQ/SkeQjcZkd9mzlMzj4m2GO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzOsg7/btsMMBr1WGQ/SkeQjcZkd9mzlMzj4m2GO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzOsg7/btsMMBr1WGQ/SkeQjcZkd9mzlMzj4m2GO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzOsg7%2FbtsMMBr1WGQ%2FSkeQjcZkd9mzlMzj4m2GO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;843&quot; height=&quot;331&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이, 첫 번째 시험에서는 61점으로 1차 시험에 탈락하게 되었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;시험을 치르며 리뉴얼 되기 전 블로그 후기에 비해 문제 유형과 난이도가 크게 달라졌음을 체감할 수 있었습니다. 특히 TLS 설정, Helm 명령어, Gateway API 등 강의에서는 상세히 다루지 않았던 부분의 난이도가 높았었습니다. 또한, 모든 문제가 영어로 출제되었기 때문에 문제 이해에 다소 어려움을 겪어 시간이 많이 소요되었습니다. 결국 제한된 시간 내에 모든 문제를 풀지 못한 채로 마무리하게 되었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;시험 결과를 받은 후 전달된 메일과 같이 틀린 문제 유형을 집중적으로 공부하였고 다시 한번 재시험을 응시하였습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;2차 시험&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 시험에서는 첫 시험에서 경험한 문제 유형이 10문제 이상 그대로 다시 출제되어 비교적 편안한 마음으로 풀 수 있었습니다. CKA 시험은 주차 별로 문제 유형이 비슷하다는 이야기가 있었는데 어느 정도 수긍이 가는 이야기였던 것 같습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvkfCn/btsMMAmltyN/OcAo3QLuyVO9nSvGl28m31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvkfCn/btsMMAmltyN/OcAo3QLuyVO9nSvGl28m31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvkfCn/btsMMAmltyN/OcAo3QLuyVO9nSvGl28m31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvkfCn%2FbtsMMAmltyN%2FOcAo3QLuyVO9nSvGl28m31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1221&quot; height=&quot;159&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;159&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 70점으로 간신히 합격 커트라인을 넘기며 자격증을 취득할 수 있었습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;시험 Tip.&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;처음으로 CNCF의 액티브 환경의 시험을 응시해 보면서 느낀 팁을 남겨봅니다.&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt; 
 &lt;li&gt;&lt;b&gt;시험 환경에서는 &lt;code&gt;ssh&lt;/code&gt; 명령어로 문제마다 인스턴스를 별도로 접근하기에, &lt;code&gt;.bashrc&lt;/code&gt;에 Alias를 등록하여 사용하기 힘듭니다.&lt;/b&gt;&lt;/li&gt; 
&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;저는 매번 &lt;code&gt;.bashrc&lt;/code&gt; 파일에 &lt;code&gt;kga&lt;/code&gt;, &lt;code&gt;kaf&lt;/code&gt;, &lt;code&gt;kdf&lt;/code&gt; 와 같이 Alias를 등록하여 사용했었는데 이런 방법을 사용할 수 없었습니다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;&lt;b&gt;Killer Shell의 가상 환경에서 커맨드 익숙해지기&lt;/b&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;CKA 가상 환경에서는 복사/붙여넣기 단축기가 다소 다릅니다. 터미널 내에서는 &lt;code&gt;Ctrl + Shift + C&lt;/code&gt;, 터미널 외부에서는 &lt;code&gt;Ctrl + C&lt;/code&gt;로 설정되어 있어, 익숙하지 않으면 많은 실수를 할 수 있습니다. Killer Shell 환경에서 최대한 커맨드에 익숙해져야 시간을 단축할 수 있습니다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;&lt;b&gt;시험 문제에는 관련된 Kubernetes 공식 문서 Link가 첨부되어 있으므로 검색할 일이 많지 않습니다.&lt;/b&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;Udemy 강의나 Killer Shell 환경에서는 Kubernetes 공식 문서에서 필요한 정보를 자주 검색하게 되지만, 실제 CKA 시험 환경에서는 문제마다 공식 문서 링크가 함께 제공되어 문서를 검색하는 경우가 많지 않았습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;&lt;b&gt;최대한 명령형으로 리소스를 생성하거나 수정하기&lt;/b&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.yaml&lt;/code&gt; 파일을 직접 작성하고 수정하는 선언형 방식은 시간이 많이 소요됩니다. 빠르게 문제를 해결하기 위해서는 명령형 커맨드에 최대한 익숙해지는 게 효율적입니다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;CKA 시험 문제 유형&lt;/h2&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;Deployment&lt;/li&gt;&lt;li&gt;Priority Class&lt;/li&gt;&lt;li&gt;(Trouble Shooting) Pod&lt;/li&gt;&lt;li&gt;(Trouble Shooting) kubelet&lt;/li&gt;&lt;li&gt;Service Expose&lt;/li&gt;&lt;li&gt;Storage Class, PV, PVC&lt;/li&gt;&lt;li&gt;PV - PVC - Pod 마운트&lt;/li&gt;&lt;li&gt;TLS&lt;/li&gt;&lt;li&gt;Gateway API&lt;/li&gt;&lt;li&gt;Helm 명령어&lt;/li&gt;&lt;li&gt;CNI&lt;/li&gt;&lt;li&gt;CRI&lt;/li&gt;&lt;li&gt;Network Policy&lt;/li&gt;&lt;li&gt;Multi Container&lt;/li&gt;&lt;li&gt;HPA&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;후기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;바우처 만료일을 일주일 앞두고 겨우 CKA 자격증에 취득하였습니다. 시간적인 부담감이 심했던 경험이 아직도 생생하게 남아있습니다. 올해 12월에 만료 예정인 CKAD, CKS 시험은 최대한 여유 있게 미리 준비하여 더욱 편안하게 자격증을 취득할 수 있도록 해야겠습니다.&lt;/p&gt;</description>
      <category>Infrastructure</category>
      <category>CKA</category>
      <category>cka 후기</category>
      <category>helm</category>
      <category>killer shell</category>
      <category>killer.sh</category>
      <category>리뉴얼</category>
      <author>커스텀 리</author>
      <guid isPermaLink="true">https://custom-li.tistory.com/223</guid>
      <comments>https://custom-li.tistory.com/223#entry223comment</comments>
      <pubDate>Sun, 16 Mar 2025 13:21:41 +0900</pubDate>
    </item>
    <item>
      <title>Nest.js에서 Dynamic Module Import는 어떻게 구성되어 있을까? (feat. Kafka Client)</title>
      <link>https://custom-li.tistory.com/222</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Node.js_logo.svg&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;361&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kUxGt/btsLSPrmqZf/K5TYkXkT5REzIygsQvvFM0/tfile.svg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kUxGt/btsLSPrmqZf/K5TYkXkT5REzIygsQvvFM0/tfile.svg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kUxGt/btsLSPrmqZf/K5TYkXkT5REzIygsQvvFM0/tfile.svg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkUxGt%2FbtsLSPrmqZf%2FK5TYkXkT5REzIygsQvvFM0%2Ftfile.svg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;374&quot; height=&quot;229&quot; data-filename=&quot;Node.js_logo.svg&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;361&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 Node.js 환경에서 Kafka Client의 소스코드를 분석하는 시간을 가지고있다. 여러 Kafka Client를 확인하던 중, &lt;code&gt;@nestjs/microservices&lt;/code&gt; 라이브러리를 알게 되었는데 Kafka, RabbitMQ, Redis와 같은 여러 서비스를 손쉽게 통합할 수 있다는 것을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히, 해당 라이브러리의 소스 코드를 확인하던 중 특이한 점을 발견했는데, 관련된 의존성을 필수적으로 모두 설치해야 하는 타 라이브러리와 달리 어플리케이션에서 필요한 의존성만 설치하면 되는 구조로 되어 있었다. Kafka를 사용해야 한다면 &lt;code&gt;kafkajs&lt;/code&gt;, Redis를 사용해야 한다면 &lt;code&gt;ioredis&lt;/code&gt; 의존성이 필요한 것과 같이 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 어떻게 원하는 의존성을 필요할 때만 추가하고 사용할 수 있는 걸까? 가장 먼저, 모듈 시스템과 Dynamic Module Import 그리고 &lt;code&gt;@nestjs/microservices&lt;/code&gt;의 Kafka Client의 소스 코드까지 하나씩 확인해 보도록 하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Module System&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js 환경에서는 여러 Module System 환경을 가지고 있다. 예를 들어, 아무런 설정 없이 Node.js를 사용한다면 CommonJS를 사용할 것이고, 별도로 설정하거나 TypeScript를 기본으로 사용한다면 ES6를 사용할 것이다. 이번에는 각 Module System의 대표적인 CommonJS와 ES6를 간략하게 확인하고 넘어가자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CommonJS&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nodejs.org/api/modules.html&quot;&gt;CommonJS&lt;/a&gt;는 Node.js의 기본 모듈 시스템으로, &lt;b&gt;require&lt;/b&gt;와 &lt;b&gt;module.exports&lt;/b&gt;를 통해 모듈을 관리한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js 초기부터 사용된 CommonJS는 동기적(synchronous)으로 모듈을 호출한다. 만약, Nest.js를 기본 설정으로 사용한다면 TypeScript의 컴파일된 파일은 CommonJS로 변환될 것이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// math.js
module.exports.add = (a, b) =&amp;gt; a + b;
module.exports.subtract = (a, b) =&amp;gt; a - b;

// app.js
console.log(&quot;Hello&quot;); // &quot;Hello&quot;
const math = require('./math');
console.log(math.add(2, 3)); // 5
console.log(math.subtract(5, 2)); // 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ESM(ECMAScript Modules)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nodejs.org/api/esm.html&quot;&gt;ESM&lt;/a&gt;은 ECMAScript 2015(ES6) 표준에 도입된 모듈 시스템으로, &lt;b&gt;import&lt;/b&gt;와 &lt;b&gt;export&lt;/b&gt;를 통해 모듈을 관리한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESM은 TypeScript 및 최신 JavaScript에서 사용하는 Module System이다. Nest.js를 사용해 보았다면 가장 익숙한 Module&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System일 것이다.ESM은 CommonJS와 달리 모듈을 비동기(asynchronous)으로 호출하며, 런타임 이전에 모듈을 미리 로드하여 의존성을 인지할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// math.mjs
export const add = (a, b) =&amp;gt; a + b;
export const subtract = (a, b) =&amp;gt; a - b;

// app.mjs
import { add, subtract } from './math.mjs';
console.log(add(2, 3)); // 5
console.log(subtract(5, 2)); // 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Dynamic Module Import&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dynamic Module Import는 런타임에 모듈을 동적으로 호출하는 방법을 말한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js의 CommonJs와 ESM 모듈 시스템은 일반적으로 정적 모듈 호출(Static Module Import)에 최적화되어 있다. 하지만, 어플리케이션이 특정 상황에서만 모듈을 호출하거나, 초기 로드 시간을 줄이기 위해 지연 로드(Lazy Loading)가 필요한 경우, 동적 모듈 호출(Dynamic Module Import)이 필요하게 된다. 이번에는 각 모듈 시스템에서 동적으로 모듈을 호출하기 위해 어떤 방법을 사용하는지 확인해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CommonJS&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CommonJS는 동기적(Synchronous)으로 동작하며, &lt;code&gt;require&lt;/code&gt; 함수를 통해 런타임에 모듈을 호출할 수 있다. 일반적인 모듈을 호출하는 방식을 이용하더라도 동적으로 모듈을 호출할 수 있기 때문에, CommonJS 에서는 동적으로 모듈을 호출하기 수월하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한번, 특정 디렉터리에 있는 파일을 동적으로 로드하여, API Routing 을 구성하는 방법을 확인해 보자.&lt;/p&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;const path = require('path');
const fs = require('fs');

const routeFiles = fs.readdirSync('./routes');
routeFiles.forEach(file =&amp;gt; {
  const route = require(path.join(__dirname, 'routes', file));
  app.use(route.path, route.handler);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CommonJS는 동적 모듈 호출이 자연스럽게 처리되므로, 별도의 타입 문제나 런타임 에러 없이 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ESM(ECMAScript Modules)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESM은 기본적으로 비동기적(asynchronous)으로 동작하며, &lt;code&gt;import&lt;/code&gt;문을 통해 정적 모듈 호출을 기본으로 지원한다. ESM 또한 CommonJS와 같이 동적 모듈 호출(Dynamic Module Import) 기능을 제공하는데, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import&quot;&gt;import()&lt;/a&gt; 함수를 통해 런타임에 모듈을 동적으로 로드할 수 있도록 지원한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;async function loadModule(moduleName) {
  try {
    const module = await import(`./modules/${moduleName}.js`);
    module.run();
  } catch (error) {
    console.error('Error loading module:', error);
  }
}

loadModule('exampleModule'); // 런타임에 exampleModule.js를 로드&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESM에서는 동적 모듈 호출을 위해 &lt;code&gt;import()&lt;/code&gt; 함수를 사용하며, &lt;code&gt;Promise&lt;/code&gt;를 반환하므로 &lt;code&gt;await&lt;/code&gt; 키워드를 사용하여 비동기적으로 모듈을 호출할 수 있다. 하지만, 모듈이 존재하지 않거나, 잘못된 경로를 사용하게 된다면 런타임 과정에서 에러가 발생할 수 있는 문제점이 있어 주의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TypeScript&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript에서는 Dynamic Import가 런타임에서 동작하지만, 컴파일 타임에 타입을 알 수 없기 때문에 다음과 같은 이슈가 발생한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입 추론 이슈: &lt;code&gt;import()&lt;/code&gt;로 호출한 모듈의 타입을 TypeScript가 추론하지 못한다.&lt;/li&gt;
&lt;li&gt;타입 선언 필요: Dynamic Module Import된 모듈은 &lt;code&gt;any&lt;/code&gt; 타입으로 처리되므로, 명시적인 타입 선언이 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이, Dynamic Module Import의 문제점은 JavaScript에서는 크게 문제가 되지 않지만, TypeScript 에서는 일부 문제점이 발생할 수 있다. 예를 들어, 파일 시스템을 사용하는 &lt;code&gt;fs&lt;/code&gt; 모듈을 호출한다고 가정해 보자.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;async function loadFsModule() {
  const fs = await import('fs');
  console.log(fs.readFileSync); // 타입 추론 불가, any로 처리
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 모듈을 동적으로 호출하더라도, TypeScript에서는 타입 추론이 불가능하므로, 명시적으로 타입을 선언해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@nestjs/microservices&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, &lt;code&gt;@nestjs/microservices&lt;/code&gt; 라이브러리는 어떤 방식으로 여러 의존성을 동적으로 호출하고 관리하고 있을까? 이번에는 &lt;code&gt;@nestjs/microservices&lt;/code&gt;의 Kafka Client의 소스코드를 확인하며 Dynamic Module Import에 대해 확인해 보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dynamic Module Import를 위한 Package.json&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js에서는 의존성을 관리하기 위해서 package.json 파일에서 &lt;code&gt;dependencies&lt;/code&gt;, &lt;code&gt;devDependencies&lt;/code&gt;, &lt;code&gt;peerDependencies&lt;/code&gt;를 사용한다. 여기서, &lt;code&gt;peerDependencies&lt;/code&gt;는 일반적으로 잘 사용하진 않지만, 필요한 의존성을 명시적으로 설치하도록 유도하는 데 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한번, &lt;code&gt;@nestjs/microservices&lt;/code&gt; 라이브러리의 &lt;a href=&quot;https://github.com/nestjs/nest/blob/master/packages/microservices/package.json&quot;&gt;package.json&lt;/a&gt;을 확인한다면 아래와 같이 작성된 것을 알 수 있다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;@nestjs/microservices&quot;,
  &quot;version&quot;: &quot;11.0.1&quot;,
  &quot;dependencies&quot;: {
    &quot;iterare&quot;: &quot;1.2.1&quot;,
    &quot;tslib&quot;: &quot;2.8.1&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@nestjs/common&quot;: &quot;11.0.1&quot;,
    &quot;@nestjs/core&quot;: &quot;11.0.1&quot;
  },
  &quot;peerDependencies&quot;: {
    &quot;@grpc/grpc-js&quot;: &quot;*&quot;,
    &quot;@nestjs/common&quot;: &quot;^11.0.0&quot;,
    &quot;@nestjs/core&quot;: &quot;^11.0.0&quot;,
    &quot;@nestjs/websockets&quot;: &quot;^11.0.0&quot;,
    &quot;amqp-connection-manager&quot;: &quot;*&quot;,
    &quot;amqplib&quot;: &quot;*&quot;,
    &quot;cache-manager&quot;: &quot;*&quot;,
    &quot;ioredis&quot;: &quot;*&quot;,
    &quot;kafkajs&quot;: &quot;*&quot;,
    &quot;mqtt&quot;: &quot;*&quot;,
    &quot;nats&quot;: &quot;*&quot;,
    &quot;reflect-metadata&quot;: &quot;^0.1.12 || ^0.2.0&quot;,
    &quot;rxjs&quot;: &quot;^7.1.0&quot;
  },
}  &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이, &lt;code&gt;@nestjs/microservices&lt;/code&gt; 라이브러리는 큰 의존성을 가지고 있지 않다. 다만, &lt;code&gt;peerDependencies&lt;/code&gt; 항목에는 필요한 여러 의존성의 버전이 상세하게 등록된 것을 알 수 있다. 해당 라이브러리의 package.json만 확인하더라도, 실제 구동하는데 필요한 의존성보다는 연관된 의존성이 많은 것을 알 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 내용을 분석하게 된다면, 해당 라이브러리는 필요할 때 원하는 의존성을 설치하도록 유도하고 있다는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 일반적으로 TypeScript 환경에서 &lt;code&gt;import&lt;/code&gt;를 사용하게 되면, 해당 모듈이 필요한 시점에 미리 설치되어 있어야 한다. 그렇다면, &lt;code&gt;@nestjs/microservices&lt;/code&gt; 라이브러리는 어떻게 필요한 의존성을 동적으로 호출하고 사용하는 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Nest.js Dynamic Module Import&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nest.js는 Dynamic Module Import를 위해 공통 모듈에서 &lt;a href=&quot;https://github.com/nestjs/nest/blob/master/packages/common/utils/load-package.util.ts#L8&quot;&gt;loadPackage&lt;/a&gt; 함수를 사용하고 있다. 해당 함수는 어떻게 Dynamic Module Import를 구현하고 있는지 확인해 보자.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;import { Logger } from '../services/logger.service';

export function loadPackage(
  packageName: string,
  context: string,
  loaderFn?: Function,
) {
  try {
    return loaderFn ? loaderFn() : require(packageName);
  } catch (e) {
    logger.error(MISSING_REQUIRED_DEPENDENCY(packageName, context));
    Logger.flush();
    process.exit(1);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 함수는 ES6 Module System을 사용하고 있으나, CommonJS의 &lt;code&gt;require&lt;/code&gt; 함수를 사용하고 있다. 어떻게 이런 방식을 사용할 수 있을 것일까? 여기서는 TypeScript의 특성으로 인해 나타난 문제점인데, TypeScript는 특별한 설정 없이 컴파일한다면 CommonJS로 변환될 것이다. 만약, 이전에 확인한 것과 같이 &lt;code&gt;import()&lt;/code&gt;형식으로 Dynamic Module Import를 구성하게 되었다면, JavaScript 파일에서 지원하지 않는 오류가 발생했다는 이슈가 발생하게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 빌드된 JavaScript 파일은 어떻게 구성되었을까?&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;&quot;use strict&quot;;
Object.defineProperty(exports, &quot;__esModule&quot;, { value: true });
exports.loadPackage = void 0;
const logger_service_1 = require(&quot;../services/logger.service&quot;);
const MISSING_REQUIRED_DEPENDENCY = (name, reason) =&amp;gt; `The &quot;${name}&quot; package is missing. Please, make sure to install this library ($ npm install ${name}) to take advantage of ${reason}.`;
const logger = new logger_service_1.Logger('PackageLoader');
function loadPackage(packageName, context, loaderFn) {
    try {
        return loaderFn ? loaderFn() : require(packageName);
    }
    catch (e) {
        logger.error(MISSING_REQUIRED_DEPENDENCY(packageName, context));
        logger_service_1.Logger.flush();
        process.exit(1);
    }
}
exports.loadPackage = loadPackage;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각한 것과 동일하게, CommonJS Module System으로 빌드되며, &lt;code&gt;require&lt;/code&gt; 함수를 사용하고 있는 것을 확인할 수 있다. 이렇게 구성하게 된다면 런타임 과정에서 이슈가 발생하지 않고 정상적으로 사용할 수 있게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Kafka Client&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js에서 Dynamic Module Import를 구성하는 것을 모두 확인해 보았다. 그렇다면, &lt;code&gt;@nestjs/microservices&lt;/code&gt; 라이브러리에서 Kafka Client를 사용할 때, 어떻게 동적으로 의존성을 호출하고 사용하는지 확인해 보자.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// client-kafka.ts

export class ClientKafka
  extends ClientProxy&amp;lt;never, KafkaStatus&amp;gt;
  implements ClientKafkaProxy
{
  ...

  constructor(protected readonly options: Required&amp;lt;KafkaOptions&amp;gt;['options']) {
    super();
    ...
    kafkaPackage = loadPackage('kafkajs', ClientKafka.name, () =&amp;gt;
      require('kafkajs'),
    );
  }

  public createClient&amp;lt;T = any&amp;gt;(): T {
    const kafkaConfig: KafkaConfig = Object.assign(
      { logCreator: KafkaLogger.bind(null, this.logger) },
      this.options.client,
      { brokers: this.brokers, clientId: this.clientId },
    );

    return new kafkaPackage.Kafka(kafkaConfig);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/nestjs/nest/blob/master/packages/microservices/client/client-kafka.ts&quot;&gt;client-kafka.ts&lt;/a&gt; 파일에서 확인하게 된다면, ClientKafka 클래스의 생성자 부분에서 kafkajs 파일을 Dynamic Module Import 방식으로 호출하고 있는 것을 확인할 수 있다. 해당 라이브러리를 호출한 후, &lt;code&gt;createClient()&lt;/code&gt; 함수에서 kafkajs의 Kafka 클래스를 반환하여 일반적인 kafkajs를 사용하는 것과 같이 구성하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, 하나 의문점이 드는 부분이 있는데, 분명히 Dynamic Module Import를 이용하여 모듈을 호출하게 된다면, &lt;code&gt;any&lt;/code&gt; 타입으로 타입을 반환받는 것으로 알고 있는데, 여기서는 어떻게 명확하게 타입을 선언할 수 있는 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 이런 상황이 발생하게 되었을 때는 1. 동일한 타입을 선언하거나, 2. 타입을 공유하는 별도의 타입 라이브러리를 구성하는 방식을 사용할텐데, &lt;code&gt;@nestjs/microservices&lt;/code&gt;는 어떤 방식을 사용하고 있는지 확인해 보자.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// kafka.interface.ts

/**
 * Do NOT add NestJS logic to this interface.  It is meant to ONLY represent the types for the kafkajs package.
 *
 * @see https://github.com/tulios/kafkajs/blob/master/types/index.d.ts
 */

export declare class Kafka {
  constructor(config: KafkaConfig);
  producer(config?: ProducerConfig): Producer;
  consumer(config: ConsumerConfig): Consumer;
  admin(config?: AdminConfig): Admin;
  logger(): Logger;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최상단에 작성된 주석과 같이, &lt;a href=&quot;https://github.com/tulios/kafkajs/blob/master/types/index.d.ts&quot;&gt;kafkajs의 타입&lt;/a&gt;을 그대로 가져와서 사용하고 있음을 확인할 수 있다. 이런 방식은 kafkajs의 타입이 변경되는 상황이 발생하게 되었을 때, 동일한 형식으로 수정해야 하는 이슈가 발생할 수 있다. 하지만, kafkajs는 현재 2년가량 유지보수가 진행되고 있지 않고, &lt;a href=&quot;https://github.com/tulios/kafkajs/issues/1603&quot;&gt;메인테이너 또한 새로운 담당자를 구하고 있으므로&lt;/a&gt; 타입 변경이 크게 발생하지 않을 것으로 보이므로 이런 방향성이 더욱 알맞는 방향일 수도 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 이렇게 해서, &lt;code&gt;@nestjs/microservices&lt;/code&gt; 라이브러리에서 원하는 의존성만 설치한 후, 동적으로 모듈을 호출하여 사용하는 방법을 확인해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실, 이번 게시글에서는 Kafka Client의 세부 구현체에 대해 분석하고, 각 라이브러리들이 어떤 방식으로 Kafka와 소통하고 있는지 작성하려 했었다. 하지만, 해당 분석을 하던 와중 &lt;code&gt;@nestjs/microservices&lt;/code&gt; 라이브러리를 알게 되었고, 어떤 방식으로 kafkajs와 같은 여러 의존성을 어떻게 통합해서 구성하고 있는지 궁금증이 생기게 되어 글을 작성하게 되었다.&lt;/p&gt;</description>
      <category>분석과 탐구</category>
      <category>@nestjs/microservices</category>
      <category>Kafka</category>
      <category>kafkajs</category>
      <category>Nest.js</category>
      <author>커스텀 리</author>
      <guid isPermaLink="true">https://custom-li.tistory.com/222</guid>
      <comments>https://custom-li.tistory.com/222#entry222comment</comments>
      <pubDate>Sun, 19 Jan 2025 23:54:46 +0900</pubDate>
    </item>
    <item>
      <title>2024 &amp;amp; 2025 회고와 방향성</title>
      <link>https://custom-li.tistory.com/221</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2024.png&quot; data-origin-width=&quot;128&quot; data-origin-height=&quot;128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4xmsz/btsLCuhXYYt/lUKZYDrPUs019VCoJV8QV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4xmsz/btsLCuhXYYt/lUKZYDrPUs019VCoJV8QV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4xmsz/btsLCuhXYYt/lUKZYDrPUs019VCoJV8QV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4xmsz%2FbtsLCuhXYYt%2FlUKZYDrPUs019VCoJV8QV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;128&quot; height=&quot;128&quot; data-filename=&quot;2024.png&quot; data-origin-width=&quot;128&quot; data-origin-height=&quot;128&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년은 많은 성장을 한 해였다. 기술적인 성장과 &amp;ldquo;나&amp;rdquo;라는 사람을 좀 더 견고하게 만들어 준 시간이었는데, 1년간 느꼈던 경험과 나의 감정을 회고하고 새로운 2025년의 방향성을 수립하여 좀 더 후회 없는 시간을 보내고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2024&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Kubernetes와 만남&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;a217cd4fc7fa7b835b4267c5ab02470e.jpeg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AWGfD/btsLDIzwDxR/tCiRGkV3DyOR3UUycHd6f0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AWGfD/btsLDIzwDxR/tCiRGkV3DyOR3UUycHd6f0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AWGfD/btsLDIzwDxR/tCiRGkV3DyOR3UUycHd6f0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAWGfD%2FbtsLDIzwDxR%2FtCiRGkV3DyOR3UUycHd6f0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;270&quot; height=&quot;152&quot; data-filename=&quot;a217cd4fc7fa7b835b4267c5ab02470e.jpeg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년은 Kubernetes와의 첫 만남으로 시작하게 되었다. 막연히 공부하고 싶다고 생각하고 있어 개인적으로 공부를 하고 있긴 했었으나 실제 업무에서 필요성이 발생하게 되었고, 본격적으로 Kubernetes를 도입하는 상황에 맞닥뜨리게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하고 싶은 일과 실제 담당하게 되는 업무 간의 괴리는 언제든지 발생한다고 생각한다. 하지만, 이번에는 그 두 가지가 일치하는 상황이었다. 좋은 기회가 나에게 다가온 것 같아 개인적인 시간과 모든 일정을 이 작업을 위주로 소요하게 되었는데, 이는 정말 후회 없는 선택이었던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업 중에는 Kubernetes의 기능뿐만 아니라 ArgoCD, Argo Rollouts과 같은 서드 파티와 GitOps Bridge와 같은 인프라 아키텍처 관리 패턴에 대해 많은 공부를 하게 되었고, 이를 실제로 적용해 보면서 문제점을 하나씩 해결하는 경험을 했었다. 새로운 것을 적용할 때마다 변경되는 데이터 지표와 업무 효율화 과정을 눈앞에서 겪으면서 엄청난 성취감을 느꼈었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 모든 인프라를 구축하고 마이그레이션을 완료하여 운영까지 진행해 보았다. 이 시간을 통해 인프라에 대한 시야가 넓어지게 된 것을 느낄 수 있었고, 급박한 상황이라 하더라도 마음만 먹으면 못 할 것이 없다는 것을 다시 한번 상기하게 되었던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ELK, OpenSearch와 만남&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;OpenSearch.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZtvUd/btsLDdfF807/UvCyigM0VhBlxERfHpK3Gk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZtvUd/btsLDdfF807/UvCyigM0VhBlxERfHpK3Gk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZtvUd/btsLDdfF807/UvCyigM0VhBlxERfHpK3Gk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZtvUd%2FbtsLDdfF807%2FUvCyigM0VhBlxERfHpK3Gk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;276&quot; height=&quot;155&quot; data-filename=&quot;OpenSearch.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DevOps 업무를 담당하게 되면서 ELK와 OpenSearch를 자연스럽게 만나게 되었다. 언젠가 해보고 싶다는 생각을 가지고 있었으나 우연한 기회로 모든 권한을 받아오게 되면서 작업을 시작했던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ELK와 같은 데이터 수집 파이프라인을 전체적으로 관리하고, OpenSearch를 새롭게 구축하고 이 모든 것을 자동화할 수 있는 프로세스를 구축하는 과정을 진행했었는데, 기존의 레거시를 이해하고 새롭게 재구성하면서 &amp;ldquo;왜 과거에는 이렇게 구성했을까?&amp;rdquo;와 같은 의문을 하나씩 해소하곤 했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업을 진행하면서, 최소한의 비용으로 여러 환경을 구축하고 몇 년간의 데이터를 마이그레이션을 하는 등 기존에는 겪어보지 못한 작업을 진행하면서 엄청난 만족감을 느꼈던 것 같다. 그리고, 이 시간 동안 여러 작업을 병행하며 진행하다 보니 의도치 않게 스케줄링 능력이 성장하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;손가락 부상&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2024-10-16T10_17_28.967.jpg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Isztx/btsLEbH3xO1/qcR8p8jIi8uuLKYG5Mtv8k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Isztx/btsLEbH3xO1/qcR8p8jIi8uuLKYG5Mtv8k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Isztx/btsLEbH3xO1/qcR8p8jIi8uuLKYG5Mtv8k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIsztx%2FbtsLEbH3xO1%2FqcR8p8jIi8uuLKYG5Mtv8k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;313&quot; height=&quot;313&quot; data-filename=&quot;2024-10-16T10_17_28.967.jpg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 1년 넘게 클라이밍을 주 2~3회가량 하고 있었는데, 어느 날 갑자기 손가락 부상을 당하게 되면서 클라이밍이라는 운동을 마무리하게 되었다. 매일 의자에 앉아 일을 하는 개발자의 입장에서 어느 정도 운동을 통해 스트레스를 해소하거나 별도의 성취감을 느끼곤 하는데, 긴 시간동안 즐겨 했던 운동을 부상으로 인해 하지 못하다 보니 개인적으로 우울함에 빠졌던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 다른 운동을 하는 것으로 대체하였으나, 2024년에서 가장 안타까웠던 경험은 이 사건이 아닐까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2025 방향성&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025.png&quot; data-origin-width=&quot;128&quot; data-origin-height=&quot;128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfRbuV/btsLEdskTTl/IlQv4hv9vv6otmXltylijk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfRbuV/btsLEdskTTl/IlQv4hv9vv6otmXltylijk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfRbuV/btsLEdskTTl/IlQv4hv9vv6otmXltylijk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfRbuV%2FbtsLEdskTTl%2FIlQv4hv9vv6otmXltylijk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;128&quot; height=&quot;128&quot; data-filename=&quot;2025.png&quot; data-origin-width=&quot;128&quot; data-origin-height=&quot;128&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년에는 새로운 기술을 배우는 것보단 &amp;ldquo;나&amp;rdquo;라는 사람을 좀 더 견고하게 만들고 알고 있는 지식을 좀 더 깊게 이해하는 시간을 가질 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Kafka와 친해지기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년에 MSK를 도입하면서 Kafka를 사용해 보았는데, 단순히 Pub/Sub 수준의 기능을 사용해 본 것이 아쉬움으로 남아있다. 이번에는 Kafka와 좀 더 친해지는 시간을 가지고, 더불어 &lt;a href=&quot;https://kafka.js.org/&quot;&gt;KafkaJS&lt;/a&gt;나 &lt;a href=&quot;https://github.com/confluentinc/confluent-kafka-javascript&quot;&gt;confluent-kafka-javascript&lt;/a&gt;와 같은 Kafka 클라이언트 라이브러리를 분석할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CKA, CKAD, CKS 취득하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes를 이용해 인프라를 구축하고 운영까지 해보았지만, 아쉬운 점은 나 자신의 이해도를 객관적으로 인지하지 못하는 점이다. CNCF 재단에서 운영하는 자격증을 취득하면서 알고 있는 지식을 다시 한번 정리하는 시간을 가질 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메타인지 향상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년은 회사 업무와 기술적인 성장을 위주로 시간을 보냈었다. 그로 인해, &amp;ldquo;나&amp;rdquo;에 대한 고민을 많이 하지 않은 시간을 오랫동안 보내게 되었는데, 이 부분이 다소 아쉬움으로 남아 있다. 2025년은 좀 더 &amp;ldquo;나&amp;rdquo; 자신과 가까워질 수 있도록 메타인지를 향상하기 위한 시간을 보낼 예정이다.&lt;/p&gt;</description>
      <category>회고</category>
      <category>2024년 회고</category>
      <category>confluent-kafka-javascript</category>
      <category>kafkajs</category>
      <author>커스텀 리</author>
      <guid isPermaLink="true">https://custom-li.tistory.com/221</guid>
      <comments>https://custom-li.tistory.com/221#entry221comment</comments>
      <pubDate>Sat, 4 Jan 2025 22:38:20 +0900</pubDate>
    </item>
    <item>
      <title>Amazon MSK 커넥션 끊김 이슈 디버깅 (feat. Nest.js, IRSA)</title>
      <link>https://custom-li.tistory.com/220</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Amazon MSK.svg&quot; data-origin-width=&quot;150&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G8OO0/btsLbcniXu8/xvyNyzMVKDkOlg89W5uRZk/tfile.svg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G8OO0/btsLbcniXu8/xvyNyzMVKDkOlg89W5uRZk/tfile.svg&quot; data-alt=&quot;Amazon MSK&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G8OO0/btsLbcniXu8/xvyNyzMVKDkOlg89W5uRZk/tfile.svg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG8OO0%2FbtsLbcniXu8%2FxvyNyzMVKDkOlg89W5uRZk%2Ftfile.svg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;133&quot; height=&quot;133&quot; data-filename=&quot;Amazon MSK.svg&quot; data-origin-width=&quot;150&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Amazon MSK&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사내 아키텍처에서는 어플리케이션의 역할을 명확히 구분하기 위해 MSA(MicroService Architecture) 를 도입하고 있다. 여기서, RabbitMQ와 Kafka를 활용해 EDA(Event Driven Architecture)를 구성하고 있으며, 각각의 메시지 큐는 어플리케이션의 역할에 따라 선택하였다. 예를 들어, &lt;b&gt;RabbitMQ&lt;/b&gt;는 &lt;b&gt;순서 보장&lt;/b&gt;과 &lt;b&gt;Queue 단위로 이벤트를 처리하기 수월한 도메인&lt;/b&gt;과 &lt;b&gt;Kafka&lt;/b&gt;는 &lt;b&gt;확장성과 처리량이 중요한 도메인&lt;/b&gt;에 적용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 메시지 큐는 AmazonMQ-RabbitMQ와 Amazon MSK-Kafka를 사용하고 있는데, 운영 환경에 MSK를 사용하는 신규 어플리케이션을 배포한 이후, &lt;b&gt;갑작스럽게 MSK 커넥션이 종료되는 문제가 발생&lt;/b&gt;하게 되었다. 이에 따라 몇 시간 동안 장애 상황이 지속되었고, 어플리케이션 로직 이슈 및 MSK 인증 방식 등 여러 복합적인 요인으로 인해 발생한 것을 확인하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이슈를 해결하기 위해 장애 발생 당시와 이후의 회고 과정에서 정말 많은 시간을 소요하게 되었는데, 해당 이슈의 근본적인 원인을 해결하기 위해 고생한 &lt;u&gt;&lt;b&gt;나를 위해&lt;/b&gt;&lt;/u&gt; 겪었던 상황을 정리해 보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 아키텍처&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;MSK IRSA Issue-Architecture.drawio.png&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m7Tlp/btsLbRpvNZh/HVvNviX3l0V0MKol1NHXL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m7Tlp/btsLbRpvNZh/HVvNviX3l0V0MKol1NHXL1/img.png&quot; data-alt=&quot;Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m7Tlp/btsLbRpvNZh/HVvNviX3l0V0MKol1NHXL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm7Tlp%2FbtsLbRpvNZh%2FHVvNviX3l0V0MKol1NHXL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;468&quot; height=&quot;424&quot; data-filename=&quot;MSK IRSA Issue-Architecture.drawio.png&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;682&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 서버 어플리케이션은 Nest.js 프레임워크로 구성되어 있으며, Amazon MSK를 사용하여 EDA(Event Driven Architecture)를 구성하고 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Producer 서버&lt;/b&gt;는 Event를 MSK에 발행(Produce)하여 이벤트를 전달&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Consumer 서버&lt;/b&gt;는 MSK에 발행된 Topic을 소비(Consume)하여 비즈니스 로직을 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 서버들은 EKS 환경에 배포되어 있으며, MSA(MicroService Architecture) 형태로 구성되어 있다. 각 서버가 MSK와 연결 및 인증을 수행하기 위해서 &lt;b&gt;IRSA(IAM Role for Service Account)&lt;/b&gt;를 이용하여 일시적으로 권한을 취득하는 방식으로 인증을 수행하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  이슈 디버그&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이슈는 운영 환경에 어플리케이션을 배포한 이후 발생하게 되었다. 배포 당시 개발망과 운영망은 서로 다른 인증 방식을 사용하고 있었는데, &lt;b&gt;개발망&lt;/b&gt;에서는 &lt;b&gt;Worker Node에 Role을 Attach &lt;/b&gt;하여 인증을 수행하였지만, &lt;b&gt;운영망&lt;/b&gt;에서는 &lt;b&gt;IRSA 기반 인증&lt;/b&gt; 방식으로 개선된 상태였다. 이는, 여러 작업이 함께 이루어지던 중 환경 통합이 되지 않았던 상황이었고, 운영망과 개발망 간의 마이그레이션이 완료되지 않던 상황에서 신규 어플리케이션이 배포되면서 문제가 발생하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;MSK IRSA Issue-개발망_운영망.drawio.png&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcaxyF/btsLbTVa0YZ/RgTQqIjXJ4CwXPMMJdDmM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcaxyF/btsLbTVa0YZ/RgTQqIjXJ4CwXPMMJdDmM0/img.png&quot; data-alt=&quot;개발망 / 운영망&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcaxyF/btsLbTVa0YZ/RgTQqIjXJ4CwXPMMJdDmM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcaxyF%2FbtsLbTVa0YZ%2FRgTQqIjXJ4CwXPMMJdDmM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;707&quot; height=&quot;378&quot; data-filename=&quot;MSK IRSA Issue-개발망_운영망.drawio.png&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;552&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;개발망 / 운영망&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, 신규 어플리케이션은 &lt;b&gt;개발 환경&lt;/b&gt;에서 &lt;a href=&quot;https://grafana.com/blog/2024/01/30/stress-testing/&quot;&gt;스트레스 테스트&lt;/a&gt; 와 &lt;a href=&quot;https://grafana.com/blog/2024/01/30/average-load-testing/&quot;&gt;평균 부하 테스트&lt;/a&gt;를 진행했을 때는 MSK 커넥션이 안정적으로 유지되면서 정상적인 동작을 수행하였으나, &lt;b&gt;운영 환경&lt;/b&gt;에서는 MSK 커넥션이 갑작스럽게 종료되는 문제가 발생하게 되었다. Terraform으로 인프라를 구성하여 Security Group과 IAM Role과 같은 보안 및 권한 부분에서는 변경 점이 없었으나, IRSA를 도입한 것만이 유일한 차이점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜 이런 이슈가 발생하게 되었는지 장애 발생 당시를 재구성해 보면서 해결 방법을 도출하는 의식의 흐름을 정리해 보도록 하자.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ 신규 어플리케이션을 운영 환경에 배포하였다.&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2024-12-05_오후_6_57_16-2.jpg&quot; data-origin-width=&quot;2275&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4Km7J/btsLaZV8H9f/HWttgxfpie3PFqHCIxhi8k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4Km7J/btsLaZV8H9f/HWttgxfpie3PFqHCIxhi8k/img.jpg&quot; data-alt=&quot;Consumer 서버의 장애 발생 상황 재구성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4Km7J/btsLaZV8H9f/HWttgxfpie3PFqHCIxhi8k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4Km7J%2FbtsLaZV8H9f%2FHWttgxfpie3PFqHCIxhi8k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;808&quot; height=&quot;125&quot; data-filename=&quot;스크린샷_2024-12-05_오후_6_57_16-2.jpg&quot; data-origin-width=&quot;2275&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Consumer 서버의 장애 발생 상황 재구성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSK를 사용하는 Producer와 Consumer의 역할을 담당하는 신규 어플리케이션을 운영 환경에 배포하였다. 배포 후 내부 테스트에서 모든 어플리케이션이 정상적으로 동작하는 것을 확인한 뒤 서버 모니터링을 종료하게 되었는데, &lt;b&gt;몇 시간이 지난 후&lt;/b&gt; 이벤트가 정상적으로 처리되지 않는 이슈가 발생하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이슈를 확인하기 위해 Producer와 Consumer 서버의 로그를 점검해 보았는데, &lt;b&gt;Producer 서버&lt;/b&gt;는 MSK와 정상적으로 연결되어 있었으나, &lt;b&gt;Consumer 서버&lt;/b&gt;에서는 &lt;code&gt;Cannot change principals during re-authentication&lt;/code&gt;과 같은 에러가 발생한 것을 확인하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, 엎친 데 덮친 격으로 Consumer 서버의 Retry 로직이 잘못 구성되어 있어, Consumer 서버는 종료되지도 않고 MSK를 사용할 수 없는 상태로 멈춰있었으며, 이벤트가 발행되더라도 이벤트를 처리하지 못하고 있는 상황이 발생하게 된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ MSK Retry 로직을 개선하여 Hotfix 버전을 배포하였다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이슈를 빠르게 해결하기 위해, Consumer 서버의 로직을 개선하게 되었는데, MSK와 연결이 종료되었을 경우 일정 횟수 내에서 재시도를 요청하도록 수정했으며, 재시도가 실패하면 프로세스가 종료되도록 로직을 추가하게 되었다. 여기서, 모든 어플리케이션들은 EKS 상에서 실행 중이므로 &lt;b&gt;서버가 종료되더라도 ReplicaSet이 자동으로 서버를 재실행하면서 Consumer 서버가 MSK와의 연결을 자동으로 복원&lt;/b&gt;할 수 있게 되어 이슈가 해결될 것으로 판단하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hotfix 버전 배포 후, Consumer 서버는 연결이 종료되더라도 자동으로 복구되면서 이벤트 처리가 정상적으로 이루어지는 것을 확인하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ 발생한 에러를 상세하게 분석해 보았다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hotfix 버전을 배포하여 Consumer 서버가 멈추며 이벤트를 처리하지 않는 상황은 해결하게 되었으나, 근본적으로 MSK와 연결이 끊기게 된 원인에 대해서는 아직 파악하지 못한 상태였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 문제를 분석하기 위해 에러가 발생한 서버의 로그를 확인해 보았는데, 서버가 실행된 지 1시간 후, &lt;code&gt;Cannot change principals during re-authentication&lt;/code&gt; 이라는 에러가 발생하며 MSK와 연결이 해제된 것을 확인하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;[SaslAuthenticator-OAUTHBEARER] SASL OAUTHBEARER authentication failed: Cannot change principals during re-authentication from IAM.arn:aws:sts::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:assumed-role/MskSaslIRSARole/aws-sdk-js-session-1733388958898: IAM.arn:aws:sts::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:assumed-role/MskSaslIRSARole/aws-sdk-js-session-1733392551401&quot;,&quot;broker&quot;:&quot;b-1.msksasl.8b7568.c3.kafka.&amp;lt;REGION&amp;gt;.amazonaws.com:9098&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러를 더 상세하게 분석해 본 결과, 문제의 원인은 '&lt;b&gt;최초 MSK 연결 시 인증한 세션과 갱신 후 세션 이름이 달라져 발생&lt;/b&gt;'한 것으로 확인되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜 최초 MSK를 인증한 세션과 갱신 후 세션 이름이 달라지는 걸까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS STS는 Assume Role 기반으로 세션을 발행하는데, &lt;b&gt;세션 이름에는 밀리초 단위의 타임스탬프가 추가&lt;/b&gt;되어 구성하게 된다. 예를 들어, &lt;code&gt;aws-sdk-js-session-&amp;lt;MS_TIMESTAMP&amp;gt;&lt;/code&gt;와 같은 형식으로 발급된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, STS는 기본적으로 1시간의 세션 유지 시간을 가지는데, 서버가 최초 인증 후 1시간 동안은 정상적으로 동작했지만, 세션 갱신 시 타임스탬프가 포함된 새로운 이름의 세션이 발급되면서 &lt;b&gt;MSK 최초 인증 세션&lt;/b&gt;과 &lt;b&gt;갱신된 세션&lt;/b&gt;&amp;nbsp;간에서 &lt;b&gt;세션 이름 불일치&lt;/b&gt;로 인해 MSK 연결이 실패하게 되는 것으로 확인되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4️⃣ aws-sdk 세션 이름을 고정해 보았다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSK 최초 인증 시점과 세션 갱신 시점에서 이름이 달라지는 문제가 원인이라면, STS 갱신 시 세션 이름이 변경되지 않도록 설정하면 문제를 해결할 수 있다고 판단하였다. aws-sdk는 &lt;a href=&quot;https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-envvars.html#envvars-list&quot;&gt;내부적으로 세션 이름 고정 기능을 지원&lt;/a&gt; 하는데, &lt;code&gt;AWS_ROLE_SESSION_NAME&lt;/code&gt; 환경 변수를 서버에 추가하게 된다면 Assume Role 요청 시와 세션 갱신 시 동일한 세션 이름을 사용할 수 있는 것을 확인하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인한 것과 같이, Consumer 서버에 환경 변수를 적용해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-08 오후 11.03.27.png&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpRqVX/btsLco8fu6i/qnfD84OQstY6HMUjhnz4yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpRqVX/btsLco8fu6i/qnfD84OQstY6HMUjhnz4yk/img.png&quot; data-alt=&quot;AWS_ROLE_SESSION_NAME 환경 변수 주입&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpRqVX/btsLco8fu6i/qnfD84OQstY6HMUjhnz4yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpRqVX%2FbtsLco8fu6i%2FqnfD84OQstY6HMUjhnz4yk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;791&quot; height=&quot;167&quot; data-filename=&quot;스크린샷 2024-12-08 오후 11.03.27.png&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;167&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWS_ROLE_SESSION_NAME 환경 변수 주입&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경 변수를 추가한 후, Consumer 서버를 실행하여 테스트를 진행해 보았는데, 서버 실행 1시간 후 세션이 갱신되었음에도 문제가 발생하지 않았으며, 서버를 1일 이상 실행한 결과 이전과 같은 이슈는 더 이상 나타나지 않게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2024-12-05_오후_8_40_56.jpg&quot; data-origin-width=&quot;2274&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jphal/btsLbnCqXJF/P4kXX9mjrk6DgltQ1Bnm8K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jphal/btsLbnCqXJF/P4kXX9mjrk6DgltQ1Bnm8K/img.jpg&quot; data-alt=&quot;세션 갱신 시점인 1시간이 지난 후에도 정상적으로 동작하는 Consumer 서버&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jphal/btsLbnCqXJF/P4kXX9mjrk6DgltQ1Bnm8K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJphal%2FbtsLbnCqXJF%2FP4kXX9mjrk6DgltQ1Bnm8K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;783&quot; height=&quot;213&quot; data-filename=&quot;스크린샷_2024-12-05_오후_8_40_56.jpg&quot; data-origin-width=&quot;2274&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;세션 갱신 시점인 1시간이 지난 후에도 정상적으로 동작하는 Consumer 서버&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이로써, &lt;/b&gt;&lt;code&gt;AWS_ROLE_SESSION_NAME&lt;/code&gt;&lt;b&gt; 환경 변수를 사용해 세션 이름을 고정하는 방식으로 MSK 커넥션 문제가 완전히 해결되게 되었다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  정말로 MSK 커넥션 문제는 해결된 것일까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 의사 결정으로 인해, &lt;code&gt;AWS_ROLE_SESSION_NAME&lt;/code&gt; 환경 변수를 통해 MSK를 사용하는 모든 서버에서 세션 이름을 고정하여 문제를 해결하고 있는 상황이다. 하지만, 이런 방법은 단기적인 해결 방법일 뿐 근본적인 문제를 해결한 방법은 아니라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;나는 이번 이슈를 해결하기 위해서는 2가지의 방안이 필요하다고 생각했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째로, &lt;b&gt;Assume Role의 세션 이름을 라이브러리 설정에서 지정할 수 있도록 개선이 필요하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 이슈의 해결 방안의 가장 큰 문제는 &lt;b&gt;어플리케이션의 문제를 인프라 단에서 해결하는 것이&lt;/b&gt;다. 만약, 인프라 담당자가 변경되거나, 실수로 환경변수를 개발자가 삭제하는 상황을 가정해 보자. 그렇게 된다면, 서버의 어플리케이션은 정상적으로 배포되겠지만, 1시간이 지난 후 세션이 갱신되었을 때 인증 문제가 발생하게 될 것이다. 이는, 더욱 많은 피해와 이슈를 분석하는 시간이 소요되게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 문제가 발생하지 않기 위해, 인프라가 아닌 라이브러리 내의 설정을 통해 세션 이름을 지정할 수 있도록 개선이 필요하다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째로, &lt;b&gt;라이브러리 내에서 세션 갱신 시 이름이 변경되더라도 문제가 발생하지 않도록 개선이 필요하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 이슈의 가장 근본적인 원인은 &lt;b&gt;최초 인증 시점과 세션 갱신 시점에서 세션 이름이 달라져 인증 문제가 발생&lt;/b&gt;한 것이다. 이 문제를 해결하기 위해, 가장 근본적인 문제인 세션 갱신 시 이름이 변경되는 상황이 발생하더라도 인증이 성공할 수 있도록 개선하는 것이다. 이 방법은 보안 측면에서는 좋지 않은 방법이라고 생각하는데, 별도의 라이브러리 내의 설정을 통해 세션 이름이 다르더라도, 인증이 성공할 수 있도록 설정하는 방법이 추가되면 특정 환경에서 유용하게 사용할 수 있지 않을까 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  이슈 제출&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이슈에 대해 나와 동일한 문제를 겪은 사람들은 많았지만, 명확한 해결 방안을 제시한 사례는 없는 것을 확인하였고, 이를 오픈소스 이슈로 제출하기로 마음먹었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 MSK에서 &lt;b&gt;SASL_OAUTHBEARER&lt;/b&gt; 메커니즘을 사용해 인증하려면 &lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/msk/latest/developerguide/configure-clients-for-iam-access-control.html#configure-clients-for-iam-access-control-sasl-oauthbearer&quot;&gt;AWS Document에서 가이드&lt;/a&gt; 하는 것과 같이 &lt;a href=&quot;https://github.com/aws/aws-msk-iam-sasl-signer-js&quot;&gt;aws-msk-iam-sasl-signer-js&lt;/a&gt; 라이브러리를 활용해야 한다. 이 라이브러리는 MSK와 IAM 기반 인증을 간소화하지만, 이번 세션 갱신 시 이름 불일치와 같은 상황에서는 이슈가 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해, 내가 생각한 해결 방법에 대한 설명과 함께 장애 상황을 재현할 수 있는 Nest.js Application, Terraform, Kubernetes 예제를 추가하여 이슈를 제출하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-12-08 오후 11.06.20.png&quot; data-origin-width=&quot;2210&quot; data-origin-height=&quot;1452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz1VnE/btsLaD6MWLC/6jHgKfjXLAEY7IcC77y210/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz1VnE/btsLaD6MWLC/6jHgKfjXLAEY7IcC77y210/img.png&quot; data-alt=&quot;aws-msk-iam-sasl-signer-js  Issue&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz1VnE/btsLaD6MWLC/6jHgKfjXLAEY7IcC77y210/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz1VnE%2FbtsLaD6MWLC%2F6jHgKfjXLAEY7IcC77y210%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;758&quot; height=&quot;498&quot; data-filename=&quot;스크린샷 2024-12-08 오후 11.06.20.png&quot; data-origin-width=&quot;2210&quot; data-origin-height=&quot;1452&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;aws-msk-iam-sasl-signer-js  Issue&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 해당 이슈를 재구성한 프로젝트가 궁금하다면, 아래의 링크를 참조하길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Nest.js&lt;/b&gt;: &lt;a href=&quot;https://github.com/archepro84/notes/tree/main/_opensource/aws-msk-iam-sasl-signer-js/msk-sasl-sts&quot;&gt;Repository Link&lt;/a&gt;&lt;br /&gt;&lt;b&gt;Terraform&lt;/b&gt;: &lt;a href=&quot;https://github.com/archepro84/notes/tree/main/_opensource/aws-msk-iam-sasl-signer-js/terraform&quot;&gt;Repository Link&lt;/a&gt;&lt;br /&gt;&lt;b&gt;Kubernetes Apps&lt;/b&gt;: &lt;a href=&quot;https://github.com/archepro84/notes/tree/main/_opensource/aws-msk-iam-sasl-signer-js/kubernetes&quot;&gt;Repository Link&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마지막으로&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이번 이슈의 근본적인 원인을 완전히 해결했는가?&lt;/b&gt; 라는 질문에는 답변하기 힘든 상황인 것 같다. MSK와 aws-sdk, SASL 인증 방식, 그리고 Assume Role에 대한 깊은 이해가 아직은 부족하다고 느끼고 있기 때문이다. 또한, 제안한 2가지의 방안 역시 인증 방식에 대한 이해도가 부족해 확신을 갖기 어려운 상태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거 Severless Freamwork에 컨트리뷰션하던 때와 같이 코드 레벨에서 문제를 수정했더라면, AWS의 세션 인증 방식을 더 깊이 이해하면서 학습을 진행했을 것이라는 아쉬움과 함께, 이번 오픈소스는 1년 이상 유지보수가 이루어지지 않고 있다 보니, 추후 문제가 해결될 가능성도 불투명한 상황이다. 그렇기 때문에 안타까움이 많이 남은 상황에서 마무리를 짓는 것이 아쉬울 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>분석과 탐구</category>
      <category>aws-msk-iam-sasl-signer-js</category>
      <category>cannot change principals during re-authentication</category>
      <category>eks</category>
      <category>IRSA</category>
      <category>MSK</category>
      <category>Nest.js</category>
      <category>oauthbearer</category>
      <category>sasl oauthbearer authentication failed</category>
      <author>커스텀 리</author>
      <guid isPermaLink="true">https://custom-li.tistory.com/220</guid>
      <comments>https://custom-li.tistory.com/220#entry220comment</comments>
      <pubDate>Sun, 8 Dec 2024 23:12:29 +0900</pubDate>
    </item>
    <item>
      <title>제로부터 시작하는 IRSA (feat.terraform)</title>
      <link>https://custom-li.tistory.com/219</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;header.png&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xnUVN/btsKDX4F7aQ/HihPTdS9DG2dMyJxxVybik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xnUVN/btsKDX4F7aQ/HihPTdS9DG2dMyJxxVybik/img.png&quot; data-alt=&quot;출처: https://blog.spikeseed.cloud/k8s-security-1/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xnUVN/btsKDX4F7aQ/HihPTdS9DG2dMyJxxVybik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxnUVN%2FbtsKDX4F7aQ%2FHihPTdS9DG2dMyJxxVybik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;435&quot; height=&quot;196&quot; data-filename=&quot;header.png&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://blog.spikeseed.cloud/k8s-security-1/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes를 클라우드 환경에서 구성할 때, 일반적으로 AWS EKS, Google GKE, Azure AKS와 같은 Cloud Service Provider(CSP)의 관리형 Kubernetes를 사용할 것이다. 우리는 이러한 서비스를 통해 Kubernetes Cluster의 Control Plane 및 Worker Node에 대한 고민 없이 관리를 자동화할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, Cluster를 업그레이드하거나, 네트워크 설정을 온프레미스의 환경에서 작업하게 된다면, 엄청나게 머리 아픈일이 아닐 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 환경에서 Kubernetes로 구성한 서비스들은 각 어플리케이션에 필요한 네트워크 설정, 권한 관리 필수적인 것이다. 특히, 권한 관리에서는 &lt;a href=&quot;https://www.cloudflare.com/ko-kr/learning/access-management/principle-of-least-privilege/&quot;&gt;&lt;b&gt;최소 권한 원칙&lt;/b&gt;&lt;/a&gt;(Zero Trust)을 지키는 것이 가장 중요한데.&amp;nbsp;예를 들어, 파일 업로드를 담당하는 서버가 S3 Bucket에 접근할 수 있도록 구성해야 한다면, 모든 어플리케이션에 권한을 부여하기 보다는 &lt;b&gt;필요한 어플리케이션에만 제한적으로 권한을 부여&lt;/b&gt;해야할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해, AWS에서는 &lt;b&gt;IRSA&lt;/b&gt; (AWS IAM Roles for Service Accounts) 기능을 제공하고 있다. IRSA를 사용한다면 &lt;b&gt;원하는 어플리케이션이 필요한 AWS 리소스에 접근할 수 있는 권&lt;/b&gt;&lt;b&gt;한을 할당&lt;/b&gt;할 수 있으며, EKS Cluster 내에서 어플리케이션 별로 필요한 &lt;b&gt;최소 권한만 부여&lt;/b&gt;할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 인프라 관리자의 입장에서도 어느 어플리케이션이 어떤 Resource에 접근했는지 모니터링할 수 있어 더욱 보안을 강화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 게시글에서는 IRSA와 관련된 개념을 하나씩 살펴보면서, Terraform을 통해 EKS와 IRSA를 생성하고, 어플리케이션을 구성하여 S3 Bucket에 접근할 수 있도록 하나의 작은 프로젝트를 시작해보도록 하자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  IRSA (AWS IAM Roles for Service Accounts)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Cross-Account-EKS-IRSA.drawio-1024x418.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JXw6h/btsKD2x0IwL/XPqZUA0ZqyeHWXJErc8dKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JXw6h/btsKD2x0IwL/XPqZUA0ZqyeHWXJErc8dKK/img.png&quot; data-alt=&quot;IRSA: https://www.qloudx.com/working-with-cross-account-aws-iam-roles-for-eks-service-accounts-irsa/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JXw6h/btsKD2x0IwL/XPqZUA0ZqyeHWXJErc8dKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJXw6h%2FbtsKD2x0IwL%2FXPqZUA0ZqyeHWXJErc8dKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;284&quot; data-filename=&quot;Cross-Account-EKS-IRSA.drawio-1024x418.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IRSA: https://www.qloudx.com/working-with-cross-account-aws-iam-roles-for-eks-service-accounts-irsa/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html&quot;&gt;IRSA&lt;/a&gt; (AWS IAM Roles for Service Accounts)&lt;/b&gt;는 AWS IAM Role을 Kubernetes의 Service Account와 연결하여 AWS STS(Security Token Service)를 통해 일시적인 권한을 발급받을 수 있게 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, EKS Cluster 내의 특정 Pod가 필요한 AWS Resource에 접근해야 한다면, IRSA를 통해 원하는 권한을 획득할 수 있게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  AWS IAM Role&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;RM1-0917-diagram-b.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;305&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HBcnd/btsKCLEaDkl/bdMT1QgudIRG24mKXxDkkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HBcnd/btsKCLEaDkl/bdMT1QgudIRG24mKXxDkkK/img.png&quot; data-alt=&quot;IAM Role: https://blog.nviso.eu/tag/cloud-policies/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HBcnd/btsKCLEaDkl/bdMT1QgudIRG24mKXxDkkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHBcnd%2FbtsKCLEaDkl%2FbdMT1QgudIRG24mKXxDkkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;305&quot; data-filename=&quot;RM1-0917-diagram-b.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;305&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IAM Role: https://blog.nviso.eu/tag/cloud-policies/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AWS IAM Role(역할)&lt;/b&gt;은 특정 AWS Resource의 작업을 수행할 수 있는 Policy(정책)을 지정하여 권한을 할당하는 서비스이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AWS IAM Role&lt;/b&gt;은 일반 사용자나 어플리케이션에 직접 연결할 수도 있지만, 필요 시 특정 리소스에 임시로 할당하여 권한을 부여할 수도 있다. 예를 들어, EC2 인스턴스가 S3 Bucket에 접근할 수 있도록 권한을 부여하려면 필요한 정책을 정의하고 &lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html&quot;&gt;EC2 인스턴스에 IAM Role을 연결&lt;/a&gt;하여 사용하거나, EC2 인스턴스가 필요 시 원하는 권한을 요청(Assume Role)하여 일시적인 권한을 획득하는 방식으로 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  AWS STS&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cloudops_11192_1.png&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEUucA/btsKCMXmJbq/D2KVVSFJnvw5Sz1whMvCkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEUucA/btsKCMXmJbq/D2KVVSFJnvw5Sz1whMvCkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEUucA/btsKCMXmJbq/D2KVVSFJnvw5Sz1whMvCkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEUucA%2FbtsKCMXmJbq%2FD2KVVSFJnvw5Sz1whMvCkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;283&quot; data-filename=&quot;cloudops_11192_1.png&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/id_credentials_temp.html&quot;&gt;AWS STS&lt;/a&gt;(Security Token Service)&lt;/b&gt;는 특정 AWS 리소스를 사용할 수 있는 '임시 보안 자격 증명'을 생성하는 서비스이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스의 이름과 같이 생성된 '임시 자격 증명'은 단기적으로만 사용할 수 있으며, 자격 증명이 만료 되면 그 어떠한 종류의 권한 요청도 실패하도록 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IAM User 또는 AWS Resource는 AWS STS를 통해 '임시 자격 증명'을 요청할 수 있으며, 해당 자격 증명에 특정 Role(역할)에 설정된 권한을 부여받아 사용할 수 있게 된다. 이를 통해 '단기적으로 사용할 수 있는 권한을 소유한 임시 자격 증명'을 생성할 수 있게 되며, 이 과정 자체를 Assume Role 이라고 부르게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;☁ Assume Role&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1_LlY90RbzoGa6B-MzmChJUA.webp&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7YJ7C/btsKCVfNq8Q/KqB6HfAOmIuDFYR6u4WPp1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7YJ7C/btsKCVfNq8Q/KqB6HfAOmIuDFYR6u4WPp1/img.webp&quot; data-alt=&quot;Assume Role Image:&amp;amp;nbsp; https://howtodoitincloud.medium.com/implementing-assume-role-47fc9383d5bc&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7YJ7C/btsKCVfNq8Q/KqB6HfAOmIuDFYR6u4WPp1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7YJ7C%2FbtsKCVfNq8Q%2FKqB6HfAOmIuDFYR6u4WPp1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;301&quot; data-filename=&quot;1_LlY90RbzoGa6B-MzmChJUA.webp&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Assume Role Image:&amp;nbsp; https://howtodoitincloud.medium.com/implementing-assume-role-47fc9383d5bc&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Assume Role&lt;/b&gt;은 특정 Role(역할)을 일시적으로 부여받아 AWS 리소스에 접근할 수 있도록 도와준다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assume Role은 IAM User에 영구적인 Role을 부여하는 것과 달리, Assume Role은 일시적인 권한을 부여하여 지정된 시간 이후에는 해당 권한이 제거되므로 보안을 강화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, EC2 인스턴스가 S3 Bucket에 접근하기 위해 Assume Role을 사용하면, EC2는 일시적으로만 S3 Bucket을 사용할 수 있게 된다. 이는 IAM User뿐만 아니라, 다양한 AWS 서비스에서도 사용 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Kubernetes Service Account&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Kubernetes&lt;/b&gt;의 &lt;a href=&quot;https://kubernetes.io/ko/docs/reference/access-authn-authz/service-accounts-admin/&quot;&gt;&lt;b&gt;Service Account&lt;/b&gt;&lt;/a&gt;는 Cluster 내 특정 Pod가 Kubernetes API Server(kube-apiserver)와 통신할 수 있는 권한을 부여하기 위해 사용한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service Account는 네임스페이스마다 생성되며, 특정 &lt;a href=&quot;https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/&quot;&gt;Pod에 권한을 부여&lt;/a&gt;하기 위해 동일한 네임스페이스 내에서 Service Account를 생성하여 연결할 수 있다. &lt;b&gt;IRSA는 이 Kubernetes Service Account와 AWS IAM Role을 연결하여 특정 Pod에 AWS Resourcce에 대한 접근 권한을 제공&lt;/b&gt;할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  제로부터 IRSA 시작하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 예시는 Terraform을 이용해 EKS와 IRSA를 구성하고, Application을 수동으로 배포하여 서비스가 IRSA를 이용해 권한을 획득하고 사용하는 것에 대해 확인해보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;해덩 예시와 관련된 모든 코드는 해당 Github Repository를 참조하길 바란다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Github Repository Terraform EKS: &lt;a href=&quot;https://github.com/archepro84/notes/tree/main/_Blog/%EA%B8%80%EB%98%9010%EA%B8%B0/2.IRSA/terraform/eks&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Github Repository Terraform IRSA: &lt;a href=&quot;https://github.com/archepro84/notes/tree/main/_Blog/%EA%B8%80%EB%98%9010%EA%B8%B0/2.IRSA/terraform/irsa&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ AWS Resource 구성하기&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금부터 구성하는 모든 인프라는 서울 리전(ap-northeast-2)를 기준으로 &lt;b&gt;$135.05&lt;/b&gt; 비용이 발생하므로 작업을 완료한다면 삭제하는 것을 꼭! 꼭! 잊지 말자.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;꼭!&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  EKS 생성하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EKS Cluster를 구성하기 위해 Terraform으로 AWS Resource를 한땀 한땀 작성할 수 있지만, 간단한 예시를 구성하기 위해 이미 구성된 Module을 이용하여 아주 손쉽게 생성할 예정이며, &lt;a href=&quot;https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/latest&quot;&gt;terraform-aws-modules/eks/aws&lt;/a&gt;를 이용해 VPC와 EKS를 함께 구성할 예정이다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;module &quot;eks&quot; {
  source  = &quot;terraform-aws-modules/eks/aws&quot;
  version = &quot;20.29.0&quot;

  cluster_name    = &quot;eks-s3-irsa&quot;
  cluster_version = &quot;1.31&quot;

  vpc_id = module.vpc.vpc_id

  ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2024-11-10_오전_2_56_32.jpg&quot; data-origin-width=&quot;1371&quot; data-origin-height=&quot;721&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nPv7O/btsKD1lHUK2/elFuJY8J7kxFwafwl4MrzK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nPv7O/btsKD1lHUK2/elFuJY8J7kxFwafwl4MrzK/img.jpg&quot; data-alt=&quot;els-s3-irsa EKS Cluster&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nPv7O/btsKD1lHUK2/elFuJY8J7kxFwafwl4MrzK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnPv7O%2FbtsKD1lHUK2%2FelFuJY8J7kxFwafwl4MrzK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;681&quot; height=&quot;358&quot; data-filename=&quot;스크린샷_2024-11-10_오전_2_56_32.jpg&quot; data-origin-width=&quot;1371&quot; data-origin-height=&quot;721&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;els-s3-irsa EKS Cluster&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  S3 생성하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 IRSA 예시에서는 &lt;b&gt;EKS에서 생성된 어플리케이션이 S3 Bucket의 파일 목록을 조회&lt;/b&gt;하는 작업을 진행할 것이다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;resource &quot;aws_s3_bucket&quot; &quot;this&quot; {
  bucket = &quot;test-irsa-s3&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, S3 Bucket은 Terraform을 통해 생성한 이후 AWS Console에서 직접 샘플 파일을 업로드해 Bucket에 테스트 데이터를 추가하도록 하자!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-10 오전 2.11.28.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/di6MIi/btsKEkLWUOg/egxw1Md8jLV4qFp3TR0JjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/di6MIi/btsKEkLWUOg/egxw1Md8jLV4qFp3TR0JjK/img.png&quot; data-alt=&quot;test-irsa-s3 S3 Bucket&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/di6MIi/btsKEkLWUOg/egxw1Md8jLV4qFp3TR0JjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdi6MIi%2FbtsKEkLWUOg%2Fegxw1Md8jLV4qFp3TR0JjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;357&quot; data-filename=&quot;스크린샷 2024-11-10 오전 2.11.28.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;test-irsa-s3 S3 Bucket&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ IRSA 구성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IRSA를 구성하기 위해서는 AWS와 같은 Cloud Service Provider(CSP)의 공식 Terraform Provider와 더불어, Kubernetes의 Service Account를 생성하기 위한 별도의 Terraform Provider(kubernetes)가 필요하게 된다. 개인적으로 &lt;b&gt;CSP의 공식 Provider 외에는 사용을 지양&lt;/b&gt;하고 있는데, 이는 완성도의 차이도 있겠지만, 내부 로직 이슈로 인해 Terraform State 관리에서 문제가 발생할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 예시를 들어보자, kubernetes Provider를 통해 Service Account를 생성한 후, 수동으로 삭제하는 작업을 진행한다면, 이후 &lt;code&gt;terraform apply&lt;/code&gt; 명령어를 실행할 때 &lt;b&gt;Terraform State가 리소스의 실제 상태를 반영하지 못해 오류가 발생&lt;/b&gt;할 수도 있다. 이러한 문제를 방지하기 위해서 필자는 최대한 CSP의 공식 Provider를 사용하는 것을 지향하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 하나의 예외가 있는데, 이는 &lt;b&gt;배포 후 유지 관리를 하지 않는 경우&lt;/b&gt;를 한정하여 사용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 예시는 단순히 예시를 보여주기 위한 것이므로, &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/kubernetes/latest&quot;&gt;kubernetes&lt;/a&gt; Terraform Provider를 이용해 Service Account를 생성하는 작업을 진행할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  AWS IAM Role 생성하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, &lt;code&gt;test-irsa-s3&lt;/code&gt; 라는 이름의 S3 Bucket의 파일을 조회할 수 있는 Policy를 생성하도록 하자. 이후, 생성한 Policy를 이용해 IAM Role을 구성하고, Kubernetes Service Account가 해당 Role을 Assume할 수 있도록 &lt;b&gt;신뢰 관계(Trust Relationships)&lt;/b&gt;를 설정해야 한다. 해당 설정을 바탕으로 Kubernetes 어플리케이션이 특정 AWS 리소스에 접근할 수 있도록 필요한 권한을 요청하고, 부여해줄 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, 신뢰 관계는 AWS 리소스에 접근을 허용할 &lt;b&gt;주체&lt;/b&gt;를 지정하는 아주 중요한 설정이므로, 이를 통해 지정된 Kubernetes Service Account만이 지정된 S3 Bucket에 접근할 수 있는 권한을 획득할 수 있도록 구성할 것이다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;resource &quot;aws_iam_role&quot; &quot;get_s3_bucket&quot; {
  name = &quot;getS3BucketIRSARole&quot;
  assume_role_policy = jsonencode({
    Version = &quot;2012-10-17&quot;
    Statement = [
      {
        Effect = &quot;Allow&quot;
        Principal = {
          Federated = &quot;arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;&amp;gt;:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/&amp;lt;OIDC_ID&amp;gt;&quot;
        }
        Action = &quot;sts:AssumeRoleWithWebIdentity&quot;
        Condition = {
          StringEquals = {
            &quot;oidc.eks.ap-northeast-2.amazonaws.com/id/&amp;lt;OIDC_ID&amp;gt;:aud&quot; = &quot;sts.amazonaws.com&quot;
            &quot;oidc.eks.ap-northeast-2.amazonaws.com/id/&amp;lt;OIDC_ID&amp;gt;:sub&quot; = &quot;system:serviceaccount:default:get-s3-bucket-sa&quot;,
          }
        }
      }
    ]
  })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, 아주 중요하게 여겨야하는 것은 &lt;code&gt;StringEquals&lt;/code&gt; 부분이다. 해당 부분은 EKS에서 구성될 Service Account의 정보를 작성하게 되는 것인데, 해당 예제에서는 &lt;code&gt;default&lt;/code&gt; 네임스페이스에 존재하는 &lt;code&gt;get-s3-bucket-sa&lt;/code&gt; Service Account를 지정한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 생성할 Service Account와 명확하게 일치해야만, Service Account가 &lt;code&gt;getS3BucketIRSARole&lt;/code&gt; 을 Assume할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  AWS Service Account 생성하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service Account는 kubernetes Terraform Provider를 사용하여 &lt;code&gt;default&lt;/code&gt; 네임스페이스 내에 &lt;code&gt;get-s3-bucket-sa&lt;/code&gt; 라는 이름으로 생성할 것이다. 여기서 중요시 여겨야할 것은 &lt;code&gt;annotations&lt;/code&gt; 부분에서 어떤 AWS IAM Role과 연결하여 IRSA를 구성할지 지정하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;resource &quot;kubernetes_service_account_v1&quot; &quot;get_s3_bucket&quot; {
  metadata {
    name      = &quot;get-s3-bucket-sa&quot;
    namespace = &quot;default&quot;

    annotations = {
      &quot;eks.amazonaws.com/role-arn&quot; : &quot;arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;:role/getS3BucketIRSARole&quot;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ IRSA 사용하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  어플리케이션 배포하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 배포할 어플리케이션은 Express.js로 구성된 S3 Bucket의 파일 목록을 조회하는 서버이다. 해당 서버는 &lt;code&gt;3000&lt;/code&gt;번 Port에서 &lt;code&gt;GET /files&lt;/code&gt; 요청을 통해 S3 Bucket의 파일 목록을 출력한다. 해당 서버는 Docker Hub에 Public으로 업로드되어 있으며, 상세한 서버 구성은 &lt;a href=&quot;https://github.com/archepro84/notes/tree/main/_Blog/%EA%B8%80%EB%98%9010%EA%B8%B0/2.IRSA/get-s3-aws-sdk&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Github Repository&lt;/a&gt;를 참조하길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 어플리케이션은 Kubernetes Deployments를 통해 구성하였다. 이전에 생성한 Service Account를 &lt;code&gt;ServiceAccountName&lt;/code&gt; 부분에 할당하였고, 이를 통해 Pod는 Service Account를 통해 AWS IAM Role을 Assume하여 최종적으로 &lt;code&gt;test-irsa-s3&lt;/code&gt;라는 S3 Bucket의 파일 목록을 조회할 수 있게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 직접 EKS에 Deployment를 배포하는 작업을 진행해보자. 아래의 명령어를 순서대로 입력한다면 kubectl이 생성된 AWS EKS Cluster를 등록할 것이고, 예시 Deployment를 배포하는 작업을 진행할 것이다.&lt;/p&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;# aws-cli를 이용해 EKS Cluster 정보를 `.kube/config`에 등록한다.
$ aws eks update-kubeconfig --region ap-northeast-2 --name eks-s3-irsa

# kubectl을 이용해 EKS Cluster에 구성된 Node 목록을 조회할 수 있다. 
$ kubectl get nodes

# kubectl을 이용해 예시 Deployment를 배포한다.
$ kubectl apply -f get-s3-aws-sdk.deployment.yaml

# kubectl을 이용해 배포되는 Pod를 조회한다.
$ kubectl get pods -o wide --watch -A&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-10 오전 12.31.25.png&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dU7zk6/btsKCOgJdKE/rTofEK3A7eFIN41Y1paYFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dU7zk6/btsKCOgJdKE/rTofEK3A7eFIN41Y1paYFK/img.png&quot; data-alt=&quot;배포된 get-s3-aws-sdk Pod&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dU7zk6/btsKCOgJdKE/rTofEK3A7eFIN41Y1paYFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdU7zk6%2FbtsKCOgJdKE%2FrTofEK3A7eFIN41Y1paYFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;153&quot; data-filename=&quot;스크린샷 2024-11-10 오전 12.31.25.png&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;배포된 get-s3-aws-sdk Pod&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  IRSA 테스트 하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로, 배포된 Deployment에 접근하여 IRSA 설정이 제대로 작동하는지 확인해보도록 하자. 현재 EKS Cluster는 서비스 및 인그레스가 설정되지 않아 외부에서 접근할 수 없는 상태인데, &lt;code&gt;kubectl&lt;/code&gt;을 이용한다면 직접 Pod의 Bash shell에 접근하여 어플리케이션에 요청을 수행할 수 있다. 그렇다면, 직접 S3 Bucket의 파일 목록을 조회할 수 있는지 확인해보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;# kubectl을 이용해 배포된 Pod의 bash shell에 접근한다.
kubectl exec -it get-s3-aws-sdk-ccb8d4654-bwzw8 /bin/sh

# curl 명령어를 이용해, S3 Bucket에 파일이 존재하는지 검증하는 작업을 수행한다.
curl localhost:3000/files&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-10 오전 12.32.16.png&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckh01f/btsKCwnfBM9/ZzJetbV8PHb9gkGKWI0hK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckh01f/btsKCwnfBM9/ZzJetbV8PHb9gkGKWI0hK1/img.png&quot; data-alt=&quot;S3 Bucket 파일 목록 조회에 성공한 Pod&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckh01f/btsKCwnfBM9/ZzJetbV8PHb9gkGKWI0hK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fckh01f%2FbtsKCwnfBM9%2FZzJetbV8PHb9gkGKWI0hK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;791&quot; height=&quot;114&quot; data-filename=&quot;스크린샷 2024-11-10 오전 12.32.16.png&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;132&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;S3 Bucket 파일 목록 조회에 성공한 Pod&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pod 내에서 &lt;code&gt;curl localhost:3000/files&lt;/code&gt; 명령을 실행하여 S3 Bucket의 파일 목록을 정상적으로 조회할 수 있었다. 이는 IRSA를 통해 Service Account가 IAM Role을 Assume 하여 필요한 권한을 획득하고, 이를 이용하여 S3 Bucket에 접근할 수 있었기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  마지막으로&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 Kubernetes를 공부하면서 새로운 개념들이 많아 적응하기가 너무 어려웠다. 특히, 빠르게 기술을 적용해야 하는 상황에서 IRSA는 가장 난해한 부분 중 하나였는데, 처음에는 Service Account 개념도, Kubernetes 내부 동작도 제대로 이해하지 못했기에 IRSA를 학습하는 데 많은 시간이 소요되었다. 하지만, 하나씩 직접 만들어보고 노력하다보니, 이제는 IRSA를 사용하는 것이 어느정도 익숙해진 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 게시글이 과거의 나와 같이 Kubernetes와 IRSA를 처음 접하는 사람들에게 도움이 되길 바라며, 이번 게시글을 마치도록 하겠다.&lt;/p&gt;</description>
      <category>Infrastructure</category>
      <category>AWS</category>
      <category>IAM role</category>
      <category>IRSA</category>
      <category>kubernetes</category>
      <category>SA</category>
      <category>service account</category>
      <author>커스텀 리</author>
      <guid isPermaLink="true">https://custom-li.tistory.com/219</guid>
      <comments>https://custom-li.tistory.com/219#entry219comment</comments>
      <pubDate>Sun, 10 Nov 2024 03:04:39 +0900</pubDate>
    </item>
    <item>
      <title>Terraform으로 생성한 IAM Role은 비정상일까?</title>
      <link>https://custom-li.tistory.com/218</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Arch_AWS-Identity-and-Access-Management_64.png&quot; data-origin-width=&quot;80&quot; data-origin-height=&quot;80&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rQslH/btsJ21nZGvQ/KHcC5YGu3sVPSyAt2e8Px0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rQslH/btsJ21nZGvQ/KHcC5YGu3sVPSyAt2e8Px0/img.png&quot; data-alt=&quot;Identity and Access Management&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rQslH/btsJ21nZGvQ/KHcC5YGu3sVPSyAt2e8Px0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrQslH%2FbtsJ21nZGvQ%2FKHcC5YGu3sVPSyAt2e8Px0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;120&quot; height=&quot;120&quot; data-filename=&quot;Arch_AWS-Identity-and-Access-Management_64.png&quot; data-origin-width=&quot;80&quot; data-origin-height=&quot;80&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Identity and Access Management&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 게시글은 OpenSearch에서 Snapshot Repository를 설정하여 로그를 백업하는 방법을 작성하려 했었다. 그러나 해당 작업을 구현하던 중 Terraform으로 생성한 IAM Role에서 이상이 있는 것을 확인하게 되었고, 이를 인지하기까지 많은 시간이 소요되었다. 이번 게시글에서는 Terraform Assume Role 문제를 해결하기 위해 겪은 과정을 공유하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Assume Role&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1_LlY90RbzoGa6B-MzmChJUA.webp&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRN1Gn/btsJ22AoehX/vV9sJsOpkwdWTAReT1s5kK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRN1Gn/btsJ22AoehX/vV9sJsOpkwdWTAReT1s5kK/img.webp&quot; data-alt=&quot;Assume Role Image: https://howtodoitincloud.medium.com/implementing-assume-role-47fc9383d5bc&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRN1Gn/btsJ22AoehX/vV9sJsOpkwdWTAReT1s5kK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRN1Gn%2FbtsJ22AoehX%2FvV9sJsOpkwdWTAReT1s5kK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;301&quot; data-filename=&quot;1_LlY90RbzoGa6B-MzmChJUA.webp&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Assume Role Image: https://howtodoitincloud.medium.com/implementing-assume-role-47fc9383d5bc&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Assume Role&lt;/b&gt;은 AWS STS를 이용해 특정 역할(Role)을 임시로 부여 받아 엑세스 권한을 획득하는 방식을 뜻한다. 각각의 개념을 먼저 알아보고, Assume Role이 어떤 역할을 하는지 살펴보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AWS STS&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/id_credentials_temp.html&quot;&gt;AWS STS&lt;/a&gt;(Security Token Service)는 특정 AWS 리소스를 사용할 수 있는 '&lt;b&gt;임시 보안 자격 증명&lt;/b&gt;'을 생성하는 서비스이다. 서비스의 이름과 같이 생성된 '임시 자격 증명'은 단기적으로만 사용할 수 있으며, 자격 증명이 만료 되면 그 어떠한 종류의 권한 요청도 실패하도록 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IAM User 또는 AWS 리소스는 AWS STS를 통해 '임시 자격 증명'을 요청할 수 있으며, 해당 자격 증명에 특정 역할(Role)에 설정된 권한을 부여받아 사용할 수 있게 된다. 이를 통해 '&lt;b&gt;단기적으로 사용할 수 있는 권한을 소유한 임시 자격 증명&lt;/b&gt;'을 생성할 수 있게 되며, 이 과정 자체를 Assume Role이라고 부르게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Assume Role&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Assume Role&lt;/b&gt;은 특정 역할(Role)을 일시적으로 부여받아 AWS 리소스에 접근할 수 있도록 도와준다. IAM User에 영구적인 Role을 부여하는 것과 달리, Assume Role은 일시적인 권한을 부여하여 지정된 시간 이후에는 해당 권한이 제거되므로 보안을 강화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, EC2 인스턴스가 S3 Bucket에 접근하기 위해 Assume Role을 사용하면, EC2는 일시적으로만 S3 Bucket을 사용할 수 있게 된다. 이는 IAM User뿐만 아니라, 다양한 AWS 서비스에서도 사용 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Assume Role 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assume Role을 사용하려면 먼저 IAM Role을 생성하고, 신뢰 관계(Trust Relationship)를 정의해야 한다. 신뢰 관계는 특정 주체(예: EC2 인스턴스, OpenSearch 등)가 해당 Role을 사용할 수 있는 권한을 명시한다. 이렇게 설정된 주체는 해당 역할(Role)을 '&lt;b&gt;Assume&lt;/b&gt;'하여 필요한 권한을 획득하고 원하는 리소스에 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이슈 해결하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서 이야기할 내용은 다소 복잡하게 들릴 수도 있다. IAM Policy, Role, User와 익숙하지 않은 OpenSearch 도메인 및 Access Policy, Security Role, Snapshot Repository 생성 스크립트를 실행하는 등 여러 과정이 포함되어 있다. 하지만 간결한 의식의 흐름을 전달하기 위해 일부 상황을 각색하여 작성할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이슈 발생 상황&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;S3 Bucket을 Snapshot Repository로 사용하려면 OpenSearch에 특정 권한을 부여해야 하는데, Terraform으로 생성한 IAM Role에서 Assume Role이 정상적으로 동작하지 않았다. 이로 인해 S3 접근 권한을 획득하지 못했는데, 이 문제를 해결하기 위해 어떤 의식의 흐름이 있었는지 하나씩 살펴보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Terraform으로 생성된 Assume Role이 S3 Bucket 권한 획득에 실패했다.&lt;/h3&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;error&quot;: {
    &quot;root_cause&quot;: [
      {
        &quot;type&quot;: &quot;repository_verification_exception&quot;,
        &quot;reason&quot;: &quot;[js-outbound-logs-repo2] path [js-outbound-logs] is not accessible on cluster-manager node&quot;
      }
    ],
    &quot;type&quot;: &quot;repository_verification_exception&quot;,
    &quot;reason&quot;: &quot;[js-outbound-logs-repo2] path [js-outbound-logs] is not accessible on cluster-manager node&quot;,
    &quot;caused_by&quot;: {
      &quot;type&quot;: &quot;i_o_exception&quot;,
      &quot;reason&quot;: &quot;Unable to upload object [js-outbound-logs/tests-&amp;lt;SIGN_PRIVATE_KEY&amp;gt;/master.dat] using a single upload&quot;,
      &quot;caused_by&quot;: {
        &quot;type&quot;: &quot;sts_exception&quot;,
        &quot;reason&quot;: &quot;User: arn:aws:sts::832035452523:assumed-role/cp-sts-grant-role/swift-ap-northeast-2-prod-&amp;lt;AWS_ACCOUNT_ID&amp;gt; is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:role/os-repo-prg-OPENSEARCHRepositoryRole (Service: Sts, Status Code: 403, Request ID: f28a9073-f14e-4282-8628-c87b4842929b)&quot;
      }
    }
  },
  &quot;status&quot;: 500
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 전문은 IAM User가 &lt;a href=&quot;https://opensearch.org/docs/latest/api-reference/snapshots/create-repository/&quot;&gt;OpenSearch의 Snapshot Repository 생성 API&lt;/a&gt; 를 호출한 응답값이다. &lt;code&gt;reason&lt;/code&gt; 에 출력된 내용을 요약하면, S3 버킷 하위에 위치한 &lt;code&gt;js-outbound-logs&lt;/code&gt; 디렉토리와 &lt;code&gt;js-outbound-logs-repo2&lt;/code&gt; 라는 OpenSearch Snapshot Repository를 연동하지 못한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전문 하위에 출력된 응답값은 S3 Bucket에 접근하지 못한 이유에 대해 상세하게 설명 되어있는데, 832035452523 AWS Account ID에서 STS를 통해 &lt;code&gt;os-repo-prg-OPENSEARCHRepositoryRole&lt;/code&gt; 을 발급하려 했으나 인증 실패로 인해 STS를 발급하지 못해 실패한 것으로 확인된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, 나를 더욱 미궁에 빠뜨린 의문은 &lt;b&gt;832035452523&lt;/b&gt;에 해당하는 계정 은 Terraform으로 배포된 AWS Account ID와 &lt;b&gt;전혀 상관 없는 값&lt;/b&gt;이며 그 어느 곳에서도 해당 계정에 대한 ID를 찾지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 신뢰 관계(Trust Relationship), 정책(Policy)는 모두 정상이었다.&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-13 오후 11.27.21.png&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;443&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9v2Ln/btsJ31tGFJS/RJ5ZhKEyBAPYdKnjRnhLiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9v2Ln/btsJ31tGFJS/RJ5ZhKEyBAPYdKnjRnhLiK/img.png&quot; data-alt=&quot;Terraform IAM User - Role&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9v2Ln/btsJ31tGFJS/RJ5ZhKEyBAPYdKnjRnhLiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9v2Ln%2FbtsJ31tGFJS%2FRJ5ZhKEyBAPYdKnjRnhLiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;362&quot; height=&quot;349&quot; data-filename=&quot;스크린샷 2024-10-13 오후 11.27.21.png&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;443&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Terraform IAM User - Role&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STS가 Assume Role을 발급받기 위한 역할(Role)은 &lt;code&gt;os-repo-prg-OpenSearchRepositoryRole&lt;/code&gt; 인데, 해당 역할에 정의된 잘못된 설정값이 있는지 검토해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저, 해당 역할은 OpenSearch(&lt;code&gt;es.amazonaws.com&lt;/code&gt;) 서비스에서 STS 발급을 요청하도록 구성되어 있다. 그로 인해, 신뢰 관계 또한 동일한 서비스가 Assume Role을 발급받을 수 있도록 &lt;code&gt;sts:AssumeRole&lt;/code&gt; &lt;b&gt;작업(Action)&lt;/b&gt;을 허용하고 있으며, S3에 접근할 수 있도록 &lt;code&gt;os-repo-prg-OpenSearchRepositoryUserPolicy&lt;/code&gt; &lt;b&gt;정책(Policy)&lt;/b&gt;을 설정하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상세한 역할(Role)과 정책(Policy)를 확인하고 싶다면 &lt;a href=&quot;https://github.com/archepro84/notes/tree/main/_Blog/%EA%B8%80%EB%98%9010%EA%B8%B0/1.aws-iam-assume-role/terraform&quot;&gt;Github Repository&lt;/a&gt;&amp;nbsp;를 참고하기 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Assume Role 발급을 요청하는 IAM User를 변경해 보았다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 확인한 것처럼 Assume Role의 대상인 역할(Role)은 모두 정상적인 것으로 확인되었고, 문제가 발생할 여지가 없다는 것을 확실하게 검증하게 되었다. 그렇다면, &quot;&lt;b&gt;Assume Role을 요청하는 주체에서 문제가 발생하는 것이 아닐까?&lt;/b&gt;&quot; 라는 생각이 들어 Assume Role을 요청하는 주체를 변경해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Assume Role 발급을 요청하는 주체는 IAM User의 Access Key를 OpenSearch에 등록하여, OpenSearch Snapshot Repository API 를 사용할 수 있도록 Security Role을 부여한 후 OpenSearch API 호출하는 방식이었다. 여기서, IAM User를 검증하기 위해 두 가지의 방법을 사용하였는데, 첫 번째는 &quot;&lt;b&gt;IAM User를 신규 생성&lt;/b&gt;&quot;하는 방식이었고, 두 번째는 &quot;&lt;b&gt;IAM User의 Access Key를 변경&lt;/b&gt;&quot;하는 작업을 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-1. Access Key 재발급받기&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷_2024-10-13_오후_10_18_37.jpg&quot; data-origin-width=&quot;2060&quot; data-origin-height=&quot;1310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zL5X9/btsJ22UHuv0/ZkVKfeYF90oYWEXDu0KC60/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zL5X9/btsJ22UHuv0/ZkVKfeYF90oYWEXDu0KC60/img.jpg&quot; data-alt=&quot;IAM User: os-repo-prg-OpenSearchRepositoryUser&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zL5X9/btsJ22UHuv0/ZkVKfeYF90oYWEXDu0KC60/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzL5X9%2FbtsJ22UHuv0%2FZkVKfeYF90oYWEXDu0KC60%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;629&quot; height=&quot;400&quot; data-filename=&quot;스크린샷_2024-10-13_오후_10_18_37.jpg&quot; data-origin-width=&quot;2060&quot; data-origin-height=&quot;1310&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IAM User: os-repo-prg-OpenSearchRepositoryUser&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 OpenSearch API는 호출할 때 IAM User의 Access Key를 사용하는데, 잘못된 Access Key를 사용하고 있을 가능성을 염두에 두고 Access Key를 재발급 받아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 사용 중인 &lt;code&gt;os-repo-prg-OpenSearchRepositoryUser&lt;/code&gt; IAM User는 &quot;&lt;b&gt;RK70&lt;/b&gt;&quot;에 해당하는 Access Key를 사용하고 있었으나, 새롭게 &quot;&lt;b&gt;7R4DB&lt;/b&gt;&quot; Access Key를 발급받았다. 하지만, 변경된 Access Key를 사용하여 OpenSearch API를 호출하더라도 동일한 에러가 발생하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-2. IAM User를 신규 생성하기&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-13 오후 10.23.48.png&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc9RCO/btsJ203DFdM/CXM8F9n0jWa2r6amqEXQy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc9RCO/btsJ203DFdM/CXM8F9n0jWa2r6amqEXQy0/img.png&quot; data-alt=&quot;IAM User: new-OpenSearchRepositoryUser&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc9RCO/btsJ203DFdM/CXM8F9n0jWa2r6amqEXQy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc9RCO%2FbtsJ203DFdM%2FCXM8F9n0jWa2r6amqEXQy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;598&quot; height=&quot;199&quot; data-filename=&quot;스크린샷 2024-10-13 오후 10.23.48.png&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IAM User: new-OpenSearchRepositoryUser&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째로 IAM User에 잘못된 설정이 되어 있는 것이 아닌지 확인하기 위해 Administrator Role을 부여하여 User를 생성하고, 해당 정보를 OpenSearch Security Role에 부여한 후 OpenSearch API를 호출해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 역시나 Opensearch API에서는 무심하게 동일한 에러 응답값을 전달받게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. IAM Role을 AWS Console로 생성해 보았다.&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-13 오전 12.37.54.png&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxiQNZ/btsJ22f5B0o/4qZnPhSHKNkxiiqYTrxuJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxiQNZ/btsJ22f5B0o/4qZnPhSHKNkxiiqYTrxuJk/img.png&quot; data-alt=&quot;IAM Role: opensearch-test-role&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxiQNZ/btsJ22f5B0o/4qZnPhSHKNkxiiqYTrxuJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxiQNZ%2FbtsJ22f5B0o%2F4qZnPhSHKNkxiiqYTrxuJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;998&quot; height=&quot;285&quot; data-filename=&quot;스크린샷 2024-10-13 오전 12.37.54.png&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IAM Role: opensearch-test-role&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 리소스는 Terraform을 통해 생성하였는데, 이번에는 AWS Console을 통해 &lt;b&gt;opensearch-test-role&lt;/b&gt; IAM Role을 생성해보았다. AWS Console에서 생성한 Role은 Terraform으로 생성한 Role과 동일한 신뢰 관계(Trust Relationship)와 정책(Policy)을 가지고 있었으며, Assume Role을 요청하는 주체도 동일하게 설정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이번에는 OpenSearch API를 요청하였을 때, 정상적으로 S3 Bucket에 접근하여 Snapshot Repository를 생성할 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-13 오후 10.31.01.png&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RmYPx/btsJ4vgLwuv/XvVcSsegSVdyuqAmxZFk4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RmYPx/btsJ4vgLwuv/XvVcSsegSVdyuqAmxZFk4k/img.png&quot; data-alt=&quot;신규 생성된 js-outbound-logs-repo2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RmYPx/btsJ4vgLwuv/XvVcSsegSVdyuqAmxZFk4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRmYPx%2FbtsJ4vgLwuv%2FXvVcSsegSVdyuqAmxZFk4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;521&quot; height=&quot;334&quot; data-filename=&quot;스크린샷 2024-10-13 오후 10.31.01.png&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;389&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;신규 생성된 js-outbound-logs-repo2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Terraform과 Console로 생성된 Role 비교해보았다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terraform으로 생성하였을 때 일부 속성값이 누락된 것이 아닐까 하는 고민이 있었지만, 가장 확실한 방법을 두 Role을 상세하게 비교해보는 것이 가장 효율적이라고 판단하였다. Terraform으로 생성된 &lt;code&gt;os-repo-prg-OPENSEARCHRepositoryRole&lt;/code&gt;과 AWS Console로 생성된 &lt;code&gt;opensearch-test-role&lt;/code&gt;을 각각 Terraform으로 import하여 &lt;b&gt;tfstate&lt;/b&gt; 파일에서 비교해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-10-13 오후 10.34.16.png&quot; data-origin-width=&quot;1655&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PRWqK/btsJ3rsPtPT/Ei7tLWHxf8L6JCxr8aT9K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PRWqK/btsJ3rsPtPT/Ei7tLWHxf8L6JCxr8aT9K0/img.png&quot; data-alt=&quot;Terraform, Console Role tfstate 비교하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PRWqK/btsJ3rsPtPT/Ei7tLWHxf8L6JCxr8aT9K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPRWqK%2FbtsJ3rsPtPT%2FEi7tLWHxf8L6JCxr8aT9K0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;763&quot; height=&quot;354&quot; data-filename=&quot;스크린샷 2024-10-13 오후 10.34.16.png&quot; data-origin-width=&quot;1655&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Terraform, Console Role tfstate 비교하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 모든 &lt;b&gt;tfstate&lt;/b&gt;의 설정값을 비교해 보았음에도 불구하고 차이점을 찾을 수 없었다. 두 Role의 설정값은 토씨하나 틀린 것이 없었으며, 다른 것 이라고는 이름 말고는 그 어느 것도 찾을 수 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 방안&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이슈는 임시적으로 &lt;b&gt;IAM Role을 Terraform으로 생성하지 않고, AWS Console을 통해 IAM Role을 생성&lt;/b&gt;하는 방법으로 대응하였다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 이슈를 디버깅하면서 &amp;ldquo;왜 안되지?&amp;rdquo; &amp;ldquo;다른게 없잖아!&amp;rdquo;와 같이 분노에 가득찬 혼잣말을 하면서 시간을 보냈었는데, 도저히 풀 수 없는 문제를 해결하고 있는 느낌을 엄청 오랜만에 다시 느끼게 된 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 게시글에서는 어째서 발생하게 된 이슈인지 근본적인 해결 방법을 도출하지 못했다. 해결 방안을 찾기 전 까지는 임시적으로 Terraform으로 IAM Policy와 User는 모두 생성하지만, IAM Role은 Terraform output으로 출력된 값을 바탕으로 AWS Console에서 수동 발급 하도록 대응하고 있다. 사실 근본적인 원인을 모두 검증한 후 글을 작성하고 싶었지만, 이번 게시글에서는 해결 방안을 찾지 못한 채 글을 작성하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 게시글에서는 근본적인 원인을 확인하고 명확한 해결 방안을 찾아내어 공유하고자 한다. 다음 게시글에서는 &lt;code&gt;terraform-provider-aws&lt;/code&gt;와 &lt;code&gt;aws-sdk-go-v2&lt;/code&gt;의 구현체를 확인하고, IAM Role 생성 시 동작 흐름에 대해 분석하여 AWS Console와 차이점을 분석해보도록 하겠다.&lt;/p&gt;</description>
      <category>분석과 탐구</category>
      <category>assume role</category>
      <category>is not accessible on cluster-manager node</category>
      <category>OpenSearch</category>
      <category>repository_verification_exception</category>
      <category>STS</category>
      <category>Terraform</category>
      <category>글또10기</category>
      <author>커스텀 리</author>
      <guid isPermaLink="true">https://custom-li.tistory.com/218</guid>
      <comments>https://custom-li.tistory.com/218#entry218comment</comments>
      <pubDate>Sun, 13 Oct 2024 23:35:15 +0900</pubDate>
    </item>
    <item>
      <title>ALB를 이용해 EKS 단일 장애점 개선하기 (이론)</title>
      <link>https://custom-li.tistory.com/217</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-14 오후 7.55.13.png&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ONqOp/btsGC2vooOx/lkkYim30cse2IjgMMzmvOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ONqOp/btsGC2vooOx/lkkYim30cse2IjgMMzmvOK/img.png&quot; data-alt=&quot;AWS Load Balancer Controller&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ONqOp/btsGC2vooOx/lkkYim30cse2IjgMMzmvOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FONqOp%2FbtsGC2vooOx%2FlkkYim30cse2IjgMMzmvOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;439&quot; height=&quot;268&quot; data-filename=&quot;스크린샷 2024-04-14 오후 7.55.13.png&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWS Load Balancer Controller&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 EKS로 인프라를 마이그레이션하면서, 수많은 챌린지를 진행하고 있습니다. 초기에는 EKS 구축에만 많은 리소스를 사용하고 있었지만, 현재는 어느정도 쿠버네티스가 익숙해졌고 EKS를 이용하여 다수의 어플리케이션이 배포된 상태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 EKS를 이용해 여러 서비스들이 배포된 상황이긴 하지만, 매번 설계한 아키텍처에서 마음에 들지 않는 부분이나 개선되었으면 좋을 부분들이 점점 눈에 띄고 있는 상태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 게시글에서는 사용에 직접적인 문제는 없지만, 개선되어야할 부분 중 하나인 &lt;b&gt;&lt;a href=&quot;https://docs.nginx.com/nginx-ingress-controller/&quot;&gt;Niginx Ingress Controller&lt;/a&gt;의&lt;/b&gt; &lt;b&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%8B%A8%EC%9D%BC_%EC%9E%A5%EC%95%A0%EC%A0%90&quot;&gt;단일 장애점(SPOF, Single Point Of Failure)&lt;/a&gt;에&lt;/b&gt; 대한 개선기를 작성해보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;먼저 알아보기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-14 오후 8.03.49.png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8etgq/btsGBclZUVp/pFWVwow281gJWi794w5mf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8etgq/btsGBclZUVp/pFWVwow281gJWi794w5mf1/img.png&quot; data-alt=&quot;Ingress&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8etgq/btsGBclZUVp/pFWVwow281gJWi794w5mf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8etgq%2FbtsGBclZUVp%2FpFWVwow281gJWi794w5mf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;792&quot; height=&quot;235&quot; data-filename=&quot;스크린샷 2024-04-14 오후 8.03.49.png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;235&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Ingress&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ Ingress&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/ingress/&quot;&gt;인그레스(Ingress)&lt;/a&gt;는&lt;/b&gt; 쿠버네티스 클러스터 외부에서 내부로 접근하는 요청을 관리하는 리소스이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인그레스(Ingress)는&lt;/b&gt; 클러스터 내의 여러 서비스(Service)에 대한 외부 트래픽을 효율적으로 라우팅 해주는 역할을 담당합니다. 주로 Application에게 안정적인 트래픽 전달을 수행하며, URL 기반의 라우팅과 쿠버네티스 내부에서 Service Name 기반의 가상 호스팅을 가능하게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ Ingress Controller&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/ingress-controllers/&quot;&gt;인그레스 컨트롤러(Ingress Controller)&lt;/a&gt;는&lt;/b&gt; 인그레스 리소스에 정의된 Rule에 따라 외부 트래픽을 지정된 서비스로 라우팅하는 역할을 담당하는 리버스 프록시(Reverse Proxy) 서버이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인그레스(Ingress)는&lt;/b&gt; 쿠버네티스 클러스터 외부에서 내부로 트래픽을 전달하기 위한 Rule을 담당하게 됩니다. 이렇게 정의된 Rule에 따라 실제로 라우팅을 수행하기 위해서는 &lt;b&gt;인그레스 컨트롤러(Ingress Controller)가&lt;/b&gt; 필요하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인그레스 컨트롤러(Ingress Controller)는 쿠버네티스 클러스터의 Gateway 역할을 담당하며, 외부 트래픽을 지정된 내부 Service에게 전달하게됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ Nginx Ingress Controller&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/kubernetes/ingress-nginx/blob/main/README.md#readme&quot;&gt;Nginx Ingress Controller&lt;/a&gt;는&lt;/b&gt; 쿠버네티스가 기본적으로 제공해주는 Ingress Controller 중 하나이며, Nginx 웹 서버를 기반으로 한 Open Source Ingress Controller이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Nginx Ingress Controller&lt;/b&gt;는 쿠버네티스의 Nginx Pod로써 구성되며, 모든 외부 트래픽은 Nginx Pod를 거쳐 정의된 Ingress Rule에 따라 지정된 Service에 라우팅됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, 모든 트래픽 라우팅을 Nginx Pod가 담당하게 되므로 기존에 인프라를 담당하였던 개발자라면 Nginx에 대한 지식만 가지고 있더라도 추가적인 리소스 없이 인프라 환경을 구축할 수 있게 되는 장점을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;현재 문제점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단일 장애점(SPOF, Single Point Of Failure)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 EKS 환경의 모든 인바운드 트래픽은 Nginx Ingress Controller를 거치게됩니다. 이는 Nginx를 사용했던 개발자라면 익숙하겠지만, 사실은 중대한 위험을 내포하고 있는 구성입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, Nginx가 다운되는 순간, 클러스터 내의 모든 어플리케이션을 도메인을 통해 접근할 수 없는 문제가 발생하게 됩니다. 이는 Nginx가 클러스터 내의 모든 어플리케이션의 트래픽을 관리하므로 Nginx 하나가 모든 클러스터의 장애를 발생시키는 &lt;b&gt;단일 장애점(SPOF, Single Point Of Failure)이&lt;/b&gt; 되는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필요하지 않은 트래픽 모니터링&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 Nginx Ingress Controller의 트래픽 모니터링은 가장 중요한 부분이었습니다. 하지만 시간이 지나면서 ELK, Datadog과 같은 외부의 3rd Party 모니터링 도구를 도입하게 되었습니다. 이러한 도구들을 통해 Nginx에서 제공하던 기능보다 더욱 향상된 트래픽 분석과 로그 관리가 가능하게 되어 Nginx의 필요성이 더욱 줄어들게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 문제점으로 인해, Nginx Ingress Controller의 필요성이 점차 줄어들게 되었고, 이를 해결하기위한 다른 방안이 필요하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS Load Balancer Controller&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/kubernetes-sigs/aws-load-balancer-controller#readme&quot;&gt;AWS Load Balancer Controller&lt;/a&gt;는&lt;/b&gt; 쿠버네티스 클러스터에서 &lt;b&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/elasticloadbalancing/latest/application/introduction.html&quot;&gt;AWS ALB(Application Load Balancer)&lt;/a&gt;를&lt;/b&gt; 통해 인바운드 트래픽을 관리한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AWS Load Balancer Controller&lt;/b&gt;를 이용한다면, 쿠버네티스의 &lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/services-networking/service/#loadbalancer&quot;&gt;Service에서 LoadBalancer 타입&lt;/a&gt;을 사용하는 것과 유사하게 AWS Load Balancer를 구성할 수 있습니다. 다만, Service의 LoadBalancer 타입과 다르게, &lt;b&gt;AWS Load Balancer Controller&lt;/b&gt;는 &lt;b&gt;ALB&lt;/b&gt; 또는 &lt;b&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/elasticloadbalancing/latest/network/introduction.html&quot;&gt;NLB(Network Load Balancer)&lt;/a&gt;를&lt;/b&gt; 사용하여 Ingress Rule에 따라 다수의 서비스로 트래픽을 라우팅하는 더 복잡한 구성이 가능하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Nginx Ingress Controller&lt;/b&gt;가 일반적으로 단일 NLB로 트래픽을 Nginx Pod로 라우팅하는 반면, &lt;b&gt;AWS Load Balancer Controller&lt;/b&gt;는 ALB 또는 NLB의 Listener를 구성하여, Ingress Rule을 바탕으로 트래픽을 적절한 Service로 라우팅하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 인바운드 트래픽에 대한 의존성을 AWS Load Balancer로 이전함으로써, 기존 Nginx 기반 아키텍처에서 발생할 수 있는 단일 장애점 문제를 제거할 수 있게 됩니다. 또한, ALB의 자동화된 스케일링 기능과 함께 &lt;b&gt;&lt;a href=&quot;https://aws.amazon.com/ko/certificate-manager/&quot;&gt;ACM(AWS Certificate Manager)&lt;/a&gt;를&lt;/b&gt; 통한 SSL/TLS 관리 및 &lt;b&gt;&lt;a href=&quot;https://aws.amazon.com/ko/waf/&quot;&gt;AWS WAF(Firewall)&lt;/a&gt;를&lt;/b&gt; 통한 세밀한 트래픽 필터링을 제공할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS Load Balancer Controlelr를 통해 Nginx 기반의 Ingress Controller가 가지고 있던 단일 장애점을 제거할 수 있었고, ALB 및 NLB를 통해 운영 효율성과 안정성을 향상시킬 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 게시글에서는 현재 문제점과 해결 방법에 대한 이론만 설명하였지만, 다음 게시글에서는 Terraform을 이용하여 &lt;b&gt;VPC&lt;/b&gt;, &lt;b&gt;EKS&lt;/b&gt;, &lt;b&gt;AWS Load Balancer Controller Helm&lt;/b&gt;을 통해 ALB가 생성되는 것과 Ingress Rule에 따라 ALB Listener Rule이 지정된 Target Group을 Routing 하도록 구성할 예정입니다.&lt;/p&gt;</description>
      <category>Infrastructure</category>
      <category>alb</category>
      <category>AWS Load Balancer Controller</category>
      <category>ingress controller</category>
      <category>Nginx Ingress Controller</category>
      <category>NLB</category>
      <category>글또 9기</category>
      <author>커스텀 리</author>
      <guid isPermaLink="true">https://custom-li.tistory.com/217</guid>
      <comments>https://custom-li.tistory.com/217#entry217comment</comments>
      <pubDate>Sun, 14 Apr 2024 20:03:29 +0900</pubDate>
    </item>
    <item>
      <title>AWS Unicorn Day 2024 방문기</title>
      <link>https://custom-li.tistory.com/216</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-31 오후 8.01.45.png&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQAI7/btsGeNFWQix/ZzguNWxWhkqRlSWFdBPKn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQAI7/btsGeNFWQix/ZzguNWxWhkqRlSWFdBPKn0/img.png&quot; data-alt=&quot;AWS Unicorn Day 2024&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQAI7/btsGeNFWQix/ZzguNWxWhkqRlSWFdBPKn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQAI7%2FbtsGeNFWQix%2FZzguNWxWhkqRlSWFdBPKn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;312&quot; data-filename=&quot;스크린샷 2024-03-31 오후 8.01.45.png&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AWS Unicorn Day 2024&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 사내에서 엄청난 격변의 시기가 지속되고 있습니다. 인프라 개선부터 기술 스택, 보안, 추후 방향성에 대한 수많은 고민들이 눈앞에 다가오게 되었습니다. 그러던 중 사내 슬랙에서 AWS 컨퍼런스가 있다는 것을 알게 되었고, 현재 겪고 있는 문제에 대한 인사이트를 얻을 수 있을 것이라고 판단하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게, AWS Unicorn Day 2024에 방문하여 세션 을 경험하며 어떤 인사이트를 얻었는지에 대해 이야기해볼까 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS Unicorn Day 2024&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pages.awscloud.com/aws-unicorn-day-2024-reg.html&quot;&gt;AWS Unicorn Day 2024&lt;/a&gt;는 다른 넥슨의 &lt;a href=&quot;https://ndc.nexon.com/main&quot;&gt;NDC&lt;/a&gt;나 네이버의 &lt;a href=&quot;https://deview.kr/2023&quot;&gt;DEVIEW&lt;/a&gt;와 달리 모든 세션을 시간마다 선택하는 것이 아니라, 한개의 Track에 있는 모든 세션을 계속 듣도록 구성되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 여러 트랙 중에서 현재 가장 많은 시간을 투자하고 있는 Container, EKS 환경에 대해서 많은 인사이트를 얻고 싶었기 때문에 &amp;ldquo;&lt;b&gt;AWS에서 컨테이너를 운영한다면? + 파트너와 함께 성장하기&lt;/b&gt;&amp;rdquo; 트랙을 선택하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Key Note&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-31 오후 8.03.47.png&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;1157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DvGiX/btsGdY9kpz3/FmGjvkqI7AFs9mcNNrFaw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DvGiX/btsGdY9kpz3/FmGjvkqI7AFs9mcNNrFaw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DvGiX/btsGdY9kpz3/FmGjvkqI7AFs9mcNNrFaw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDvGiX%2FbtsGdY9kpz3%2FFmGjvkqI7AFs9mcNNrFaw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;757&quot; height=&quot;1157&quot; data-filename=&quot;스크린샷 2024-03-31 오후 8.03.47.png&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;1157&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Keynote 트랙은 AWS와 이번 컨퍼런스의 스폰서로 참여한 각 회사들의 사례를 발표하는 것으로 시작하였습니다. 기술적인 인사이트 뿐만 아니라, 전략, 성장 이야기와 같은 다양한 내용에 대한 세션을 진행하는 것으로 컨퍼런스의 시작을 알렸습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨퍼런스장에 도착한 후 처음에는 여러 스폰서 부스들을 돌아다니느라 몇개의 세션을 듣지 못하였고, 가장 먼저 트레블월렛의 세션을 듣게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;AWS와 클라우드 여정을 함께하고 있는 '트레블월렛' 사례를 통한 기술 혁신 인사이트&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 세션은 비즈니스적인 측면에서 발생한 문제점과 해당 문제점을 AWS를 통해 어떻게 개선하였는지에 대한 내용을 바탕으로 진행되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사내의 모든 인프라를 On-Premise에서 Cloud 환경으로 마이그레이션한 것과 함께 Infra Architecture에 대한 개선 사항을 어떻게 진행하였는지 정리했던 것이 가장 기억에 남았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Infra Architecture 개선 사항&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Compute&lt;/b&gt;: EKS &amp;rarr; Multi Cluster 환경으로 구성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Monitoring&lt;/b&gt;: ELK 기반 모니터링
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cloud Watch Alaram &amp;rarr; Slack Bot으로 이슈 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Network&lt;/b&gt;: AWS DX(Direct Connect), VPN을 이용한 B2B 연결&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Network&lt;/b&gt;: Multi VPC 환경으로 구성 &amp;rarr; 국제 표준 인증인 &lt;a href=&quot;https://www.coalitionfortheicc.org/pcicc&quot;&gt;PCICC&lt;/a&gt; 를 위해&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Integragion&lt;/b&gt;: Visa Cloud Connect(VCC) 기반 Processing 사업 확장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Ochestration&lt;/b&gt;: AWS Support Plan으로 관리적 측면 향상&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Security&lt;/b&gt;: AWS &lt;a href=&quot;https://docs.aws.amazon.com/whitepapers/latest/aws-best-practices-ddos-resiliency/aws-best-practices-ddos-resiliency.html&quot;&gt;DDoS 대응 Best Practices&lt;/a&gt; 적용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Network&lt;/b&gt;: VCC 와 Third Party, Private Link를 이용하여 해결&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dependency&lt;/b&gt;: 서비스 운영 의존성 해결 및 B2B 사업 의존성 해결&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;아임웹의 고도화된 트래픽 관리: AWS를 이용한 70만 고객 사이트 지원 전략&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 세션은 &lt;b&gt;&lt;a href=&quot;https://imweb.me/&quot;&gt;아임웹&lt;/a&gt;&lt;/b&gt;이라는 서비스가 어떤 과정을 통해 발전되었으며, 아키텍처가 어떻게 변화하게 되었는지에 대한 세션이었습니다. 주로, 트래픽이 많이 발생하게 되었을 때 어떤 인프라에서 문제가 발생하게 되었는지, 그리고 그 문제점을 어떻게 개선했는지에 대한 내용이 많았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로, KeyNote 세션 중에서 가장 많은 기술적 인사이트를 얻었던 세션인 것 같습니다. 고객들이 동일한 아임웹 서비스 내에서 서로 DDoS를 주고받는다는 어처구니 없는 상황이 발생할 수 있다는 것을 알게 되기도 했습니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인프라 아키텍처 개선 순서도&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;WAF 추가&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DDoS는 줄일 수 있었음. &amp;rarr; 하지만, &lt;b&gt;실제 트래픽이 차단되는 문제가 발생&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Cloud Front의 비 로그인 유저 캐싱&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비로그인 유저 Cashing으로 50% Origin 트래픽이 감소하게 됨 &amp;rarr; 하지만, &lt;b&gt;주문 폭증 시 장애가 발생&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ECS + Fargate로 Compute 개선&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;느린 Autoscaling &amp;rarr; 빠른 Autoscaling (Container) &amp;rarr; 하지만, &lt;b&gt;DB의 문제가 발생&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Aurora 3 + Serverless + Mixed Cluster&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Aurora 3로 Serverless, DB도 빠른 Autoscaling으로 해결 &amp;rarr; 하지만, &lt;b&gt;빅 이벤트에는 장애가 전파&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자동 트래픽 격리 시스템 추가&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자체 개발 트래픽 격리 시스템 (Tenancy 분리) 추가 &amp;rarr; 하지만, &lt;b&gt;ECS 만으로는 격리가 너무 불편&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;EKS 환경으로 마이그레이션이 필수적이었다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Track 2&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-31 오후 8.10.44.png&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;1115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d9RoWN/btsGfAe2Rnz/REJGLKJr4TTVriZ0N5qRtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d9RoWN/btsGfAe2Rnz/REJGLKJr4TTVriZ0N5qRtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d9RoWN/btsGfAe2Rnz/REJGLKJr4TTVriZ0N5qRtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd9RoWN%2FbtsGfAe2Rnz%2FREJGLKJr4TTVriZ0N5qRtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;1115&quot; data-filename=&quot;스크린샷 2024-03-31 오후 8.10.44.png&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;1115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점심을 먹은 후 오후 시간에는 이번 컨퍼런스에서 가장 중요한 기술적인 인사이트를 얻을 수 있는 Track 시간입니다. Track 2 세션들을 컨테이너에 대한 내용이 주를 이루고 있었으며, MSP 회사들의 사례를 기반으로 파트너사와 어떻게 성장하는지에 대한 세션이 준비되어 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 Track 2 세션을 모두 들었지만 개인적으로 가장 인상깊었던 몇가지의 세션만 아래의 정리하도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;AWS 보안 서비스를 이용하여 안전한 컨테이너 운영환경 만들기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS의 솔루션 아키텍트 분들이 진행하였으며, 현재 사내에서 가장 중점적으로 고민하고 있는 보안적인 측면에 대해 상세한 원칙과 해결 방안에 대해 중점적으로 진행한 세션입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; 컨테이너 보안 원칙&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/wellarchitected/latest/security-pillar/permissions-management.html&quot;&gt;최소 권한 원칙&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;데이터 전송 및 저장 시 데이터 보호(암호화) 원칙&lt;/li&gt;
&lt;li&gt;모든 계층(Layer)에서 효율적인 보안 체계 적용&lt;/li&gt;
&lt;li&gt;Multi Account Architecture 수립
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 Workload의 문제가 다른 Workload에 피해가 전달되지 않도록 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;중앙 집중식 &lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/prescriptive-guidance/latest/security-reference-architecture/log-archive.html&quot;&gt;Log Archive Account&lt;/a&gt; 수립&lt;/li&gt;
&lt;li&gt;시간 절약 및 위험 감소화를 위한 자동화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패턴화된 문제점을 해결하기 위한 자동화를 구성&lt;/li&gt;
&lt;li&gt;하지만, 자동화는 처음이 아닌 해당 문제점의 &lt;b&gt;시간 대비 효율이 발생한 시점에 도입 고려&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; 컨테이너 호스트 보안&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너에 실행된 최적화 OS 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ECS/EKS에 최적화된 AMI, &lt;b&gt;&lt;a href=&quot;https://aws.amazon.com/ko/bottlerocket/&quot;&gt;Bottlerocket&lt;/a&gt;&lt;/b&gt; AMI 도입&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://www.cisecurity.org/benchmark/kubernetes&quot;&gt;K8S CIS benchmark&lt;/a&gt;&lt;/b&gt; 준수 여부를 주기적으로 확인&lt;/li&gt;
&lt;li&gt;Private Subnet에 Work Node 배포&lt;/li&gt;
&lt;li&gt;Work Node의 주기적인 교체 자동화&lt;/li&gt;
&lt;li&gt;Host 접근 최소화 및 감사
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SSM의 Session Manager 사용 고려&lt;/li&gt;
&lt;li&gt;SSH 접근과 Keypair 필요성 제거&lt;/li&gt;
&lt;li&gt;Worker Nodes 를 immutable(불변) 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ECR Image&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ECR의 Scan on Push를 하면, 컨테이너 이미지의 취약점을 검사할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ECR의 Basic은 추가 비용이 존재하지 않으므로, 검토 필요&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;inspector - 고급 스캔은 취약점 스캔까지 가능 (유료)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지속적으로 CV가 된 상황을 가정하여 스캔을 진행함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이미지 Push 시 &lt;code&gt;--image-tag-mutability IMMUTABLE&lt;/code&gt; 속성을 사용하는 것을 권장&lt;/li&gt;
&lt;li&gt;Container Image Life Cycle 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;트래블월렛의 EKS 전환 여정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 컨퍼런스에서 가장 많은 인사이트를 얻은 세션이었습니다. 현재 Legacy 환경의 사내 인프라를 EKS 마이그레이션하는 과정 중에 있는데, 해당 세션에서는 과거에 겪었던 문제점을 EKS로 마이그레이션하면서 겪었던 문제점들을 순차적으로 발표하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; 트레블 월렛 서비스의 초기 EKS 구축&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EKS를 NodeGroup으로 분류하여, &lt;code&gt;Dev&lt;/code&gt; 및 &lt;code&gt;Prod&lt;/code&gt; VPC로 구성함&lt;/li&gt;
&lt;li&gt;Namespace 마다 &lt;b&gt;NLB(IP 고정 목적)&lt;/b&gt;, &lt;b&gt;ALB(Ingress Controller)&lt;/b&gt; 구성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&amp;rarr; ALB(WAF, Ingress Controller)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서비스별 Namespace 격리 &amp;rarr; 파트너사 별 Namespace 격리&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; Multi Cluster 확장의 문제점 (1)&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Management Tool의 증가&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EKS 추가 구축 시 Management Tool 들도 같이 늘어나는 문제점&lt;/li&gt;
&lt;li&gt;Grafana, ArgoCD 관리의 어려움이 발생&lt;/li&gt;
&lt;li&gt;해결하기 위해, &lt;b&gt;Management EKS&lt;/b&gt; 별도 구축
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;나중에 EKS가 늘어나도 Grafana, ArgoCD는 늘어나지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; Multi Cluster 확장의 문제점 (2)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ELB 운영 이슈&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EKS, Namespace 추가 시 NLB, ALB가 같이 추가됨&lt;/li&gt;
&lt;li&gt;NLB, ALB 자원 및 운영 비용 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Security Group 운영 이슈&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;너무 많은 보안 정책들 &lt;code&gt;/24&lt;/code&gt;, &lt;code&gt;/32&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;버전 관리가 안됨 (IaC 사용 안함)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트래픽 제어의 어려움 (Canary 배포)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Canary Pod가 원하는 비율만큼 트래픽을 받기 어려움&lt;/li&gt;
&lt;li&gt;그렇다고 Pod 수를 늘리는 것은 리소스 낭비 (현재 4개로 운영 중)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;istio 도입&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;어플리케이션 네트워크 기능을 유연하고 쉽게 자동화할 수 있는 서비스 메시 플랫폼&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ELB &amp;amp; Securithy Group 관리 해결&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ALB 통합&lt;/li&gt;
&lt;li&gt;Security Group &lt;code&gt;/24&lt;/code&gt;, &lt;code&gt;/32&lt;/code&gt; &amp;rarr; &lt;code&gt;any&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/24&lt;/code&gt;, &lt;code&gt;/32&lt;/code&gt; 보안 정책 관리 및 GitOps를 통합 버전 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트래픽 제어 해결 (Canary 배포)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pod 수를 늘리지 않고 해결&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; Multi Cluster 확장의 문제점 (3)&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;EKS 버전 이슈&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;평균 4개월 한 번씩, 새로운 Kuberenetes 마이너 버전이 릴리즈됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EKS 는 최초 릴리즈 이후 총 &lt;b&gt;26개월&lt;/b&gt;까지 지원한다.&lt;/li&gt;
&lt;li&gt;강제 업그레이드 전에 &lt;b&gt;무중단 버전 업그레이드&lt;/b&gt;가 필요함.&lt;/li&gt;
&lt;li&gt;정식 지원 기간(12개월)마다 업데이트하는 정책 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;EKS 이중화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신규 버전의 EKS 구축&lt;/li&gt;
&lt;li&gt;기존 EKS와 동일 환경 구축 (GitOps)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;해결 방안&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ALB를 통해 EKS 트래픽 조절&lt;/li&gt;
&lt;li&gt;신규버전 EKS Role 트래픽 변경 &amp;rarr; (구버전 EKS의 트래픽 제거)&lt;/li&gt;
&lt;li&gt;트래픽 제거가 확인된 이후에 &lt;b&gt;신규&lt;/b&gt; 버전으로 업그레이드&lt;/li&gt;
&lt;li&gt;트래픽 50%, 50%로 변경 및 Resource (EC2, Pod 등)도 균등하게 나눔&lt;/li&gt;
&lt;li&gt;Prod 환경에서 작업시 트래픽을 완전 제거하여 안전한 작업이 가능하게 구성함&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에필로그&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 컨퍼런스에 참가하면서 백엔드 개발자로 커리어를 변화하기 위한 시기에 컨퍼런스를 듣고 정리하던 &lt;a href=&quot;https://custom-li.tistory.com/119&quot;&gt;여러&lt;/a&gt; &lt;a href=&quot;https://custom-li.tistory.com/189&quot;&gt;게시글&lt;/a&gt;들이 떠올랐습니다. 그당시에는 컨퍼런스에서 발표하는 내용들에 대해 명확하게 이해를 하는 것 보단, 알지못하던 지식을 하나씩 정리하는 것에 가까웠습니다. 하지만, 이번 컨퍼런스에서는 &amp;ldquo;&lt;b&gt;이미 알고 있는 지식을 어떻게 더욱 향상시킬 수 있을까?&lt;/b&gt;&amp;rdquo;에 가까웠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에 비해 시간이 많이 지나기도 했고, 백엔드 개발자로써 커리어를 쌓아나간지 2년이 지났다는 것을 느끼게 되었습니다. 하지만, 아직까지 부족한 점도 많은 것을 깨달았고, 여러 회사에서 맞닿은 문제점을 어떻게 헤쳐나갔는지에 대해 알 수 있었던 것이 너무 좋은 경험이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에도 컨퍼런스를 방문할 수 있는 기회가 있다면, 적극적으로 참여하고 싶습니다.&lt;/p&gt;</description>
      <category>Conference</category>
      <category>AWS Unicorn Day 2024</category>
      <category>AWS 컨퍼런스</category>
      <category>글또9기</category>
      <category>방문기</category>
      <category>컨퍼런스</category>
      <author>커스텀 리</author>
      <guid isPermaLink="true">https://custom-li.tistory.com/216</guid>
      <comments>https://custom-li.tistory.com/216#entry216comment</comments>
      <pubDate>Sun, 31 Mar 2024 23:16:32 +0900</pubDate>
    </item>
    <item>
      <title>ISMS 인증을 받기 위한 인프라 개선 요구사항</title>
      <link>https://custom-li.tistory.com/215</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ISMS.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqMYi8/btsFP7ypvGm/vwGCTHKSDvKjpNijkriYNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqMYi8/btsFP7ypvGm/vwGCTHKSDvKjpNijkriYNK/img.png&quot; data-alt=&quot;ISMS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqMYi8/btsFP7ypvGm/vwGCTHKSDvKjpNijkriYNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqMYi8%2FbtsFP7ypvGm%2FvwGCTHKSDvKjpNijkriYNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;151&quot; height=&quot;151&quot; data-filename=&quot;ISMS.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ISMS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 스타트업은 성공 가능성을 빠르게 검증하는 것을 최우선으로 삼곤 한다. 이 과정에서 보안적인 측면은 종종 후순위로 밀리는데, 이는 나중에 큰 문제를 초래하게 될 수 있다. 만약, 서비스 중인 비즈니스에서 Access Key가 노출되어 중요한 데이터가 외부에 공개되거나, 사용자 정보가 탈취당하는 상황이 발생한다면 어떻게 해야할까? 단순히 AWS 고객센터에 메일을 전달하고 답변을 기다리는 것만으로는 충분하지 않을 것이다. 이러한 긴급한 상황에서는 명확한 대응 정책이 필요하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문제를 예방하고 효과적으로 관리하기 위해서는 ISMS와 같은 인증을 통한 체계적인 정보보호 관리가 필요하게 된다. 이번 게시글에서는 정보보호 관리 체계인 ISMS에 대한 기본 개념과 클라우드 인프라 관점에서 필요한 요구사항들을 정리해보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ISMS&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://isms.kisa.or.kr/main/ispims/intro/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ISMS&lt;/a&gt;(Information Security Management System)는 정보보호를 위한 일련의 조치와 활동이 인증기준에 적합함을 인터넷진흥원 또는 인증기관이 증명하는 제도이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ISMS, 즉 정보보호 관리 체계는 개발 환경에서 접근 권한을 제한하고, 중요한 데이터를 암호화하거나, 장애 대응 프로세스를 수립하는 등 정보 보안 위험을 관리하기 위한 정책과 절차, 기술적 수단을 체계적으로 조합한 관리 체계이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;한걸음 더. ISMS-P&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://isms.kisa.or.kr/main/ispims/intro/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ISMS-P&lt;/a&gt;(Personal information &amp;amp; Information Security Management System)는 정보보호 및 개인정보보호를 위한 일련의 조치와 활동이 인증기준에 적합함을 인터넷진흥원 또는 인증기관이 증명하는 제도이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ISMS&lt;/b&gt;에서는 정보보호를 중점적으로 검증을 진행하였다면, &lt;b&gt;ISMS-P&lt;/b&gt;는 개인정보보호에 대한 부분을 중점적으로 검증하는 관리 체계이다. ISMS-P는 사용자 데이터를 안전하게 저장하고 처리하기 위해 어떤 암호화 기술을 사용하는지, 사용자 데이터 접근 시 어떤 보안 프로토콜을 따라야 하는지 등 구체적인 보안 지침을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 ISMS 인증을 받아야하는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ISMS 인증&lt;/b&gt;은 조직이 국가 표준에 따라 정보 보안 관리 체계를 적절히 운영하고 있음을 외부에게 인증하는 과정 중 하나이다. 해당 인증으로 인해 조직은 정보 보안 관련 위험을 체계적으로 관리하고 있으며, 지속적으로 개선하고 있음을 외부에 공표하는 것과 동일한 역할을 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ISMS 인증을 위한 인프라 요구사항&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-16 오후 11.49.47.png&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;553&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DMMgg/btsFP8xktOm/qfvh0uTpFrQoCjblokKr01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DMMgg/btsFP8xktOm/qfvh0uTpFrQoCjblokKr01/img.png&quot; data-alt=&quot;ISMS-P 요구사항(https://isms.kisa.or.kr/main/ispims/intro/)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DMMgg/btsFP8xktOm/qfvh0uTpFrQoCjblokKr01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDMMgg%2FbtsFP8xktOm%2Fqfvh0uTpFrQoCjblokKr01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;588&quot; height=&quot;361&quot; data-filename=&quot;스크린샷 2024-03-16 오후 11.49.47.png&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;553&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ISMS-P 요구사항(https://isms.kisa.or.kr/main/ispims/intro/)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ISMS 인증을 받기 위해서는 정보보호와 관련된 수많은 요구사항이 존재할 것이다. 하지만, 이번 게시글에서는 사내 인프라 관리자 또는 DevOps 개발자를 기준으로 설명할 것이며, AWS 클라우드 인프라를 이용하여 ISMS를 받고자 할 때 주목해야할 요구사항들을 살펴볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정보보안 거버넌스 및 정책 수립&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 사내 인프라는 온프레미스 또는 클라우드 환경으로 구현할 것이다. 해당 인프라에서 정보 보안 정책을 어떻게 수립하는지에 따라 우리는 인프라의 구성요소와 대응 방안을 명확하게 알 수 있을것이다. 즉, 명확한 &lt;a href=&quot;https://wiki.wikisecurity.net/governance:%EC%A0%95%EB%B3%B4%EB%B3%B4%EC%95%88_%EA%B1%B0%EB%B2%84%EB%84%8C%EC%8A%A4%EC%9D%98_%EC%A0%95%EC%9D%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;정보보안 거버넌스&lt;/a&gt;와 정책을 수립하게 되었을 때, 더욱 안전한 인프라라는 것을 인지할 수 있게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 정책을 수립할 때에는 수집하는 데이터를 어떻게 보호할 것인지, 개발자 또는 외부 사용자가 사내 인프라를 어떻게 접근하는지, 재해가 발생할 때, 어떻게 복구할 것인지에 대한 정책이 명확하게 수립되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그렇게 되었을 때, 서비스에서 문제가 발생하더라도 가이드라인을 통해 해결 방안을 따를 수 있게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인프라 리소스 관리 정책 수립&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 클라우드 인프라 내에서 리소스 관리 정책은 소유권과 책임에 초점을 맞춰야할 것이다. 예를 들어, 조직의 서비스를 이용하는 사용자의 정보와 EC2 서버의 콘솔에서 출력되는 데이터는 동일한 책임을 가지지 않을 것이다. 이러한 차이를 명확하게 구분하는 것이 리소스 관리에 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, AWS 리소스에 태깅 정책을 도입한다면 리소스의 소유권을 명확하게 분류할 수 있을 것이다. 이로인해, 모니터링 기능을 구현할 때에도 분류된 소유권에 따라 리소스 상태를 실시간으로 파악할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼, 인프라에서 관리되는 여러 리소스들에 명확한 소유권과 책임을 부여하는 정책을 부여한다면, 리소스의 효율적인 활용과 더불어 보안 유지에도 강점을 가질 수 있게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;접근 제어 정책 수립&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인프라를 관리하는 모든 개발자들은 동일한 권한을 가질 순 없다. 사내에서 A, B팀이 존재한다고 가정하였을 때, A 팀은 A 팀의 리소스만 관리해야할 것이고, B 팀은 B 팀의 리소스만 관리해야할 것이다. 이처럼 인프라 리소스를 사용하기 위한 접근 제어 정책을 수립해야할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근 제어 정책을 수립해야할 때 고려해야 하는 것은 &amp;ldquo;&lt;b&gt;최소한&lt;/b&gt;&amp;rdquo;의 권한을 부여해야한다는 것이다. 일반적으로 팀 내에서 인프라 관리자가 아닌 일반 개발자 또한 특정 인프라 리소스에 대한 정보를 조회하기 위한 작업을 수행할 것이다. 그렇다면 해당 개발자에게는 어떤 권한을 줘야하는 것일까? 바로 &amp;ldquo;&lt;b&gt;팀 내 소속된 특정 인프라 리소스의 READ 권한&lt;/b&gt;&amp;rdquo;만 할당해야하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 해당 접근 제어 정책은 사내 개발자에게만 종속되는 것이 아닌, 외부 시스템이 접근할 수 있는 인프라 리소스에 대한 접근 제어 정책도 함께 수립되어야할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼, 인프라 리소스에 대한 접근 제어 정책을 수립할 때에는 최소한의 권한을 부여해야하며, 잘못된 접근 제어 정책을 수립하게 되었을 경우 많은 문제가 발생한다는 것을 인지해야할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 암호화 정책 수립&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인프라를 아무리 안전하게 관리한다 하더라도, 인프라 내 데이터는 언제나 보안 위험에 노출될 수 있다. 데이터 탈취 사고가 발생했을 때, 암호화된 데이터는 외부에서 읽을 수 없게 보호될 것이다. 이는 데이터 암호화 정책이 필수적인 요구사항이라는 것을 의미하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 해당 데이터 암호화 정책은 관리중인 데이터 뿐만 아니라, 내부 또는 외부로 전송되는 데이터 또한 암호화를 하여 해당 데이터를 보호해야할 것이다. 일반적으로 AWS 클라우드 인프라를 구성하게 된다면, KMS와 같은 서비스를 이용하여 데이터를 암호화하여 보호한다거나, SSL/TLS와 같은 기술을 이용하여 전송되는 데이터를 암호화하여 관리할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼, 인프라에서 발생 또는 전달되는 모든 데이터에 대한 암호화 정책을 수립한다면 잘못된 보안 관리로 인해 발생하는 문제에 대한 해결방안이 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;운영 보안 정책 수립&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 인프라를 구축하고 난 이후에는 주로 EC2, EKS 같은 서비스의 직접적인 문제에만 신경을 썼을 것이다. 그러나, 정보 보호 관점에서는 예상치 못한 인프라 리소스의 동작을 감지하고 대응하는 것이 필수적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, AWS Access Key가 외부에 노출되어 악의적인 행위로 이어지는 경우를 가정해보자. 대부분 해당 피해를 인지하는 시점은 요금이 발생하거나, 엄청난 시간이 지난 이후에 우연히 발견하게 될 것이다. 이를 방지하기 위해서는 로그를 관리하거나 모니터링 시스템을 도입하여 잠재적인 보안 위협을 조기에 식별하고 대응할 수 있는 운연 보안 정책이 필요하게 된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 보안 정책은 위와 같이 인프라에서 발생되는 보안 사고를 어떻게 식별하고 대응할 것인지에 대한 명확한 지침을 수립하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장애 대응 프로세스 수립&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 사고가 발생하였을 때는 이미 돌이킬 수 없는 상황일 것이다. 그렇다면, 이미 보안 사고가 발생하였을 때에는 어떻게 해야할까? 긴급한 상황에서 AWS 고객센터에 전화하거나 외부에 자문을 구하는 것은 결코 빠른 방법이 아닐 것이다. 이처럼, 보안 사고가 발생하였을 때를 가정한 장애 대응 프로세스를 수립한다면, 급박한 상황 속에서도 여유롭게 대처할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 사고가 발생하지 않는 것이 가장 중요한 것이지만, 보안 사고는 언제나 발생할 수 있다는 생각으로 접근하는 것이 더욱 안전한 인프라를 구축하는 과정 중 하나일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 Document&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ISMS-P 인증기준 안내서
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://isms.kisa.or.kr/main/ispims/notice/&quot;&gt;https://isms.kisa.or.kr/main/ispims/notice/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Infrastructure</category>
      <category>ISMS</category>
      <category>ISMS-P</category>
      <author>커스텀 리</author>
      <guid isPermaLink="true">https://custom-li.tistory.com/215</guid>
      <comments>https://custom-li.tistory.com/215#entry215comment</comments>
      <pubDate>Sun, 17 Mar 2024 02:52:36 +0900</pubDate>
    </item>
    <item>
      <title>효율적인 인프라 관리를 위한 Terraform 모듈 입문하기</title>
      <link>https://custom-li.tistory.com/214</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;terraform.225x256.png&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cp1wzX/btsFnVSX5Rb/ucmeZPN5exrbIbhYX9k8i1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cp1wzX/btsFnVSX5Rb/ucmeZPN5exrbIbhYX9k8i1/img.png&quot; data-alt=&quot;Terraform&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cp1wzX/btsFnVSX5Rb/ucmeZPN5exrbIbhYX9k8i1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcp1wzX%2FbtsFnVSX5Rb%2FucmeZPN5exrbIbhYX9k8i1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;142&quot; height=&quot;162&quot; data-filename=&quot;terraform.225x256.png&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Terraform&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;  &lt;/span&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Terraform&lt;/b&gt;과 같은 &lt;b&gt;Infrastructure as Code(IaC)&lt;/b&gt; 도구를 사용한다면, 대규모 인프라를 효츌적으로 관리할 수 있다. 그러나, 단일 Terraform 프로젝트로 대규모 인프라를 관리하게 된다면, 많은 리소스들이 하나의 코드 베이스에 포함되어 관리가 복잡해질 수 있다. 이로 인해 코드의 가독성이 떨어지고, 유지보수가 어려워질 뿐만 아니라 재사용성 또한 감소하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terraform Module을 사용하게 된다면 이러한 문제를 해결할 수 있게 된다. 이번 게시글에서는 모듈이란 무엇인지, 어떻게 Terraform Module을 사용할 수 있는지에 대해 알아보도록하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &lt;span&gt; &lt;/span&gt;Terraform Module&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://developer.hashicorp.com/terraform/language/modules&quot;&gt;Terraform Module&lt;/a&gt;&lt;/b&gt;은 코드 베이스를 논리적으로 분리하여 각각의 인프라 리소스를 독립적으로 관리할 수 있게 해주는 문법이다.복잡한 인프라를 여러 프로젝트로 나누어 관리할 수 있게 되어 중복되는 코드를 줄여 재사용성과 유지보수성을 크게 향상시킬 수 있는 장점을 가지고 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Terraform Module이 필요한 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 초기 설정을 관리하는 프로젝트, EC2 인스턴스를 생성하는 프로젝트, CloudWatch 설정을 구성하는 프로젝트를 별도로 관리한다고 가정해보자. 여기서, 단일 Terraform 프로젝트로 이러한 컴포넌트들을 관리하게 된다면, 프로젝트의 복잡성이 증가하고, &lt;code&gt;terraform destroy&lt;/code&gt; 명령어 실행 시, 원치 않는 리소스까지 삭제될 위험이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제점을 개선하기 위해, 우리는 Terraform Module을 이용하여 원하는 특정 리소스를 모듈 단위로 관리할 수 있도록 프로젝트를 명확하게 분리하는 작업을 진행할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Terraform Module 시작하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terraform 모듈은 아래와 같은 문법으로 사용하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;module &quot;vpc&quot; {
  source = &quot;../modules/vpc&quot;

  aws_region = var.aws_region

  vpc_cidr = var.vpc_cidr
  vpc_name = var.vpc_name

  public_subnets_cidr  = var.public_subnets_cidr
  private_subnets_cidr = var.private_subnets_cidr
  availability_zones   = var.availability_zones
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시에서, &lt;code&gt;source&lt;/code&gt; 매개 변수는 모듈의 위치를 지정한다. 여기서는 상위 폴더에 있는 &lt;code&gt;modules/vpc&lt;/code&gt; 디렉토리에 있는 VPC 모듈을 참조하게 된다. 이 모듈은 VPC, Subnet 리소스를 생성하고 관리하게 되는데, 해당 변수들은 모듈에 필요한 입력값을 제공하고, 모듈 내에서는 이 변수들을 사용하여 리소스를 구성하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ Provider 정의하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terraform Module을 구성할 때, 가장 먼저 수행해야할 작업은 모듈을 위한 별도의 디렉토리를 생성하는 것이다. 모듈과 일반 Terraform 프로젝트 사이에는 구조적인 차이가 존재하지 않기 때문에, 모든 리소스를 정의하기 위한 기본 설정 파일인 &lt;code&gt;versions.tf&lt;/code&gt; 를 준비하는 것이 중요하다. 이 파일에서는 사용할 클라우드 서비스 프로바이더(CSP)와 Terraform의 버전, 그리고 필요한 프로바이더를 명시하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;provider &quot;aws&quot; {
  # provider는 terraform이 사용할 클라우드 서비스를 명시해준다.
  region = var.aws_region
}

terraform {
  # terraform 버전을 명시해준다.
  required_version = &quot;&amp;gt;= 1.3&quot;

  # 필수로 사용할 provider를 명시해준다.
  required_providers {
    aws = {
      source  = &quot;hashicorp/aws&quot;
      version = &quot;&amp;gt;= 5.0.0&quot;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈을 정의할 때 사용할 프로바이더와 버전을 명시함으로써, 모듈을 사용하는 프로젝트에서 해당 모듈에 대한 제약사항을 미리 인지할 수 있다. 이는 모듈의 호환성을 보장할 수 있게 되며, 해당 모듈을 호출하는 개발자가 모듈을 더욱 쉽게 관리할 수 있게 해주도록 도와주게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ Variables 정의하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terraform Module이 VPC와 Subnet을 생성할 수 있도록, 필요한 인프라 리소스 정보를 외부에서 주입받을 수 있도록 &lt;code&gt;variable&lt;/code&gt;을 정의해야한다. 이렇게 구현하게 된다면 해당 모듈이 외부에서 원하는 동작을 명확하게 수행할 수 있게 되어 모듈의 재사용성과 유연성이 높아지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;variable &quot;aws_region&quot; {
  description = &quot;The AWS region to deploy to&quot;
  type        = string
}

variable &quot;vpc_cidr&quot; {
  description = &quot;The CIDR block for the VPC&quot;
  type        = string
}

variable &quot;vpc_name&quot; {
  description = &quot;The name of the VPC&quot;
  type        = string
}

variable &quot;public_subnets_cidr&quot; {
  description = &quot;The CIDR blocks for the public subnet&quot;
  type        = list(string)
}

variable &quot;private_subnets_cidr&quot; {
  description = &quot;The CIDR blocks for the private subnet&quot;
  type        = list(string)
}

variable &quot;availability_zones&quot; {
  description = &quot;The availability zones to deploy to&quot;
  type        = list(string)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ Resource 정의하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 필요 정보를 &lt;code&gt;variable&lt;/code&gt;에서 주입받았다면, 이제 VPC와 관련된 리소스를 실제로 정의해야한다. 먼저 VPC를 정의하고, 이 VPC 내에서 Public, Private Subnet을 정의하도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;##################
### VPC
##################
resource &quot;aws_vpc&quot; &quot;this&quot; {
  cidr_block = var.vpc_cidr

  tags = {
    Name = var.vpc_name
  }
}

##################
### Subnet
##################

locals {
  public_subnet_length     = length(var.public_subnets_cidr)
  private_subnet_length    = length(var.private_subnets_cidr)
  availability_zone_length = length(var.availability_zones)
}

resource &quot;aws_subnet&quot; &quot;public&quot; {
  count = local.public_subnet_length

  vpc_id     = aws_vpc.this.id
  cidr_block = var.public_subnets_cidr[count.index]

  map_public_ip_on_launch = true # Public IP를 자동으로 할당
  availability_zone       = var.availability_zones[count.index % local.availability_zone_length]

  tags = {
    Name        = &quot;public-subnet-${count.index + 1}&quot;
    NetworkType = &quot;Public&quot;
  }
}

resource &quot;aws_subnet&quot; &quot;private&quot; {
  count = local.private_subnet_length

  vpc_id     = aws_vpc.this.id
  cidr_block = var.private_subnets_cidr[count.index]

  availability_zone = var.availability_zones[count.index % local.availability_zone_length]

  tags = {
    Name        = &quot;private-subnet-${count.index + 1}&quot;
    NetworkType = &quot;Private&quot;
  }
}

##################
### Route Table
##################

resource &quot;aws_route_table&quot; &quot;public&quot; {
  vpc_id = aws_vpc.this.id

  tags = {
    Name        = &quot;public-route-table&quot;
    NetworkType = &quot;Public&quot;
  }
}

resource &quot;aws_internet_gateway&quot; &quot;this&quot; {
  vpc_id = aws_vpc.this.id
}

resource &quot;aws_route&quot; &quot;public&quot; {
  route_table_id         = aws_route_table.public.id
  destination_cidr_block = &quot;0.0.0.0/0&quot;
  gateway_id             = aws_internet_gateway.this.id
}

resource &quot;aws_route_table_association&quot; &quot;public&quot; {
  count = local.public_subnet_length

  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

resource &quot;aws_route_table&quot; &quot;private&quot; {
  vpc_id = aws_vpc.this.id

  tags = {
    Name        = &quot;private-route-table&quot;
    NetworkType = &quot;Private&quot;
  }
}

resource &quot;aws_route_table_association&quot; &quot;private&quot; {
  count = local.private_subnet_length

  subnet_id      = aws_subnet.private[count.index].id
  route_table_id = aws_route_table.private.id
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 내에서 생성하는 네트워크 관련 리소스들, 즉 Subnet과 Route Table에는 &lt;code&gt;NetworkType&lt;/code&gt; 태그를 추가하였다. 이렇게 태그를 추가하게 되었을 때, 해당 태그를 바탕으로 리소스가 어떤 역할을 담당하는지 알 수 있을 뿐더러 Subnet에 대한 정보를 명확하게 알지 못하더라도 Terraform Data에서 태그를 바탕으로 필터링할 수 있어 더욱 편리하게 리소스를 관리할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4️⃣ Resource 정보 내보내기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 리소스를 정의하였다면, 해당 모듈에서 생성된 다양한 정보를 외부로 전달해줄 수 있어야한다. 우리가 일반적으로 사용하였던 &lt;code&gt;output&lt;/code&gt;을 통해 생성된 리소스 정보를 외부로 보낼 수 있게 되는데, 일반적인 Terraform 프로젝트에서는 &lt;code&gt;output&lt;/code&gt;이 Terminal에 출력되는 용도로 사용하였지만, Module에서는 &lt;code&gt;output&lt;/code&gt;이 외부로 전달해주는 역할을 담당하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 생성된 VPC와 Subnet과 관련된 정보를 외부로 전달하도록 구현해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;output &quot;vpc_id&quot; {
  value = aws_vpc.this.id
}

output &quot;public_subnet_ids&quot; {
  value = aws_subnet.public[*].id
}

output &quot;private_subnet_ids&quot; {
  value = aws_subnet.private[*].id
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5️⃣ Module 호출하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 vpc 모듈 구현이 완료되었으므로, 이 모듈을 사용하여 실제 프로젝트를 구성해야 할 것이다. 모듈을 호출하기 위해 &lt;code&gt;module&lt;/code&gt; 문법을 사용하고, &lt;code&gt;terraform init&lt;/code&gt; 명령어로 모듈을 초기화한 후, &lt;code&gt;terraform apply&lt;/code&gt; 명령어로 인프라를 배포해보도록하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;module &quot;vpc&quot; {
  source = &quot;../modules/vpc-module&quot;

  aws_region = &quot;ap-northeast-2&quot;

  vpc_cidr = &quot;10.0.0.0/16&quot;
  vpc_name = &quot;tf-module-vpc&quot;

  public_subnets_cidr  = [&quot;10.0.16.0/20&quot;, &quot;10.0.32.0/20&quot;]
  private_subnets_cidr = [&quot;10.0.48.0/20&quot;, &quot;10.0.64.0/20&quot;]
  availability_zones   = [&quot;ap-northeast-2a&quot;, &quot;ap-northeast-2c&quot;]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 4.30.23.png&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUVXS1/btsFme6B5TB/M4Ddndgh0byzP6kkRXzNDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUVXS1/btsFme6B5TB/M4Ddndgh0byzP6kkRXzNDk/img.png&quot; data-alt=&quot;terraform init&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUVXS1/btsFme6B5TB/M4Ddndgh0byzP6kkRXzNDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUVXS1%2FbtsFme6B5TB%2FM4Ddndgh0byzP6kkRXzNDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;127&quot; data-filename=&quot;스크린샷 2024-03-03 오후 4.30.23.png&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;terraform init&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;terraform init&lt;/code&gt; 명령어는 Terraform 프로젝트를 초기화하는데, 더불어 필요한 모듈 및 프로바이더 플러그인을 다운로드하게된다. 이후 &lt;code&gt;terraform apply&lt;/code&gt; 를 실행하면, 설정된 VPC와 서브넷이 생성된다. 이 과정으로 통해, Terraform 모듈을 이용하여 인프라를 구성할 수 있게 되며, 성공적으로 1개의 VPC와 각 2개의 Public, Private Subnet이 생성되는 것을 확인할 수 있게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Directory Structure&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.
├── module-example
│&amp;nbsp;&amp;nbsp; ├── main.tf
│&amp;nbsp;&amp;nbsp; ├── provider.tf
│&amp;nbsp;&amp;nbsp; └── variables.tf
└── modules
    └── vpc
        ├── main.tf
        ├── outputs.tf
        ├── variables.tf
        └── versions.tf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-03 오후 5.27.29.png&quot; data-origin-width=&quot;1557&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/94bD9/btsFt3PD55n/c0Rgu4fUr0rutDBXzG6GKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/94bD9/btsFt3PD55n/c0Rgu4fUr0rutDBXzG6GKk/img.png&quot; data-alt=&quot;VPC, Subnet Resources&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/94bD9/btsFt3PD55n/c0Rgu4fUr0rutDBXzG6GKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F94bD9%2FbtsFt3PD55n%2Fc0Rgu4fUr0rutDBXzG6GKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1557&quot; height=&quot;519&quot; data-filename=&quot;스크린샷 2024-03-03 오후 5.27.29.png&quot; data-origin-width=&quot;1557&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VPC, Subnet Resources&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최종적으로, 위와 같은 폴더 구조와 함께 VPC, Subnet 리소스가 생성된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terraform Module을 사용함으로써, 복잡한 인프라 리소르를 여러 프로젝트로 분리하여 효율적으로 관리할 수 있게 되었다. 이번 예시에서는 VPC 관련 모듈에 초점을 맞추었지만, 실제로는 VPC, EC2, ELB, ECR, ECS, Code Pipeline, Route 5r3 등 다양한 리소스를 포함하는 모듈을 생성하여 전체 백엔드 아키텍처를 설계할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 여러 모듈을 하나에 프로젝트에 구성하게 되었을 때 발생하는 문제점을 여러 모듈로 분리함으로써 가독성 또한 증가하게 될 것이며, Terraform Module 또한 반복문으로 생성 가능하기 때문에 여러 리소스를 더욱 가변적으로 관리할 수 있게 되어, Terraform 프로젝트가 더욱 퀄리티가 높아지게 될 것이다.&lt;/p&gt;</description>
      <category>Infrastructure/Terraform</category>
      <category>networktype</category>
      <category>subnet</category>
      <category>Terraform</category>
      <category>Terraform Module</category>
      <category>VPC</category>
      <author>커스텀 리</author>
      <guid isPermaLink="true">https://custom-li.tistory.com/214</guid>
      <comments>https://custom-li.tistory.com/214#entry214comment</comments>
      <pubDate>Sun, 3 Mar 2024 17:32:34 +0900</pubDate>
    </item>
  </channel>
</rss>