<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Cloud</title>
    <link>https://story0410.tistory.com/</link>
    <description>dml113의 AWS 이야기</description>
    <language>ko</language>
    <pubDate>Tue, 7 Apr 2026 15:27:22 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>dml113</managingEditor>
    <item>
      <title>[Production Traffic] 특정 API 호출 시 CPU와 Memory가 급증할 경우를 대비한 사전 시뮬레이션</title>
      <link>https://story0410.tistory.com/39</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;들어가며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;장애는 무작위가 아닌 &amp;lsquo;예고된 반복&amp;rsquo;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;운영 환경에서 장애는 종종 예고 없이 찾아오는 것처럼 보이지만, &lt;b&gt;대부분은 이미 수많은 징후를 포함한 반복적인 패턴&lt;/b&gt; 속에서 발생합니다.&amp;nbsp;&amp;nbsp; &lt;br /&gt;그중 대표적인 것이 &lt;b&gt;특정 API의 리소스 폭증&lt;/b&gt;입니다. &lt;br /&gt;&lt;br /&gt;이번&amp;nbsp;포스팅에서는&amp;nbsp;다음과&amp;nbsp;같은&amp;nbsp;상황을&amp;nbsp;가정하여&amp;nbsp;테스트를&amp;nbsp;진행했습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;gt;&amp;nbsp; &quot;만약 특정 API가 예상치 못하게 CPU와 Memory를 고갈시킨다면, 현재의 ECS Auto Scaling 정책은 이 상황에 얼마나 잘 대응할 수 있을까?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;코드는&amp;nbsp;수정하지&amp;nbsp;않고,&amp;nbsp;인프라&amp;nbsp;수준에서&amp;nbsp;ECS&amp;nbsp;(EC2&amp;nbsp;타입)&amp;nbsp;기반&amp;nbsp;Auto&amp;nbsp;Scaling&amp;nbsp;정책을&amp;nbsp;설정하고&amp;nbsp;`/v1/stress`&amp;nbsp;API를&amp;nbsp;반복&amp;nbsp;호출해&amp;nbsp;실제&amp;nbsp;리소스&amp;nbsp;폭증을&amp;nbsp;유발해&amp;nbsp;봤습니다.&amp;nbsp;&amp;nbsp; &lt;br /&gt;이를 통해 &lt;b&gt;지표 기반 Auto Scaling 정책의 실효성, 한계, 그리고 보완 방향&lt;/b&gt;을 사전에 점검해볼 수 있었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 상황 설명&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;운영 환경에서는 전체 트래픽은 정상적이어도 &lt;b&gt;특정 API&lt;/b&gt;로 인해 CPU 사용률이 급격히 상승하는 경우가 존재합니다. 또한 예기치 못한 리소스 폭증은 ECS 서비스 전체의 안정성을 해치고, 때로는 EC2 인스턴스 자체가 고갈될 수도 있기에 사후 대응이 아닌 사전 예방을 위해, 이런 상황을 &lt;b&gt;테스트 환경에서 직접 시뮬레이션&lt;/b&gt; 해보기로 결정하였습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기존&amp;nbsp;대응&amp;nbsp;방식과&amp;nbsp;한계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;초기에는&amp;nbsp;다음과&amp;nbsp;같은&amp;nbsp;ECS&amp;nbsp;Task&amp;nbsp;사양으로&amp;nbsp;서비스를&amp;nbsp;구성했습니다:&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 89px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;항&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;설정&amp;nbsp;값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Task&amp;nbsp;수&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;2개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Task&amp;nbsp;spec&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;0.25&amp;nbsp;vCPU,&amp;nbsp;0.5&amp;nbsp;GB&amp;nbsp;Memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Launch&amp;nbsp;type&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;EC2&amp;nbsp;(ECS&amp;nbsp;Capacity&amp;nbsp;Provider)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;Scaling&amp;nbsp;정책&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;없음&amp;nbsp;(고정&amp;nbsp;Task&amp;nbsp;수)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이 구성으로도 일반적인 API 요청 처리에는 큰 문제가 없었지만, `/v1/stress`처럼 CPU와 Memory를 집중적으로 사용하는 요청이 들어올 경우 &lt;b&gt;각 Task가 리소스를 빠르게 고갈&lt;/b&gt;하며, 다음과 같은 문제가 발생했습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;- CPU 사용률 90% 이상&lt;/b&gt;이 수 분간 지속&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;- Task 수 고정으로 인해 Auto Scaling 불가&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1792&quot; data-origin-height=&quot;653&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PGdt1/btsPF2GOM8r/NMEUdUSeSwa3FsdJKVFVFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PGdt1/btsPF2GOM8r/NMEUdUSeSwa3FsdJKVFVFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PGdt1/btsPF2GOM8r/NMEUdUSeSwa3FsdJKVFVFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPGdt1%2FbtsPF2GOM8r%2FNMEUdUSeSwa3FsdJKVFVFK%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;1792&quot; height=&quot;653&quot; data-origin-width=&quot;1792&quot; data-origin-height=&quot;653&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;성능 측정 결과&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;해당 조건에서 K6로 /v1/stress API를 테스트했을 때의 수치는 다음과 같습니다:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baPvDA/btsPEpb7Ptu/h1v769hLnc9KOnzcbcjK0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baPvDA/btsPEpb7Ptu/h1v769hLnc9KOnzcbcjK0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baPvDA/btsPEpb7Ptu/h1v769hLnc9KOnzcbcjK0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaPvDA%2FbtsPEpb7Ptu%2Fh1v769hLnc9KOnzcbcjK0k%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;1378&quot; height=&quot;107&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;문제 해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이러한 문제들을 해결하기 위해, 다음 단계에서는 다음과 같은 개선을 시도하였습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;-&lt;/b&gt;&amp;nbsp;&lt;b&gt;Task 사양 상향 조정&lt;/b&gt; (0.25 &amp;rarr; 0.5 vCPU)&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;- ECS Service Auto Scaling 정책 적용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;적용 방법 (구체 구성)&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;앞서 언급한 두 가지 전략 &amp;mdash; &lt;b&gt;Task 사양 상향 조정&lt;/b&gt;과 &lt;b&gt;Auto Scaling 정책 적용&lt;/b&gt; &amp;mdash; 을 실험 환경에 직접 반영해 보았습니다.&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. Task 사양 상향 조정&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;먼저 Task 정의(Task Definition)의 리소스 설정을 다음과 같이 변경하였습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;기존 값&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;&lt;b&gt;변경 값&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;vCPU&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;0.25&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;&lt;b&gt;0.5&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Memory&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;0.5 GB&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;&lt;b&gt;1 GB&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;기존에 비해 약 &lt;b&gt;2배 이상 리소스를 확보&lt;/b&gt;함으로써, 각 Task가 더 많은 요청을 감당할 수 있도록 했습니다.&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. ECS Auto Scaling 정책 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Task 사양만 높이는 것은 &lt;b&gt;일시적 완화책&lt;/b&gt;일 뿐, 트래픽이 지속적으로 유입되거나 급격히 증가하는 경우에는 &lt;b&gt;Task 수 자체의 자동 확장&lt;/b&gt;이 필요합니다.&lt;br /&gt;이를 위해 ECS Service에 &lt;b&gt;CPU 사용률 기반 Auto Scaling&lt;/b&gt;을 설정했습니다.&lt;/p&gt;
&lt;p data-end=&quot;841&quot; data-start=&quot;830&quot; data-ke-size=&quot;size16&quot;&gt;설정 조건:&lt;/p&gt;
&lt;p data-end=&quot;841&quot; data-start=&quot;830&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;정책 유형&lt;/b&gt;: Target Tracking&lt;/p&gt;
&lt;p data-end=&quot;841&quot; data-start=&quot;830&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;Target CPU 사용률&lt;/b&gt;: 60%&lt;/p&gt;
&lt;p data-end=&quot;841&quot; data-start=&quot;830&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;최소 Task 수&lt;/b&gt;: 2개&lt;/p&gt;
&lt;p data-end=&quot;841&quot; data-start=&quot;830&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;최대 Task 수&lt;/b&gt;: 6개&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;size18&quot;&gt;테스트 조건 요약&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;200&quot; data-start=&quot;168&quot;&gt;테스트 도구: &lt;a data-end=&quot;198&quot; data-start=&quot;178&quot;&gt;k6&lt;/a&gt;&lt;/li&gt;
&lt;li data-end=&quot;225&quot; data-start=&quot;201&quot;&gt;요청 API: /v1/stress&lt;/li&gt;
&lt;li data-end=&quot;244&quot; data-start=&quot;226&quot;&gt;요청 수: 동시접속자 5명, 60초 동안 진행&lt;/li&gt;
&lt;li data-end=&quot;377&quot; data-start=&quot;297&quot;&gt;테스트 목적: &lt;b&gt;리소스 폭증 시 ECS Auto Scaling 정책이 실제로 응답 속도 및 시스템 자원 사용에 어떤 영향을 주는지&lt;/b&gt; 측정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;개선 전 (기존 고정 Task 수, 0.25 vCPU / 0.5GB Memory)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;평균 응답 시간 (avg)&lt;/td&gt;
&lt;td&gt;356.1ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;중앙값 응답 시간 (med)&lt;/td&gt;
&lt;td&gt;345.2ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;90 percentile (p90)&lt;/td&gt;
&lt;td&gt;622.8ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;95 percentile (p95)&lt;/td&gt;
&lt;td&gt;739.5ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU 사용률&lt;/td&gt;
&lt;td&gt;&lt;b&gt;90% 이상&lt;/b&gt; 지속&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;실패율&lt;/td&gt;
&lt;td&gt;0%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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-origin-width=&quot;1378&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baPvDA/btsPEpb7Ptu/h1v769hLnc9KOnzcbcjK0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baPvDA/btsPEpb7Ptu/h1v769hLnc9KOnzcbcjK0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baPvDA/btsPEpb7Ptu/h1v769hLnc9KOnzcbcjK0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaPvDA%2FbtsPEpb7Ptu%2Fh1v769hLnc9KOnzcbcjK0k%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;1378&quot; height=&quot;107&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;개선 후 (0.5 vCPU / 1GB Memory + Auto Scaling 2~6 Tasks)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;평균 응답 시간 (avg)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;290.6ms&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;중앙값 응답 시간 (med)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;287.4ms&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;90 percentile (p90)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;393.9ms&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;95 percentile (p95)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;413.7ms&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU 사용률 (평균)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;47%&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;실패율&lt;/td&gt;
&lt;td&gt;0%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcvaxP/btsPEtyPist/K37POja9M8rrml3xq4Ip61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcvaxP/btsPEtyPist/K37POja9M8rrml3xq4Ip61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcvaxP/btsPEtyPist/K37POja9M8rrml3xq4Ip61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcvaxP%2FbtsPEtyPist%2FK37POja9M8rrml3xq4Ip61%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;1408&quot; height=&quot;113&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;113&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;1079&quot; data-start=&quot;1066&quot; data-ke-size=&quot;size18&quot;&gt;개선 효과&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1276&quot; data-start=&quot;1081&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1126&quot; data-start=&quot;1081&quot;&gt;평균 응답 시간이 약 &lt;b&gt;65ms 개선&lt;/b&gt; (약 &lt;b&gt;18% 성능 향상&lt;/b&gt;)&lt;/li&gt;
&lt;li data-end=&quot;1178&quot; data-start=&quot;1127&quot;&gt;지연이 심한 상위 10~5% 구간(p90/p95)에서도 &lt;b&gt;300ms 이상 응답 확보&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1231&quot; data-start=&quot;1179&quot;&gt;CPU 사용률이 안정 구간(40~50%)에 위치하면서, &lt;b&gt;스케일 아웃 트리거 발생&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1276&quot; data-start=&quot;1232&quot;&gt;Task 수가 증가하면서 리소스를 유연하게 분산, &lt;b&gt;서비스 안정성 향상&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1761&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tflbT/btsPFsTh5K8/panBkiYK3za1DqaC339QvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tflbT/btsPFsTh5K8/panBkiYK3za1DqaC339QvK/img.png&quot; data-alt=&quot;개선하기 전과 개선한 후의 지표&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tflbT/btsPFsTh5K8/panBkiYK3za1DqaC339QvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtflbT%2FbtsPFsTh5K8%2FpanBkiYK3za1DqaC339QvK%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;1761&quot; height=&quot;454&quot; data-origin-width=&quot;1761&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;개선하기 전과 개선한 후의 지표&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;회고&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;내가 배운 점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Auto Scaling은 단순히 '붙이면 끝'이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;ECS Auto Scaling을 적용하기 전까지는 단순히 Task 수만 늘리면 되는 줄 알았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;하지만 실제로는 아래와 같은 &lt;b&gt;튜닝 요소&lt;/b&gt;들이 중요하다는 점을 깨달았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;Scaling 정책의 트리거 지표(CPU, Memory 등)&lt;/b&gt; 선정&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;Scaling 반영 주기(Cooldown time) &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;최소/최대 Task 수의 적절한 범위&lt;/b&gt; 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 코드를 변경하지 않아도 &amp;lsquo;운영 품질&amp;rsquo;을 충분히 개선할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;코드 레벨에서 비동기 처리, 리팩토링 등은 물론 중요하지만, &lt;b&gt;인프라 레벨의 대응 정책만으로도 리소스 과부하 문제를 어느 정도 해소&lt;/b&gt;할 수 있다는 것을 직접 경험했습니다.&lt;br /&gt;특히 서비스 안정성은 &lt;b&gt;트래픽 증가 상황에서 자동으로 확장되고, 축소되는 구조를 사전에 확보하는 것&lt;/b&gt;이 핵심임을 체감했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 평균값이 아니라 percentile 지표를 보는 것이 효율적일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;단순 평균(avg)만으로는 문제가 드러나지 않았고, &lt;b&gt;p90, p95 구간의 튐 현상&lt;/b&gt;에서야 비로소 실제 사용자 경험 저하가 확인되었습니다.&lt;br /&gt;SLA나 운영지표는 반드시 &lt;b&gt;percentile 기반으로 분석&lt;/b&gt;해야 한다는 교훈을 얻었습니다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;다음 개선 방향&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. Memory 기반의 Scaling 정책 병행&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;현재는 CPU 기반으로만 Scaling이 동작하지만, /v1/stress API처럼 메모리 사용률이 급격히 증가하는 경우를 위해 &lt;b&gt;Memory 사용률 기준의 보조 정책&lt;/b&gt;도 병행 적용할 계획입니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. Application Load Balancer의 Target 반응 속도 연동 지표 추적&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;Scaling이 발생했을 때, 실제 사용자에게 얼마나 빠르게 개선 효과가 도달했는지를 &lt;b&gt;ALB의 Target Response Time 지표&lt;/b&gt;와 연동하여 측정하는 구조도 함께 고려하고 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이번 테스트를 통해 리소스에 대한 지표의 중요도와 리소스의 설정값으로 인해 인프라에 어떤 영항을 끼치는지에 대해서 알게되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이번 테스트로 앞으로 모니터링 시스템 구축을 더 찾아보고 보기 쉽게 정리할 수 있도록 하겠습니다.&lt;/p&gt;</description>
      <category>Production Traffic</category>
      <author>dml113</author>
      <guid isPermaLink="true">https://story0410.tistory.com/39</guid>
      <comments>https://story0410.tistory.com/39#entry39comment</comments>
      <pubDate>Sun, 3 Aug 2025 15:33:21 +0900</pubDate>
    </item>
    <item>
      <title>[Production Traffic] MySQL 인덱스 전후 성능 차이, 실무 부하 테스트로 증명하기</title>
      <link>https://story0410.tistory.com/38</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;들어가며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;현재 운영 중인 애플리케이션에서 특정 API의 응답 속도가 눈에 띄게 느려지는 현상이 발생하고 있었습니다.&amp;nbsp;&amp;nbsp;사용자 입장에서는 페이지가 늦게 뜨고, 모니터링 시스템에서는 평균 응답 속도가 높게 유지되며 경고 알람이 자주 발생했습니다. &lt;br /&gt;&lt;br /&gt;문제가 된 API는 단순히 MySQL에서 특정 조건(`email`)을 만족하는 데이터를 조회하는 쿼리 하나뿐이었습니다.&amp;nbsp; 로직도&amp;nbsp;복잡하지&amp;nbsp;않고,&amp;nbsp;데이터의&amp;nbsp;양도&amp;nbsp;수백만&amp;nbsp;건&amp;nbsp;수준이라&amp;nbsp;아직&amp;nbsp;임계점이라고&amp;nbsp;보기도&amp;nbsp;어려운&amp;nbsp;상황이었죠. &lt;br /&gt;&lt;br /&gt;그래서 이 문제를 해결하기 위해, 쿼리 성능 분석과 함께 MySQL의 &lt;b&gt;인덱스(Index)를&lt;/b&gt; 활용한 성능 개선 작업을 진행했습니다.&amp;nbsp; &amp;nbsp;그리고 이 효과를 &lt;b&gt;k6 부하 테스트 도구&lt;/b&gt;를 통해 수치로 증명해 보았습니다. &lt;br /&gt;&lt;br /&gt;이번 글에서는 &lt;b&gt;인덱스를 적용하기 전/후의 부하 테스트 결과를 통해 성능 차이를 어떻게 체감할 수 있었는지 &lt;/b&gt;실제&amp;nbsp;데이터를&amp;nbsp;바탕으로&amp;nbsp;정리해보려고&amp;nbsp;합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;테스트 환경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;본&amp;nbsp;테스트는&amp;nbsp;현재&amp;nbsp;운영&amp;nbsp;중인&amp;nbsp;Golang&amp;nbsp;기반&amp;nbsp;애플리케이션을&amp;nbsp;대상으로&amp;nbsp;진행했습니다.&amp;nbsp;&amp;nbsp; &lt;br /&gt;애플리케이션은&amp;nbsp;MySQL&amp;nbsp;8.0.42&amp;nbsp;버전을&amp;nbsp;사용하고&amp;nbsp;있으며,&amp;nbsp;API&amp;nbsp;형태로&amp;nbsp;클라이언트와&amp;nbsp;통신합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;데이터&amp;nbsp;흐름&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;사용자&amp;nbsp;정보는&amp;nbsp;다음과&amp;nbsp;같은&amp;nbsp;방식으로&amp;nbsp;처리됩니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;1. 데이터 저장 (POST)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;경로:&amp;nbsp;`POST&amp;nbsp;/v1/user` &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Request&amp;nbsp;Body&amp;nbsp;예시:&lt;/p&gt;
&lt;pre id=&quot;code_1754132231144&quot; class=&quot;json&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;go&quot;&gt;&lt;code&gt;{
	&quot;requestid&quot;: &quot;999999999999&quot;,
	&quot;uuid&quot;: &quot;7c5a3c6a-758f-4bc5-9bdf-3e573a0ad729&quot;,
	&quot;username&quot;: &quot;dbdump500001&quot;,
	&quot;email&quot;: &quot;dbdump500001@example.org&quot;,
	&quot;status_message&quot;: &quot;I&amp;rsquo;m happy&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;2. 데이터&amp;nbsp;조회&amp;nbsp;(GET)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp; -&amp;nbsp;경로:&amp;nbsp;`GET&amp;nbsp;/v1/user`&lt;br /&gt;&amp;nbsp; -&amp;nbsp;쿼리&amp;nbsp;예시:&lt;/p&gt;
&lt;pre id=&quot;code_1754132291244&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/v1/user?email=dbdump500001@example.org&amp;amp;requestid=999999999999&amp;amp;uuid=7c5a3c6a-758f-4bc5-9bdf-3e573a0ad729&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- 하지만 실질적으로는 `email` 값만을 기준으로 DB에서 데이터를 조회하고 있으며,&amp;nbsp; `requestid`와 `uuid`는 쿼리에는 포함되지만 데이터베이스에 저장하거나 검색 조건으로 사용하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;기술스택&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;-&amp;nbsp;**언어:**&amp;nbsp;Go&amp;nbsp;(Golang) &lt;br /&gt;-&amp;nbsp;**DBMS:**&amp;nbsp;MySQL&amp;nbsp;8.0.42 &lt;br /&gt;-&amp;nbsp;**부하&amp;nbsp;테스트&amp;nbsp;도구:**&amp;nbsp;[k6](&lt;a href=&quot;https://k6.io)&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://k6.io)&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&amp;nbsp;상황:&amp;nbsp;인덱스&amp;nbsp;적용&amp;nbsp;전&amp;nbsp;성능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;애플리케이션에서&amp;nbsp;사용자의&amp;nbsp;이메일(`email`)을&amp;nbsp;기준으로&amp;nbsp;데이터를&amp;nbsp;조회하는&amp;nbsp;요청이&amp;nbsp;점점&amp;nbsp;증가하면서,&amp;nbsp;&amp;nbsp; &lt;br /&gt;DB&amp;nbsp;쿼리&amp;nbsp;성능&amp;nbsp;저하가&amp;nbsp;체감될&amp;nbsp;정도로&amp;nbsp;발생하기&amp;nbsp;시작했습니다. &lt;br /&gt;&lt;br /&gt;실제 운영 중인 서비스에서는 사용자 수가 많아질수록 단일 칼럼(email)에 대한 &lt;b&gt;풀스캔(full table scan)&lt;/b&gt; 발생 빈도가 높아졌고,&amp;nbsp;&amp;nbsp; &lt;br /&gt;이에&amp;nbsp;따라&amp;nbsp;다음과&amp;nbsp;같은&amp;nbsp;문제가&amp;nbsp;확인되었습니다: &lt;br /&gt;&lt;br /&gt;-&amp;nbsp;평균&amp;nbsp;응답&amp;nbsp;속도가&amp;nbsp;높음 &lt;br /&gt;-&amp;nbsp;응답&amp;nbsp;시간&amp;nbsp;편차가&amp;nbsp;큼&amp;nbsp;(최대&amp;nbsp;975ms까지&amp;nbsp;도달) &lt;br /&gt;-&amp;nbsp;p95&amp;nbsp;응답&amp;nbsp;시간&amp;nbsp;기준으로도&amp;nbsp;700ms를&amp;nbsp;초과&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;부하 테스트 결과 (인덱스 적용 전)&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;k6를 이용해 `/v1/user` 경로에 대해 `email` 기준 동시접속자 5명으로 20초 동안 GET 요청을 반복 수행한 결과입니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;- 평균 응답 시간: 356.1ms&amp;nbsp;&amp;nbsp; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;- 95퍼센타일(p95): 739.54ms&amp;nbsp;&amp;nbsp; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;- 최대 응답 시간: 975.71ms&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;원인&amp;nbsp;분석&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;현재 테이블에는 `email` 칼럼에 &lt;b&gt;인덱스가 설정되어 있지 않으며&lt;/b&gt;,&amp;nbsp; 그에&amp;nbsp;따라&amp;nbsp;`WHERE&amp;nbsp;email&amp;nbsp;=?`&amp;nbsp;조건이&amp;nbsp;들어가는&amp;nbsp;쿼리마다&amp;nbsp;전체&amp;nbsp;테이블을&amp;nbsp;스캔하는&amp;nbsp;방식으로&amp;nbsp;동작하고&amp;nbsp;있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이는 다음과 같은 상황에서 치명적입니다:&lt;br /&gt;-&amp;nbsp;사용자가&amp;nbsp;많을수록&amp;nbsp;성능이&amp;nbsp;선형적으로&amp;nbsp;저하됨 &lt;br /&gt;-&amp;nbsp;오토스케일링이&amp;nbsp;DB&amp;nbsp;부하를&amp;nbsp;감당하지&amp;nbsp;못하는&amp;nbsp;병목&amp;nbsp;지점이&amp;nbsp;됨 &lt;br /&gt;-&amp;nbsp;조회&amp;nbsp;요청이&amp;nbsp;많아지면&amp;nbsp;API&amp;nbsp;전체&amp;nbsp;응답&amp;nbsp;성능에&amp;nbsp;악영향을&amp;nbsp;줌&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개선&amp;nbsp;방법:&amp;nbsp;인덱스&amp;nbsp;적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;문제의 핵심은 `email` 컬럼에 대한 &lt;b&gt;빈번한 조건 조회&lt;/b&gt;가 발생하고 있음에도,&amp;nbsp;&amp;nbsp; &lt;br /&gt;해당 컬럼에 &lt;b&gt;적절한 인덱스가 존재하지 않았던 것&lt;/b&gt;이었습니다. &lt;br /&gt;&lt;br /&gt;이에&amp;nbsp;따라&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;인덱스를&amp;nbsp;생성했습니다:&lt;/p&gt;
&lt;pre id=&quot;code_1754135001673&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE INDEX idx_users_email ON user(email);&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;size14&quot;&gt;&lt;b&gt;테이블명:&lt;/b&gt; users&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;인덱스명:&lt;/b&gt; idx_users_email&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;대상 컬럼:&lt;/b&gt; email&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;인덱스를 추가한 이후, 기존의 WHERE email =? 쿼리는 &lt;b&gt;풀스캔이 아닌 인덱스 탐색(index seek)&lt;/b&gt; 방식으로 처리되기 시작했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;인덱스 적용 후 예상된 변화&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;email 조건에 대한 탐색 성능 향상&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;응답 시간의 &lt;b&gt;일관성 개선 (지연 시간 편차 축소)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;전체 응답 속도 평균 및 p95 이하로 안정화 기대&lt;/p&gt;
&lt;p data-end=&quot;560&quot; data-start=&quot;546&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;560&quot; data-start=&quot;546&quot; data-ke-size=&quot;size18&quot;&gt;테스트 환경 고정값&lt;/p&gt;
&lt;p data-end=&quot;560&quot; data-start=&quot;546&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;DB 버전:&lt;/b&gt; MySQL 8.0.42&lt;/p&gt;
&lt;p data-end=&quot;560&quot; data-start=&quot;546&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;API 언어:&lt;/b&gt; Golang&lt;/p&gt;
&lt;p data-end=&quot;560&quot; data-start=&quot;546&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;테스트 방식:&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;560&quot; data-start=&quot;546&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;767&quot; data-start=&quot;631&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;661&quot; data-start=&quot;631&quot;&gt;/v1/user 엔드포인트로 GET 요청&lt;/li&gt;
&lt;li data-end=&quot;701&quot; data-start=&quot;664&quot;&gt;? email=dbdump500001@example.org&lt;/li&gt;
&lt;li data-end=&quot;742&quot; data-start=&quot;704&quot;&gt;requestid, uuid는 전달되지만 저장되지 않음&lt;/li&gt;
&lt;li data-end=&quot;767&quot; data-start=&quot;745&quot;&gt;email 기준으로만 조회 쿼리 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;성능&amp;nbsp;측정&amp;nbsp;결과와&amp;nbsp;비교&amp;nbsp;분석&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;인덱스를&amp;nbsp;적용한&amp;nbsp;후&amp;nbsp;동일한&amp;nbsp;조건으로&amp;nbsp;k6&amp;nbsp;부하&amp;nbsp;테스트를&amp;nbsp;다시&amp;nbsp;실행했습니다.&amp;nbsp;&amp;nbsp; &lt;br /&gt;아래는&amp;nbsp;테스트&amp;nbsp;결과&amp;nbsp;주요&amp;nbsp;지표입니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;- 평균 응답 시간: 약 19ms&amp;nbsp;&amp;nbsp; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;- 최소 응답 시간: 13ms&amp;nbsp;&amp;nbsp; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;- 중앙값 응답 시간: 17.5ms&amp;nbsp;&amp;nbsp; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;- 최대 응답 시간: 31ms&amp;nbsp;&amp;nbsp; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;- 95 퍼센타일 응답 시간: 28ms&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;비교&amp;nbsp;요약&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 79px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;지표&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;인덱스&amp;nbsp;적용&amp;nbsp;전&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;인덱스&amp;nbsp;적용&amp;nbsp;후&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;향상율&amp;nbsp;(대략)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 22px;&quot;&gt;평균&amp;nbsp;응답&amp;nbsp;시간&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 22px;&quot;&gt;356ms&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 22px;&quot;&gt;19ms&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 22px;&quot;&gt;약&amp;nbsp;95%&amp;nbsp;감소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 15px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 15px;&quot;&gt;95퍼센타일&amp;nbsp;응답&amp;nbsp;시간&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 15px;&quot;&gt;739ms&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 15px;&quot;&gt;28m&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 15px;&quot;&gt;약&amp;nbsp;96%&amp;nbsp;감소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;최대&amp;nbsp;응답&amp;nbsp;시간&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;975ms&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;31ms&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;약 97% 감소&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;아래의 사진은 왼쪽이 index 설정하기 전이며, 오른쪽이 index 설정 후의 결괏값입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh5Oxr/btsPGlMSB7w/nqcLpzt7hM3RSiOwUrE6qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh5Oxr/btsPGlMSB7w/nqcLpzt7hM3RSiOwUrE6qK/img.png&quot; data-origin-width=&quot;1495&quot; data-origin-height=&quot;113&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.1553%; margin-right: 10px;&quot; data-widthpercent=&quot;52.77&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh5Oxr/btsPGlMSB7w/nqcLpzt7hM3RSiOwUrE6qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh5Oxr%2FbtsPGlMSB7w%2FnqcLpzt7hM3RSiOwUrE6qK%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;1495&quot; height=&quot;113&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhPIFa/btsPDDWdYD4/SR8HVc1FGMXczkhx4IOV10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhPIFa/btsPDDWdYD4/SR8HVc1FGMXczkhx4IOV10/img.png&quot; data-origin-width=&quot;1421&quot; data-origin-height=&quot;120&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.6819%;&quot; data-widthpercent=&quot;47.23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhPIFa/btsPDDWdYD4/SR8HVc1FGMXczkhx4IOV10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhPIFa%2FbtsPDDWdYD4%2FSR8HVc1FGMXczkhx4IOV10%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;1421&quot; height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;분석&amp;nbsp;및&amp;nbsp;의의&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- 인덱스 생성만으로 평균 응답 속도가 356ms에서 19ms로 급격히 개선되었습니다.&lt;br /&gt;-&amp;nbsp;p95&amp;nbsp;응답&amp;nbsp;시간도&amp;nbsp;739ms에서&amp;nbsp;28ms로&amp;nbsp;대폭&amp;nbsp;단축되어,&amp;nbsp;사용자&amp;nbsp;체감&amp;nbsp;성능이&amp;nbsp;크게&amp;nbsp;향상됨을&amp;nbsp;의미합니다.&amp;nbsp;&amp;nbsp; &lt;br /&gt;-&amp;nbsp;쿼리의&amp;nbsp;전체&amp;nbsp;테이블&amp;nbsp;스캔이&amp;nbsp;아닌,&amp;nbsp;인덱스를&amp;nbsp;활용한&amp;nbsp;빠른&amp;nbsp;탐색으로&amp;nbsp;전환되었기&amp;nbsp;때문입니다.&amp;nbsp;&amp;nbsp; &lt;br /&gt;- 이번 테스트를 통해 &lt;b&gt;작은 인덱스 하나가 서비스 성능에 얼마나 큰 영향을 미치는지&lt;/b&gt; 직접 체감할 수 있었습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;실무적&amp;nbsp;교훈과&amp;nbsp;회고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이번 프로젝트를 통해 얻은 가장 큰 교훈은 다음과 같습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.&amp;nbsp;쿼리&amp;nbsp;성능&amp;nbsp;문제의&amp;nbsp;첫&amp;nbsp;단추는&amp;nbsp;인덱스부터&amp;nbsp;점검해야&amp;nbsp;한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;성능 저하 문제를 만났을 때 가장 먼저 확인할 것은 데이터베이스 쿼리의 실행 계획입니다.&lt;br /&gt;특히&amp;nbsp;자주&amp;nbsp;사용하는&amp;nbsp;조건&amp;nbsp;칼럼에&amp;nbsp;적절한&amp;nbsp;인덱스가&amp;nbsp;없으면,&amp;nbsp;&amp;nbsp; &lt;br /&gt;작은&amp;nbsp;데이터셋이라도&amp;nbsp;예상보다&amp;nbsp;훨씬&amp;nbsp;느린&amp;nbsp;응답&amp;nbsp;시간을&amp;nbsp;초래할&amp;nbsp;수&amp;nbsp;있음을&amp;nbsp;몸소&amp;nbsp;경험했습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.&amp;nbsp;인덱스&amp;nbsp;하나가&amp;nbsp;서비스&amp;nbsp;전체&amp;nbsp;성능에&amp;nbsp;미치는&amp;nbsp;영향은&amp;nbsp;매우&amp;nbsp;크다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이번 사례처럼, 복잡한 최적화나 캐시 도입 없이도&amp;nbsp; 단순히 인덱스 생성만으로 API 응답 속도가 95% 이상 개선될 수 있다는 사실은&amp;nbsp; 모든 개발자가&amp;nbsp;놓치지 말아야 할 기본 중 기본입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.&amp;nbsp;부하&amp;nbsp;테스트를&amp;nbsp;통한&amp;nbsp;성능&amp;nbsp;검증은&amp;nbsp;필수&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;성능 개선 후 무작정 &amp;ldquo;빠르다&amp;rdquo;라고 판단하는 대신,&amp;nbsp; 부하&amp;nbsp;테스트&amp;nbsp;도구(k6&amp;nbsp;등)를&amp;nbsp;사용해&amp;nbsp;체계적으로&amp;nbsp;수치를&amp;nbsp;확인하고&amp;nbsp;비교하는&amp;nbsp;과정이&amp;nbsp;매우&amp;nbsp;중요합니다.&amp;nbsp;&amp;nbsp; &lt;br /&gt;이를&amp;nbsp;통해&amp;nbsp;실제&amp;nbsp;사용자&amp;nbsp;환경과&amp;nbsp;유사한&amp;nbsp;조건에서&amp;nbsp;신뢰할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;데이터를&amp;nbsp;확보할&amp;nbsp;수&amp;nbsp;있었습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 지나친 인덱스 남발은 피해야 한다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;하지만 무조건적으로 인덱스를 사용한다고 좋은 것은 아닙니다. 인덱스를 사용하면 읽기 성능은 향상하지만, 쓰기 성능에 대해서는 오히려 악효과가 됩니다. 따라서 조회 패턴을 분석하여, 필요한 칼럼에만 최소한의 인덱스를 설정할 줄 알아야 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이번&amp;nbsp;경험을&amp;nbsp;통해&amp;nbsp;다시&amp;nbsp;한&amp;nbsp;번&amp;nbsp;데이터베이스&amp;nbsp;설계와&amp;nbsp;운영에서&amp;nbsp;인덱스의&amp;nbsp;중요성을&amp;nbsp;실감했습니다.&amp;nbsp;&amp;nbsp; &lt;br /&gt;앞으로도 서비스 성능 이슈가 발생하면 우선적으로 쿼리 실행 계획과 인덱스를 점검하는 습관을 꼭 지키겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;</description>
      <category>Production Traffic</category>
      <author>dml113</author>
      <guid isPermaLink="true">https://story0410.tistory.com/38</guid>
      <comments>https://story0410.tistory.com/38#entry38comment</comments>
      <pubDate>Sat, 2 Aug 2025 22:24:14 +0900</pubDate>
    </item>
    <item>
      <title>[Production Traffic] DynamoDB 쿼리가 느려터졌다면? GSI로 빠르게 만들자 &amp;ndash; 실전 튜닝 사례</title>
      <link>https://story0410.tistory.com/37</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;배경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;최근&amp;nbsp;사용&amp;nbsp;중인&amp;nbsp;Golang&amp;nbsp;애플리케이션에서&amp;nbsp;특정&amp;nbsp;API의&amp;nbsp;응답&amp;nbsp;속도가&amp;nbsp;느려,&amp;nbsp;사용자에게&amp;nbsp;정보를&amp;nbsp;빠르게&amp;nbsp;제공하지&amp;nbsp;못하는&amp;nbsp;문제가&amp;nbsp;있었습니다.&amp;nbsp;&amp;nbsp; &lt;br /&gt;분석&amp;nbsp;결과,&amp;nbsp;DynamoDB에서&amp;nbsp;`Scan`을&amp;nbsp;사용해&amp;nbsp;데이터를&amp;nbsp;조회하고&amp;nbsp;있었고,&amp;nbsp;이로&amp;nbsp;인해&amp;nbsp;성능이&amp;nbsp;크게&amp;nbsp;저하되고&amp;nbsp;있었습니다. &lt;br /&gt;&lt;br /&gt;쿼리&amp;nbsp;효율화를&amp;nbsp;위해&amp;nbsp;Global&amp;nbsp;Secondary&amp;nbsp;Index(GSI)를&amp;nbsp;도입해&amp;nbsp;문제를&amp;nbsp;해결하고자&amp;nbsp;했습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인프라&amp;nbsp;구성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;0.25vCPU, 0.5GB을 가진 ECS Task 2대로 구성하였으며, 해당 애플리케이션은 다음과 같은 구조에서 동작합니다:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPjoCx/btsPDmE0m0j/OfUs3eKESGkuZeGaXx5ie1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPjoCx/btsPDmE0m0j/OfUs3eKESGkuZeGaXx5ie1/img.png&quot; data-alt=&quot;Client &amp;amp;rarr; ALB &amp;amp;rarr; ECS (Golang App) &amp;amp;rarr; DynamoDB&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPjoCx/btsPDmE0m0j/OfUs3eKESGkuZeGaXx5ie1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPjoCx%2FbtsPDmE0m0j%2FOfUs3eKESGkuZeGaXx5ie1%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;673&quot; height=&quot;133&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;133&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Client &amp;rarr; ALB &amp;rarr; ECS (Golang App) &amp;rarr; DynamoDB&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;905&quot; data-start=&quot;863&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;905&quot; data-start=&quot;863&quot; data-ke-size=&quot;size14&quot;&gt;- POST Method: 새로운 데이터를 DynamoDB에 삽입 &lt;br /&gt;- GET Method: 특정 조건으로 데이터를 조회 (문제 발생 지점)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 상황&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;인프라&amp;nbsp;부하&amp;nbsp;테스트를&amp;nbsp;위해&amp;nbsp;[k6](&lt;a href=&quot;https://k6.io/)를&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://k6.io/)를&lt;/a&gt; 사용하여 &lt;b&gt;동시접속자 100명&lt;/b&gt;이 약 20초간 &lt;b&gt;POST &amp;rarr; GET&lt;/b&gt; 요청을 반복 수행하도록 설정했습니다.&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;그&amp;nbsp;결과,&amp;nbsp;다음과&amp;nbsp;같은&amp;nbsp;성능&amp;nbsp;병목이&amp;nbsp;관측되었습니다: &lt;br /&gt;&lt;br /&gt;- 테스트 중 전체 요청 수: &lt;b&gt;5,746건&lt;/b&gt;, 초당 요청 처리량: &lt;b&gt;278 RPS&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;`GET&amp;nbsp;/v1/product?id=xxx&amp;amp;requestid=yyy&amp;amp;uuid=zzz`&amp;nbsp;요청이&amp;nbsp;문제의&amp;nbsp;핵심 &lt;br /&gt;-&amp;nbsp;DynamoDB&amp;nbsp;테이블은&amp;nbsp;`id`를&amp;nbsp;Partition&amp;nbsp;Key로&amp;nbsp;사용하고&amp;nbsp;있었으나, &lt;br /&gt;&amp;nbsp;&amp;nbsp;실제 조회 조건은 `requestid`와 `uuid` 조합이 필요 &amp;rarr; &lt;b&gt;`Scan + FilterExpression` 사용 중&lt;/b&gt;&lt;br /&gt;- 평균 응답 시간: &lt;b&gt;101.83ms&lt;/b&gt;&lt;br /&gt;- P90 응답 시간: &lt;b&gt;197.79ms&lt;/b&gt;, P95 응답 시간: &lt;b&gt;276.16ms&lt;/b&gt;, 최대 응답 시간:&lt;b&gt; 510.68ms&lt;/b&gt;&lt;br /&gt;- 실패율: &lt;b&gt;0.01% (1건)&lt;/b&gt; 으로 무시 가능 수준 &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;표면적으로 평균 응답 시간은 양호해 보이지만, &lt;/b&gt;실제 서비스 상황에서는 다양한 요청 조건에서 &lt;b&gt;Query Latency가 500ms 이상까지 증가&lt;/b&gt;하며 사용자 체감이 발생할 수 있는 수준이었습니다. &lt;br /&gt;&lt;br /&gt;특히,&amp;nbsp;데이터가&amp;nbsp;누적됨에&amp;nbsp;따라&amp;nbsp;Scan&amp;nbsp;성능이&amp;nbsp;기하급수적으로&amp;nbsp;저하될&amp;nbsp;우려가&amp;nbsp;있었고,&amp;nbsp;이는&amp;nbsp;추후&amp;nbsp;확장성과&amp;nbsp;운영&amp;nbsp;안정성&amp;nbsp;측면에서도&amp;nbsp;**중대한&amp;nbsp;병목&amp;nbsp;요소**로&amp;nbsp;판단되었습니다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;해결방법&lt;/h3&gt;
&lt;p data-end=&quot;400&quot; data-start=&quot;187&quot; data-ke-size=&quot;size14&quot;&gt;DynamoDB는 기본적으로 테이블의 &lt;b&gt;파티션 키(Partition Key)&lt;/b&gt;&amp;nbsp;조합으로만 Query가 가능합니다. 하지만, 본 애플리케이션은 uuid와 requestid를 기준으로 조건 검색이 필요했기 때문에 &lt;b&gt;Scan + FilterExpression&lt;/b&gt;이라는 비효율적인 방식으로 동작 중이었습니다.&lt;/p&gt;
&lt;p data-end=&quot;496&quot; data-start=&quot;402&quot; data-ke-size=&quot;size14&quot;&gt;이를 해결하기 위해 &lt;b&gt;GSI(Global Secondary Index)&lt;/b&gt; 를 도입하여, uuid를 파티션 키로, requestid를 정렬 키로 설정하였습니다.&lt;/p&gt;
&lt;h4 data-end=&quot;496&quot; data-start=&quot;402&quot; data-ke-size=&quot;size20&quot;&gt;코드 변경: Scan &amp;rarr; Query&lt;/h4&gt;
&lt;p data-end=&quot;276&quot; data-start=&quot;148&quot; data-ke-size=&quot;size14&quot;&gt;기존에는 DynamoDB에서 데이터를 조회할 때 &lt;b&gt;Scan + FilterExpression&lt;/b&gt; 방식으로 동작하고 있었습니다.&lt;br /&gt;이 방식은 전체 테이블을 순회하므로, 데이터가 많아질수록 응답 시간이 길어지고 비용도 증가합니다.&lt;/p&gt;
&lt;p data-end=&quot;388&quot; data-start=&quot;278&quot; data-ke-size=&quot;size14&quot;&gt;이를 해결하기 위해, &lt;b&gt;Global Secondary Index (GSI)&lt;/b&gt; 를 생성하고 Golang 애플리케이션 내 코드를 Scan 기반에서 Query 기반으로 리팩토링했습니다.&lt;/p&gt;
&lt;p data-end=&quot;388&quot; data-start=&quot;278&quot; data-ke-size=&quot;size14&quot;&gt;아래는 리팩토링된 전체 코드이며, &lt;b&gt;환경변수 TABLE_INDEX_NAME 이 존재할 경우 GSI 기반 쿼리(Query)&lt;/b&gt; 를 수행하고, &lt;b&gt;존재하지 않을 경우 fallback 으로 Scan&lt;/b&gt; 을 수행하는 구조입니다.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;&lt;code&gt;main.go&lt;/code&gt; (클릭하여 전체 코드 보기)&lt;/summary&gt;
&lt;pre id=&quot;code_1753794964752&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package main
import (
	&quot;fmt&quot;
	&quot;net/http&quot;
	&quot;os&quot;

	&quot;github.com/aws/aws-sdk-go/aws&quot;
	&quot;github.com/aws/aws-sdk-go/aws/session&quot;
	&quot;github.com/aws/aws-sdk-go/service/dynamodb&quot;
	&quot;github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute&quot;
	&quot;github.com/gin-gonic/gin&quot;
)

type Product struct {
	ID        string `json:&quot;id&quot;`
	UUID      string `json:&quot;uuid&quot;`
	RequestID string `json:&quot;requestid&quot;`
	Name      string `json:&quot;name&quot;`
	Price     int    `json:&quot;price&quot;`
}

func main() {
	tableName := os.Getenv(&quot;TABLE_NAME&quot;)
	indexName := os.Getenv(&quot;TABLE_INDEX_NAME&quot;)

	sess := session.Must(session.NewSession())
	svc := dynamodb.New(sess)

	router := gin.Default()

	router.GET(&quot;/v1/product&quot;, func(c *gin.Context) {
		id := c.Query(&quot;id&quot;)
		requestid := c.Query(&quot;requestid&quot;)
		uuid := c.Query(&quot;uuid&quot;)

		var result Product
		var err error

		if indexName != &quot;&quot; {
			// GSI 쿼리
			queryInput := &amp;amp;dynamodb.QueryInput{
				TableName:              aws.String(tableName),
				IndexName:              aws.String(indexName),
				KeyConditionExpression: aws.String(&quot;#uuid = :uuid AND #requestid = :requestid&quot;),
				ExpressionAttributeNames: map[string]*string{
					&quot;#uuid&quot;:      aws.String(&quot;uuid&quot;),
					&quot;#requestid&quot;: aws.String(&quot;requestid&quot;),
				},
				ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
					&quot;:uuid&quot;:      {S: aws.String(uuid)},
					&quot;:requestid&quot;: {S: aws.String(requestid)},
				},
			}

			queryOutput, err := svc.Query(queryInput)
			if err != nil || len(queryOutput.Items) == 0 {
				c.JSON(http.StatusNotFound, gin.H{&quot;error&quot;: &quot;Product not found&quot;})
				return
			}
			err = dynamodbattribute.UnmarshalMap(queryOutput.Items[0], &amp;amp;result)
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{&quot;error&quot;: &quot;Failed to unmarshal result&quot;})
				return
			}
		} else {
			// Scan fallback
			scanInput := &amp;amp;dynamodb.ScanInput{
				TableName: aws.String(tableName),
				FilterExpression: aws.String(&quot;#uuid = :uuid AND #requestid = :requestid&quot;),
				ExpressionAttributeNames: map[string]*string{
					&quot;#uuid&quot;:      aws.String(&quot;uuid&quot;),
					&quot;#requestid&quot;: aws.String(&quot;requestid&quot;),
				},
				ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
					&quot;:uuid&quot;:      {S: aws.String(uuid)},
					&quot;:requestid&quot;: {S: aws.String(requestid)},
				},
			}

			scanOutput, err := svc.Scan(scanInput)
			if err != nil || len(scanOutput.Items) == 0 {
				c.JSON(http.StatusNotFound, gin.H{&quot;error&quot;: &quot;Product not found&quot;})
				return
			}
			err = dynamodbattribute.UnmarshalMap(scanOutput.Items[0], &amp;amp;result)
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{&quot;error&quot;: &quot;Failed to unmarshal result&quot;})
				return
			}
		}

		c.JSON(http.StatusOK, result)
	})

	router.Run(&quot;:8080&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;위 코드 구조의 핵심은 다음과 같습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;- TABLE_INDEX_NAME 환경변수가 존재&lt;/b&gt;하면 &amp;rarr; GSI 기반 &lt;b&gt;Query&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;- 환경변수가 없으면&lt;/b&gt; &amp;rarr; 기존 &lt;b&gt;Scan + FilterExpression&lt;/b&gt; fallback&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;- uuid를 Partition Key로&lt;/b&gt;, requestid를 Sort Key로 하는 GSI가 필요&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;성능 개선 결과 (Index 적용 후) -&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- 전체 요청 수: &lt;b&gt;7,024건&lt;/b&gt;, 초당 요청 처리량: &lt;b&gt;341.59 RPS&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- 평균 응답 시간: &lt;b&gt;35.56ms&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- P90 응답 시간: &lt;b&gt;79.77ms&lt;/b&gt;, P95 응답 시간: &lt;b&gt;119.79ms&lt;/b&gt;, 최대 응답 시간: &lt;b&gt;413.33ms&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- 실패율: &lt;b&gt;0.01% (1건)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;GSI 설정 이후 &lt;b&gt;평균 응답 시간 101.83ms &amp;rarr; 35.56ms&lt;/b&gt; 로 &lt;b&gt;65% 이상 성능 개선&lt;/b&gt;되었으며, &lt;b&gt;RPS 약 23% 증가&lt;/b&gt; (278 &amp;rarr; 341) 했습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;회고&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;GSI 하나로 성능이 3배 빨라졌다 &amp;ndash; 설정 하나의 위력&lt;/h4&gt;
&lt;p data-end=&quot;221&quot; data-start=&quot;150&quot; data-ke-size=&quot;size14&quot;&gt;최근에 작은 프로젝트를 하나 운영하면서 &quot;그렇게 중요해 보이지 않았던 설정 하나가 얼마나 치명적일 수 있는지&quot; 뼈저리게 느꼈다.&lt;/p&gt;
&lt;p data-end=&quot;454&quot; data-start=&quot;223&quot; data-ke-size=&quot;size14&quot;&gt;처음엔 그냥 DynamoDB 테이블 하나 만들고, id 기준으로 Primary Key를 설정해 데이터를 쌓고 있었다. 애플리케이션은 Go(Golang)으로 작성했고, AWS ECS EC2 Task에서 실행되도록 구성했으며, 해당 Task에서 직접 DynamoDB와 연동해 데이터를 조회하는 구조였다. 처음에는 문제 없어 보였다. 실제로 API도 잘 동작했고, 예상한 대로 결과도 나왔다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;454&quot; data-start=&quot;223&quot; data-ke-size=&quot;size14&quot;&gt;Scan으로는 문제가 많았고, 이 서비스는 클라이언트에서 uuid랑 requestid로 데이터를 조회해야 했는데, 테이블은 id 기준으로 구성돼 있었다. 즉, 조건에 맞는 데이터를 가져오려면 무조건 &lt;b&gt;Scan + FilterExpression&lt;/b&gt;으로 모든 항목을 돌면서 일일이 체크해야 했다.&lt;/p&gt;
&lt;p data-end=&quot;662&quot; data-start=&quot;610&quot; data-ke-size=&quot;size14&quot;&gt;작은 데이터셋에선 티가 안 났다. 근데 k6로 부하 테스트를 돌려보니 문제가 명확해졌다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;구분 Scan 사용 시:&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;평균 응답 시간&lt;/td&gt;
&lt;td&gt;101.83ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P90&lt;/td&gt;
&lt;td&gt;197.79ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P95&lt;/td&gt;
&lt;td&gt;276.16ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;최대&lt;/td&gt;
&lt;td&gt;510.68ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이건 단순히 느린 수준이 아니라, 지속적으로 사용하기에는 위험했다. 특히 사용자가 많아지거나, 테이블이 더 커졌을 때 상황은 문제가 커졌다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;GSI 하나로 체감 성능이 달라졌다&lt;/h4&gt;
&lt;p data-end=&quot;959&quot; data-start=&quot;914&quot; data-ke-size=&quot;size14&quot;&gt;그래서 도입한 게 &lt;b&gt;Global Secondary Index(GSI)&lt;/b&gt; 였다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1008&quot; data-start=&quot;961&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;984&quot; data-start=&quot;961&quot;&gt;Partition Key: uuid&lt;/li&gt;
&lt;li data-end=&quot;1008&quot; data-start=&quot;985&quot;&gt;Sort Key: requestid&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1183&quot; data-start=&quot;1010&quot; data-ke-size=&quot;size14&quot;&gt;딱 우리가 조회하고 싶은 구조 그대로를 Index로 설계해서, 기존의 Scan을 Query로 대체했다. 단, 이게 모든 환경에서 항상 적용될 수는 없기에 코드 내에 TABLE_INDEX_NAME 이라는 환경 변수를 사용해서 &lt;b&gt;인덱스 존재 여부에 따라 Scan &amp;harr; Query를 자동 전환&lt;/b&gt;하도록 구성했다.&lt;/p&gt;
&lt;p data-end=&quot;1202&quot; data-start=&quot;1185&quot; data-ke-size=&quot;size14&quot;&gt;그리고 다시 성능을 측정해봤다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;구분 GSI 적용 후:&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;평균 응답 시간&lt;/td&gt;
&lt;td&gt;35.56ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P90&lt;/td&gt;
&lt;td&gt;79.77ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P95&lt;/td&gt;
&lt;td&gt;119.79ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;최대&lt;/td&gt;
&lt;td&gt;413.33ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-end=&quot;1202&quot; data-start=&quot;1185&quot; data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;응답 속도가 3배 가까이 줄어들었다.&lt;/b&gt;&lt;br /&gt;특히 평균 응답 시간이 100ms &amp;rarr; 35ms로 줄어든 건 정말 체감이 클 수밖에 없다.&lt;/p&gt;
&lt;h4 data-end=&quot;1202&quot; data-start=&quot;1185&quot; data-ke-size=&quot;size20&quot;&gt;그리고 데이터는 절대 그대로 있지 않는다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;지금은 괜찮다고 안심할 수 없다.&lt;/p&gt;
&lt;p data-end=&quot;1815&quot; data-start=&quot;1678&quot; data-ke-size=&quot;size14&quot;&gt;데이터는 무조건 늘어나게 되어 있다. 1MB, 10MB였던 데이터가 몇 달 후엔 1GB, 10GB가 되고, 그때도 Scan을 돌리고 있다면? 성능 차이는 &lt;b&gt;선형이 아니라 기하급수적으로 벌어질&lt;/b&gt; 것이다. 지금 당장은 안 터질 수도 있다.&lt;/p&gt;
&lt;p data-end=&quot;1815&quot; data-start=&quot;1678&quot; data-ke-size=&quot;size14&quot;&gt;하지만 언젠간 반드시 병목이 된다.&lt;/p&gt;
&lt;h3 data-end=&quot;1815&quot; data-start=&quot;1678&quot; data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;p data-end=&quot;1953&quot; data-start=&quot;1876&quot; data-ke-size=&quot;size14&quot;&gt;DynamoDB는 굉장히 빠르고 유연한 서비스다.&lt;br /&gt;하지만 아무리 좋은 기술도 &lt;b&gt;잘못 설계된 구조 위에서는 성능을 보장할 수 없다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2026&quot; data-start=&quot;1955&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1975&quot; data-start=&quot;1955&quot;&gt;데이터 접근 패턴을 먼저 정의하고&lt;/li&gt;
&lt;li data-end=&quot;2002&quot; data-start=&quot;1976&quot;&gt;그에 맞는 PK, SK, GSI 구조를 잡고&lt;/li&gt;
&lt;li data-end=&quot;2026&quot; data-start=&quot;2003&quot;&gt;fallback 구조도 코드에 녹여두고&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2099&quot; data-start=&quot;2028&quot; data-ke-size=&quot;size14&quot;&gt;이런 기초적인 설계가 가장 중요하다는 걸 이번에 다시 깨달았다.&lt;br /&gt;&lt;b&gt;앞으로는 절대 Scan부터 쓰는 일은 없을 것 같다.&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;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Production Traffic</category>
      <author>dml113</author>
      <guid isPermaLink="true">https://story0410.tistory.com/37</guid>
      <comments>https://story0410.tistory.com/37#entry37comment</comments>
      <pubDate>Tue, 29 Jul 2025 23:27:56 +0900</pubDate>
    </item>
    <item>
      <title>[AWS Service] 생성한 EC2에 EBS 마운트하는 방법</title>
      <link>https://story0410.tistory.com/36</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;안녕하세요. 이번 글에서는 생성한 EC2에 EBS를 마운트하는 방법에 대해서 적어보도록 하겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;먼저 EC2에 마운트 작업을 하기 때문에 Default VPC를 사용하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사전준비&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;먼저 Default VPC 혹은 VPC를 생성하고, 임의의 EC2를 생성합니다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1. EBS 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;먼저 생성한 EC2에 붙힐 EBS를 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cggiWN/btsOz8AXfgr/veFd7cLDEtzKXkdICKkv21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cggiWN/btsOz8AXfgr/veFd7cLDEtzKXkdICKkv21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cggiWN/btsOz8AXfgr/veFd7cLDEtzKXkdICKkv21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcggiWN%2FbtsOz8AXfgr%2FveFd7cLDEtzKXkdICKkv21%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;1670&quot; height=&quot;584&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;원하는 Volume type와 Size 등등 잘 설정하여 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1699&quot; data-origin-height=&quot;1488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wtjTK/btsOyoyJmuB/Ojq4KJ7FuW4PtK3Het6zo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wtjTK/btsOyoyJmuB/Ojq4KJ7FuW4PtK3Het6zo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wtjTK/btsOyoyJmuB/Ojq4KJ7FuW4PtK3Het6zo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwtjTK%2FbtsOyoyJmuB%2FOjq4KJ7FuW4PtK3Het6zo0%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;1699&quot; height=&quot;1488&quot; data-origin-width=&quot;1699&quot; data-origin-height=&quot;1488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2. EC2에 EBS 연결&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;EC2에 생성한 EBS를 Attach 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;607&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mAzrK/btsOzfOzSJL/D2fJID6PPsKPwQWtgUgRrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mAzrK/btsOzfOzSJL/D2fJID6PPsKPwQWtgUgRrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mAzrK/btsOzfOzSJL/D2fJID6PPsKPwQWtgUgRrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmAzrK%2FbtsOzfOzSJL%2FD2fJID6PPsKPwQWtgUgRrK%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;1670&quot; height=&quot;607&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;607&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;원하는 Instance를 선택하고 Device name을 선택 후 Attach 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;707&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGj827/btsOziduCy6/9Cio4uBYJ6OCptY5HLPGT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGj827/btsOziduCy6/9Cio4uBYJ6OCptY5HLPGT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGj827/btsOziduCy6/9Cio4uBYJ6OCptY5HLPGT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGj827%2FbtsOziduCy6%2F9Cio4uBYJ6OCptY5HLPGT1%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;1686&quot; height=&quot;707&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;707&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3. EBS 마운트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;EBS를 마운트하기 위해 먼저 EC2에 접근합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;접근 후 아래 명령어를 실행하면 아래와 같이 마운트되지 않은 디스크가 추가되어 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749735949750&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;lsblk

# output:
NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nvme0n1       259:0    0    8G  0 disk
├─nvme0n1p1   259:1    0    8G  0 part /
├─nvme0n1p127 259:2    0    1M  0 part
└─nvme0n1p128 259:3    0   10M  0 part /boot/efi
nvme1n1       259:4    0  100G  0 disk&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;확인하였다면 이제 파일시스템을 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749736077187&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkfs -t ext4 /dev/nvme1n1

# output:
mke2fs 1.46.5 (30-Dec-2021)
Creating filesystem with 26214400 4k blocks and 6553600 inodes
Filesystem UUID: 92eddf0f-e69f-4ab9-a418-44426d648382
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
        4096000, 7962624, 11239424, 20480000, 23887872

Allocating group tables: done
Writing inode tables: done
Creating journal (131072 blocks):
done
Writing superblocks and filesystem accounting information: done&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;파일시스템까지 생성하였다면 마운트할 디렉토리를 생성합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1749736209655&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir /data&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;전 /data라는 디렉토리에다가 마운트해보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1749736586181&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mount /dev/nvme1n1 /data&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;4. 마운트 확인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;아래 명령어를 사용하여 정상적으로 마운트되었는지 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749736738938&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df -h

# output:
Filesystem        Size  Used Avail Use% Mounted on
devtmpfs          4.0M     0  4.0M   0% /dev
tmpfs             453M     0  453M   0% /dev/shm
tmpfs             181M  432K  181M   1% /run
/dev/nvme0n1p1    8.0G  1.6G  6.5G  20% /
tmpfs             453M     0  453M   0% /tmp
/dev/nvme0n1p128   10M  1.3M  8.7M  13% /boot/efi
tmpfs              91M     0   91M   0% /run/user/0&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;5. 마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;오늘은 생성된 EC2에 새로운 Volume을 마운트하는 작업을 해봤습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;읽어주셔서 감사합니다.&lt;/p&gt;</description>
      <category>AWS Service</category>
      <author>dml113</author>
      <guid isPermaLink="true">https://story0410.tistory.com/36</guid>
      <comments>https://story0410.tistory.com/36#entry36comment</comments>
      <pubDate>Thu, 12 Jun 2025 23:01:35 +0900</pubDate>
    </item>
    <item>
      <title>[Kubernetes] 여러 Kubernetes 네임스페이스에서 단일 ALB Ingress 사용하기</title>
      <link>https://story0410.tistory.com/35</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;이번 글에서는 Amazon EKS에서 단일 AWS Application Load Balancer(ALB)를 사용하여 여러 Kubernetes 네임스페이스에 배포된 서비스에 대한 인그레스 트래픽을 관리하는 방법을 소개합니다. 구체적으로, 서로 다른 네임스페이스에 배포된 두 개의 서비스(HTTPD와 NGINX)에 대해 ALB Ingress Controller를 설정하고 URL 경로를 기준으로 트래픽을 라우팅하는 방법을 설명합니다.&lt;/p&gt;
&lt;h3 data-end=&quot;310&quot; data-start=&quot;301&quot; data-ke-size=&quot;size23&quot;&gt;사전 준비&lt;/h3&gt;
&lt;p data-end=&quot;336&quot; data-start=&quot;311&quot; data-ke-size=&quot;size16&quot;&gt;시작하기 전에 다음 사항들을 확인해야 합니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;506&quot; data-start=&quot;337&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;369&quot; data-start=&quot;337&quot;&gt;EKS 클러스터가 정상적으로 작동하고 있어야 합니다.&lt;/li&gt;
&lt;li data-end=&quot;506&quot; data-start=&quot;370&quot;&gt;ALB Ingress Controller가 클러스터에 설치되어 있어야 합니다. 설치 방법은 &lt;a href=&quot;https://docs.aws.amazon.com/eks/latest/userguide/alb-ingress.html&quot;&gt;공식 문서&lt;/a&gt;를 참조하세요.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. httpd 네임스페이스에 HTTPD 애플리케이션 배포하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;우선 httpd 네임스페이스에 HTTPD 서비스를 배포합니다. 아래는 HTTPD 배포 및 서비스에 대한 매니페스트 파일입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739681204209&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# httpd.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-deployment
  namespace: httpd
spec:
  selector:
    matchLabels:
      app: httpd
  replicas: 2
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: httpd
spec:
  type: NodePort
  selector:
    app: httpd
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1223&quot; data-start=&quot;1167&quot;&gt;위 매니페스트는 httpd 네임스페이스에 HTTPD 컨테이너 2개로 구성된 배포를 생성합니다.&lt;/li&gt;
&lt;li data-end=&quot;1275&quot; data-start=&quot;1224&quot;&gt;서비스는 NodePort 유형으로 설정되어 있으며, 80번 포트를 외부로 노출합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. nginx 네임스페이스에 NGINX 애플리케이션 배포하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;다음으로, nginx 네임스페이스에 NGINX 서비스를 배포합니다. 아래는 NGINX 배포 및 서비스에 대한 매니페스트 파일입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739681292562&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# nginx.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: nginx
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP&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;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2011&quot; data-start=&quot;1954&quot;&gt;이 매니페스트는 nginx 네임스페이스에 2개의 NGINX 컨테이너로 구성된 배포를 생성합니다.&lt;/li&gt;
&lt;li data-end=&quot;2063&quot; data-start=&quot;2012&quot;&gt;서비스는 NodePort 유형으로 설정되어 있으며, 80번 포트를 외부로 노출합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. ALB Ingress 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이제, 두 네임스페이스의 서비스를 ALB를 통해 라우팅할 수 있도록 Ingress 리소스를 설정합니다. 먼저, HTTPD 애플리케이션에 대한 Ingress를 설정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739681334262&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# httpd-ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: instance
    alb.ingress.kubernetes.io/group.name: &quot;test&quot;
spec:
  rules:
    - http:
        paths:
          - path: /httpd
            pathType: Prefix
            backend:
              service:
                name: httpd
                port:
                  number: 80&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;size14&quot;&gt;위 매니페스트는 /httpd 경로로 들어오는 요청을 httpd 서비스로 라우팅합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739681411846&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# nginx-ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: skills-ingress
  namespace: nginx
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: instance
    alb.ingress.kubernetes.io/group.name: &quot;test&quot;
spec:
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx
                port:
                  number: 80&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이 매니페스트는 / 경로로 들어오는 요청을 nginx 서비스로 라우팅합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;결과&lt;/h4&gt;
&lt;p data-end=&quot;3470&quot; data-start=&quot;3410&quot; data-ke-size=&quot;size14&quot;&gt;위의 매니페스트 파일들을 적용한 후 생성되는 ALB에서는 다음과 같은 경로로 트래픽을 라우팅할 수 있습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3542&quot; data-start=&quot;3472&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3504&quot; data-start=&quot;3472&quot;&gt;/ 경로로 접속하면 NGINX 페이지가 표시됩니다.&lt;/li&gt;
&lt;li data-end=&quot;3542&quot; data-start=&quot;3505&quot;&gt;/httpd 경로로 접속하면 HTTPD 페이지가 표시됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-is-last-node=&quot;&quot; data-end=&quot;3648&quot; data-start=&quot;3544&quot; data-ke-size=&quot;size14&quot;&gt;이처럼 하나의 ALB를 사용하여 여러 네임스페이스에 배포된 서비스들에 대해 경로 기반으로 트래픽을 라우팅할 수 있습니다. 이 방법을 통해 다양한 서비스들을 효율적으로 관리할 수 있습니다.&lt;/p&gt;</description>
      <category>Kubernetes</category>
      <author>dml113</author>
      <guid isPermaLink="true">https://story0410.tistory.com/35</guid>
      <comments>https://story0410.tistory.com/35#entry35comment</comments>
      <pubDate>Sun, 16 Feb 2025 13:50:40 +0900</pubDate>
    </item>
    <item>
      <title>[AWS Service] MWAA와 Glue를 연동하여 데이터 파이프라인 구축하기</title>
      <link>https://story0410.tistory.com/34</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;안녕하세요! 오늘은 Amazon MWAA (Managed Workflows for Apache Airflow)와 AWS Glue를 연동하여 데이터 파이프라인을 구축하는 방법에 대해 알아보겠습니다. MWAA는 Airflow를 관리형 서비스로 제공하여 데이터 엔지니어링 워크플로우를 쉽게 관리할 수 있도록 해줍니다. Glue는 서버리스 ETL(추출, 변환, 로드) 서비스로, 데이터를 정리하고 변환하는 데 사용됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1.&lt;span&gt;&amp;nbsp;&lt;/span&gt;S3 버킷 생성&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;먼저, S3 버킷을 두 개 생성합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;mwaa-scripts-s3&lt;/b&gt;: MWAA 스크립트, DAG, 플러그인, 요구 사항 파일을 저장하는 데 사용됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;mwaa-dataset-s3&lt;/b&gt;: 처리할 CSV 파일을 업로드할 데이터셋 저장소로 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2.&lt;span&gt;&amp;nbsp;&lt;/span&gt;로컬 환경 설정 (MWAA Local Runner)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;Amazon MWAA에서 바로 작업을 시작하기 전에 로컬 환경에서 코드를 테스트하는 것이 좋습니다. 다음 단계를 따라 로컬 환경을 설정합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2.1. Bastion 호스트 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;원하는 인스턴스 타입을 사용하여 인스턴스를 사용합니다. 다음 명령을 실행하여 Docker와 Git을 설치합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739595187904&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo dnf install -y docker
sudo systemctl enable --now docker
sudo usermod -a -G docker ec2-user
sudo su - ec2-user
sudo dnf install -y git&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2.2. MWAA Local Runner 클론 및 설정&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;GitHub에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;aws-mwaa-local-runner&lt;span&gt;&amp;nbsp;&lt;/span&gt;리포지토리를 클론하고 디렉터리로 이동합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739595234629&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git clone https://github.com/aws/aws-mwaa-local-runner.git
cd aws-mwaa-local-runner&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;Docker 이미지를 빌드합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739595246310&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;./mwaa-local-env build-image&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;로컬 서버를 시작합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739595257924&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;./mwaa-local-env start&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3.&lt;span&gt;&amp;nbsp;&lt;/span&gt;Airflow DAG 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;Airflow DAG 파일을 작성하여 S3 파일 감지 후 Glue 작업을 트리거하도록 설정합니다. 아래는 예시 DAG 코드입니다 (&lt;/span&gt;/dags/etl-dags.py&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;).&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739595285262&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from airflow import DAG
from airflow.providers.amazon.aws.operators.glue import GlueJobOperator
from airflow.providers.amazon.aws.sensors.s3 import S3KeySensor
from datetime import datetime, timedelta

default_args = {
    &quot;owner&quot;: &quot;airflow&quot;,
    &quot;depends_on_past&quot;: False,
    &quot;start_date&quot;: datetime(2024, 2, 15),
    &quot;email_on_failure&quot;: False,
    &quot;email_on_retry&quot;: False,
    &quot;retries&quot;: 1,
    &quot;retry_delay&quot;: timedelta(minutes=5),
}

dag = DAG(
    &quot;s3_trigger_glue_jobs&quot;,
    default_args = default_args,
    schedule_interval = None,
    catchup = False
)

s3_sensor = S3KeySensor(
    task_id = &quot;s3_file_sensor&quot;,
    bucket_name = &quot;mwaa-dataset-s3-0410&quot;,
    bucket_key = &quot;raw/*.csv&quot;,
    wildcard_match = True,
    aws_conn_id=&quot;aws_default&quot;,
    timeout=600,
    poke_interval=30,
    dag=dag,
)

glue_task = GlueJobOperator(
    task_id = &quot;glue_job&quot;,
    job_name = &quot;mwaa-job&quot;,
    script_location=f&quot;s3://mwaa-scripts-s3-0410/scripts/glue.py&quot;,
    s3_bucket=&quot;mwaa-scripts-s3-0410&quot;,
    create_job_kwargs={&quot;GlueVersion&quot;: &quot;4.0&quot;, &quot;NumberOfWorkers&quot;: 2, &quot;WorkerType&quot;: &quot;G.1X&quot;}

)

s3_sensor &amp;gt;&amp;gt; glue_task&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;4.&lt;span&gt;&amp;nbsp;&lt;/span&gt;Glue Job 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;mwaa-job이라는 이름을 가진 job을 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caULBN/btsMj58tUch/ClTFKikJtTApDjKKFXDd41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caULBN/btsMj58tUch/ClTFKikJtTApDjKKFXDd41/img.png&quot; data-origin-width=&quot;1772&quot; data-origin-height=&quot;376&quot; data-is-animation=&quot;false&quot; style=&quot;width: 65.4827%; margin-right: 10px;&quot; data-widthpercent=&quot;66.25&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caULBN/btsMj58tUch/ClTFKikJtTApDjKKFXDd41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaULBN%2FbtsMj58tUch%2FClTFKikJtTApDjKKFXDd41%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;1772&quot; height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSOQqk/btsMlhGOW4I/KLxvRAJ43lkjVrHmks8E0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSOQqk/btsMlhGOW4I/KLxvRAJ43lkjVrHmks8E0K/img.png&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;784&quot; data-is-animation=&quot;false&quot; style=&quot;width: 33.3545%;&quot; data-widthpercent=&quot;33.75&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSOQqk/btsMlhGOW4I/KLxvRAJ43lkjVrHmks8E0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSOQqk%2FbtsMlhGOW4I%2FKLxvRAJ43lkjVrHmks8E0K%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;1882&quot; height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bO2j7M/btsMjnvdTBq/r9UUVzvTReeAPGssIDUG2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bO2j7M/btsMjnvdTBq/r9UUVzvTReeAPGssIDUG2k/img.png&quot; data-origin-width=&quot;1873&quot; data-origin-height=&quot;778&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.0811%; margin-right: 10px;&quot; data-widthpercent=&quot;49.66&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bO2j7M/btsMjnvdTBq/r9UUVzvTReeAPGssIDUG2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbO2j7M%2FbtsMjnvdTBq%2Fr9UUVzvTReeAPGssIDUG2k%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;1873&quot; height=&quot;778&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJy7Dk/btsMjFoSW1U/ekEKY7FivM4UnlmjWzx4QK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJy7Dk/btsMjFoSW1U/ekEKY7FivM4UnlmjWzx4QK/img.png&quot; data-origin-width=&quot;1889&quot; data-origin-height=&quot;774&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.7561%;&quot; data-widthpercent=&quot;50.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJy7Dk/btsMjFoSW1U/ekEKY7FivM4UnlmjWzx4QK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJy7Dk%2FbtsMjFoSW1U%2FekEKY7FivM4UnlmjWzx4QK%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;1889&quot; height=&quot;774&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;AWS Glue에서 생성한 스크립트를 Bastion 호스트의&lt;span&gt;&amp;nbsp;&lt;/span&gt;/scripts/glue.py에 저장합니다. 이 스크립트는 MWAA에서 트리거될 Glue 작업을 정의합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1876&quot; data-origin-height=&quot;791&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddrk1j/btsMkx4EX6N/OSdwcx8xBG8ReZQiV6NqBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddrk1j/btsMkx4EX6N/OSdwcx8xBG8ReZQiV6NqBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddrk1j/btsMkx4EX6N/OSdwcx8xBG8ReZQiV6NqBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fddrk1j%2FbtsMkx4EX6N%2FOSdwcx8xBG8ReZQiV6NqBk%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;1876&quot; height=&quot;791&quot; data-origin-width=&quot;1876&quot; data-origin-height=&quot;791&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;5.&lt;span&gt;&amp;nbsp;&lt;/span&gt;S3 버킷에 파일 업로드&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;다음 명령을 사용하여 스크립트, DAG, 요구 사항 파일 등을&lt;span&gt;&amp;nbsp;&lt;/span&gt;mwaa-scripts-s3&lt;span&gt;&amp;nbsp;&lt;/span&gt;버킷에 업로드합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739595820676&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aws s3 cp scripts/ s3://mwaa-scripts-s3-0410/scripts --recursive
aws s3 cp dags/ s3://mwaa-scripts-s3-0410/dags/ --recursive
aws s3 cp requirements/ s3://mwaa-scripts-s3-0410/requirements/ --recursive
aws s3 cp plugins/ s3://mwaa-scripts-s3-0410/plugins/ --recursive&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;6.&lt;span&gt;&amp;nbsp;&lt;/span&gt;MWAA 환경 생성 및 설정&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;AWS 콘솔에서 MWAA 환경을 생성합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MWAA가 사용할 IAM 역할에&lt;span&gt;&amp;nbsp;&lt;/span&gt;Admin&lt;span&gt;&amp;nbsp;&lt;/span&gt;권한을 부여해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1766&quot; data-origin-height=&quot;1645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJUlrq/btsMk1YvlsA/k9oJ8PBsMkfv3JpKUdBSm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJUlrq/btsMk1YvlsA/k9oJ8PBsMkfv3JpKUdBSm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJUlrq/btsMk1YvlsA/k9oJ8PBsMkfv3JpKUdBSm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJUlrq%2FbtsMk1YvlsA%2Fk9oJ8PBsMkfv3JpKUdBSm1%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;1766&quot; height=&quot;1645&quot; data-origin-width=&quot;1766&quot; data-origin-height=&quot;1645&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1733&quot; data-origin-height=&quot;3789&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7ANan/btsMjG9bLRL/xf6gn10AQEOOrE6uOEpfZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7ANan/btsMjG9bLRL/xf6gn10AQEOOrE6uOEpfZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7ANan/btsMjG9bLRL/xf6gn10AQEOOrE6uOEpfZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7ANan%2FbtsMjG9bLRL%2Fxf6gn10AQEOOrE6uOEpfZK%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;1733&quot; height=&quot;3789&quot; data-origin-width=&quot;1733&quot; data-origin-height=&quot;3789&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;7.&lt;span&gt;&amp;nbsp;&lt;/span&gt;Airflow UI에서 DAG 실행&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;Airflow UI에 접속하여 DAG를 트리거합니다. &quot;Trigger DAG&quot; 버튼을 클릭하여 DAG를 실행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1859&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HY8UV/btsMj8xn5VL/jxdpeyoiYJggiKa0pbH0DK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HY8UV/btsMj8xn5VL/jxdpeyoiYJggiKa0pbH0DK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HY8UV/btsMj8xn5VL/jxdpeyoiYJggiKa0pbH0DK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHY8UV%2FbtsMj8xn5VL%2FjxdpeyoiYJggiKa0pbH0DK%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;1859&quot; height=&quot;436&quot; data-origin-width=&quot;1859&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;8.&lt;span&gt;&amp;nbsp;&lt;/span&gt;성공 확인&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;Airflow UI에서 DAG 실행 상태를 확인합니다. 모든 작업이 성공적으로 완료되면 다음과 유사한 화면이 표시됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;609&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byXmnx/btsMjOTCKMD/bqOUPqojrcuVuoCttaqYK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byXmnx/btsMjOTCKMD/bqOUPqojrcuVuoCttaqYK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byXmnx/btsMjOTCKMD/bqOUPqojrcuVuoCttaqYK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyXmnx%2FbtsMjOTCKMD%2FbqOUPqojrcuVuoCttaqYK1%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;1258&quot; height=&quot;609&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;609&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;결론&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;이 가이드에서는 Amazon MWAA와 AWS Glue를 연동하여 데이터 파이프라인을 구축하는 방법을 설명했습니다. 이 구성을 통해 데이터 워크플로우를 자동화하고 확장 가능한 데이터 처리 시스템을 구축할 수 있습니다.&lt;/p&gt;</description>
      <category>AWS Service</category>
      <author>dml113</author>
      <guid isPermaLink="true">https://story0410.tistory.com/34</guid>
      <comments>https://story0410.tistory.com/34#entry34comment</comments>
      <pubDate>Sat, 15 Feb 2025 23:26:59 +0900</pubDate>
    </item>
    <item>
      <title>[Kubernetes] Kubernetes Mutating Webhook을 이용한 Sidecar 자동 주입 가이드</title>
      <link>https://story0410.tistory.com/33</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;이번 글에서는 Kubernetes Mutating Webhook을 사용하여 Sidecar를 자동으로 주입하는 방법에 대해 알아봅니다. 이를 통해 각 Pod에 Sidecar 컨테이너를 수동으로 추가하는 번거로움을 덜고, 일관된 환경을 유지할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1. Prerequisites&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;시작하기 전에 다음 도구가 설치되어 있어야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;git&lt;/li&gt;
&lt;li&gt;kubectl&lt;/li&gt;
&lt;li&gt;make&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2. 프로젝트 복제 및 설정&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;먼저, 다음 명령어를 사용하여 필요한 Kubernetes Mutating Webhook 튜토리얼 저장소를 복제합니다.\&lt;/p&gt;
&lt;pre id=&quot;code_1739528125102&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git clone https://github.com/morvencao/kube-mutating-webhook-tutorial.git
cd kube-mutating-webhook-tutorial&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3. Sidecar Injector 이미지 빌드 및 ECR Push&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;Sidecar Injector 이미지를 사용하기 위해 AWS ECR (Elastic Container Registry)을 생성하고, 이미지를 빌드하여 푸시합니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;IMAGE&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;변수를 ECR 리포지토리 URL 및 원하는 태그로 설정해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739528177535&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;make docker-build docker-push IMAGE=&amp;lt;ECR REPO URL&amp;gt;:&amp;lt;IMAGE TAG&amp;gt;

# ex) make docker-build docker-push IMAGE=950274644703.dkr.ecr.ap-northeast-2.amazonaws.com/webhook:latest&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;4. ConfigMap 수정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;Kubernetes에 배포하기 전에 Sidecar로 사용할&lt;/span&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이미지로 설정하려면 &lt;/span&gt;deploy/configmap.yaml&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일을 수정합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739528286302&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ~/kube-mutating-webhook-tutorial/deploy/configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: sidecar-injector
  labels:
    app: sidecar-injector
data:
  sidecarconfig.yaml: |
    containers:
    - name: sidecar-curl
      image: curlimages/curl # 원하는 이미지 사용
      imagePullPolicy: IfNotPresent
      args:
      - --version
      command:
        - sleep
        - infinity&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;5. Kustomization 수정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;deploy/kustomization.yaml&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일에서 불필요한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;nginx-configmap&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;리소스를 제거하고, 사용할 이미지를 지정합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739528345490&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ~/kube-mutating-webhook-tutorial/deploy/kustomization.yaml

namespace: sidecar-injector

resources:
- namespace.yaml
- clusterrole.yaml
- clusterrolebinding.yaml
- deployment.yaml
- service.yaml
- serviceaccount.yaml
- configmap.yaml

images:
- name: sidecar-injector
  newName: &amp;lt;AccountID&amp;gt;.dkr.ecr.ap-northeast-2.amazonaws.com/webhook
  newTag: latest
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;6. Kubernetes 배포&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;모든 설정이 완료되면, 다음 명령어를 사용하여 Kubernetes에 배포합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739528406737&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;make deploy IMAGE=&amp;lt;ECR REPO URL&amp;gt;:&amp;lt;IMAGE TAG&amp;gt;

# make deploy IMAGE=12345678.dkr.ecr.ap-northeast-2.amazonaws.com/webhook:latest&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;7. Namespace 생성 및 Label 추가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;Sidecar Injection을 활성화할 Namespace를 생성하고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;sidecar-injection=enabled&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;레이블을 추가합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739528433602&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl create ns test-ns
kubectl label namespace test-ns sidecar-injection=enabled&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;레이블이 제대로 추가되었는지 확인합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739528446160&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl get namespace -L sidecar-injection

# output:
NAME                 STATUS   AGE   SIDECAR-INJECTION
default              Active   26m
test-ns              Active   13s   enabled
kube-public          Active   26m
kube-system          Active   26m
sidecar-injector     Active   17m&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;8. 테스트 Pod 배포&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;테스트를 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;test-ns&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Namespace에 Pod를 배포합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739528474369&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl -n test-ns run alpine \
    --image=alpine \
    --restart=Never \
    --command -- sleep infinity&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;9. 배포 확인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;다음 명령어를 사용하여 Pod가 정상적으로 실행 중인지 확인합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739528494169&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl -n test-ns get pod

# output: 
NAME                     READY     STATUS        RESTARTS   AGE
alpine                   2/2       Running       0          10s&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;READY&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;열이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;2/2&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;로 표시되면 Sidecar가 성공적으로 주입된 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;마무리&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;이 가이드에서는 Kubernetes Mutating Webhook을 사용하여 Sidecar를 자동으로 주입하는 방법을 설명했습니다. 이를 통해 애플리케이션 개발 및 배포 프로세스를 간소화하고, 일관된 환경을 유지할 수 있습니다.&lt;/p&gt;</description>
      <category>Kubernetes</category>
      <author>dml113</author>
      <guid isPermaLink="true">https://story0410.tistory.com/33</guid>
      <comments>https://story0410.tistory.com/33#entry33comment</comments>
      <pubDate>Fri, 14 Feb 2025 19:22:10 +0900</pubDate>
    </item>
    <item>
      <title>[Kubernetes] EKS 클러스터에 ReadOnly 및 Admin 권한 설정하기</title>
      <link>https://story0410.tistory.com/32</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;안녕하세요! 이번 글에서는 Amazon EKS (Elastic Kubernetes Service) 클러스터에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;ReadOnly&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;및&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Admin&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;권한을 설정하는 방법에 대해 알아보겠습니다. 이 설정을 통해 특정 IAM 역할 (Role)에게 클러스터 내의 리소스에 대한 읽기 전용 또는 관리자 권한을 부여할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사전 준비 사항&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;warm-readonly-eks-role&lt;span&gt;&amp;nbsp;&lt;/span&gt;생성 및 Bastion 연결&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;ReadOnly&lt;span&gt;&amp;nbsp;&lt;/span&gt;권한을 부여할 IAM 역할을 미리 생성하고, 필요에 따라 Bastion 호스트에 연결해둡니다. 마찬가지로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Admin&lt;span&gt;&amp;nbsp;&lt;/span&gt;역할을 위한 IAM 역할도 미리 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 클러스터 접근 및 설정&lt;/h4&gt;
&lt;pre id=&quot;code_1739525465235&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# clusterrole.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: readonly-role
rules:
- apiGroups:
  - '*'
  resources:
  - 'pods'
  verbs:
  - get
  - list
  - watch&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1739525518476&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# clusterrolebinding.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: readonly-role
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: readonly-role
subjects:
- kind: Group
  name: readonly-role
  apiGroup: rbac.authorization.k8s.io&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1739525395312&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 권한 부족으로 apply 도중 오류가 발생할 수 있으므로 root 권한으로 접속합니다.
aws configure

# 설정 파일들을 apply 합니다.
kubectl apply -f &amp;lt;설정 파일 경로&amp;gt;

# aws-auth ConfigMap 파일을 수정합니다.
kubectl edit -n kube-system configmap/aws-auth&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;위에 있는 manifest를 apply합니다. kubectl edit&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어를 사용하면 텍스트 편집기가 열립니다. 아래와 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;mapRoles&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;섹션에 새로운 역할을 추가합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2.&lt;span&gt;&amp;nbsp;&lt;/span&gt;ReadOnly 권한 설정&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;다음은&lt;span&gt;&amp;nbsp;&lt;/span&gt;ReadOnly&lt;span&gt;&amp;nbsp;&lt;/span&gt;권한을 부여하는 설정 예시입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;aws-auth&lt;span&gt;&amp;nbsp;&lt;/span&gt;ConfigMap에 다음 내용을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739525611388&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::&amp;lt;AccountID&amp;gt;:role/eksctl-skills-cluster-nodegroup-ng-NodeInstanceRole-WZqydbY21JGQ
      username: system:node:{{EC2PrivateDNSName}}
    - groups:
      - readonly-role  # ReadOnly 권한을 부여할 그룹
      rolearn: arn:aws:iam::&amp;lt;AccountID&amp;gt;:role/bastion-role # ReadOnly 역할을 할 IAM Role ARN
      username: readonly-role
kind: ConfigMap
metadata:
  creationTimestamp: &quot;2024-05-03T23:06:55Z&quot;
  name: aws-auth
  namespace: kube-system
  resourceVersion: &quot;4087&quot;
  uid: d8525b90-6e1e-4da6-8782-a1d5b98999bf&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;groups&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;readonly-role 는&lt;span&gt;&amp;nbsp;&lt;/span&gt;ReadOnly&lt;span&gt;&amp;nbsp;&lt;/span&gt;권한을 가진 그룹을 나타냅니다. 이 그룹 이름은 임의로 설정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;rolearn&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;arn:aws:iam::&amp;lt;AccountID&amp;gt;:role/bastion-role은&lt;span&gt;&amp;nbsp;&lt;/span&gt;ReadOnly&lt;span&gt;&amp;nbsp;&lt;/span&gt;역할을 수행할 IAM 역할의 ARN (Amazon Resource Name)입니다. 실제 IAM 역할 ARN으로 바꿔야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;username&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;readonly-role은 Kubernetes 내에서 이 역할에 매핑될 사용자 이름입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3.&lt;span&gt; 동작 테스트&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;동작 테스트를 위해 설정한 creditional을 삭제 후 instance에 생성한 bastion-role로 설정 후 다시 진행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739525669405&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rm -rf ~/.aws/*&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;아래와 같이 pod를 생성하려고 하면 권한 에러가 발생합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739525852174&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kubectl run nginx --image nginx&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;EKS 클러스터에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;ReadOnly&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;권한을 설정함으로써, 클러스터 리소스에 대한 접근 권한을 세밀하게 제어하고 보안을 강화할 수 있습니다. 이 가이드를 통해 EKS 클러스터의 보안을 더욱 효과적으로 관리하시기 바랍니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Kubernetes</category>
      <author>dml113</author>
      <guid isPermaLink="true">https://story0410.tistory.com/32</guid>
      <comments>https://story0410.tistory.com/32#entry32comment</comments>
      <pubDate>Fri, 14 Feb 2025 18:38:00 +0900</pubDate>
    </item>
    <item>
      <title>KubeArmor로 Kubernetes 환경에 보안 정책 적용하기</title>
      <link>https://story0410.tistory.com/31</link>
      <description>&lt;pre id=&quot;code_1739516185603&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
  name: ksp-group-1-proc-path-block
  namespace: green
spec:
  severity: 5
  message: &quot;block /bin/sleep&quot;
  selector:
    matchLabels:
      app: green
  process:
    matchPaths:
    - path: /bin/sleep
  action:
    Block&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;안녕하세요! 오늘은 Kubernetes 클러스터에 KubeArmor를 사용하여 간단한 보안 정책을 적용하는 방법에 대해 알아보겠습니다. KubeArmor는 Kubernetes 환경에서 런타임 보안을 강화할 수 있는 강력한 도구입니다. 이 튜토리얼에서는 KubeArmor CLI를 설치하고, 몇 가지 예시 YAML 파일을 사용하여 프로세스, 파일 접근, 시스템 호출에 대한 보안 규칙을 정의하고 적용하는 방법을 설명합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1. KubeArmor CLI 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;먼저, KubeArmor CLI를 설치해야 합니다. 아래 명령어를 사용하여 KubeArmor를 설치합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739515669664&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -sfL http://get.kubearmor.io/ | sudo sh -s -- -b /usr/local/bin
# sudo access is needed to install it in /usr/local/bin directory. But, if you prefer not to use sudo, you can install it in a different directory which is in your PATH.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Helm을 설치하였다면 아래와 같이 KubeArmor를 설치할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739516011520&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;karmor install&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2. KubeArmor 설정 파일 (kubearmor.yaml)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;다음은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;/bin/sleep&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;실행을 차단하는 간단한 KubeArmor 정책 예제입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739516192954&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
  name: ksp-group-1-proc-path-block
  namespace: green
spec:
  severity: 5
  message: &quot;block /bin/sleep&quot;
  selector:
    matchLabels:
      app: green
  process:
    matchPaths:
    - path: /bin/sleep
  action:
    Block&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;apiVersion&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;security.kubearmor.com/v1&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 사용할 API 버전을 지정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;kind&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;KubeArmorPolicy&lt;span&gt;&amp;nbsp;&lt;/span&gt;- KubeArmor 정책 리소스임을 나타냅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;metadata&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;name&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;ksp-group-1-proc-path-block&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 정책의 이름을 지정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;namespace&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;green&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 정책이 적용될 네임스페이스를 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;spec&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;severity&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;5&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 이벤트의 중요도를 나타냅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;message&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;block /bin/sleep&quot;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 이벤트 발생 시 표시될 메시지입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;selector&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;matchLabels&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;app&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;green&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 정책이 적용될 Pod의 레이블을 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;process&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;matchPaths&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;path&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;/bin/sleep&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 차단할 프로세스 실행 경로를 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;action&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;Block&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 지정된 프로세스 실행을 차단합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3. 배포 설정 파일 (deployment.yaml)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;다음은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;busybox&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이미지를 사용하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;/bin/sleep&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어를 실행하는 간단한 Deployment 예제입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739516265286&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: green-app
  namespace: green
spec:
  replicas: 1
  selector:
    matchLabels:
      app: green
  template:
    metadata:
      labels:
        app: green
    spec:
      containers:
        - name: green-container
          image: busybox
          command:
            - &quot;sh&quot;
            - &quot;-c&quot;
            - &quot;echo 'Starting /bin/sleep'; /bin/sleep 3600&quot;
          resources:
            limits:
              memory: &quot;64Mi&quot;
              cpu: &quot;250m&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;4. 보안 정책 추천&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;KubeArmor는 자동으로 보안 정책을 추천해주는 기능을 제공합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739516339804&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;karmor recommend -n green&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;5. 파일 접근 차단 정책&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;다음은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;/account/&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;디렉터리에 대한 접근을 차단하는 정책 예제입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739516377064&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
  name: ksp-group-1-proc-path-block
  namespace: green
spec:
  severity: 5
  message: &quot;a critical directory was accessed&quot;
  selector:
    matchLabels:
      app: green
  file:
    matchDirectories:
    - dir: /account/
      fromSource:
      - path: /bin/cat
  action:
    Block&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;6. 디렉터리 접근 감사 정책&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;다음은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;/usr/bin/&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;디렉터리에 대한 접근을 감사하는 정책 예제입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739516400057&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
  name: ksp-group-1-proc-path-block
  namespace: green
spec:
  tags:
  - WARNING
  severity: 10
  message: &quot;block /usr/bin&quot;
  selector:
    matchLabels:
      app: green
  process:
    matchDirectories:
    - dir: /usr/bin/
      recursive: true
  action:
    Audit&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;7. 시스템 호출 감사 정책&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;다음은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;sleep&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;프로세스가 종료될 때 로그를 출력하는 정책 예제입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739518101008&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
  name: ksp-group-1-proc-path-block
  namespace: green
spec:
  severity: 3
  message: &quot;sleep syscall&quot;
  selector:
    matchLabels:
      app: green
  syscalls:
    matchSyscalls:
    - syscall:
      - exit
  action:
    Audit&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot;&gt;KubeArmor를 사용하면 Kubernetes 환경에서 런타임 보안을 쉽게 강화할 수 있습니다. 이 튜토리얼에서는 KubeArmor CLI를 설치하고, 간단한 YAML 파일을 사용하여 프로세스, 파일 접근, 시스템 호출에 대한 보안 규칙을 정의하고 적용하는 방법을 설명했습니다. KubeArmor를 사용하여 Kubernetes 클러스터를 더욱 안전하게 보호하세요!&lt;/span&gt;&lt;/p&gt;</description>
      <category>Kubernetes/CNCF</category>
      <author>dml113</author>
      <guid isPermaLink="true">https://story0410.tistory.com/31</guid>
      <comments>https://story0410.tistory.com/31#entry31comment</comments>
      <pubDate>Fri, 14 Feb 2025 16:31:53 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] Docker Swarm으로 마이크로서비스 구축하기</title>
      <link>https://story0410.tistory.com/30</link>
      <description>&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;안녕하세요! 오늘은 Docker Swarm을 사용하여 마이크로서비스 아키텍처를 구축하는 방법에 대해 알아보겠습니다. Docker Swarm은 여러 Docker 호스트를 클러스터로 구성하여 컨테이너를 쉽게 관리할 수 있게 해주는 도구입니다.&lt;/p&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1. Docker 설치 및 Swarm 초기화&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;먼저, 마스터 서버에 Docker를 설치하고 Swarm을 초기화합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739515059447&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
dnf install -y docker
systemctl enable --now docker

# Swarm 초기화
docker swarm init --advertise-addr 10.0.1.228&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;Swarm을 초기화하면 워커 노드를 추가할 수 있는 명령어가 출력됩니다. 이 명령어를 복사해두세요.&lt;/p&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2. 워커 노드 추가&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;워커 노드로 사용할 서버에 접속하여 복사한 명령어를 실행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739515099531&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker swarm join --token SWMTKN-1-4uu7ft977083s6zxn6j939lpqv7upypv2sdlm93efdb9vj7yji-6cdxe8dyfr0a7bxsduwwtr3nd 10.0.3.180:2377&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3. 노드 상태 확인&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;마스터 노드에서 다음 명령어를 실행하여 노드 상태를 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739515123458&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker node ls&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;4. 마스터 노드 설정&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;마스터 노드에 컨테이너를 생성하지 않도록 설정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739515235723&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker node update --availability pause &amp;lt;Node ID&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;5. 오버레이 네트워크 생성&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;노드 간 컨테이너 통신을 위한 오버레이 네트워크를 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739515256297&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker network create --driver overlay network&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;6. 서비스 배포&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;ECR 이미지를 사용하여 서비스를 배포합니다. DNS 설정과 레지스트리 인증을 위한 옵션을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1739515295665&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker service create --with-registry-auth --dns 8.8.8.8 --network network -p 8080:8080 --replicas=3 --name getuser 950274644703.dkr.ecr.ap-northeast-2.amazonaws.com/get-user:latest
docker service create --with-registry-auth --dns 8.8.8.8 --network network -p 8081:8080 --replicas=3 --name managed 950274644703.dkr.ecr.ap-northeast-2.amazonaws.com/managed:latest
docker service create --with-registry-auth --dns 8.8.8.8 --network network -p 8082:8080 --replicas=3 --name servercheck 950274644703.dkr.ecr.ap-northeast-2.amazonaws.com/servercheck:latest&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;마무리&lt;/h4&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;이렇게 하면 Docker Swarm을 사용하여 마이크로서비스 아키텍처를 구축할 수 있습니다. 각 서비스는 3개의 레플리카로 실행되며, 오버레이 네트워크를 통해 서로 통신할 수 있습니다. 또한, 각 서비스는 서로 다른 포트로 외부에 노출되어 있어 쉽게 접근할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: oklch(0.99 0.004 106.471); color: oklch(0.304 0.04 213.681); text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Docker Swarm을 사용하면 서비스의 확장성과 가용성을 쉽게 관리할 수 있으며, 롤링 업데이트 등의 기능도 활용할 수 있습니다. 이를 통해 효율적이고 안정적인 마이크로서비스 운영이 가능해집니다.&lt;/p&gt;</description>
      <category>Linux</category>
      <author>dml113</author>
      <guid isPermaLink="true">https://story0410.tistory.com/30</guid>
      <comments>https://story0410.tistory.com/30#entry30comment</comments>
      <pubDate>Wed, 12 Feb 2025 23:15:03 +0900</pubDate>
    </item>
  </channel>
</rss>