<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>son_doobu96.log</title>
        <link>https://velog.io/</link>
        <description>DevOps를 꿈꾸는 엔지니어 지망생!</description>
        <lastBuildDate>Thu, 02 Mar 2023 11:07:09 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>son_doobu96.log</title>
            <url>https://velog.velcdn.com/images/son_doobu96/profile/6a19a8eb-c11d-4c2b-897e-b26c7f28dcdb/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. son_doobu96.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/son_doobu96" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[성능 테스트]]></title>
            <link>https://velog.io/@son_doobu96/%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@son_doobu96/%EC%84%B1%EB%8A%A5-%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Thu, 02 Mar 2023 11:07:09 GMT</pubDate>
            <description><![CDATA[<h3 id="◎-테스트의-목적">◎ 테스트의 목적</h3>
<ul>
<li>시스템 확장성을 가졌는지 확인</li>
<li>성능을 개선하기 위해 확장해야 하는 시스템이 무엇인지 파악</li>
<li>부하가 많이 발생할 때 문제 상황 개선</li>
<li>각 시스템의 병목 지점을 예측하고 진단 및 개선</li>
</ul>
<p>여기서의 가용성과 확장성은 아래와 같다.</p>
<h4 id="■-가용성">■ 가용성</h4>
<p>가용성(Availability)이란 시스템이 정상적으로 사용 가능한 정도를 말한다.
정상적인 사용시간(Uptime) / 전체 시간(Uptime + Downtime)으로 계산되며 이러한 가용성의 핵심은 Uptime의 비율을 늘림으로써
단일 장애점(Single Point of Failure)을 없애는 것이다.</p>
<p>고가용성의 경우에는 소실되는 데이터의 방지를 목적으로 한다.</p>
<h4 id="■-확장성">■ 확장성</h4>
<p>확장 가능한 시스템이란 요구되는 시스템의 성능에 따라 동적으로 서버 구성이 변경되고, 시스템 처리 능력을 최적화할 수 있는 시스템을 의미한다.</p>
<p>위의 가용성을 고가용성으로 유지하기 위해 필요한 것이 바로 확장성이다.</p>
<hr>
<h3 id="◎-성능-테스트의-방법">◎ 성능 테스트의 방법</h3>
<h4 id="발생할-수-있는-문제-상황">발생할 수 있는 문제 상황</h4>
<ul>
<li>응답 속도(Latency) 저하</li>
<li>시스템 잠금(Lock) 경합</li>
<li>부하 발생시 애플리케이션 또는 서버 에러 발생</li>
<li>데이터 일관성 문제와 손실</li>
</ul>
<p>따라서 성능 테스트는 지표를 통해 시스템을 점검하고 발생할 수 있는 병목 지점을 예측학 진단 및 개선하는 활동이다.</p>
<h4 id="성능테스트의-종류">성능테스트의 종류</h4>
<p><strong>■ 6가지 성능테스트 유형</strong></p>
<blockquote>
<p><strong>▶ Load Test (most common out of the five)</strong></p>
</blockquote>
<ul>
<li>적정 부하(= Peak시점의 부하)를 유지하는 테스트</li>
</ul>
<p>Load Testing은 부하가 임계치(Threshold Value)에 도달할 때까지 시스템의 부하를 지속적으로 증가시키면서 시스템을 확인하는, Performance Testing의 한 유형이다. 
동시단말사용자(concurrent users)와 트랜잭션을 증가시키면서 테스트 중인 애플리케이션의 동작을 확인하는 것을 의미합니다. </p>
<p>Load Testing의 주요 목적은 과부하 상태(under heavy load)에서 시스템이 잘 작동될 때, 응답시간과 애플리케이션의 지구력(staying power)을 모니터링하는 것입니다. </p>
<p>Load Testing은 테스트중인 애플리케이션이 견딜 수 있는 부하의 양을 확인하기 위해 수행됩니다. </p>
<blockquote>
<p><strong>▶ Stress Test</strong></p>
</blockquote>
<ul>
<li>적정 부하의 2배를 유지하는 테스트</li>
</ul>
<p>Stress Testing은 CPU, Memory, Disk Space 등과 같은 하드웨어 자원이 충분하지 않을 때, 소프트웨어의 안정성을 확인하는 Performance Testing의 한 유형입니다. 
Stress Testing의 기본 개념은 시스템의 오류를 확인하고, 어떻게 시스템이 정상적으로 복구되는지를 살펴보는 것입니다.</p>
<p>Stress Testing은 시스템 하드웨어 자원에서 처리할 수 없는 많은 수의 동시단말사용자(concurrent users)/프로세스로 소프트웨어에 부하를 주는 Negative Testing입니다. </p>
<p>Stress Testing의 기본 개념은 시스템의 장애를 확인하고 시스템이 어떻게 정상적으로 복구되는지를 주시하는 것입니다. 이러한 품질 특성을 회복성(recoverability)이라고 합니다.</p>
<blockquote>
<p><strong>▶ Endurance Test</strong></p>
</blockquote>
<ul>
<li>장시간 부하를 유지하는 테스트</li>
</ul>
<p>Endurance testing은 시스템의 동작을 확인하기 위해, 장기간에 걸쳐 예상되는 부하량에 기반하여 시스템을 테스트 하는 것을 포함합니다. 
예를 들어, 시스템이 3시간동안 작업을 수행하도록 설계되었다면, 동일 시스템이 6시간동안 지속되어도 시스템이 지구력을 유지하는지를 확인하는 것입니다. </p>
<p>가장 일반적인 테스트 케이스는 메모리 누수, 시스템 장애 또는 무작위적인 동작(random behavior)과 같은 시스템의 동작을 확인하기 위해 수행됩니다. 때때로, Endurance testing은 ‘Soak testing’이라고도 합니다.</p>
<blockquote>
<p><strong>▶ Spike Test</strong></p>
</blockquote>
<ul>
<li>순간적인 대량 부하를 발생시키는 테스트</li>
</ul>
<p>Spike testing은 Stress Testing의 Subset입니다. 
테스트 대상 시스템에 대해, 상용 운영환경에서 예상되는 부하 이상의 Workload를 짧은 기간동안 반복적으로 증가시킬 때 나타나는 성능 특성을 검증하기 위해 수행합니다.</p>
<blockquote>
<p><strong>▶ Scalability Testing</strong></p>
</blockquote>
<ul>
<li>시스템의 최대치를 확인하는 테스트</li>
</ul>
<p>Scalability Testing은 사용자 부하, 트랜잭션 수, 데이터 볼륨 등과 같은 비기능 측면에서 확장할 수 있는 역량을 판단하기 위한 소프트웨어 애플리케이션 테스트입니다. 이 테스트의 주요 목적은 더이상 확장(Scaling)하지 못하도록 막는 ‘시스템의 Peak’가 무엇인지 확인하는 것입니다.</p>
<blockquote>
<p><strong>▶ Volume testing</strong></p>
</blockquote>
<ul>
<li>데이터 효율성 테스트</li>
</ul>
<p>Volume testing은 처리해야 할 많은 양의 데이터를 가진 애플리케이션의 효율성을 확인하기 위한 테스트입니다. 이 테스트의 주요 목표는 다양한 Database Volumes 하에서 애플리케이션의 성능을 모니터링 하는 것입니다.</p>
<hr>
<h3 id="◎-성능-테스트-용어">◎ 성능 테스트 용어</h3>
<h4 id="throughput-처리량">Throughput (처리량)</h4>
<p>시간당 처리량으로 데이터 전송량에 포커스를 맞춘 성능 지표로 매우 실용적인 지표이다.
Throughput 성능 지표는 RPS(request per second) 와 TPS(transaction per second)로 구성되며</p>
<p>볼륨의 성능을 측정할 경우에는 IOPS(Input/Output per second)라는 단위를 사용한다.</p>
<ul>
<li>1초에 처리하는 HTTP 요청 수 (rps)</li>
<li>(동영상 스트리밍 서비스와 같이 대역폭이 중요한 경우) 네트워크로 전송되는 데이터 전송 속도</li>
</ul>
<h4 id="latency-처리시간">Latency (처리시간)</h4>
<p>요청이 처리되는 시간을 나타낸다.</p>
<h4 id="response-time-응답시간">Response Time (응답시간)</h4>
<p>응답을 받기까지의 시간</p>
<h4 id="network-bandwitdh대역폭">Network Bandwitdh(대역폭)</h4>
<p>네트워크에서 특정 시간 내에 전송 될 수 있는 데이터의 최대 용량
네트워크에서 잠재적으로 동시에 전송될 수 있는 데이터의 최대치</p>
<ul>
<li>실질적인 성능을 나타내는 지표가 아니다.</li>
</ul>
<h4 id="cpu">CPU</h4>
<p>컴퓨터에서 수행할 수 있는 모든 작업을 담당하며 소프트웨어의 명령과 함께 
시스템 메모리의 프로그램에 대한 명령을 실행한다.</p>
<p>CPU의 핵심 기능은 랜덤 액세스 메모리(RAM)에서 명령을 가져온 다음 명령을 디코딩하고 실행하는 것입니다. 프로세스를 차례로 실행합니다.</p>
<blockquote>
<p>CPU의 범용 기능</p>
</blockquote>
<ul>
<li>코어 : 
코어는 기본적으로 CPU의 프로세서입니다. 컴퓨팅 고대로 거슬러 올라가면 프로세서에는 단 하나의 코어만 있었습니다. 이제 컴퓨터에는 일반적으로 2개에서 64개의 코어가 있습니다. CPU에 코어가 많을수록 성능과 효율성이 향상됩니다.</li>
<li>동시 멀티스레딩/하이퍼스레딩 : 
동시 멀티스레딩(Intel 프로세서에서 하이퍼스레딩이라고 함)은 처리가 단일 코어가 아닌 여러 소프트웨어 스레드에 위임되는 경우입니다. 이를 통해 더 많은 작업을 동시에 수행할 수 있으므로 하나의 코어를 두 개의 &quot;논리적&quot; 코어로 효과적으로 전환할 수 있습니다.</li>
<li>캐시 :
CPU에는 자체 초고속 메모리가 내장되어 있어 RAM이나 SSD 유형 보다 빠릅니다 . CPU 캐시는 L1-L3에서 배열되며 L1이 가장 빠릅니다. CPU는 필요한 정보를 그곳에 빠르게 저장합니다.</li>
<li>메모리 관리 장치(MMU) : 
MMU는 모든 메모리 및 캐싱 작업을 담당합니다. 일반적으로 CPU에 통합되어 가져오기-디코드-실행 주기 동안 CPU와 RAM 사이의 중개자 역할을 하여 필요에 따라 데이터를 왕복합니다. 또한 소프트웨어에서 제공하는 가상 주소를 RAM에서 사용하는 물리적 주소로 변환합니다.</li>
<li>제어 장치 : 
제어 장치는 CPU 작동을 조율합니다. RAM, 논리 장치 및 I/O 장치에 수신된 명령에 따라 작동하는 방법을 알려줍니다.</li>
</ul>
<h4 id="gpu">GPU</h4>
<p>이미지와 비디오를 렌더링하는 컴퓨터 구성요소이다. 자체 RAM이 있거나 통합형 CPU와 메모리를 공유한다.</p>
<p>GPU는 특수 CPU와 같으며 특히 멀티태스킹에 적합하다.
CPU와의 가장 큰 차이는 작업을 순차적으로 처리하는 대신 작업을 분할하여 병렬로 실행한다.</p>
<p>따라서 더 많은 코어를 가지고 있고 이러한 추가 코어를 통해 더 많은 계산 및 작업을 한번에 더 효율적을 처리가 가능하다.
하지만 할 수 있는 일의 범위는 보다 제한적이다.</p>
<h4 id="graceful-shutdown">Graceful Shutdown</h4>
<p>프로그램이 종료될 때 최대한 side effect를 내지 않기 위해 로직들을 잘 처리하고 종료하는 것을 의미한다.</p>
<hr>
<h3 id="◎-응답-성능의-병목-현상-해결-방법">◎ 응답 성능의 병목 현상 해결 방법</h3>
<p>먼저 처리량이 몰리는 병목 지점을 명학히 확인해야 한다. 이후 지연시간에 문제를 발생시키는 요소들에 맞는 해결방법을 진행해야 한다.</p>
<p>아래는 대표적인 예들이다.</p>
<ul>
<li>많은 사용자의 서비스 등록</li>
<li>많은 데이터의 저장<ul>
<li>1,2번과 같은 경우 DB에 데이터가 증가합니다. secondary 복제본 등을 이용해 읽기/쓰기를 분리하거나, 
검색에 최적화된 인덱스 사용을 고려할 수 있습니다.</li>
</ul>
</li>
<li>단기간 동안의 사용자 요청 증가(peak traffic)<ul>
<li>Auto Scaling이 해결책이 될 수 있습니다. 다만 버스트 성능에 대해 이해해야 합니다.</li>
</ul>
</li>
<li>배치 작업을 진행하는 데이터베이스<ul>
<li>DB가 주기적으로 스냅샷을 만들거나, 데이터 일관성을 위해 레플리카와의 sync 과정을 진행하는 등의 
배치 작업이 이루어질 경우, primary DB는 성능 저하가 발생할 수 있습니다. 
이 때 사용자들의 요청과 맞물려 서비스 수준을 맞추기 어려울 수 있습니다.</li>
</ul>
</li>
<li>많은 양의 로그 수집 처리<ul>
<li>애플리케이션이 잘 작동할 때에는 로그를 많이 남기지 않지만, 
애플리케이션에 문제가 발생하면 추적을 위해 많은 로그를 남깁니다. 
다만 이러한 상황이 반복적으로 진행될 경우, 에러 로그 수집 그 자체가 애플리케이션 병목을 일으킬 수 있습니다.</li>
</ul>
</li>
<li>시스템 재시작 후의 캐시 초기화<ul>
<li>큰 문제를 발생시키는 것은 아니지만, 캐시가 초기화되면서 시스템으로 직접적인 요청 횟수가 증가할 수 있습니다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Mornitoring 기본 개념]]></title>
            <link>https://velog.io/@son_doobu96/Mornitoring-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@son_doobu96/Mornitoring-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Tue, 28 Feb 2023 16:18:29 GMT</pubDate>
            <description><![CDATA[<h3 id="◎-모니터링이란-무엇인가">◎ 모니터링이란 무엇인가</h3>
<p>모니터링이란 지속적 또는 비정기적 관찰을 통해 발생할 수 있는 문제를 확인하고 이에 대한 대응을 하기 위해 필요한 메트릭을 수집하는 일련의 과정이다.</p>
<h4 id="■-무엇을-수집하는가">■ 무엇을 수집하는가?</h4>
<p>모니터링에서 수집하는 지표를 메트릭(metric)이라고 한다.
메트릭이란 시간에 따라 측정한 결과값으로 어떠한 수치로서 나타낼 수 있는 값을 말한다.</p>
<ul>
<li>CPU 사용량</li>
<li>성공한 요청의 수</li>
<li>메시지 대기열의 길이</li>
</ul>
<h4 id="■-모니터링의-목표">■ 모니터링의 목표?</h4>
<ul>
<li>시간을 기준으로 측정되는 주요 메트릭을 최소화하여 고가용성을 달성한다.</li>
<li>사용량을 추적하여, 배포에 앞서 세운 가설들을 검증하고 개선해나간다.</li>
</ul>
<blockquote>
<p>▶ 구글의 모니터링 목표와 메트릭</p>
</blockquote>
<ul>
<li>장기적인 트렌드 분석 (Analyzing long-term trends)<ul>
<li>데이터베이스가 얼마만큼의 용량을 차지하며, 얼마나 빨리 용량이 증가하는가?</li>
<li>DAU(일간 활성 사용자수)는 얼마나 빨리 증가하는가?</li>
</ul>
</li>
<li>시간의 경과 및 실험 그룹 간의 비교 (Comparing over time or experiment groups)<ul>
<li>어떤 데이터베이스를 썼을 때 쿼리가 빠른가?</li>
<li>캐시용 노드를 추가했을 때, 캐시 적중률(hit rate)이 얼마나 향상되는가?</li>
<li>지난 주보다 사이트가 얼마나 느려졌는가?</li>
</ul>
</li>
<li>경고 (Alerting)<ul>
<li>인프라의 어떤 부분이 고장났는가? 혹은 고장날 수 있는가?
<a href="https://sre.google/sre-book/monitoring-distributed-systems/">구글 SRE-BOOK</a></li>
</ul>
</li>
</ul>
<blockquote>
<p>▶ 마이크로소프트의 모니터링 목표와 메트릭</p>
</blockquote>
<ul>
<li>캐시 사용률</li>
<li>CPU, Memory</li>
<li>인스턴스의 갯수</li>
<li>연결 유지
<a href="https://docs.microsoft.com/ko-kr/azure/data-explorer/using-metrics">마이크로 소프트 Azure data-explorer</a></li>
</ul>
<h4 id="■-모니터링의-구분">■ 모니터링의 구분</h4>
<p><strong>▶ 블랙박스 모니터링</strong></p>
<ul>
<li>관찰자가 박스를 기준으로 밖에서 바라본다.</li>
<li>CPU/메모리/스토리지 등 인프라 수준의 모니터링에 유용하다.</li>
<li>클러스터 작동 여부 등 쿠버네티스 컴포넌트 그 자체를 모니터링</li>
<li>단점으로는 애플리케이션이 왜 오류를 내는지 알 수 없다.</li>
</ul>
<p><strong>▶ 화이트박스 모니터링</strong></p>
<ul>
<li>관찰자가 박스를 기준으로 안에서 바라본다.</li>
<li>HTTP 요청, 500에러의 발생 횟수, 레이턴시 등이 이에 해당한다.</li>
<li>현상이 발생한 근거에 집중한다.</li>
</ul>
<hr>
<h4 id="■-모니터링시의-주의점">■ 모니터링시의 주의점</h4>
<p>▶ 계층에 따라 모니터링을 구분한다.</p>
<ul>
<li>쿠버네티스<ul>
<li>노드 &gt; 클러스터 컴포넌트 &gt; 파드</li>
</ul>
</li>
<li>ECS<ul>
<li>클러스터 &gt; 서비스 &gt; 태스크</li>
</ul>
</li>
<li>EC2: 인스턴스에 대한 메트릭</li>
<li>Lambda: 함수에 대한 메트릭</li>
</ul>
<p>▶ Proxy 서버
모니터링에서 가장 각별하게 주의해야 할 점은 내가 세운 목표를 위해 얻어야 하는 지표가 리소스에 따라 어떻게 다른지 확실하게 아는것이다.</p>
<p>WAS 서버를 모니터링한다면 각 노드의 컴퓨팅 자원을 모니터링하여 서버의 작동이 원활한지 부하는 심하지 않은지 체크했다면
앞단에 위치하게 되는 Proxy서버의 경우에는 HTTP 라우팅에 그 목적이 있기 때문에 요청 그 자체와 관련된 메트릭을 모니터링해야 한다.</p>
<hr>
<h4 id="■-어떤-메트릭이-중요한가">■ 어떤 메트릭이 중요한가?</h4>
<p>네 가지의 황금 시그널 (The Four Golden Signals)</p>
<p>◐ 대기 시간 (Latency)
대기 시간은 서비스가 요청에 응답하는 데 걸리는 시간을 나타냅니다. 핵심은 지속 시간뿐만 아니라 성공적인 요청의 대기 시간과, 실패한 요청의 대기 시간을 구별하는 데에도 중점을 두어야 합니다.</p>
<p>◐ 트래픽 (Traffic)
트래픽은 서비스에 대한 수요 측정입니다. 대표적인 예로는, 초당 HTTP 요청 수가 있습니다.</p>
<p>◐ 오류 (Errors)
오류는 실패한 요청/전체 요청 의 비율로 측정됩니다. 대부분의 경우 이러한 실패는 명시적이지만(예: HTTP 500 오류) 암시적일 수도 있습니다(예: &quot;결과 없음&quot;이라는 메시지를 본문으로 전달하는 HTTP 200 응답).</p>
<p>◐ 포화 수준 (Saturation)
포화는 서비스 또는 시스템 리소스를 “얼마나 가득 채워서 사용하는가”로 설명할 수 있습니다. 전형적인 예로는 과도한 CPU 자원 사용이 있습니다. CPU 자원이 부족하면, 스로틀링을 초래하고 결과적으로 응용 프로그램의 성능을 저하시킵니다.</p>
<h4 id="■-모니터링의-패턴">■ 모니터링의 패턴?</h4>
<p>◐ USE 패턴
USE 패턴은 모든 리소스에 대한 사용률(Utilization), 포화도(Saturation), 오류(Errors)를 체크하는 패턴을 의미합니다.</p>
<p>◐ RED 패턴
RED 패턴은 비율(Rate), 오류(Errors) 및 기간(Duration)을 주요 메트릭으로 정의하는 패턴입니다.</p>
<hr>
<h4 id="■-모니터링의-핵심">■ 모니터링의 핵심</h4>
<p><strong>SLA(Service Level Agreements) : 서비스 수준 협약</strong>
서비스를 운영하는데 있어서 서비스 제공자와 사용자간에 맺는 약속이다.
&quot;어느 정도의 서비스를 제공해야 제대로 제공했는지?&quot;에 대한 명확한 명시가 되어 있다.</p>
<p>따라서 이를 통해 명문화 되는 것이 <strong>SLO(Service Level Objectives) 서비스 수준 목표</strong>이다. 이를 통해 서비스 제공자와 사용자는 약속의 기준점을 정할 수 있다.</p>
<p>그리고 그 약속의 기준의 잣대가 되는 것이 <strong>SLI(Service Level Indicator) 서비스 수준 척도</strong>이다. 서비스의 수준을 판단할 수 있는 몇 가지를 정량적으로 측정한 값으로 주요 예시는 아래와 같다.</p>
<ul>
<li>응답 속도: 요청에 대한 응답이 리턴되기까지의 시간</li>
<li>에러율: 전체 요청 수 대비</li>
<li>처리량(throughput): 초당 처리할 수 있는 요청 수</li>
<li>가용성: 서비스가 사용 가능한 상태로 존재하는 시간의 비율</li>
<li>내구성: 데이터 저장이 중요한 목적인 서비스의 경우 특히 중요</li>
</ul>
<h4 id="■-지표를-설정하는법">■ 지표를 설정하는법?</h4>
<p>지표는 판단의 근거이다. 따라서 서비스의 분류에 따라 중요한 지표가 무엇인지를 명확하게 설정해야 한다.</p>
<ul>
<li>사용자가 직접 대면하는 시스템<ul>
<li>보통 프론트엔드에 해당하며, 이 경우 <strong>가용성, 응답 시간, 처리량</strong>이 중요합니다.</li>
</ul>
</li>
<li>저장소 시스템<ul>
<li><strong>응답 시간, 가용성, 내구성</strong>이 중요합니다.</li>
</ul>
</li>
<li>빅데이터 시스템<ul>
<li>데이터 파이프라인이 이에 해당하며, <strong>처리량</strong>, 그리고 <strong>엔드포인트 간 응답 시간</strong>이 중요합니다.</li>
</ul>
</li>
</ul>
<h4 id="■-척도의-수집하는-방법">■ 척도의 수집하는 방법?</h4>
<p>척도란 SLI의 정의를 표준화시키는 것이라고 생각하면 편하다.</p>
<blockquote>
<p>예를들어
집계 간격은 1분
집계 범위는 클러스터에서 수행되는 모든 태스크
측정의 빈도는 매 10분
집계할 내용은 전체 GET 요청 등이다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[후기]aws-certified-solutions-architect-associate 자격증 시험 후기]]></title>
            <link>https://velog.io/@son_doobu96/%ED%9B%84%EA%B8%B0aws-certified-solutions-architect-associate-%EC%9E%90%EA%B2%A9%EC%A6%9D-%EC%8B%9C%ED%97%98-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@son_doobu96/%ED%9B%84%EA%B8%B0aws-certified-solutions-architect-associate-%EC%9E%90%EA%B2%A9%EC%A6%9D-%EC%8B%9C%ED%97%98-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Mon, 27 Feb 2023 13:42:30 GMT</pubDate>
            <description><![CDATA[<h3 id="사건의-발단">사건의 발단</h3>
<p>23년 2월 15일 같이 스터디를 하던 팀원들과 이야기 하던 중 정말 충동적으로 SAA를 신청하게 되었다.</p>
<p>이미 합격한 1분과 신청한 1분의 조합은 나를 자극하기에 충분했고 몇 마디를 듣다보니 어느새 17만원을 결재하고 있었다. ㅋㅋㅋㅋㅋ</p>
<h3 id="시험-준비-과정">시험 준비 과정</h3>
<p>시험 일자는 2월 27일 준비는 16일부터 진행했다. 일단 합격한 사람의 말을 따라 좋다는 Udemy강의를 구매했다.
이때부터는 거의 브레이크가 뽑힌 8톤 트럭마냥 앞만 봤던 것 같다.</p>
<p>처음에는 굉장히 자신감이 있었다. 대학교때부터 외우는건 잘했으니까...</p>
<p>아날로그가 더 편한 내가 손으로 강의를 필기하며 하루 하루가 지나다 보니
필기한 페이지가 40페이지를 넘어간다...
<img src="https://velog.velcdn.com/images/son_doobu96/post/dd06a0e7-e10d-4491-a933-b964b17145e8/image.jpg" alt=""></p>
<p>점점 뭔가 잘못되어가는 것을 느꼈지만 이미 브레이크를 뽑았기에...  돌릴 수는 없었다.</p>
<h4 id="시험-준비기간-중의-프로젝트">시험 준비기간 중의 프로젝트</h4>
<p>이때가 정말 힘들었다. 어느 하나에도 집중하지 못하게 만드는 불안감과 줄어가는 시간의 급박함은 내 행동에 반영됐고 프로젝트를 진행하면서 무조건 18시 전에 끝내고 saa에 집중한다는 생각을 가지고 프로젝트를 진행하다보니 여기 저기 기본적인 실수들을 반복하면서 프로젝트를 진행했다.</p>
<p>팀원들에게 피해를 끼치고 싶지 않았고 욕심도 있어서 최대한 열심히는 했지만 많은 걸 얻지는 못한 프로젝트여서 아쉬움이 남았지만... 뒤돌아볼 시간이 없었다..</p>
<h3 id="last-2days">Last 2days</h3>
<p>이젠 잠도 안오기 시작했다. 자려고 누우면 30분 안에 시험이 꿈에 나왔다.
결국 계속 자리에 앉아서 필기한 내용을 읽고 문제를 하나 하나 뜯어보면서 요구하는 사항이 뭘지를 뜯어봤다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/f93f0482-de03-47f9-a7a4-a949751cabba/image.jpg" alt=""></p>
<p>그리고 비슷한 서비스들을 구분하면서 어떠한 상황에서 사용해야 할지를 최대한 체크했다. 그렇게 잠도 안오는 2일을 보냈다...</p>
<h3 id="시험-당일">시험 당일</h3>
<p>원래 긴장을 정말 안하는 스타일인데, 사실 긴장은 안했다ㅋㅋㅋㅋ
떨어지면 17만원 또 내고 말지라는 마인드로 갔는데 시험을 보고 난 후가 정말 긴장됐다. </p>
<p>같이 시험을 보러간 DevOps 동기랑 이런 저런 얘기를 하면서 정말 많이 찡찡댔던것 같다..</p>
<p>그렇게 기다리고 기다리다 보니 합격 목걸이가 들어왔다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/fd0909e9-92f6-466b-b34b-98727237cfdd/image.png" alt=""></p>
<p>ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ</p>
<h3 id="시험-후기">시험 후기</h3>
<p>정말 다양한 AWS 서비스들에 대해 알게 되었고 무엇보다 중요한건 비용 효율적, 성능 우선적인 AWS 아키텍처 설계에 대해 많이 알게 되어 조금은 뿌듯했던 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TROUBLESHOOTING] 자동 재고 확보 시스템 MSA 구축]]></title>
            <link>https://velog.io/@son_doobu96/TROUBLESHOOTING-%EC%9E%90%EB%8F%99-%EC%9E%AC%EA%B3%A0-%ED%99%95%EB%B3%B4-%EC%8B%9C%EC%8A%A4%ED%85%9C-MSA-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@son_doobu96/TROUBLESHOOTING-%EC%9E%90%EB%8F%99-%EC%9E%AC%EA%B3%A0-%ED%99%95%EB%B3%B4-%EC%8B%9C%EC%8A%A4%ED%85%9C-MSA-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Tue, 21 Feb 2023 12:00:22 GMT</pubDate>
            <description><![CDATA[<h3 id="step-4-목표">Step 4 목표</h3>
<p>SQS를 트리거로 해 Factory API에 생산을 요청하는 서비스 구현</p>
<hr>
<p>먼저 기존 Step 3번까지의 프로세스가 잘 작동하는지를 확인 후 작업을 진행했다.</p>
<p>Serverless 프레임워크를 통해 새로운 람다함수를 배포할 디렉토리를 만들고 함수를 작성해 나갔다.</p>
<pre><code>const axios = require(&#39;axios&#39;).default

const consumer = async (event) =&gt; {
for (const record of event.Records) {
    console.log(&quot;Message Body: &quot;, record.body);

// let data = JSON.stringify(record.body)
//   console.log(data)
// let parser = JSON.parse(data)
//   console.log(parser)
//   console.log(parseInt(record.body.MessageId))
// let mData = JSON.stringify(data.MessageAttributes)
//   console.log(mData)
// let pData = JSON.stringify(mData.MessageAttributeProductId)
//   console.log(pData)
// let fData = JSON.stringify(mData.MessageAttributeFactoryId)
//   console.log(fData)
// const data = await record.body
const payload = {
    MessageGroupId : &quot;stock-arrival-group&quot;, //&quot;stock-arrival-group&quot;,
    MessageAttributeProductId : &quot;ebb60806-b0bf-11ed-8e89-069658f3b1c6&quot;, //string(추가 생산이 필요한 제품 아이디),
    MessageAttributeProductCnt : &quot;100&quot;, //string(추가 생산 요청 수량),
    MessageAttributeFactoryId : &quot;d5f87cb3-b0bf-11ed-8e89-069658f3b1c6&quot; , //string(추가 생산을 요청할 공장 아이디),
    MessageAttributeRequester : &quot;손동훈&quot;, //string(추가 생산 요청 담당자)
    CallbackUrl : &lt;https://생산 요청 엔드포인트&gt; //string(생산 내역으로 데이터베이스에 재고를 추가할 서버의 주소)
}

  console.log(&quot;페이로드 확인: &quot;, payload)


 axios.post(&#39;http://project3-factory-api.coz-devops.click/api/manufactures&#39;, payload)
.then(function (response) {
  console.log(response);
})
.catch(function (error) {
  console.log(error);
});

} 
};
module.exports = {
  consumer,
};</code></pre><p>위의 수 많은 주석에서 알 수 있듯...ㅎㅎ JSON 참조 코드를 작성하는데 실패해서 일단 오늘은 아키텍처의 연결을 완벽하게 진행하는 것에 초점을 맞추기로 했다.</p>
<p>이 후 배포를 통해 함수의 작동을 확인했다.</p>
<p>일단 구현은 잘되어 함수가 잘 작동하는 것을 알 수 있었다.</p>
<hr>
<h3 id="◎-완성-후의-아키텍처-설계">◎ 완성 후의 아키텍처 설계</h3>
<h4 id="이게-첫날에-팀과-함께-만들었던-아키텍처-설계였다">이게 첫날에 팀과 함께 만들었던 아키텍처 설계였다.</h4>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/4edcc87f-bc6f-4645-9646-2182470697f9/image.png" alt=""></p>
<p>완성한 아키텍처의 구현을 확인한 지금 다행인 건 크게 다르지는 않았다는 점이고 깨달은점은 이전 아키텍처는 보안적 설계가 꽤나 허술했다는 점이었다.</p>
<p>방과 후 강의를 진행해 주시는 홍정민 강사님께서 나한테 이렇게 말씀해주셨다.</p>
<p>&quot;말이 안돼는 이야기긴 하지만 <strong>보안, 네트워크, 리소스</strong>&quot;중에서 하나를 빼려면 뭘 빼시겠어요?</p>
<p>굉장히 당황스러웠다... 저 셋중 뭐 하나라도 빠져도 되는게 있나?
일단 &#39;리소스&#39;를 빼는게 낫지 않을까요? 라고 대답했다.</p>
<p>그러자 한 번 더 물어보셨다.</p>
<p>&quot;그럼 네트워크, 보안 중에서는요?&quot;</p>
<p>&quot;네트워크가 빠지면 아무것도 못하지 않나요?&quot; 자신 있게 말했다.</p>
<p>&quot;보안이 없으면 다 털릴텐데요?&quot;</p>
<p>순간에는 이해가 안됐지만 서비스를 구현하면서 이해하게 되었다.
아무렇지 않게 클릭하는 이 리소스들 모든 게 사실은 돈이었다.
올바르게 공부한 DevOps라면 회사의, 나의 서비스가 안전할 수 있도록 하는 마음가짐을 가지는 게 먼저라는 생각이 들었다.</p>
<p>이 생각을 완전하게 적용하지는 못했지만 오늘 팀과 함께 설계 후의 아키텍처를 다시 그려보았다. 
처음에는 2시간이 넘게 걸리던 것이 30분도 안돼서 뚝딱 나오게 되었다.</p>
<h4 id="설계-후의-아키텍처">설계 후의 아키텍처</h4>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/8a2c8d75-95c4-4b98-ae54-0071134be9ee/image.png" alt=""></p>
<h4 id="간단한-설명">간단한 설명</h4>
<ul>
<li>유저는 프론트엔드 페이지를 통해 상품을 주문하고 그 API 요청을 트리거로 람다가 실행되어 판매의 영역이 형성된다.</li>
<li>재고가 부족한 경우 판매는 발생할 수 없으므로 생산 요청이 발생한다.</li>
<li>SNS를 통해 추후 서비스의 확장성과 유연성을 고려하였고 SQS를 활용해 비동기적 연결, DLQ를 이용해 메시지가 전달되지 못하는 장애 상황을 대비했다.</li>
<li>생산이 완료되면 외부 서비스인 Factory가 요청을 통해 RDS의 재고 현황을 업데이트 한다.</li>
</ul>
<h4 id="발생가능한-문제점">발생가능한 문제점?</h4>
<ul>
<li>외부 서비스의 RDS 접근? Nat gateway의 보안정책을 통해 제한된 리소스 연결을 진행하면 된다고 생각했다.</li>
<li>SNS, SQS로의 접근? SQS의 경우 엑세스 정책을 SNS의 토픽으로 제한하는 방법으로 해결할 수 있다고 생각했다.</li>
</ul>
<p>SNS에도 같은 방법을 적용할 수 있지만 확장성과 유연성을 고려해 배치한 
SNS에 하나의 람다 서비스에 대한 엑세스 정책을 허용하는 방법은 리소스의 설계 방향과 다르다고 생각하여 좀 더 깊은 고민이 필요하다는 생각이 들었다.</p>
<hr>
<h3 id="팀별-면접">팀별 면접</h3>
<p>오늘 팀별로 작성한 아키텍처와 그 아키텍처를 왜 구성했는지에 대한 간단한 면접을 진행했다. 내 생각과 답변을 정리해보면 좋을 것 같아 추가로 가져와 봤다.</p>
<ol>
<li>왜 Lambda를 사용했는가?</li>
</ol>
<ul>
<li>첫 번째 이유는 서버리스 서비스를 구현하기 위해서였다. 람다는 관리가 필요하지 않은 서버리스 서비스로써 트래픽에 따라 자동으로 관리되는 컴퓨팅 리소스이기에 해당 서비스에 람다가 적합하다고 생각했다.</li>
<li>두 번째 이유는 비용이다. 람다의 비용에는 요청, 호출 수와 함수의 실행시간이 포함되지만 가장 중요한 건 월별 1,000,000건의 요청과 400,000GB의 실행시간이 무료로 제공된다는 점이다.
이것이 람다를 선택함으로써 서비스의 초기 투자비용을 줄여줌으로써 서비스 정착의 안정성을 높여 줄 수 있는 중요 요인이라고 생각했다.</li>
</ul>
<ol start="2">
<li>구현한 서비스가 무엇인가?</li>
</ol>
<ul>
<li>판매와 생산의 영역으로 구분되는 서비스를 구현하였다.
그리고 여기서 중요한 점은 판매와 생산이 &quot;구분&quot;된다는 점이다. 동기적으로 이어진 서비스가 아니라 구분되어진 비동기적 연결의 서비스이기 때문에
유연한 서비스 운영과 장애 대처가 가능하다.</li>
</ul>
<ol start="3">
<li>SNS를 왜 사용했는가?</li>
</ol>
<ul>
<li>위의 포스팅에서의 언급처럼 SNS의 가장 큰 장점은 토픽으로 관리되는 서비스의 유연성과 확장성에 있다고 생각한다. 따라서 유연한 서비스 운영과 추후 확장 가능성까지 고려하여 SNS를 사용하게 되었다.</li>
<li>FIFO를 사용하지 않은 이유는 우리가 설계하려는 서비스와 맞지 않는다 생각했기 때문이다. FIFO와 Standard의 가장 큰 차이는 순서를 보장하느냐 아니냐이다. 
우리가 FIFO를 선택하지 않은 이유는 제한된 처리량의 서비스를 만들고 싶지 않았고 리소스의 배치 위치상 순서의 보장이 필요하지 않았기 때문이다.
만약 백엔드 역할을 하는 람다의 앞단에 SNS와 SQS가 위치하는 상항이라면 
순서가 매우 중요해진다, 생산과 판매의 순서가 꼬이면 안되기 때문이다.
하지만 백엔드 뒤에 위치하는 현 설계에서 SNS와 SQS가 전달해야 하는 메시지의 종류는 &quot;생산&quot;이라는 하나의 키워드로 통합될 수 있기에 순서를 보장할 필요가 없다고 생각했다.</li>
</ul>
<ol start="4">
<li>SQS를 왜 사용했는가?</li>
</ol>
<ul>
<li>SQS는 비동기적 연결을 가능하게 해주는 메시지 큐로써 생산과 판매의 서비스 영역을 분리하기 위해 사용했다.
만약 SQS가 아닌 API 게이트웨이를 통해 서비스가 연결되었다고 치면 동기적으로 연결된 서비스는 두 가지 문제점을 가지게 된다. 재고가 없는 경우 구매자는 어떠한 응답도 받을 수가 없다. 그리고 생산 영역 또는 판매 영역에서 장애가 발생하는 경우 두 서비스를 모두 이용 불가능하다.
우리는 이러한 문제점을 해결하기 위해 SQS를 통한 비동기적 연결로 서비스를 구현했고 이를 통해 앞선 사례에서 발생할 수 있는 문제점을 보완하고 보다 안정적인 서비스를 구현하기 위해 SQS를 사용했다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TROUBLESHOOTING] 자동 재고 확보 시스템 MSA 아키텍처 구축]]></title>
            <link>https://velog.io/@son_doobu96/TROUBLESHOOTING-MSA-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@son_doobu96/TROUBLESHOOTING-MSA-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Mon, 20 Feb 2023 12:07:11 GMT</pubDate>
            <description><![CDATA[<h3 id="step1-lambda-서버sales-api---db-연결">Step1. Lambda 서버(Sales API) - DB 연결</h3>
<p>Lambda를 통해 Sales-api를 구축하고 이를 미리 배포된 RDS DB와 연결하는 작업을 구현한다.</p>
<p>먼저 제공된 코드를 확인 후 작업을 진행했다.</p>
<pre><code>const serverless = require(&quot;serverless-http&quot;);
const express = require(&quot;express&quot;);
const app = express();
app.use(express.json())

const AWS = require(&quot;aws-sdk&quot;) // STEP 2
const sns = new AWS.SNS({ region: &quot;ap-northeast-2&quot; }) // STEP 2

const {
  connectDb,
  queries: { getProduct, setStock }
} = require(&#39;./database&#39;)

app.get(&quot;/product/donut&quot;, connectDb, async (req, res, next) =&gt; {
  const [ result ] = await req.conn.query(
    getProduct(&#39;CP-502101&#39;)
  )

  await req.conn.end()
  if (result.length &gt; 0) {
    return res.status(200).json(result[0]);
  } else {
    return res.status(400).json({ message: &quot;상품 없음&quot; });
  }
});

app.post(&quot;/checkout&quot;, connectDb, async (req, res, next) =&gt; {
  const [ result ] = await req.conn.query(
    getProduct(&#39;CP-502101&#39;)
  )
  if (result.length &gt; 0) {
    const product = result[0]
    if (product.stock &gt; 0) {
      await req.conn.query(setStock(product.product_id, product.stock - 1))
      return res.status(200).json({ message: `구매 완료! 남은 재고: ${product.stock - 1}`});
    }
    else {
      await req.conn.end()
      return res.status(200).json({ message: `구매 실패! 남은 재고: ${product.stock}`});
    }
  } else {
    await req.conn.end()
    return res.status(400).json({ message: &quot;상품 없음&quot; });
  }
});

app.use((req, res, next) =&gt; {
  return res.status(404).json({
    error: &quot;Not Found&quot;,
  });
});

module.exports.handler = serverless(app);
module.exports.app = app;</code></pre><p>이후 적절한 핸들러에 맞게 serverless.yaml을 구성했다.</p>
<pre><code>service: sales-api
frameworkVersion: &#39;3&#39;

provider:
  name: aws
  runtime: nodejs14.x
  region: ap-northeast-2

functions:
  api:
    handler: handler.handler
    events:
      - httpApi: &#39;*&#39;</code></pre><p>사실 Step1에서 가장 힘들었던 부분은 익숙하지 않은 SQL문이었다.
MySQL 엔진을 사용하는 RDS로 구성되어 있어 MySQL 접속 명령어를 통해 RDS에 접속하였다.</p>
<pre><code>$ mysql -h &lt;hostname&gt; -u &lt;uername&gt; -p
이후 패스워드를 입력해준다.</code></pre><p>이후 내 데이터베이스를 선택 후 데이터를 입력해주었다.</p>
<p>GET, POST 요청을 확인할 수 있었다.</p>
<hr>
<h3 id="step2-재고없음-메세지-전달-시스템-구성">Step2. “재고없음” 메세지 전달 시스템 구성</h3>
<p>해당 파트에서는 재고가 없다는 메시지를 전달 할 수 있도록 함수를 수정하고 SNS를 작동시키고 이를 SQS에 전달하는 것을 목표로 했다.</p>
<p>이 부분에서 가장 고생했던 부분은 Serverless Framework에 대한 미숙함이었다. Serverless 프레임워크를 처음 사용 하다보니 SQS SNS의 구현까지는 했으나 서비스를 분리하고 올바른 구독 관계를 생성하지 못해 꽤나 애를 먹었다.</p>
<p>그래서 먼저 콘솔을 통해 전체 관계를 구성 후 테라폼으로 이를 문서화 하기로 생각한 후 프레임워크로 진행하지 못한 부분을 콘솔로 진행했다.</p>
<p>먼저 Lambda함수에서 구매실패 부분에 아래 함수를 추가해줬다.</p>
<pre><code>const now = new Date().toString()
const message = `도너츠 재고가 없습니다. 제품을 생산해주세요! \n메시지 작성 시각: ${now}`
const params = {
  Message: message,
  Subject: &#39;도너츠 재고 부족&#39;,
  MessageAttributes: {
    MessageAttributeProductId: {
      StringValue: product.product_id,
      DataType: &quot;String&quot;,
    },
    MessageAttributeFactoryId: {
      StringValue: req.body.MessageAttributeFactoryId,
      DataType: &quot;String&quot;,
    },
  },
  TopicArn: process.env.TOPIC_ARN
}

const result = await sns.publish(params).promise()</code></pre><p>이후 SNS 서비스와 SQS 서비스를 생성하고 잘 연결까지 진행했는데 SQS에 메시지가 쌓이지 않는 문제가 발생했다.</p>
<p>아직 SQS를 트리거하는 함수가 있지 않은 상황이라 메시지가 삭제되는 것이 이상한데 아예 들어오지 않았다는 것을 금방 알 수 있었다.</p>
<p>함수를 여러번 수정해 보고 SQS 대기열을 삭제했다 고쳤다를 반복했지만 문제는 해결되지 않았다...</p>
<p>문제는 SQS의 엑세스 정책에 있었다.</p>
<p>SQS 엑세스 정책에서 접근 허용이 root 계정으로 제한되어 있는 것을 발견하고 이를 수정하여 문제를 해결할 수 있었다.</p>
<hr>
<h3 id="step-3--메세지를-factory-api로-전송하는-lambda-구성-및-dlq-추가">Step 3 : 메세지를 Factory API로 전송하는 Lambda 구성 및 DLQ 추가</h3>
<p>아직 Factroy API를 구현하지 않았기 때문에 기본 틀 정도만을 구현했다.</p>
<p>먼저 SQS의 DLQ를 생성하여 연결해주었고 새로운 람다 함수를 생성해 기존 SQS를 트리거로 붙여주어 구성을 마쳤다.</p>
<hr>
<h3 id="오늘-느낀점">오늘 느낀점</h3>
<p>사실 내용적으로 별 볼일 없는 포스팅이었다. 솔직히 말하면 쉬운 작업이었지만 문제는 내 수행 능력이 오늘 너무 부족했다...
머리로 이해하지만 구현하지 못하는 답답함을 느끼면서 정신적으로 많이 피폐해진 프로젝트였지만 한편으로는 DevOps라면 다양한 프레임워크에 대한 시각을 넓혀야 한다는 필요성을 느끼게 해준 프로젝트였다.</p>
<p>그리고 제발... 기본을 돌아보자...엑세스 정책에서 3시간은 정말 아니었다 ㅠㅠ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자동 재고 확보 시스템 MSA 설계]]></title>
            <link>https://velog.io/@son_doobu96/%EC%9E%90%EB%8F%99-%EC%9E%AC%EA%B3%A0-%ED%99%95%EB%B3%B4-%EC%8B%9C%EC%8A%A4%ED%85%9C-MSA-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@son_doobu96/%EC%9E%90%EB%8F%99-%EC%9E%AC%EA%B3%A0-%ED%99%95%EB%B3%B4-%EC%8B%9C%EC%8A%A4%ED%85%9C-MSA-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Fri, 17 Feb 2023 10:37:00 GMT</pubDate>
            <description><![CDATA[<h2 id="시나리오">시나리오</h2>
<blockquote>
<p>&lt;도넛-스테이츠&gt;는 온라인으로 도너츠를 판매합니다.
웹사이트를 통해서 주문 버튼을 누르는 것으로 구매(Sales API)가 가능합니다.
창고에 재고가 있다면 재고가 감소하고 구매가 완료됩니다.
유튜브스타 hoyong.LEE가 도넛-스테이츠의 도너츠가 맛있다고 영상을 올렸습니다.
그를 따르는 데브옵스 수강생들이 몰려듭니다. 주문이 급등합니다.
창고에 재고가 없기 때문에 구매가 불가능한 경우가 발생합니다.
창고의 도너츠 재고가 다 떨어지면 제조 공장에 알려서 다시 창고를 채우는 시스템을 구축해야합니다.
제조 공장인 &lt;팩토리-스테이츠&gt;에 주문을 요청(Leagcy Factory API)할 수 있습니다.
주문이 요청되면 일정 시간이 지난 후 창고에 재고가 증가합니다.</p>
</blockquote>
<h4 id="요구사항-1--재고부족으로-인한-구매실패에-대한-조치">요구사항 1 : 재고부족으로 인한 구매실패에 대한 조치</h4>
<ul>
<li>Sales API 를 통해 요청을 받은 서버가 데이터베이스에서 재고 상황을 확인합니다.</li>
<li>재고가 있다면 감소시키고 응답으로 판매완료 내용을 전달합니다.</li>
<li>재고가 없는 경우 공장에 주문을 진행합니다</li>
<li>재고가 없다는 내용을 담은 메세지 페이로드가 주제별로 생성됩니다.</li>
<li>메세지가 느슨하게 연결된 시스템을 통해 처리될 수 있도록 따로 보관됩니다.</li>
</ul>
<h4 id="요구사항-2--메세지-누락-상황에-대한-조치">요구사항 2 : 메세지 누락 상황에 대한 조치</h4>
<ul>
<li>빈번한 요청으로 메세지 누락이 발생합니다.</li>
<li>비동기 처리를 위해 보관 된 메세지가 처리되지 않은 경우 메세지들을 체계적으로 관리할 다른 처리 공간을 생성해야합니다.</li>
<li>메시지 처리 보관 리소스와 처리되지 않은 메세지 처리 리소스가 연결되어야합니다.</li>
</ul>
<h4 id="요구사항-3--legacy-시스템factory-→-warehouse-성능문제에-대한-조치">요구사항 3 : Legacy 시스템(Factory → Warehouse) 성능문제에 대한 조치</h4>
<ul>
<li>안정적으로 이벤트가 전달 될 수 있는 시스템을 구축해야합니다.</li>
<li>메세지를 소비하는 리소스를 통해 Factory API가 호출됩니다.</li>
<li>수신된 메세지에 의해 트리거가 된 컴퓨팅 리소스가 상품 재고를 증가시킵니다.</li>
</ul>
<hr>
<h4 id="설계한-다이어그램">설계한 다이어그램</h4>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/be803e97-f493-4215-9b31-3bf54abdd536/image.png" alt=""></p>
<h4 id="구현된-설계-사항">구현된 설계 사항</h4>
<ul>
<li>API 게이트웨이를 트리거로 하는 백엔드 람다 서버</li>
<li>SNS, SQS 시스템을 트리거로 하는 생산 요청 람다 서버</li>
<li>전달되지 않은 요청에 대한 장애 조치(DLQ)</li>
<li>외부 서버(Factory)에서 데이터를 업로드할 S3</li>
<li>S3로 부터 트리거 받아 재고 현황을 SQS에 전달하는 람다 함수</li>
<li>SQ로 부터 트리거 받아 재고 현황을 업데이트 하는 람다 함수</li>
</ul>
<h4 id="미비된-설계-사항">미비된 설계 사항</h4>
<ul>
<li>재고 현황 업데이트 시 전달되지 않은 요청에 대한 장애 조치</li>
</ul>
<blockquote>
<p>Proejct의 진행 시작부터 MSA 아키텍처를 다이어그램으로 설계하려 하니 꽤나 어려웠다. 팀원들끼리 이게 맞지 않을까요? 저게 맞지 않을까요? 정말 많은 이야기를 하면서 설계를 마칠 수 있었다. 
구현은 이제... 다음주의 나의 몫이니.. 잘 진행해 봐야겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[볼륨과 스테이트 풀셋]]></title>
            <link>https://velog.io/@son_doobu96/%EB%B3%BC%EB%A5%A8%EA%B3%BC-%EC%8A%A4%ED%85%8C%EC%9D%B4%ED%8A%B8-%ED%92%80%EC%85%8B</link>
            <guid>https://velog.io/@son_doobu96/%EB%B3%BC%EB%A5%A8%EA%B3%BC-%EC%8A%A4%ED%85%8C%EC%9D%B4%ED%8A%B8-%ED%92%80%EC%85%8B</guid>
            <pubDate>Wed, 15 Feb 2023 09:51:44 GMT</pubDate>
            <description><![CDATA[<h3 id="◎-볼륨과-스테이트풀셋의-기본-개념">◎ 볼륨과 스테이트풀셋의 기본 개념</h3>
<h4 id="■-파드는-stateless하다">■ 파드는 Stateless하다.</h4>
<ul>
<li>파드는 일시적이다. (언제든지 삭제될 수 있다.)</li>
<li>파드 그 자체는 Stateless하다. 따라서 디플로이먼트를 통해 교체와 배치되는 파드들은 상호 대체 가능하다.</li>
</ul>
<p>=&gt; 파드의 데이터는 언제든지 삭제 될 수 있다.</p>
<h4 id="■-영속적인-데이터를-남기는-방법">■ 영속적인 데이터를 남기는 방법?</h4>
<ul>
<li>파드 그 자체에 상태(데이터)를 남겨야 하는 애플리케이션 : MySQL, mongoDB, redis와 같은 DB</li>
<li>볼륨(Volume) : 프로그램이 종료되어도 사라지지 않는 영속적인 데이터를 저장하기 위해 분리된 서비스</li>
</ul>
<p>=&gt; 볼륨을 이용해 파드가 삭제되어도 데이터를 영속적으로 남길 수 있다.</p>
<hr>
<h4 id="■-스테이트풀셋">■ 스테이트풀셋</h4>
<p>스테이트풀셋은 애플리케이션 구성(파드)을 복제하더라도, 스토리지 클래스를 이용해 파드가 필요로 하는 볼륨을 자동으로 프로비저닝하여 연결합니다. </p>
<p>따라서 첫번째 파드를 primary(master), 두번째 파드를 secondary 복제본처럼 구성하는 것도 가능하다.</p>
<h4 id="▶-디플로이먼트와-스테이트풀셋의-공통점">▶ 디플로이먼트와 스테이트풀셋의 공통점</h4>
<p>동일한 컨테이너 스펙을 기반으로 둔 파드들을 관리한다. </p>
<h4 id="▶-디플로이먼트와-스테이트풀셋의-차이점">▶ 디플로이먼트와 스테이트풀셋의 차이점</h4>
<blockquote>
<p>● 디플로이먼트</p>
</blockquote>
<ul>
<li>파드의 순서와 고유성을 보장하지 않는다.</li>
<li>파드를 상호 대체 가능한 리소스로 취급한다.</li>
</ul>
<blockquote>
<p>● 스테이트풀셋</p>
</blockquote>
<ul>
<li>파드의 순서와 고유성을 보장</li>
<li>파드를 상호 대체 할 수 없는 리소스로 취급한다.</li>
</ul>
<h4 id="■-스테이트풀셋을-사용할-때의-주의사항">■ 스테이트풀셋을 사용할 때의 주의사항</h4>
<ul>
<li>파드에 지정된 스토리지는 관리자에 의해 퍼시스턴트 볼륨 프로비저너를 기반으로 하는 storage class를 요청해서 프로비저닝하거나 사전에 프로비전이 되어야 합니다.</li>
<li>헤드리스 서비스가 필요합니다.</li>
</ul>
<hr>
<h3 id="◎-생각해볼만한-문제">◎ 생각해볼만한 문제</h3>
<blockquote>
<p>Q.애플리케이션에 HTTP 500과 같은 에러가 발생한 경우, 컨테이너를 다시 실행해야 할 것 입니다. HTTP 에러가 발생했다는 것을 어떻게 알 수 있을까요? 어떻게 해야 쿠버네티스가 에러가 발생한 컨테이너를 자동으로 재시작하게 만들 수 있을까요?</p>
</blockquote>
<p>Project2에서 ECS를 활용하면서 해당 기능을 사용한 적이 있다. 로드밸런서를 이용해 대상그룹 내에 포함된 컨테이너들의 연결 상태를 체크하는 Health Check 기능이었다.</p>
<p>Kubernetes에도 같은 기능을 하는 장치들이 있다. </p>
<ul>
<li><p><strong>Liveness Probe</strong> : 컨테이너의 활성 상태를 체크 (컨테이너의 동작을 체크한다.)</p>
<ul>
<li>Pod가 정상 실행되었더라도 애플리케이션에 문제가 생겨서 접속이 안되는 경우를 감지하고 문제 발생시 Pod를 죽이고 재실행한다.</li>
</ul>
</li>
<li><p><strong>Readiness Probe</strong> : 서비스 가능 상태를 체크 
(컨테이너가 요청을 처리할 준비가 되었는지 확인 한다.)</p>
<ul>
<li>Liveness Probe와의 차이는 Pod의 제거 및 재실행이 아닌 Pod를 서비스로부터 제외시킨다는 점이다.</li>
</ul>
</li>
<li><p><strong>Startup Probe</strong> : 컨테이너 내 애플리케이션이 시작되었는지를 나타낸다.</p>
<ul>
<li>startup probe를 정의할 경우 헬스체크에 성공할 때 까지 다른 나머지 probe는 활성화 되지 않는다. 만약 startup probe가 실패하면, kubelet이 컨테이너를 죽이고, 컨테이너 재시작 정책에 따라 컨테이너를 재시작한다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>위의 3가지 Probe들을 활용해 컨테이너 혹은 애플리케이션을 나의 서비스 환경에 맞게 상태 체크를 진행할 수 있다.</p>
</blockquote>
<p>위의 세 가지 Probe들이 이용하는 상태 체크의 방식은 크게 3가지로 분류된다.</p>
<hr>
<p><strong>▶ Command Probe</strong></p>
<ul>
<li>Command Probe에서의 상태체크는 쉘 명령으로 수행된다. </li>
<li>결과 값이 0이면 성공, 0이 아니면 실패로 간주한다.</li>
</ul>
<pre><code>&lt;# Command Probe yaml 예시&gt;
apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/busybox
    imagePullPolicy: Always
    ports:
    - containerPort: 8080
 &lt;# livenessProbe 중 Command Probe 방법을 이용&gt;
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5</code></pre><blockquote>
<p><code>initialDelaySeconds</code> : Kubelet에게 첫 번째 probe를 실행하기 전 
대기 해야 할 초를 정의한다. (컨테이너 실행 후 대기시간)
<code>PeriodSeconds</code> : Kubelet이 5초마다 LivenessProbe를 실행하도록 정의한다.</p>
</blockquote>
<p>▶ Command Probe의 Health Check를 학인하는 방법은 아래와 같다.
<code>$ kubectl describe pod liveness-exec</code>
describe 명령어를 통해 pod의 상세정보를 확인하면 된다.</p>
<pre><code># 쿠버네티스 공식문서 기반 로그 예시
 Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  57s                default-scheduler  Successfully assigned default/liveness-exec to node01
  Normal   Pulling    55s                kubelet, node01    Pulling image &quot;registry.k8s.io/busybox&quot;
  Normal   Pulled     53s                kubelet, node01    Successfully pulled image &quot;registry.k8s.io/busybox&quot;
  Normal   Created    53s                kubelet, node01    Created container liveness
  Normal   Started    53s                kubelet, node01    Started container liveness
  Warning  Unhealthy  10s (x3 over 20s)  kubelet, node01    Liveness probe failed: cat: can&#39;t open &#39;/tmp/healthy&#39;: No such file or directory
  Normal   Killing    10s                kubelet, node01    Container liveness failed liveness probe, will be restarted</code></pre><hr>
<h4 id="▶-http-probe">▶ HTTP probe</h4>
<ul>
<li>가장 많이 사용하는 방식이다. HTTP GET을 이용해 컨테이너의 상태를 체크한다.</li>
<li>응답 코드가 200~399 사이에만 Probe를 정상으로 판단하고 그 이외의 값일 경우 비정상으로 판단한다.</li>
<li>HTTP GET prot에 대한 DDos 공격등의 보안적인 이슈가 발생할 수 있어 서비스 포트와 Probe포트를 분리하여 사용할 수도 있다.</li>
</ul>
<pre><code>&lt;# HTTP Probe yaml 예시&gt;
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/liveness
    args:
    - /server
&lt;# livenessProbe 중 HTTP Probe 방법을 이용&gt;
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3</code></pre><p>HTTP 요청의 경우 Kubelet에 내장된 함수를 이용해 GET 요청을 보내고 상태를 체크한다.
공식 문서에서 소개하는 healthz HandleFunc는 아래와 같다.</p>
<pre><code>http.HandleFunc(&quot;/healthz&quot;, func(w http.ResponseWriter, r *http.Request) {
    duration := time.Now().Sub(started)
    if duration.Seconds() &gt; 10 {
        w.WriteHeader(500)
        w.Write([]byte(fmt.Sprintf(&quot;error: %v&quot;, duration.Seconds())))
    } else {
        w.WriteHeader(200)
        w.Write([]byte(&quot;ok&quot;))
    }
})</code></pre><p>▶ HTTP probe의 Health Check를 학인하는 방법은 아래와 같다.
<code>$ kubectl describe pod liveness-http</code></p>
<hr>
<h4 id="▶-tcp-probe">▶ TCP Probe</h4>
<ul>
<li>지정된 포트에 TCP 연결을 시도하여 연결이 성공하면 컨테이너가 정상인 것으로 판단한다.</li>
</ul>
<pre><code>&lt;# TCP Probe yaml 예시&gt;
apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: registry.k8s.io/goproxy:0.1
    ports:
    - containerPort: 8080
&lt;# livenessProbe 중 TCP Probe 방법을 이용&gt;
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20</code></pre><p>▶ TCP Probe의 Health Check를 학인하는 방법은 아래와 같다.
<code>$ kubectl describe pod goproxy</code></p>
<h3 id="■-개인적인-견해">■ 개인적인 견해</h3>
<blockquote>
<p>Probe의 사용이 왜 pod라는 kind에서 명세되어야 할까를 처음에는 고민되었다. 하지만 Probe의 목적을 생각하니 그 해답이 나왔다.</p>
</blockquote>
<p>만약 Probe가 pod spec이 아닌 Service spec에 위치하게 된다, 그 서비스 타입을 load balacer로 가정하겠다.</p>
<p>Probe는 상태를 체크하기 위해 요청을 보낼 것이다. 결국 이것도 요청이라는 점이 이때 떠올랐다. 
load balacer는 그 요청을 각 pod들에 무작위로 전달할 것이고 상태체크를 지속적으로 하기 때문에 결국에는 상태 확인을 할 수 있겠지만 결국 5개의 컨테이너의 상태 체크를 위해서는 요청이 5번 진행될때까지를 기다려야 한다는 점이었다.</p>
<p>그렇기 때문에 컨테이너의 갯수가 수십 수백개가 된다면 사실상 사용이 불가능한 수준에 이를 것이기에 Probe를 pod spec에 명시하지 않았을까 라고 생각하게 되었다.</p>
<hr>
<blockquote>
<p>Q. 왜 파드와 PV(퍼시스턴스볼륨)를 직접 연결하지 않는걸까요?</p>
</blockquote>
<p>먼저 볼륨과 퍼시스턴스 볼륨에 대해 알아봤다.</p>
<p><strong>▶ 볼륨과 퍼시스턴스 볼륨의 등장 배경?</strong></p>
<blockquote>
<p>파드는 Stateless한 특징을 가지고 있다. 즉 데이터의 소실 가능성을 언제든 가지고 있는 불완전한 서비스이다.
이 때문에 지속적으로 보관이 필요한 데이터의 소실 문제를 해결하기 위해 볼륨이 등장하게 되었다. </p>
</blockquote>
<p><strong>▶ 볼륨과 퍼시스턴스 볼륨의 공통점?</strong></p>
<ul>
<li>볼륨은 기본적을 파드의 구성요소로써 컨테이너와 동일하게 파드 스펙에서 정의될 수 있다.</li>
<li>기본적으로 볼륨은 디렉토리의 개념이다.</li>
<li>볼륨은 파드의 모든 컨테이너에서 사용(공유) 가능하며 이는 접근하려는 컨테이너에서 마운트함으로써 수행할 수 있다.</li>
</ul>
<p><strong>▶ 볼륨(Voulume)과 퍼시스턴스 볼륨(Persistent Voulume)의 차이점?</strong></p>
<ul>
<li><p>볼륨 (Voulume)</p>
<ul>
<li>볼륨의 수명주기는 파드와 같다. (인스턴스 스토어)</li>
<li>Pod와 직접 연결되어 있기 때문에 속도가 빠르다.</li>
</ul>
</li>
<li><p>퍼시스턴스(Persistent Voulume)</p>
<ul>
<li>퍼시스턴스 볼륨의 수명주기는 파드와 별개의 수명주기를 갖는다.
(AWS EBS)</li>
<li>보다 안정적이고 영구적인 데이터 보관이 필요할 때 사용한다.</li>
</ul>
</li>
</ul>
<p><strong>▶볼륨에서 반드시 알아야 할 퍼시스턴스 볼륨 클레임(Persistent Voulume Claim)의 개념</strong></p>
<p>쿠버네티스 공식문서에서 PVC는 사용자의 스토리지에 대한 요청이라고 나와 있다. 해당 요청을 통해 PV의 특정 크기 및 접근 모드 등을 요청할 수 있다. </p>
<p>PVC에는 두 가지의 프로비저닝이 존재하는데 </p>
<blockquote>
<p><strong>정적 프로비저닝</strong>의 경우 
클러스터 관리자가 여러 PV를 만들고 각 개발자에게 실제 스토리지의 세부 사항을 제공하여 요청을 받는 형태이다.</p>
</blockquote>
<blockquote>
<p><strong>동적 프로비저닝</strong>의 경우 
스토리지클래스를 기반으로 관리자가 생성한 PV가 사용자의 PVC와 일치하지 않으면 요청된 PVC에 맞춰 PV를 동적으로 프로비저닝 하려고 시도하는 형태이다.</p>
</blockquote>
<p>지금까지는 위의 질문에 대한 기본 개념이었고 아래부터가 해답이다.</p>
<p>나는 이 해답을 두 개로 정리했다.</p>
<h4 id="첫-번째-내-생각">첫 번째, 내 생각</h4>
<p>Pod와 PV를 직접 연결하게 된다면 각 개발자가 PV를 Pod에 mount함으로써 PV 규격의 통일성을 해치고 IaC를 통해 관리하는 쿠버네티스의 장점을 소실하게 된다. </p>
<p>하지만 PVC를 통해 연결한다면 각 개발자는 PV를 ‘mount’하는 것이 아니라 ‘요청’할 수 있게 되고 어떤 PV를 연결시켜 줄지에 대한 권한은 클러스터 관리자인 DevOps에게로 이전된다.</p>
<p>이렇게 PVC를 통해 요청에 맞는 적절한 PV를 연결시켜 줌으로써 각 개발자는 PV를 Pod에 마운트한 것처럼 사용할 수 있고</p>
<p>DevOps는 각 PV를 리소스로써 활용하면서 IaC를 통해 관리하는 장점을 유지하면서도 원활한 서비스를 구성할 수 있다고 생각한다.</p>
<h4 id="두-번째-ai의-답변">두 번째, AI의 답변</h4>
<p>PV와 포드를 분리하는 이유는 Kubernetes 워크로드의 유연성, 이식성 및 내구성을 향상시키기 때문이다.</p>
<p><strong>워크로드의 유연성</strong>이란? 
PV가 필요한 상태 저장 애플리케이션이 있는 경우 PV를 포드에서 분리하고 PVC를 통해 포드와 연결함으로써 상황, 환경에 맞는 PV를 적절히 선택하여 사용할 수 있고 반드시 고정된 PV에 포드에 데이터가 저장되어지지 않아도 된다.</p>
<p><strong>워크로드의 이식성</strong>이란?
애플리케이션을 수평으로 확장해야 하는 경우 더 많은 포드를 생성하고 이를 PVC를 통해 포드와 연결시켜줌으로써 데이터가 모든 포드 간에 공유 될 수 있다.</p>
<p><strong>워크로드의 내구성</strong>이란?
PV의 경우 포드가 삭제된 후에도 지속되도록 설계되어 있다. PVC를 이용할 경우 의존성까지 줄일 수 있게 되어 컴퓨팅 리소스와 스토리지 리소스간에 영향을 미치지 않고 독립적으로 확장 및 변경이 가능하다.</p>
<hr>
<p>아래는 PVC를 통해 포드와 PV를 연결하는 매니패스트 구성이다.
쿠버네티스 공식 문서에 따르면 아래 3가지 순서대로 진행하라고 되어 있다.</p>
<ol>
<li>PV 생성</li>
<li>PV에 바인딩되는 PVC 생성</li>
<li>PVC를 이용하는 Pod 생성</li>
</ol>
<pre><code># 1. PV 생성
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mongodb-pv
spec:
  capacity:                                    # 크기
    storage: 1Gi
  accessModes:
    - ReadWriteOnce                        # 하나의 PVC만 읽거나 쓴다
    - ReadOnlyMany                         # 여러 PVC가 읽기만 한다
  persistentVolumeReclaimPolicy: Retain   # PVC 와 연결이 해제되면 지우거나 삭제하지 않고 유지한다
  hostPath:
    path: /tmp/mongodb</code></pre><pre><code># 2. PV에 바인딩되는 PVC 생성
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongodb-pvc
spec:
  resources:
    requests:
      storage: 1Gi      #스토리지 사이즈
  accessModes:        # 연결할 PV는 아래 옵션으로 접근이 가능해야 한다. (Label을 맞추는 것과 동일한 조건의 개념으로 이해했다)
  - ReadWriteOnce
  storageClassName: &quot;&quot;  # 동적 프로비저닝을 위해 사용</code></pre><pre><code># 3. PVC를 이용하는 Pod 생성
apiVersion: v1
kind: Pod
metadata:
  name: mongodb 
spec:
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:
    - name: mongodb-data
      mountPath: /data/db
    ports:
    - containerPort: 27017
      protocol: TCP
  volumes:
  - name: mongodb-data
    persistentVolumeClaim:
      claimName: mongodb-pvc    # PVC 의 이름</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[쿠버네티스 Deployment 명세 작성 및 롤링 업데이트 & 롤백 실습]]></title>
            <link>https://velog.io/@son_doobu96/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-Deployment-%EB%AA%85%EC%84%B8-%EC%9E%91%EC%84%B1-%EB%B0%8F-%EB%A1%A4%EB%A7%81-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%EB%A1%A4%EB%B0%B1-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@son_doobu96/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-Deployment-%EB%AA%85%EC%84%B8-%EC%9E%91%EC%84%B1-%EB%B0%8F-%EB%A1%A4%EB%A7%81-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%EB%A1%A4%EB%B0%B1-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Tue, 14 Feb 2023 11:18:58 GMT</pubDate>
            <description><![CDATA[<h3 id="◎-오늘-실습의-목표">◎ 오늘 실습의 목표</h3>
<blockquote>
<ul>
<li>Deployment 명세를 작성 및 이해 한다.</li>
</ul>
</blockquote>
<ul>
<li>롤링업데이트를 진행할 수 있어야 한다.</li>
<li>롤백을 진행할 수 있어야 한다.</li>
</ul>
<p>모든 진행은 <a href="https://kubernetes.io/ko/docs/concepts/workloads/controllers/deployment/#%EB%94%94%ED%94%8C%EB%A1%9C%EC%9D%B4%EB%A8%BC%ED%8A%B8-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8">쿠버네티스 공식문서</a>를 통해 진행했습니다.</p>
<h4 id="■-deployment-v1-명세-작성">■ Deployment-v1 명세 작성</h4>
<p>먼저 kind에 대해 알 필요가 있다. Kubenertes에서 kind는 리소스의 타입을 명시한다고 생각하면 된다. 내가 만드려는 것이 어떤 리소스의 타입인지를 정확히 알고 명세를 작성할 수 있어야 한다.</p>
<p>내가 만드려는 것은 하나의 파드가 아니라 그 파드에 대한 선언적 컨트롤러를 만들기를 원하기 때문에 Deployment를 작성했다.</p>
<blockquote>
<p>▶ Deployment 명세에 반드시 들어가야 하는 항목은 총 4가지이다.</p>
</blockquote>
<ol>
<li>apiVersion</li>
<li>kind</li>
<li>metadata</li>
<li>spec</li>
</ol>
<p>먼저 내가 만들려고 하는 것을 확실하게 알았다면 kind에 대한 고민은 필요가 없다.
그리고 apiVersion의 경우에도 kind에 맞는 설정이 필요하기 때문에 고민이 필요한 부분은 아니다.</p>
<p>apiVersion은 <code>kubectl api-resources</code> 명령어를 통해 확인할 수 있다.</p>
<h4 id="▶-metadata-field-작성">▶ metadata field 작성</h4>
<p>다음 가장 먼저 신경쓸 부분이 metadata의 필드이다.
metadata는 쉽게 설명하자면 해당 Deployment를 나타내는 정보라고 생각해도 좋다.</p>
<p>가장 먼저 metadata에 포함되어야 할 요소들을 파악했다.
내가 생성할 리소스 타입의 이름을 설정해야 하기에 <code>name:</code>을 포함해 주어야 한다. </p>
<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: cozserver</code></pre><p>metadata field까지 작성한 Deployment-v1.yaml의 내용이다.</p>
<h4 id="▶-spec-field-작성">▶ spec field 작성</h4>
<p>spec 필드는 내가 생성할 Deployment의 작동을 정의하고 Deployment가 관리할 pod에 대한 정의가 포함되어야 한다.</p>
<p>내가 작성한 spec field의 포함내용은 아래와 같다.
<code>replicas:</code> Deployment를 통해 생성하고 관리할 pod의 수를 나타낸다.</p>
<ul>
<li><code>replicas: 4</code>는 4개의 pod를 생성 및 관리하겠다는 의도이다.</li>
</ul>
<p><code>selector:</code> Deployment를 통해 관리할 pod의 집합을 명세한다.</p>
<ul>
<li><code>matchLabels:</code>는 해당 레이블로 정의된 pod들을 Deployment로 관리하겠다는 의도이다. </li>
</ul>
<p><code>template:</code> Deployment를 통해 생성 및 관리할 pod의 정의이다.</p>
<ul>
<li>위의 Deployment를 명세하면서 metadata를 정의해줬듯이 pod에 대한 정의인 template에도 metadata를 정의해 줘야 한다. <code>labels:</code>를 붙여 각 pod들을 Deployment가 레이블로 관리 될 수 있도록 정의해줬다.</li>
</ul>
<p>Deployment에서 가장 중요하게 생각할 부분이 바로 <code>labels:</code>이다.</p>
<p>오늘 작성하게 될 명세에서는 <code>labels:</code>가 여러번 등장하게 되는데 이를 분별적으로 이해할 수 있어야 한다.</p>
<p>이 후 부분은 pod에 대한 정의이다. 컨테이너의 이름과 이미지, 포트를 설정해줬다.</p>
<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: cozserver
&lt;#↓ 생성 및 관리할 pod의 수를 정의한다.&gt;
spec:
  replicas: 4
&lt;#↓ deployment를 통해 관리할 label을 정의한다.&gt;
  selector:
    matchLabels:
      app: cozserver
&lt;#↓ 생성 및 관리할 pod의 정의이다.&gt;
  template:
&lt;#↓ pod를 label화 해준다. → Deployment의 관리하에 두기 위해&gt;
    metadata:
      labels:
        app: cozserver
    spec:
      containers:
      - name: cozserver
        image: sebcontents/cozserver:1.0
&lt;#↓ 리소스 설정은 아래 값이 Default값이다 고로 따로 설정해주지 않아도 괜찮다.&gt;
        resources:
          limits:
            memory: &quot;128Mi&quot;
            cpu: &quot;500m&quot;
        ports:
        - containerPort: 8080</code></pre><p>이후 kubectl apply -f Deployment-v1.yaml 을 통해 Deployment를 생성해줬다. </p>
<h4 id="■-로드밸런서-서비스-생성">■ 로드밸런서 서비스 생성</h4>
<p>기본적으로 pod는 외부 IP를 가지지 않기 때문에 서비스를 외부로 노출시킬 수 없다. 이때 포트 포워딩을 해서 외부에 서비스를 노출시킬수도 있지만
레플리카가 4개이기 때문에 엔드포인트가 나누어진다는 단점이 있다.</p>
<p>이러한 문제를 상쇄하기 위해 로드밸런서를 적용하기로 했다.</p>
<pre><code>apiVersion: v1
kind: Service
metadata:
  name: cozserver
  namespace: default
spec:
&lt;#↓ 로드밸런서를 통해 관리할 label을 정의한다.&gt;
  selector:
    app: cozserver
&lt;#↓ 서비스 타입을 정의한다.&gt;
  type: LoadBalancer
  ports:
  - name: cozserver
    protocol: TCP
    port: 80
    targetPort: 8080</code></pre><p>로드밸런서가 성공적으로 생성되었다면 클러스터 IP 및 노드 포트가 자동으로 생성 되며 <code>kubectl describe svc &lt;loadbalancer name&gt;</code> 명령어를 통해 확인해보면 로드밸런서가 pod들의 ip:port로 된 엔드포인트들을 하위 구성으로 가지고 있는 것을 확인할 수 있다.</p>
<p>또한 생성된 외부 IP를 통해 접근이 가능해진다. </p>
<hr>
<h4 id="■-deployment-v2-명세-작성-및-롤링-업데이트">■ Deployment-v2 명세 작성 및 롤링 업데이트</h4>
<p>Deployment의 버전 업데이트를 진행해야 하는 상황이라면 아마도 컨테이너를 구성하는 이미지가 변경된 상황일것이다.</p>
<p>명령어를 통해 업데이트를 진행할 수도 있지만 보다 많은 IaC 작성 습관을 들이기 위해 Deployment-v2.yaml을 작성해봤다.</p>
<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: cozserver
&lt;#↓ 생성 및 관리할 pod의 수를 정의한다.&gt;
spec:
  replicas: 4
&lt;#↓ deployment를 통해 관리할 label을 정의한다.&gt;
  selector:
    matchLabels:
      app: cozserver
&lt;#↓ 생성 및 관리할 pod의 정의이다.&gt;
  template:
&lt;#↓ pod를 label화 해준다. → Deployment의 관리하에 두기 위해&gt;
    metadata:
      labels:
        app: cozserver
    spec:
      containers:
      - name: cozserver
        image: sebcontents/cozserver:2.0
&lt;#↓ 리소스 설정은 아래 값이 Default값이다 고로 따로 설정해주지 않아도 괜찮다.&gt;
        resources:
          limits:
            memory: &quot;128Mi&quot;
            cpu: &quot;500m&quot;
        ports:
        - containerPort: 8080</code></pre><p>이걸 바로 <code>kubectl apply -f Deployment-v2.yaml</code> 을 통해 롤링 업데이트를 진행할 수도 있다.</p>
<p>kubenetes의 배포 전략에서 Default 설정은 롤링 업데이트이고 그 구성은 
maxSurge: 25% maxUnavaliable: 25%로 설정되어 있다.</p>
<p>maxSurge와 maxUnavaliable을 통해 내가 구성할 서비스에 맞게 배포의 속도를 조정할 수 있다.</p>
<p>만약 직접 확인해 보고 싶다면 <code>kubectl describe deployment &lt;deployment name&gt;</code> 명령어를 통해 확인해 보길 바란다.</p>
<p>나는 이걸 직접 설정해보고 싶었다. 하지만 공식 문서에 활용 사례가 나오질 않았고 엔지니어님께 여쭤 봄으로써 확인 사례를 알 수 있었다.</p>
<p>spec field에 아래 내용을 추가했다.</p>
<pre><code>  strategy: RollingUpdate
  rollingupdate: 
    maxSurge: 1
    maxUnavaliable: 1</code></pre><p>전체 갯수를 내가 의도한 pod 수인 4개의 +1개를 넘지 않도록 설정했고 업데이트 중 발생할 수 있는 최대로 불가능한 pod의 수를 1개로 지정하여 전체적인 배포 속도가 천천히 안정적으로 진행될 수 있도록 설정했다.</p>
<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: cozserver
spec:
&lt;#↓ 업데이트 전략 설정.&gt;
  strategy: RollingUpdate
  rollingupdate: 
    maxSurge: 1
    maxUnavaliable: 1
&lt;#↓ 생성 및 관리할 pod의 수를 정의한다.&gt;
  replicas: 4
&lt;#↓ deployment를 통해 관리할 label을 정의한다.&gt;
  selector:
    matchLabels:
      app: cozserver
&lt;#↓ 생성 및 관리할 pod의 정의이다.&gt;
  template:
&lt;#↓ pod를 label화 해준다. → Deployment의 관리하에 두기 위해&gt;
    metadata:
      labels:
        app: cozserver
    spec:
      containers:
      - name: cozserver
        image: sebcontents/cozserver:2.0
&lt;#↓ 리소스 설정은 아래 값이 Default값이다 고로 따로 설정해주지 않아도 괜찮다.&gt;
        resources:
          limits:
            memory: &quot;128Mi&quot;
            cpu: &quot;500m&quot;
        ports:
        - containerPort: 8080</code></pre><p>이제 <code>kubectl apply -f Deployment-v2.yaml</code> 명령어를 통해 롤링 업데이트를 진행했다. 각 apply 진행시 <code>--record</code> 옵션을 통해 rollout history를 남겨주었다. </p>
<hr>
<h4 id="■-의도적으로-장애가-발생하는-deployment-v3-명세-작성-및-롤링-업데이트--롤백-적용">■ 의도적으로 장애가 발생하는 Deployment-v3 명세 작성 및 롤링 업데이트 &amp; 롤백 적용</h4>
<p>서비스 장애 발생시 쿠버네티스를 통해 rollback하는 실습을 진행해 보기 위해 장애가 발생하는 Deployment-v3.yaml을 작성했다. </p>
<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: cozserver
spec:
&lt;#↓ 업데이트 전략 설정.&gt;
  strategy: RollingUpdate
  rollingupdate: 
    maxSurge: 1
    maxUnavaliable: 1
&lt;#↓ 생성 및 관리할 pod의 수를 정의한다.&gt;
  replicas: 4
&lt;#↓ deployment를 통해 관리할 label을 정의한다.&gt;
  selector:
    matchLabels:
      app: cozserver
&lt;#↓ 생성 및 관리할 pod의 정의이다.&gt;
  template:
&lt;#↓ pod를 label화 해준다. → Deployment의 관리하에 두기 위해&gt;
    metadata:
      labels:
        app: cozserver
    spec:
      containers:
      - name: cozserver
        image: sebcontents/cozserver:3.0
&lt;#↓ 리소스 설정은 아래 값이 Default값이다 고로 따로 설정해주지 않아도 괜찮다.&gt;
        resources:
          limits:
            memory: &quot;128Mi&quot;
            cpu: &quot;500m&quot;
        ports:
        - containerPort: 8080</code></pre><p><code>kubectl apply -f Deployment-v3.yaml</code>을 통해 롤링 업데이트를 진행하고 이를 롤백하기 위해 알아봐야 할 것들이 있다.</p>
<p>먼저 <code>--record</code> 옵션이다. 지금까지 apply 적용시 --record 옵션을 통해 rollout history를 남겼다고 위에 적어놨는데.</p>
<p>이를 <code>kubectl rollout history deployment &lt;deployment name&gt;</code> 을 통해 확인할 수 있다.</p>
<pre><code>REVISION  CHANGE-CAUSE
1         kubectl apply --filename=deployment-v1.yaml --record=true
2         kubectl apply --filename=deployment-v2.yaml --record=true
3         kubectl apply --filename=deployment-v3.yaml --record=true</code></pre><p>현재 적용된 버전이 v3이고 이전 버전으로 롤백하고 싶다면 공식문서에 나와있는 롤백을 위한 kubectl 명령어를 사용 하면된다.</p>
<p><code>kubectl rollout undo deployment &lt;deployment name&gt; --to-revision=2</code>
나는 해당 명령어를 통해 revision 2에 기록된 v2로 롤백을 시도하도록 적용했다.</p>
<p>그 후 다시 rollout history를 확인해 보면 아래와 같이 변경되어 있을 것이다.</p>
<pre><code>REVISION  CHANGE-CAUSE
1         kubectl apply --filename=deployment-v1.yaml --record=true
3         kubectl apply --filename=deployment-v3.yaml --record=true
4         kubectl apply --filename=deployment-v2.yaml --record=true</code></pre><p>이렇게 오늘의 실습을 마쳤다. yaml파일을 많이 만지면서 라인을 잘 설정해야 한다는 걸 다시 한번 느꼈고 IaC를 통해 관리되는 쿠버네티스의 편리함을 다시 한번 느낀것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쿠버네티스 서비스 타입과 배포 전략]]></title>
            <link>https://velog.io/@son_doobu96/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%84%9C%EB%B9%84%EC%8A%A4-%ED%83%80%EC%9E%85%EA%B3%BC-%EB%B0%B0%ED%8F%AC-%EC%A0%84%EB%9E%B5</link>
            <guid>https://velog.io/@son_doobu96/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%84%9C%EB%B9%84%EC%8A%A4-%ED%83%80%EC%9E%85%EA%B3%BC-%EB%B0%B0%ED%8F%AC-%EC%A0%84%EB%9E%B5</guid>
            <pubDate>Mon, 13 Feb 2023 13:49:58 GMT</pubDate>
            <description><![CDATA[<h3 id="쿠버네티스의-서비스-타입-servicetypes">쿠버네티스의 서비스 타입 (ServiceTypes)</h3>
<h4 id="▶-clusterip-">▶ ClusterIP :</h4>
<p>클러스터 내부 IP에서 서비스를 노출합니다. 이 값을 선택하면 클러스터 내에서만 서비스에 연결할 수 있습니다. type서비스에 대해 명시적으로 지정하지 않은 경우 사용되는 기본값입니다 . 인 그레스 또는 게이트웨이 API를 사용하여 서비스를 공개할 수 있습니다.</p>
<p>디폴트 설정으로 서비스에 클러스터 IP(내부 IP)를 할당한다. 쿠버네티스 내에서는 서비스에 접근이 가능하지만 클러스터 외부에서는 접근이 불가능하다. (외부IP 할당 X)</p>
<blockquote>
<p>각 pod들은 생성 및 삭제가 주기적으로 일어난다. 그럴때마다 각 pod에 부여된 IP가 변화 되는데 ClusterIP를 통해 라벨링된 pod들을 셀렉터로 묶어 고정된 IP로 호출할 수 있다.</p>
</blockquote>
<h4 id="▶-nodeport-">▶ NodePort :</h4>
<p>정적 포트( )에서 각 노드의 IP에 서비스를 노출합니다 NodePort. 노드 포트를 사용할 수 있도록 하기 위해 Kubernetes는 서비스를 요청한 것과 동일하게 클러스터 IP 주소를 설정합니다 type: ClusterIP.</p>
<p>클러스터 IP뿐 아니라 모든 노드의 IP와 포트를 통해서도 접근이 가능하게 된다. </p>
<blockquote>
<p>각 노드가 외부와 통신할 수 있는 포트를 개방한다.</p>
</blockquote>
<h4 id="▶-loadbalancer-">▶ LoadBalancer :</h4>
<p>클라우드 제공자의 부하 분산 장치를 사용하여 서비스를 외부에 노출합니다.</p>
<p>외부 IP를 가지고 있는 로드밸런서를 할당함으로써 클러스터 외부에서 접근이 가능하도록 만든다.</p>
<blockquote>
<p>로드밸런서의 역할은 부하분산과 동시에 분산된 노드의 엔드포인트를 하나로 모으는 것이다. 이를 통해 사용자가 서비스에 접근하기 쉽게 만들어준다. 즉 각 노드 포트를 로드밸런서를 통해 엮어줌으로써 트래픽 분산과 동시에 통합된 엔드포인트의 사용이 가능하다.</p>
</blockquote>
<h4 id="▶-externalname-">▶ ExternalName :</h4>
<p>해당 값과 함께 레코드를 반환하여 서비스를 필드의 내용 externalName(예: )에 매핑합니다. 어떤 종류의 프록시도 설정되지 않습니다. foo.bar.example.comCNAME</p>
<p>외부 서비스를 쿠버네티스 내부에서 호출하고자 할 때 사용한다. 클러스터 내의 pod들은 클러스터 IP를 가지고 있기 때문에 클러스터 IP 대역 밖의 서비스를 호출하고자하면 NAT 설정등 복잡한 설정이 필요하다. 이때 ExternalName 타입으로 설정하고 주소를 DNS로 설정해주면 서비스로 들어오는 모든 요청을 포워딩해준다. (DNS가 아닌 직접 IP를 이용해도 된다.)</p>
<hr>
<h3 id="쿠버네티스의-배포-전략">쿠버네티스의 배포 전략</h3>
<h4 id="■-쿠버네티스가-지원하는-배포-전략">■ 쿠버네티스가 지원하는 배포 전략</h4>
<h4 id="▶-재생성-recreate">▶ 재생성 (Recreate):</h4>
<p>이전 버전을 삭제하고 새 버전 생성</p>
<p>IaC를 이용한 가장 기본적인 방법으로 이전 버전을 삭제하고 새 버전을 생성하는 방식으로 배포전략이 구성된다 하지만 서비스 중단 시간(Downtime)이 있다는 문제가 있다.</p>
<h4 id="▶-롤링-배포">▶ 롤링 배포:</h4>
<p>이전 버전을 scale down하고, 새 버전을 scale up 하는 방식으로 단계별로 교체, 롤아웃(rollout)이라고 부릅니다.</p>
<p>쿠버네티스의 롤링배포의 default 설정은 25% max unavailable, 25% max surge이지만 사용자의 설정을 통해 조정 가능하며 정수로도 설정 가능하다.
이를 통해 배포의 속도를 조정할 수 있다.</p>
<p>max unavailable : 최대 이용 불가능한 pod 수(%)</p>
<ul>
<li>업데이트시 사용할 수 없는 최대 pod수를 결정한다.</li>
</ul>
<p>max surge : 최대 생성 가능한 pod 수(%)</p>
<ul>
<li>업데이트시 추가로 생성할 수 있는 pod의 수를 결정한다. 예를 들어 max surge가 3이고 replica가 5로 설정되어 있다면 업데이트시 최대 8개의 pod가 존재할 수 있다.</li>
</ul>
<h4 id="▶-블루-그린-배포-전략">▶ 블루-그린 배포 전략</h4>
<p>새 버전과 구버전으로 2세트를 마련하고 이를 한꺼번에 교체하는 전략
무중단 서비스에 가깝지만 필연적으로 배포 중단 시점이 발생한다.</p>
<p>deployment의 Labels를 통해 2개의 버전을 운영하고 서비스 명세의 selector를 교체하는 방식으로 배포를 진행한다. </p>
<h4 id="▶카나리-배포-전략">▶카나리 배포 전략</h4>
<p>특정 서버나 소수 유저들에게 새로운 버전을 배포하고 추후 안전하다는 판단이 되면 모든 서버에 새로운 버전을 배포하는 점진적 배포방식이다.</p>
<p>대표적으로 버전 업데이트 된 레플리카 수를 늘려가거나 퍼센테이지를 조정하며 배포를 진행한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쿠버네티스 nginx pod 띄우기]]></title>
            <link>https://velog.io/@son_doobu96/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-nginx-pod-%EB%9D%84%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@son_doobu96/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-nginx-pod-%EB%9D%84%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Sun, 12 Feb 2023 10:42:09 GMT</pubDate>
            <description><![CDATA[<p>쿠버네티스에 익숙해지기 위해 간단한 개인 미션을 진행하였다.</p>
<h3 id="목표--쿠버네티스를-이용해-nginx-pod를-띄우고-포트-포워딩을-통해-local에서-접속이-가능하도록-구성">목표 : 쿠버네티스를 이용해 nginx pod를 띄우고 포트 포워딩을 통해 local에서 접속이 가능하도록 구성</h3>
<p>이번 실습은 GKE를 통해 진행했다.</p>
<p>AWS의 경우 조금 사용해 봤다 보니 큰 어색함이나 불편함이 없었지만 GKE의 경우 처음 사용해보다 보니 명령어나 리전설정등 모든 부분에서 조금 어색함이 있어 초반에 많은 에러를 겪은 것 같다.</p>
<h4 id="먼저-아래의-명령어를-통해-gke-클러스터를-생성했다">먼저 아래의 명령어를 통해 GKE 클러스터를 생성했다.</h4>
<pre><code>gcloud container clusters create k8s \ #클러스터 생성 명령어
--cluster-version 1.25.5-gke.2000 \    #클러스터 버전 설정
--zone us-west1-a \                    #클러스터 리전 설정
--num-nodes 1 \                        #사용할 노드의 수 설정
--machine-type n1-standard-4 \         #인스턴스 유형 지정
--enable-network-policy \              #네트워크 정책 기능 활성화
--enable-vertical-pod-autoscaling      #VerticalPodAutoscaler 활성화</code></pre><h4 id="이후-아래의-명령어를-통해-사용자에게-클러스터-내-모든-리소스에-접근할-수-있는-권한을-줬다">이후 아래의 명령어를 통해 사용자에게 클러스터 내 모든 리소스에 접근할 수 있는 권한을 줬다.</h4>
<pre><code>kubectl create clusterrolebinding user-cluster-admin-binding --clusterrole=cluster-admin --user=&lt;GCP 메일주소&gt;</code></pre><h4 id="이후-nginx-이미지를-이용해-pod를-생성해줬다">이후 nginx 이미지를 이용해 Pod를 생성해줬다.</h4>
<pre><code>#pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest
    resources:
      limits:
        memory: &quot;128Mi&quot;
        cpu: &quot;500m&quot;
    ports:
      - containerPort: 80</code></pre><p>아래 명령어를 통해 pod가 생성됨을 확인할 수 있다.</p>
<pre><code>kubectl get pod -o wide</code></pre><p><img src="https://velog.velcdn.com/images/son_doobu96/post/019a6f30-c898-4aab-b433-07b4b144d803/image.png" alt=""></p>
<p>이후 아래 명령어를 통해 nginx pod에 포트포워딩을 진행했다.</p>
<pre><code>kubectl port-forward nginx 5000:80</code></pre><p><img src="https://velog.velcdn.com/images/son_doobu96/post/5d910639-ef81-46d0-a539-ccc0f4071448/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/89fd4245-2048-44b4-8827-889a1af6256b/image.png" alt=""></p>
<p>성공적으로 접속이 가능한 모습이다.</p>
<hr>
<p>사실 해당 과정이 어려웠다기 보다 처음 사용하는 툴이다보니 어색함이 컸다.
조금씩 친해지면서 더 많은 작업을 진행햅도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[컨테이너 오케스트레이션]]></title>
            <link>https://velog.io/@son_doobu96/%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%98%A4%EC%BC%80%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@son_doobu96/%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%98%A4%EC%BC%80%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%9D%B4%EC%85%98</guid>
            <pubDate>Sun, 12 Feb 2023 08:57:13 GMT</pubDate>
            <description><![CDATA[<h3 id="■-오케스트레이션이란-무엇인가">■ 오케스트레이션이란 무엇인가?</h3>
<h4 id="orchestrate의-사전적-의미">orchestrate의 사전적 의미</h4>
<p>‘to plan and organize something carefully and sometimes secretly in order to achieve a desired result’</p>
<ul>
<li>Cambridge Dictionary</li>
</ul>
<p>즉 컨테이너 오케스트레이션 도구는, 수십~수백개의 컨테이너를 관리하고자 할 때 보다 더 잘 관리하기 위한 툴이다. </p>
<h3 id="■-컨테이너-오케스트레이션-툴이-필요한-이유">■ 컨테이너 오케스트레이션 툴이 필요한 이유?</h3>
<p>마이크로서비스로 아키텍처 트렌드가 변화함에 따라 컨테이너의 갯수가 증가했고 여기에 확장성, 장애대응등을 위한 스케일링, 레플리카까지 추가되며 그 갯수가 이제는 수백 수천개에 이르게 되었다.</p>
<p>그러다보니 일일히 컨테이너를 관리할 수 있는 시기가 지나게 되었고 효율적으로 컨테이너를 관리할 수 있는 툴이 필요하게 되었다.</p>
<p>컨테이너 오케스트레이션의 대표적인 툴로 쿠버네티스가 현재 가장 많이 사용된다.</p>
<h3 id="■-컨테이너-오케스트레이션의-기능">■ 컨테이너 오케스트레이션의 기능?</h3>
<blockquote>
<p>Scheduling – 수 많은 서버 중 최적의 서버에 컨테이너를 배포한다. (배치 실행, 구성 관리)
Self Healing – 배포된 컨테이너에 문제가 생겼을 때 자동으로 대응한다. (자가 치유, 고가용성)
Load Balancing – 서비스 트래픽을 배포된 컨테이너에 분산 (로드밸런싱)
Auto Scaling – 서비스 제공을 위해 필요한 경우 자동으로 용량 증설 (확장성)
Rolling Update – 배포된 컨테이너 환경에 대한 업데이트 진행 </p>
</blockquote>
<h3 id="■-컨테이너-오케스트레이션이-적절하지-않은-상황">■ 컨테이너 오케스트레이션이 적절하지 않은 상황</h3>
<ul>
<li><p>여러 Tier(단계)로 나뉘어지지 않은 모놀리식 아키텍처에서는 적합하지 않습니다.</p>
<ul>
<li>모놀리식 아키텍처는 먼저 마이크로서비스 분해 전략을 세우는 것이 우선입니다.</li>
</ul>
</li>
<li><p>고작 서너 개에 불과한 컨테이너만을 다루려면 적합하지 않습니다.</p>
<ul>
<li>서너 개의 불과한 컨테이너는 docker-compose로도 충분합니다.</li>
</ul>
</li>
<li><p>비교적 단순한 아키텍처에, 스케일링이 필요하지 않은 경우 적합하지 않습니다.</p>
<ul>
<li>이후 트래픽이 증가하거나, 스케일링이 필요한 경우, 서버리스 서비스를 사용하면 
다소 저렴한 가격에 관리형 서비스를 이용할 수 있으며, AWS 내에서도 오토 스케일링과 같은 
관리형 서비스가 배포 환경에서 제공됩니다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Terraform IaC AWS 구성2]]></title>
            <link>https://velog.io/@son_doobu96/Terraform-IaC-AWS-%EA%B5%AC%EC%84%B12</link>
            <guid>https://velog.io/@son_doobu96/Terraform-IaC-AWS-%EA%B5%AC%EC%84%B12</guid>
            <pubDate>Thu, 09 Feb 2023 11:29:37 GMT</pubDate>
            <description><![CDATA[<h3 id="구성하려는-아키텍처-구성도">구성하려는 아키텍처 구성도</h3>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/502c23a1-7544-40d3-b65c-e06636686d51/image.png" alt=""></p>
<h3 id="진행-단계">진행 단계</h3>
<h4 id="▶-step-1-vpc-생성">▶ STEP 1 VPC 생성</h4>
<p><strong>1. VPC 및 서브넷 생성</strong></p>
<ul>
<li>프라이빗 서브넷과 퍼블릭 서브넷이 각각 두개, 총 네개가 있어야 합니다.</li>
</ul>
<p><strong>2. VPC 보안 그룹 생성</strong></p>
<ul>
<li>퍼블릭 웹 서버가 사용할 VPC 보안 그룹을 만들어야 합니다.</li>
<li>프라이빗 DB 웹 서버가 사용할 VPC 보안 그룹을 만들어야 합니다.</li>
</ul>
<p><strong>3. DB 서브넷 그룹 생성</strong></p>
<ul>
<li>RDS 인스턴스가 사용할 VPC 서브넷 그룹을 만들어야 합니다.</li>
</ul>
<h4 id="▶-step-2-ec2-생성">▶ STEP 2 EC2 생성</h4>
<p>만들어야 하는 사양</p>
<ul>
<li>AMI: Ubuntu Server 18</li>
<li>인스턴스 타입: t2.micro</li>
<li>사용자 데이터<pre><code>#!/bin/bash
echo &quot;Hello, World&quot; &gt; index.html
nohup busybox httpd -f -p ${var.server_port} &amp;</code></pre></li>
<li>키 페어: 수동으로 만들고 EC2에 할당합니다.</li>
</ul>
<h3 id="advanced-challenges">Advanced Challenges</h3>
<h4 id="step-3-자습서-db-인스턴스-생성">STEP 3: 자습서: DB 인스턴스 생성</h4>
<ul>
<li>자습서에 표시된 사양대로 RDS 인스턴스를 생성합니다.</li>
</ul>
<h4 id="step-4-애플리케이션-로드-밸런서-및-auto-scaling-group-적용">STEP 4: 애플리케이션 로드 밸런서 및 Auto Scaling Group 적용</h4>
<ul>
<li>Auto Scaling Group은 최소 2개, 최대 10개로 설정해놓습니다.</li>
</ul>
<h3 id="이-포스팅에서는-advanced-challenges를-다룹니다">이 포스팅에서는 Advanced Challenges를 다룹니다.</h3>
<hr>
<p>IaC 작성 참고 : <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs">Terraform Registry</a></p>
<h4 id="▶-진행-순서">▶ 진행 순서</h4>
<p>기존과 같이 진행해야 할 순서를 정한 후 테라폼을 작성했습니다.
순서는 아래와 같이 작성했습니다.</p>
<h4 id="1-rds-생성">1. RDS 생성</h4>
<p>가장 먼저 프라이빗 서브넷에 위치할 RDS를 생성했습니다.
가장 주의할 점은 RDS를 프라이빗 서브넷으로 올바르게 위치시켜야 했기에
프라이빗 서브넷 그룹을 명시해줬습니다.</p>
<pre><code>#RDS 생성
resource &quot;aws_db_instance&quot; &quot;Sprint_terraform_RDS&quot; {
  allocated_storage    = 10
  db_name              = &quot;Sprint_terraform_RDS&quot;
  availability_zone    = &quot;ap-northeast-2c&quot;
  db_subnet_group_name = &quot;sprint_subnetgroup&quot;
  vpc_security_group_ids = [aws_security_group.Sprint_security_DB.id]

  engine               = &quot;mysql&quot;
  engine_version       = &quot;8.0&quot;
  instance_class       = &quot;db.t3.micro&quot;
  username             = &quot;admin&quot;
  password             = &quot;admin1234&quot;
  skip_final_snapshot  = true
//  deletion_protection = true

  tags = {
    Name = &quot;Sprint_terraform_RDS&quot;
  }
}
</code></pre><h4 id="2-탄력적-ip-생성">2. 탄력적 IP 생성</h4>
<p>다음은 탄력적 IP주소를 생성해줬습니다. 프라이빗 서브넷에 위치한 RDS와의 통신을 위해 퍼블릭 서브넷에 유동적인 IP가 아닌 고정 IP가 필요했기에 이 작업을 진행했습니다.</p>
<pre><code># 탄력적 IP 생성
resource &quot;aws_eip&quot; &quot;sprint_eip&quot; {
  vpc      = true

  tags = {
    Name = &quot;sprint_eip&quot;
  }
}</code></pre><h4 id="3-nat-gateway-생성">3. NAT Gateway 생성</h4>
<p>프라이빗 서브넷에 위치한 RDS가 외부와 통신하기 위해 NAT Gateway를 이용했습니다.</p>
<pre><code># NAT Gateway 생성
resource &quot;aws_nat_gateway&quot; &quot;sprint_natgw&quot; {
  allocation_id = aws_eip.sprint_eip.id
  subnet_id     = aws_subnet.sprint_public2.id

  tags = {
    Name = &quot;sprint_natgw&quot;
  }
  depends_on = [aws_internet_gateway.sprint_gw]
}</code></pre><h4 id="4-프라이빗-서브넷에-위치할-라우팅-테이블-생성">4. 프라이빗 서브넷에 위치할 라우팅 테이블 생성</h4>
<p>다음으로 라우팅 테이블을 세팅하여 프라이빗 서브넷과 NAT Gateway를 연결해 줬습니다.</p>
<pre><code># 프라이빗 서브넷 라우팅 테이블 생성
resource &quot;aws_route_table&quot; &quot;Sprint_route_table_private&quot; {
  vpc_id = aws_vpc.Sprint_Terraform_VPC.id

  route {
    cidr_block = &quot;0.0.0.0/0&quot;
    gateway_id = aws_nat_gateway.sprint_natgw.id
  }

  tags = {
    Name = &quot;Sprint_route_table_private&quot;
  }
}</code></pre><h4 id="5-라우팅-테이블과-프라이빗-서브넷-연결">5. 라우팅 테이블과 프라이빗 서브넷 연결</h4>
<pre><code># 라우팅 테이블 - 서브넷 private1 연결
resource &quot;aws_route_table_association&quot; &quot;Connect_rt_pv1&quot; {
  subnet_id      = aws_subnet.sprint_private1.id
  route_table_id = aws_route_table.Sprint_route_table_private.id
}

# 라우팅 테이블 - 서브넷 private2 연결
resource &quot;aws_route_table_association&quot; &quot;Connect_rt_pv2&quot; {
  subnet_id      = aws_subnet.sprint_private2.id
  route_table_id = aws_route_table.Sprint_route_table_private.id
}</code></pre><h4 id="6-타겟-그룹-생성">6. 타겟 그룹 생성</h4>
<p>위의 작업을 진행 후 ec2 인스턴스로 rds에 접속이 가능함을 확인했고 최종적으로 오토스케일링 작업을 위해 로드밸런서를 생성하기로 했습니다.
그래서 그 이전 타겟 그룹을 생성했습니다.</p>
<pre><code># 타겟 그룹 생성
resource &quot;aws_lb_target_group&quot; &quot;SprintTg&quot; {
  name     = &quot;SprintTg&quot;
  port     = 8080
  protocol = &quot;HTTP&quot;
  vpc_id   = aws_vpc.Sprint_Terraform_VPC.id

  health_check {
    enabled = true
    healthy_threshold = 3
    interval = 10
    matcher = 200
    path = &quot;/&quot;
    port = &quot;traffic-port&quot;
    protocol = &quot;HTTP&quot;
    timeout = 3
    unhealthy_threshold = 2
  }
}</code></pre><h4 id="7-타겟-그룹과-인스턴스-연결">7. 타겟 그룹과 인스턴스 연결</h4>
<pre><code># 타겟 그룹과 인스턴스 연결(sprint_terraform_EC2_2a)
resource &quot;aws_lb_target_group_attachment&quot; &quot;SprintTg_attachment_2a&quot; {
  target_group_arn = aws_lb_target_group.SprintTg.arn
  target_id        = aws_instance.sprint_terraform_EC2_2a.id
  port             = 8080
}

# 타겟 그룹과 인스턴스 연결(sprint_terraform_EC2_2c)
resource &quot;aws_lb_target_group_attachment&quot; &quot;SprintTg_attachment_2c&quot; {
  target_group_arn = aws_lb_target_group.SprintTg.arn
  target_id        = aws_instance.sprint_terraform_EC2_2c.id
  port             = 8080
}</code></pre><h4 id="8-로드밸런서-생성">8. 로드밸런서 생성</h4>
<pre><code>resource &quot;aws_lb&quot; &quot;SprintAlb&quot; {
  name               = &quot;SprintAlb&quot;
  internal           = false
  load_balancer_type = &quot;application&quot;
  security_groups    = [aws_security_group.Sprint_security_PW.id]
  subnets            = [aws_subnet.sprint_public1.id, aws_subnet.sprint_public2.id]

  tags = {
    Environment = &quot;SprintAlb&quot;
  }
}</code></pre><h4 id="9-로드밸런서-리스너-생성">9. 로드밸런서 리스너 생성</h4>
<pre><code># 로드밸런서 리스너 생성
resource &quot;aws_lb_listener&quot; &quot;Sprint_alb_listner&quot; {
  load_balancer_arn = aws_lb.SprintAlb.arn
  port              = &quot;80&quot;
  protocol          = &quot;HTTP&quot;

  default_action {
    type = &quot;forward&quot;
    target_group_arn = aws_lb_target_group.SprintTg.arn
    }
  }</code></pre><p>해당 작업을 완료 후
로드밸런서를 통한 DNS주소 접속이 가능함을 확인했습니다.</p>
<h4 id="10-오토스케일링-시작-템플릿-설정">10. 오토스케일링 시작 템플릿 설정</h4>
<p>오토스케일링 작업을 수행하기 위해 필요한 사전구성을 작성해줬습니다.
기존의 EC2를 생성할때 사용한 내용을 기반으로 동일한 인스턴스를 생성할 수 있도록 만들어줍니다.</p>
<pre><code># 오토스케일링 시작 템플릿 설정
resource &quot;aws_launch_configuration&quot; &quot;sprint_as_config&quot; {
  name          = &quot;sprint_as_config&quot;
  image_id      = &quot;ami-0cb1d752d27600adb&quot; # ap-northeast-2
  instance_type = &quot;t2.micro&quot;
  associate_public_ip_address = true
  security_groups = [aws_security_group.Sprint_security_PW.id]
  key_name = &quot;sprint_1234&quot;

# 사용자 데이터 입력
  user_data = &lt;&lt;-EOF
  #!/bin/bash
  echo &quot;Hello, World&quot; &gt; index.html
  nohup busybox httpd -f -p ${var.server_port} &amp;
  EOF

  depends_on = [
    aws_key_pair.sprint_1234
  ]
    lifecycle {
    create_before_destroy = true
  }
}
</code></pre><h4 id="11-오토스케일링-그룹-생성">11. 오토스케일링 그룹 생성</h4>
<p>사전 구성을 마쳤다면 실제로 오토스케일링 그룹을 생성해 인스턴스가 잘 생성되는지를 확인해줍니다.</p>
<pre><code># 오토스케일링 그룹 생성
resource &quot;aws_autoscaling_group&quot; &quot;sprint_as_group&quot; {
  name                      = &quot;sprint_as_group&quot;
  max_size                  = 10
  min_size                  = 2
  health_check_grace_period = 300
  health_check_type         = &quot;ELB&quot;
  force_delete              = true
  launch_configuration      = aws_launch_configuration.sprint_as_config.name
  vpc_zone_identifier       = [aws_subnet.sprint_public1.id, aws_subnet.sprint_public2.id]
  target_group_arns = [aws_lb_target_group.SprintTg.arn]

  tags = [{
      key                 = &quot;Name&quot;
      value               = &quot;${var.instance_name}&quot;
      propagate_at_launch = true
  }]
}</code></pre><h4 id="12-기존-작업-수정">12. 기존 작업 수정</h4>
<p>위의 작업까지 성공적으로 끝났을때 인스턴스가 4개가 존재하고 있었습니다.
타겟그룹에도 4개의 인스턴스가 위치하고 있었죠 그래서 기존에 인스턴스를 생성하는 리소스와 타겟그룹에 인스턴스를 추가하는 리소스를 삭제했습니다.</p>
<hr>
<h3 id="실습에서-느낀점">실습에서 느낀점</h3>
<p>테라폼을 활용해 실습을 하면서 레지스트리를 정말 많이 뒤져가면서 대부분을 하드코딩으로 진행했습니다. 하지만 Variable, data, Output, backend, lockfile등 테라폼을 통해 활용할 수 있는 많은 기능이 있는것을 작업이 끝난 후에야 알게되어서ㅠㅠ 테라폼을 더 깊게 공부해야 겠다는 생각이 들었습니다.!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Terraform AWS IaC 작성 ]]></title>
            <link>https://velog.io/@son_doobu96/Terraform-AWS-IaC-%EC%9E%91%EC%84%B1</link>
            <guid>https://velog.io/@son_doobu96/Terraform-AWS-IaC-%EC%9E%91%EC%84%B1</guid>
            <pubDate>Wed, 08 Feb 2023 10:28:51 GMT</pubDate>
            <description><![CDATA[<h3 id="구성하려는-아키텍처-구성도">구성하려는 아키텍처 구성도</h3>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/502c23a1-7544-40d3-b65c-e06636686d51/image.png" alt=""></p>
<h3 id="진행-단계">진행 단계</h3>
<h4 id="▶-step-1-vpc-생성">▶ STEP 1 VPC 생성</h4>
<p><strong>1. VPC 및 서브넷 생성</strong></p>
<ul>
<li>프라이빗 서브넷과 퍼블릭 서브넷이 각각 두개, 총 네개가 있어야 합니다.</li>
</ul>
<p><strong>2. VPC 보안 그룹 생성</strong></p>
<ul>
<li>퍼블릭 웹 서버가 사용할 VPC 보안 그룹을 만들어야 합니다.</li>
<li>프라이빗 DB 웹 서버가 사용할 VPC 보안 그룹을 만들어야 합니다.</li>
</ul>
<p><strong>3. DB 서브넷 그룹 생성</strong></p>
<ul>
<li>RDS 인스턴스가 사용할 VPC 서브넷 그룹을 만들어야 합니다.</li>
</ul>
<h4 id="▶-step-2-ec2-생성">▶ STEP 2 EC2 생성</h4>
<p>만들어야 하는 사양</p>
<ul>
<li>AMI: Ubuntu Server 18</li>
<li>인스턴스 타입: t2.micro</li>
<li>사용자 데이터<pre><code>#!/bin/bash
echo &quot;Hello, World&quot; &gt; index.html
nohup busybox httpd -f -p ${var.server_port} &amp;</code></pre></li>
<li>키 페어: 수동으로 만들고 EC2에 할당합니다.</li>
</ul>
<h3 id="advanced-challenges">Advanced Challenges</h3>
<h4 id="step-3-자습서-db-인스턴스-생성">STEP 3: 자습서: DB 인스턴스 생성</h4>
<ul>
<li>자습서에 표시된 사양대로 RDS 인스턴스를 생성합니다.</li>
</ul>
<h4 id="step-4-애플리케이션-로드-밸런서-및-auto-scaling-group-적용">STEP 4: 애플리케이션 로드 밸런서 및 Auto Scaling Group 적용</h4>
<ul>
<li>Auto Scaling Group은 최소 2개, 최대 10개로 설정해놓습니다.</li>
</ul>
<h3 id="이-포스팅에서는-step-2번까지만을-다루도록-하겠다">이 포스팅에서는 STEP 2번까지만을 다루도록 하겠다.</h3>
<hr>
<p>IaC 작성 참고 : <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs">Terraform Registry</a></p>
<h4 id="▶-진행-순서">▶ 진행 순서</h4>
<p>가장 먼저 구성해야 할 아키텍처들이 서로 어떤 연관을 가지고 있고 어떤 연결 구성이 필요한지에 맞춰 Terraform IaC를 구성할 순서를 계획하고 필요한 개념들을 알아봤습니다.</p>
<p>이후 아래의 단계에 맞춰 인스턴스의 생성 및 연결 확인까지의 과정을 진행했습니다.</p>
<p><strong>1. VPC 생성</strong></p>
<ul>
<li>2개의 가용영역</li>
<li>서울 리전<pre><code>terraform {
required_providers {
aws = {
 source  = &quot;hashicorp/aws&quot;
 version = &quot;~&gt; 4.0&quot;
}
}
}
</code></pre></li>
</ul>
<h1 id="configure-the-aws-provider">Configure the AWS Provider</h1>
<p>provider &quot;aws&quot; {
  region = &quot;ap-northeast-2&quot;
}</p>
<h1 id="vpc-생성">VPC 생성</h1>
<p>resource &quot;aws_vpc&quot; &quot;Sprint_Terraform_VPC&quot; {
  cidr_block = &quot;10.0.0.0/16&quot;
  tags = {
    Name = &quot;Sprint_Terraform_VPC&quot;}
}   </p>
<pre><code>이렇게 서울 리전에 cidr_block 10.0.0.0/16의 대역대를 가진 VPC를 생성했습니다. 

**2. Subnet 생성 (퍼블릭2, 프라이빗2)**
   - 2개의 인스턴스가 위치할 2개의 서브넷 생성</code></pre><h1 id="서브넷-생성">서브넷 생성</h1>
<p>resource &quot;aws_subnet&quot; &quot;sprint_public1&quot; {
  vpc_id            = aws_vpc.Sprint_Terraform_VPC.id
  cidr_block        = &quot;10.0.10.0/24&quot;
  availability_zone = &quot;ap-northeast-2a&quot;</p>
<p>  tags = {
    Name = &quot;sprint_public1&quot;
  }
}</p>
<p>resource &quot;aws_subnet&quot; &quot;sprint_public2&quot; {
  vpc_id            = aws_vpc.Sprint_Terraform_VPC.id
  cidr_block        = &quot;10.0.11.0/24&quot;
  availability_zone = &quot;ap-northeast-2c&quot;</p>
<p>  tags = {
    Name = &quot;sprint_public2&quot;
  }
}</p>
<p>resource &quot;aws_subnet&quot; &quot;sprint_private1&quot; {
  vpc_id            = aws_vpc.Sprint_Terraform_VPC.id
  cidr_block        = &quot;10.0.128.0/24&quot;
  availability_zone = &quot;ap-northeast-2a&quot;</p>
<p>  tags = {
    Name = &quot;sprint_private1&quot;
  }
}</p>
<p>resource &quot;aws_subnet&quot; &quot;sprint_private2&quot; {
  vpc_id            = aws_vpc.Sprint_Terraform_VPC.id
  cidr_block        = &quot;10.0.144.0/24&quot;
  availability_zone = &quot;ap-northeast-2c&quot;</p>
<p>  tags = {
    Name = &quot;sprint_private2&quot;
  }
}  </p>
<pre><code>위의 IaC를 통해 서브넷 4개를 구성했습니다. Public 서브넷에는 인스턴스가 위치할 예정이고 프라이빗 서브넷은 그룹화 하여 RDS가 위치할 예정입니다.
가용 영역을 2a와 2c로 선정한 이유는 사용할 인스턴스 유형인 t2.micro가 지원하는 가용 영역이 2a와 2c이기 때문입니다.

**3. 보안 그룹 생성 (Public, DB)**
   - 인스턴스용, DB용 보안 그룹 생성
</code></pre><h1 id="public-보안그룹-생성-ssh-http-busybox-allow">Public 보안그룹 생성 (SSH, HTTP, busybox allow)</h1>
<p>resource &quot;aws_security_group&quot; &quot;Sprint_security_PW&quot; {
  name        = &quot;Sprint_security_PW&quot;
  description = &quot;Allow TLS inbound traffic&quot;
  vpc_id      = aws_vpc.Sprint_Terraform_VPC.id</p>
<p>  ingress {
    description      = &quot;SSH from VPC&quot;
    from_port        = 22
    to_port          = 22
    protocol         = &quot;tcp&quot;
    cidr_blocks      = [&quot;0.0.0.0/0&quot;]
  }
  ingress {
    description      = &quot;HTTP from VPC&quot;
    from_port        = 80
    to_port          = 80
    protocol         = &quot;tcp&quot;
    cidr_blocks      = [&quot;0.0.0.0/0&quot;]
  }
    ingress {
    description      = &quot;busybox from VPC&quot;
    from_port        = 8080
    to_port          = 8080
    protocol         = &quot;tcp&quot;
    cidr_blocks      = [&quot;0.0.0.0/0&quot;]
  }
  egress {
    from_port        = 0
    to_port          = 0
    protocol         = &quot;-1&quot;
    cidr_blocks      = [&quot;0.0.0.0/0&quot;]
    ipv6_cidr_blocks = [&quot;::/0&quot;]
  }</p>
<p>  tags = {
    Name = &quot;Sprint_security_PW&quot;
  }
}</p>
<h1 id="db-보안그룹-생성-mysqlaurora-allow">DB 보안그룹 생성 (MySQL/Aurora allow)</h1>
<p>resource &quot;aws_security_group&quot; &quot;Sprint_security_DB&quot; {
  name        = &quot;Sprint_security_DB&quot;
  description = &quot;Allow TLS inbound traffic&quot;
  vpc_id      = aws_vpc.Sprint_Terraform_VPC.id</p>
<p>  ingress {
    description      = &quot;MySQL/Aurora from VPC&quot;
    from_port        = 3306
    to_port          = 3306
    protocol         = &quot;tcp&quot;
    cidr_blocks      = [&quot;0.0.0.0/0&quot;]
  }
  egress {
    from_port        = 0
    to_port          = 0
    protocol         = &quot;-1&quot;
    cidr_blocks      = [&quot;0.0.0.0/0&quot;]
    ipv6_cidr_blocks = [&quot;::/0&quot;]
  }</p>
<p>  tags = {
    Name = &quot;Sprint_security_DB&quot;
  }
}   </p>
<pre><code>이후 각각 인스턴스와 RDS에서 활용할 보안그룹을 생성했습니다.
인스턴스에 접근하기 위한 ssh와 외부 접근을 위한 http, test를 위한 8080 총 3개의 포트를 allow 설정했습니다.

처음에는 Terraform Registry를 참고해 ingress 설정을 아래와 같이 구성했습니다.</code></pre><p>cidr_blocks      = [aws_vpc.Sprint_Terraform_VPC.cidr_block]
ipv6_cidr_blocks = [aws_vpc.Sprint_Terraform_VPC.ipv6_cidr_block]</p>
<pre><code>하지만 확인 단계에서 접근이 불가능하였습니다. 이때 보안그룹의 설정이 vpc 대역대에서의 접근만이 가능하도록 설정된 것을 확인하였고 아래와 같이 코드를 수정하여 진행하였습니다. (ipv6는 사용 예정이 없어 삭제했습니다.)</code></pre><p>cidr_blocks      = [&quot;0.0.0.0/0&quot;]</p>
<pre><code>
**4. DB 서브넷 그룹 생성**
   - RDS를 서브넷의 한 공간에만 위치시킬 예정이기에 그룹화</code></pre><h1 id="db-서브넷-그룹-생성">DB 서브넷 그룹 생성</h1>
<p>resource &quot;aws_db_subnet_group&quot; &quot;sprint_subnetgroup&quot; {
  name       = &quot;sprint_subnetgroup&quot;
  subnet_ids = [aws_subnet.sprint_private1.id, aws_subnet.sprint_private2.id]</p>
<p>  tags = {
    Name = &quot;sprint_subnetgroup&quot;
  }
}</p>
<pre><code>**5. 인터넷 게이트웨이 생성**
   - VPC가 외부 통신이 가능하도록 설정</code></pre><h1 id="인터넷-게이트웨이-생성">인터넷 게이트웨이 생성</h1>
<p>resource &quot;aws_internet_gateway&quot; &quot;sprint_gw&quot; {
  vpc_id = aws_vpc.Sprint_Terraform_VPC.id</p>
<p>  tags = {
    Name = &quot;sprint_gw&quot;
  }
}</p>
<pre><code>
**6. 라우팅 테이블 생성**
   - 인터넷 게이트웨이와 라우팅 테이블 연결</code></pre><h1 id="라우팅-테이블-생성">라우팅 테이블 생성</h1>
<p>resource &quot;aws_route_table&quot; &quot;Sprint_route_table&quot; {
  vpc_id = aws_vpc.Sprint_Terraform_VPC.id</p>
<p>  route {
    cidr_block = &quot;0.0.0.0/0&quot;
    gateway_id = aws_internet_gateway.sprint_gw.id
  }</p>
<p>  tags = {
    Name = &quot;Sprint_route_table&quot;
  }
}   </p>
<pre><code>
**7. 라우팅 테이블 - 서브넷 public1 &amp; 2 연결**
</code></pre><h1 id="라우팅-테이블---서브넷-public1-연결">라우팅 테이블 - 서브넷 public1 연결</h1>
<p>resource &quot;aws_route_table_association&quot; &quot;Connect_rt_p1&quot; {
  subnet_id      = aws_subnet.sprint_public1.id
  route_table_id = aws_route_table.Sprint_route_table.id
}</p>
<h1 id="라우팅-테이블---서브넷-public2-연결">라우팅 테이블 - 서브넷 public2 연결</h1>
<p>resource &quot;aws_route_table_association&quot; &quot;Connect_rt_p2&quot; {
  subnet_id      = aws_subnet.sprint_public2.id
  route_table_id = aws_route_table.Sprint_route_table.id
}</p>
<pre><code>
처음에는 리소스를 구성하며 인터넷 게이트웨이와 중복 연결을 구성하여 문제가 발생했지만 문제 확인 후 제거하여 진행에 큰 문제는 없었습니다. 

**8. 키 페어 생성**
   - 인스턴스에 접속하기 위한 키페어 생성</code></pre><p>#키 페어 생성
resource &quot;aws_key_pair&quot; &quot;sprint_1234&quot; {
key_name = &quot;sprint_1234&quot;
public_key = tls_private_key.rsa.public_key_openssh
}
resource &quot;tls_private_key&quot; &quot;rsa&quot; {
algorithm = &quot;RSA&quot;
rsa_bits  = 4096
}
resource &quot;local_file&quot; &quot;sprint_1234&quot; {
content  = tls_private_key.rsa.private_key_pem
filename = &quot;sprint_1234&quot;
}   </p>
<pre><code>key-pair를 생성하고 로컬에 저장하도록 리소스를 구성했습니다.
이후 ignore 처리를 통해 key-pair를 보호중에 있습니다.

하지만 한가지의 문제점이 있습니다. 바로 destroy 후 다시 생성할 경우 기존 key-pair를 destroy후 새로 생성한다는 점입니다. 접속에 있어 문제는 없지만 보안적으로 문제가 될 수 있기에 추후 해결책을 2편에 같이 포스팅하도록 하겠습니다.

**9. EC2 인스턴스 생성**
    - 각 서브넷에 EC2 인스턴스 생성
</code></pre><h1 id="ec2_2a-인스턴스-생성">EC2_2a 인스턴스 생성</h1>
<p>resource &quot;aws_instance&quot; &quot;sprint_terraform_EC2_2a&quot; {
  ami           = &quot;ami-0cb1d752d27600adb&quot; # ap-northeast-2
  instance_type = &quot;t2.micro&quot;
  associate_public_ip_address = true
  vpc_security_group_ids = [aws_security_group.Sprint_security_PW.id]
  availability_zone = &quot;ap-northeast-2a&quot;
  subnet_id = aws_subnet.sprint_public1.id
  key_name = &quot;sprint_1234&quot;</p>
<p>  credit_specification {
    cpu_credits = &quot;unlimited&quot;
  }</p>
<h1 id="사용자-데이터-입력">사용자 데이터 입력</h1>
<p>  user_data = &lt;&lt;-EOF
  #!/bin/bash
  echo &quot;Hello, World&quot; &gt; index.html
  nohup busybox httpd -f -p ${var.server_port} &amp;
  EOF</p>
<p>  depends_on = [
    aws_key_pair.sprint_1234
  ]</p>
<pre><code>tags = {
Name = &quot;sprint_terraform_EC2_2a&quot;</code></pre><p>  }
}</p>
<h1 id="ec2_2b-인스턴스-생성">EC2_2b 인스턴스 생성</h1>
<p>resource &quot;aws_instance&quot; &quot;sprint_terraform_EC2_2c&quot; {
  ami           = &quot;ami-0cb1d752d27600adb&quot; # ap-northeast-2
  instance_type = &quot;t2.micro&quot;
  associate_public_ip_address = true
  vpc_security_group_ids = [aws_security_group.Sprint_security_PW.id]
  availability_zone = &quot;ap-northeast-2c&quot;
  subnet_id = aws_subnet.sprint_public2.id
  key_name = &quot;sprint_1234&quot;</p>
<p>  credit_specification {
    cpu_credits = &quot;unlimited&quot;
  }</p>
<h1 id="사용자-데이터-입력-1">사용자 데이터 입력</h1>
<p>  user_data = &lt;&lt;-EOF
  #!/bin/bash
  echo &quot;Hello, World&quot; &gt; index.html
  nohup busybox httpd -f -p ${var.server_port} &amp;
  EOF</p>
<p>  depends_on = [
    aws_key_pair.sprint_1234
  ]</p>
<pre><code>tags = {
Name = &quot;sprint_terraform_EC2_2c&quot;</code></pre><p>  }
}</p>
<pre><code>Registry 문서를 참고하여 필요한 옵션을 추가하여 구성하였습니다.
퍼블릭 IP에 대한 설정과 보안그룹 가용 영역, key-pair를 지정해 주었습니다.

</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[IaC와 불변적 인프라]]></title>
            <link>https://velog.io/@son_doobu96/IaC%EC%99%80-%EB%B6%88%EB%B3%80%EC%A0%81-%EC%9D%B8%ED%94%84%EB%9D%BC</link>
            <guid>https://velog.io/@son_doobu96/IaC%EC%99%80-%EB%B6%88%EB%B3%80%EC%A0%81-%EC%9D%B8%ED%94%84%EB%9D%BC</guid>
            <pubDate>Tue, 07 Feb 2023 05:15:43 GMT</pubDate>
            <description><![CDATA[<h3 id="iac란-무엇인가">IaC란 무엇인가?</h3>
<p>IaC란 코드형 인프라(Infrastructure as Code), 즉 IaC는 설정을 코드로 작성하여 클라우드 인프라스트럭처의 생성/수정/삭제를 자동화하는 방법입니다.</p>
<p>쉽게 말해 <strong>IaC는 인프라스트럭처의 설계도</strong>로
서버, 데이터베이스, 네트워크, 배포 프로세스, 테스트 등 거의 모든 것을 코드로 관리할 수 있다.</p>
<h4 id="iac의-장점">IaC의 장점?</h4>
<ul>
<li>인프라를 만드는 과정이 자동화되므로, 오류가 훨씬 덜 발생하고 안전하다.</li>
<li>IaC는 쉽게 공유할 수 있고, 버전 관리에도 용이하다.</li>
<li>코드와 현재 상태를 비교하여, 추후 인프라 상태의 변경에 따르는 위험을 분석하고 검증할 수 있다.</li>
<li>배포 과정을 소수의 시스템 관리자만 진행하는 것이 아닌, 개발자 스스로가 배포하고 인프라를 통제할 수 있는 환경으로 만들 수 있다.</li>
</ul>
<h3 id="iac의-종류">IaC의 종류</h3>
<h4 id="■-절차형">■ 절차형</h4>
<p>프로그래밍 언어를 이용해서 직접 순차적으로 인프라를 생성하도록 코드를 작성하는 방법이다. 선언형에 비해 더 강력한 일들을 할 수 있으나, 실제 적용된 결과를 가늠하기 어렵고, 코드를 읽기에 직관적이지 않다.</p>
<h4 id="■-절차형-iac의-종류">■ 절차형 IaC의 종류</h4>
<ul>
<li>AWS CDK</li>
<li>Pulumi</li>
</ul>
<h4 id="■-선언형-iac">■ 선언형 IaC</h4>
<p>선언형 언어 JSON, YAML 등을 사용한다. 실제 인프라가 적용된 결과(기대하는 상태)와 적용할 내용(YAML 등)이 직관적으로 매핑된다.</p>
<h4 id="■-선언형-iac-종류">■ 선언형 IaC 종류</h4>
<ul>
<li>CloudFormation (AWS에서만 사용가능)</li>
<li>Azure Blueprint (Azure에서만 사용가능)</li>
<li>Cloud Deployment Manager (GCP에서만 사용가능)</li>
<li>Terraform: 어떤 클라우드 서비스에도 적용되는 범용 IaC 도구이다.</li>
</ul>
<hr>
<h3 id="인프라-변경에-의한-사고">인프라 변경에 의한 사고</h3>
<p>인프라 변경에 의한 사고를 <strong>Configuration Drift</strong>라고 한다. 사람에 의해 배포된 인프라가 삭제 혹은 잘못 변경되거나 AWS의 경우 보안 그룹등을 잘 못 변경하는 경우 시스템 전체의 영향을 미칠 수 있다.</p>
<p>■ 문제 해결 방법?
AWS를 기준으로 문제를 해결하기 위한 도구들을 설명한다.</p>
<h3 id="관리형-서비스">관리형 서비스</h3>
<h4 id="1-잘못-설정된것을-찾기-위한-도구-aws-config">1. 잘못 설정된것을 찾기 위한 도구: AWS Config</h4>
<ul>
<li>바른 설정을 지정해놓고, 찾고 고칠 수 있게 만들어준다</li>
</ul>
<h4 id="2-사고-감지-도구-aws-cloudformation-drift-detection">2. 사고 감지 도구: AWS CloudFormation Drift Detection</h4>
<h3 id="개발-방법에-가까운-솔루션">개발 방법에 가까운 솔루션</h3>
<h4 id="정상-작동-상태를-파일로-저장-terraform-state-files">정상 작동 상태를 파일로 저장: Terraform state files</h4>
<ul>
<li>Terraform의 상태 정의 파일은 인프라의 실제 상태와의 비교 대상으로서 현재 상황을 진단/점검할 수 있다.</li>
</ul>
<hr>
<h3 id="configuration-drift-예방책">Configuration Drift 예방책</h3>
<h4 id="immutable-infrastructure-불변적-인프라-방법론">Immutable Infrastructure (불변적 인프라 방법론)</h4>
<ul>
<li><p>한번 생성했으면 수정하지 않는다.</p>
<ul>
<li>프로비저닝 및 배포했으면, 콘솔에 접속해서 수동으로 설정하지 않습니다.</li>
<li>즉 변경은 삭제 후 생성을 의미합니다.</li>
</ul>
</li>
<li><p>인스턴스 내부 구성(사용자 스크립트 등)이 필요할 경우 AMI로 만들어 놓습니다.</p>
<ul>
<li>즉 Develop → Deploy → Configure가 아니라</li>
<li>Develop → Configure → Deploy여야 합니다.</li>
</ul>
</li>
<li><p>코드형 인프라(IaC)를 사용합니다.</p>
</li>
</ul>
<hr>
<h3 id="가변적mutable-인프라와-불변적immutable-인프라의-차이">가변적(mutable) 인프라와 불변적(immutable) 인프라의 차이</h3>
<h4 id="■-가변적mutable-인프라">■ 가변적(mutable) 인프라</h4>
<ul>
<li>서버는 끊임없이 업데이트 되고 수정된다.</li>
<li>엔지니어 관리자는 서버에 ssh로 접속하고 수동으로 패키지를 업/다운그레이드 하고 서버 하나하나의 설정 파일을 수정하고 새 코드를 직접 서버에 배포한다.</li>
</ul>
<h4 id="■-불변적immutable-인프라">■ 불변적(immutable) 인프라</h4>
<ul>
<li>서버가 배포된 이후 절대 변경되지 않는 형태의 인프라 패러다임이다.</li>
<li>업데이트 및 수정 사항의 발생 경우 공용 이미지에 적절한 수정을 한 새 서버가 프로비저닝 되어 기존 서버를 대체 한다.</li>
<li>새 서버의 검증이 완료되면 기존 서버는 더 이상 사용하지 않게 된다.</li>
</ul>
<h4 id="■-둘은-어떠한-차이가-있는가">■ 둘은 어떠한 차이가 있는가?</h4>
<h4 id="--정책의-도입-시점">- ‘정책’의 도입 시점</h4>
<blockquote>
<p>가변적 인프라의 경우 ‘배포 이후’ 변경되도록 설계되어 있다. 불변적 인프라의 경우 ‘변경되지 않고 아예 교체되도록’ 만들어졌다.</p>
</blockquote>
<h4 id="---클라우드를-수용하는가">-  ‘클라우드’를 수용하는가?</h4>
<blockquote>
<p>가변적 인프라의 도입 시기는 클라우드 컴퓨팅이 상용화 되기 이전 물리서버를 중심으로 서버를 구성할때이다.
불변적 인프라의 서버를 ‘변경’한다는 개념은 가변적 인프라의 도입 시기였던 주된 서버였던 물리서버에서는 ‘교체’를 의미한다.</p>
</blockquote>
<p>이는 많은 비용 투자를 수반하기에 서버의 다운타임을 최대한 줄인채 서버를 ‘조금씩 변경하는’것에 초점을 맞추었고 당시에는 이것이 가장 효율적인 방법이었다.
하지만 이런 수동적인 변경은 인프라의 통일성을 해치고 이로 인해 서버 복제에 있어 치명적인 단점을 가지고 있으며 서버 하나하나가 동일하지 않은 각각의 고유한 객체로써 작용하며 매우 취약한 서비스 구조를 가지게 되었다.</p>
<blockquote>
<p>하지만 불변적 인프라에 대한 개념이 대두된 시기는 클라우드 컴퓨팅이 상용화되면서 온디멘드/가상화를 통해 저렴하고 적은 시간 비용을 투자해 서버의 생성이 가능해졌다. 이를 통해 기존의 ‘물리적 서버’가 가지는 단점을 개선하는 방향으로 멱등성을 가지는 불변적 인프라의 개념이 등장하게 되었다.</p>
</blockquote>
<h4 id="---문제-해결에서의-차이점">-  ‘문제 해결’에서의 차이점</h4>
<p>서버에 문제가 발생했을 때 둘의 대처 방법과 그 장단점이 극명하게 드러난다.
<strong>가변적 인프라</strong>의 경우 서버에 작은 문제가 발생하면 즉흥적인 변경으로 그 문제를 해결하곤 한다. 이를 문서화하려는 노력으로 문제에 대한 로깅을 할 수도 있겠지만 이미 생겨난 수많은 변경점들은 계속해서 새로운 충돌점을 생성해내고 이로인한 컨피그레이션 드리프트의 딜레마에 빠지곤 한다.</p>
<p><strong>불변적 인프라</strong>는 이러한 단점을 극복하기 위해 선언적 코드를 통해 서버를 문서화하고 이로 인해 원자성이 보장되기에 급작스러운 트래픽 증감에 따른 유연한 대처에 있어 효율적이다. 또한 서버 에러가 발생시에도 새로운 서버를 간단하게 배포 후 기존 서버를 닫는 형식으로 문제를 쉽게 해결할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kubernetes란 무엇일까?]]></title>
            <link>https://velog.io/@son_doobu96/Kubernetes%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@son_doobu96/Kubernetes%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Mon, 06 Feb 2023 11:31:11 GMT</pubDate>
            <description><![CDATA[<h3 id="◎-도커-네트워크의-개념">◎ 도커 네트워크의 개념</h3>
<blockquote>
<p>bridge, host, none은 Docker 데몬(daemon)이 실행되면서 디폴트로 생성되는 네트워크입니다. 대부분의 경우에는 이러한 디폴트 네트워크를 이용하는 것 보다는 사용자가 직접 네트워크를 생성해서 사용하는 것이 권장됩니다.
기본적으로 새 컨테이너를 시작하면 자동으로 기본 bridge 네트워크에 연결된다</p>
</blockquote>
<h4 id="■-도커-네트워크의-종류">■ 도커 네트워크의 종류</h4>
<p>bridge 네트워크</p>
<ul>
<li>기본 네트워크 유형이며 컨테이너가 서로 및 호스트와 통신할 수 있도록 한다. 
호스트의 네트워크 스택을 사용하고 컨테이너 간에 기본적인 격리를 제공한다. 
브리지 네트워크에 연결된 각 컨테이너에는 고유한 IP 주소가 할당되며 
ping 및 traceroute와 같은 표준 네트워킹 도구를 사용하여 컨테이너 간에 통신할 수 있다.</li>
</ul>
<p>Host 네트워크</p>
<ul>
<li>호스트의 네트워크 스택을 사용하며 컨테이너 간에 격리를 제공하지 않다. 
호스트 네트워크에 연결된 컨테이너는 호스트의 IP 주소와 포트를 공유한다. 
권한 있는 포트에서 수신 대기하거나 특정 IP 주소를 사용해야 하는 컨테이너를 실행해야 할 때 유용할 수 있다.</li>
</ul>
<p>오버레이 네트워크</p>
<ul>
<li>이를 통해 여러 호스트에 걸쳐 컨테이너를 연결할 수 있다. 
VXLAN 프로토콜을 사용하여 여러 Docker 호스트에 걸쳐 있는 가상 네트워크를 만든다. 
이는 swarm 클러스터의 여러 호스트에서 컨테이너화된 애플리케이션을 실행할 때 유용할 수 있다.</li>
</ul>
<p>Macvlan 네트워크</p>
<ul>
<li>이를 통해 네트워크에 연결된 각 컨테이너에 고유한 MAC 주소를 할당할 수 있으므로 
LAN 네트워크에서 고유한 IP 주소를 할당할 수 있다.</li>
</ul>
<p>None 네트워크</p>
<ul>
<li>이 네트워크를 사용하면 모든 네트워크에서 컨테이너 연결을 끊을 수 있다. 
이는 다른 컨테이너와 통신할 수 없는 &quot;방화벽&quot; 모드에서 컨테이너를 실행하려는 경우에 유용할 수 있다.</li>
</ul>
<hr>
<h3 id="◎-쿠버네티스란">◎ 쿠버네티스란?</h3>
<p>컨테이너화된 애플리케이션의 배포, 확장 등을 관리하는 것을 자동화하기 위한 플랫폼
(컨테이너 오케스트레이션 엔진)이다.</p>
<p>도커는 설치된 호스트(도커 호스트)를 동시에 여러 대 동작시키거나 중앙에서 통합, 관리할 수 없는 단점이 있어
여러 호스트로 구성하거나 일정 규모 이상의 서비스 혼경에서 사용할 수 있는 시스템을 구축하기가 힘들다.</p>
<p>쿠버네티스로 대표되는 컨테이너 오케이스트레이션 엔진을 사용해 이러한 시스템을 구축할 수 있다.</p>
<h4 id="■-컨테이너-오케스트레이션-엔진의-종류">■ 컨테이너 오케스트레이션 엔진의 종류</h4>
<ul>
<li>도커스윔</li>
<li>아파치 메소스</li>
<li>쿠버네티스</li>
<li>등</li>
</ul>
<hr>
<h3 id="◎-쿠버네티스로-할-수-있는-것">◎ 쿠버네티스로 할 수 있는 것?</h3>
<p>컨테이너란 ‘어떤 애플리케이션을 실행하도록 빌드된 컨테이너 이미지를 기반으로 기동된 워크로드’ 이러한 컨테이너를 서비스 환경에서 사용하기 위해서는 아래의 과제들을 고려해야 한다.</p>
<ul>
<li>여러 쿠버네티스 노드 관리</li>
<li>컨테이너 스케줄링</li>
<li>롤링 업데이트</li>
<li>스케일링/오토 스케일링</li>
<li>컨테이너 모니터링</li>
<li>자동화된 복구</li>
<li>서비스 디스커버리</li>
<li>로드 밸런싱</li>
<li>데이터 관리</li>
<li>워크로드 관리</li>
<li>로그 관리</li>
<li>선언적 코드를 사용한 관리</li>
<li>그 외 에코시스템 과의 연계 및 확장</li>
</ul>
<hr>
<h3 id="◎-쿠버네티스의-특징">◎ 쿠버네티스의 특징?</h3>
<h4 id="■-1-선언적-코드를-사용한-관리iac">■ 1. 선언적 코드를 사용한 관리(IaC)</h4>
<p>쿠버네티스는 YAML 형식이나 JSON형식으로 작성한 선언적 코드(매니패스트)를 통해 qovgksms 컨테이너로 주변 리소스를 곤리할 수 있다. 즉 IaC(Infrastructure as Code)를 구현할 수 있다. </p>
<pre><code>&lt;매니패스트 파일 예제&gt;
apiVersion: apps/v1 
kind: Deployment 
metadata:   
  name: sample-deployment 
spec:   
  replicas: 3   
  selector:     
    matchLabels:       
      app: sample-app   
    template:     
      metadata:       
        labels:         
          app: sample-app     
      spec:       
        containers:         
    - name: nginx-container           
           image: nginx:1.16</code></pre><h4 id="■-2-스케일링오토-스케일링">■ 2. 스케일링/오토 스케일링</h4>
<p>쿠버네티스는 컨테이너 클러스터를 구성하여 여러 쿠버네티스 노드를 관리한다.
쿠버네티스의 오토스케일링은 같은 컨테이너 이미지를 기반으로 하여 여러 컨테이너 레플리카를 배포하여 부하 분산 및 다중화 구조를 생성한다. (부하에 따라 자동으로 레플리카 수를 늘이거나 줄인다.)</p>
<h4 id="■-3-스케줄링">■ 3. 스케줄링</h4>
<p>컨테이너를 쿠버네티스 노드에 배포할 때 어떤 쿠버네티스 노드에 배포할지를 결정하는 단계이다.
이때 워크로드의 특징이나 노드의 성능을 그 기준으로 삼는다. 
ex) 디스크 I/O가 많은 컨테이너를 디스크가 SSD인 쿠버네티스 노드에 배치</p>
<p>또 쿠버네티스 클러스터를 GCP/AWS/OpenStack 등에 구축한 경우 쿠버네티스 노드에 가용영역 등을 식별하는 추가 정보가 부여되어 있어 쉽게 멀티존 위에 컨테이너를 분산 배치할 수 있다.</p>
<blockquote>
<p>■ 막간 용어</p>
</blockquote>
<ul>
<li><strong>어피니티(Affinity)</strong> : ‘선호도’라는 뜻을 가지고 있으며 레이블, 토폴로지 및 기타 요소를 기반으로 노드에서 Pod 예약 규칙을 지정할 수 있는 쿠버네티스 기능이다. 
즉 선호도 규칙을 정하는 것이라고 생각하면 된다.</li>
<li><strong>안티어피니티(Anti-Affinity)</strong> : 특정 포드가 동일한 노드에 함께 배치되지 않도록 지정하는 일종의 포드 스케줄링 제약 조건이다.
중요한 포드가 다른 포드의 장애 또는 성능문제의 영향을 받지 않도록 하기 위해 사용한다.</li>
<li><strong>포드(Pod)</strong> : 쿠버네티스 객체 모델에서 가장 작고 단순한 단위이다.
클러스터에서 실행중인 단일 인스턴스를 나타낸다.
포드에는 하나 이상의 컨테이너가 포함되며 동일한 호스트, 동일한 네트워크 네임스페이스를 공유한다.</li>
</ul>
<h4 id="■-4-리소스-관리">■ 4. 리소스 관리</h4>
<p>컨테이너 배치에 대한 지정이 없을 경우 쿠버네티스 노드의 CPU나 메모리의 여유 리소스 상태에 따라 스케줄링된다.</p>
<h4 id="■-5-자동화된-복구">■ 5. 자동화된 복구</h4>
<p>다중화(fault tolerant) 관점에서 쿠버네티스의 중요한 콘셉트 중 하나이다.
쿠버네티스는 표준으로 컨테이너 프로세스를 모니터링하고 프로세스 정지를 감지하면 다시 컨테이너 스케줄링을 실행하여 컨테이너를 자동으로 재배포한다. 
클러스터 노드에 장애가 발생하거나 노드를 축출했을 경우 그 노드의 컨테이너가 사라진다 해도 서비스에 영향 없이 애플리케이션을 자동으로 복구 할 수 있다.</p>
<p>복구 실행 조건에는 프로세스 모니터링 외에 HTTP/TCP나 셸 스크립트로 헬스 체크의 성공 여부를 설정할 수도 있다.</p>
<h4 id="■-6-로드-밸런싱과-서비스-디스커버리">■ 6. 로드 밸런싱과 서비스 디스커버리</h4>
<p>여러대로 구성된 애플리케이션을 하나의 애플리케이션으로 사용자에게 보여주고 접속시키려면 목적지가 되는 엔드 포인트를 하나로 통합할 필요가 있다. 로드밸런서의 주소가 그 역할을 한다.</p>
<p>컨테이너 확장시 엔드포인트가 되는 서비스에 컨테이너의 자동 등록과 삭제 컨테이너 장애시 분리, 롤링 업데이트 시 필요한 사전 분리 작업을 자동으로 진행한다. (엔드포인트의 관리를 쿠버네티스에게 맡긴다.)</p>
<p>컨테이너를 사용해 시스템을 구축시 마이크로서비스 아키텍처를 많이 선택하게 된다, 이때 각각의 마이크로서비스는 서로를 참조하는데 이때 서비스 디스커버리 기능이 매우 유용하다. 
이를 통해 각각의 마이크로 서비스가 정의된 복수의 매니페스트를 이용하여 시스템 전체를 쉽게 연계 할 수 있다.</p>
<h4 id="■-7-데이터-관리">■ 7. 데이터 관리</h4>
<p>쿠버네티스는 백엔드 데이터 스토어로 etcd를 채용하고 있다.
etcd는 클러스트를 구성하여 이중화가 가능하고 컨테이너나 서비스의 매니페스트 파일도 이중화 구조로 저장한다.
컨테이너가 사용하는 설정 파일이나 인증 정보 등의 데이터를 저장하는 구조도 가지고 있어 컨테이너 공통 설정이나 애플리케이션에서 사용되는 데이터베이스 인증 정보 등을 안전하고 이중화된 상태로 쿠버네티스에서 집중적으로 관리할 수 있다.</p>
<hr>
<h4 id="■-쿠버네티스를-지원하는-미들웨어">■ 쿠버네티스를 지원하는 미들웨어</h4>
<ul>
<li>Ansible : 쿠버네티스에 컨테이너 배포</li>
<li>Apache Ignite : 쿠버네티스 서비스 디스커버리를 사용한 클러스터 생성과 스케일링</li>
<li>Fluentd : 쿠버네티스에 컨테이너 로그 전송</li>
<li>GitLab : CI/CD를 구현하기 위한 일련의 다양한 도구와 쿠버네티스의 통합</li>
<li>Jenkins : 잡(Job) 실행 시 잡 실행자용 컨테이너를 쿠버네티스에 배포</li>
<li>OpenStack : 클라우드 사업자와 연계한 쿠버네티스 구축</li>
<li>Prometheus : 쿠버네티스 모니터링</li>
<li>Spark : 잡을 쿠버네티스에서 네이티브로 실행한다.(YARN을 대체한다.)</li>
<li>Spinnaker : 쿠버네티스에 컨테이너 배포</li>
<li>Kubeflow : 쿠버네티스에 ML 플랫폼 배포</li>
<li>Rook : 쿠버네티스에 분산 파일 시스템 배포</li>
<li>V itess : 쿠버네티스에 MySQL 클러스터 배포
그 외 다수</li>
</ul>
<hr>
<p>출처 : <a href="http://www.yes24.com/Product/Goods/102847901?pid=123487&amp;cosemkid=go16286626147970940&amp;gclid=Cj0KCQiA54KfBhCKARIsAJzSrdrGVlOlchyBPK-L_JxDrvcUHh2GEzpz7RSG-UZygNVCAcBIsZz-hAgaAve6EALw_wcB">http://www.yes24.com/Product/Goods/102847901?pid=123487&amp;cosemkid=go16286626147970940&amp;gclid=Cj0KCQiA54KfBhCKARIsAJzSrdrGVlOlchyBPK-L_JxDrvcUHh2GEzpz7RSG-UZygNVCAcBIsZz-hAgaAve6EALw_wcB</a></p>
<p>해당 자료는 Kubernetes 완벽 가이드 도서의 내용을 바탕으로 정리되었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TROUBLESHOOTING] 서버리스 사진첩]]></title>
            <link>https://velog.io/@son_doobu96/TROUBLESHOOTING-%EC%84%9C%EB%B2%84%EB%A6%AC%EC%8A%A4-%EC%82%AC%EC%A7%84%EC%B2%A9</link>
            <guid>https://velog.io/@son_doobu96/TROUBLESHOOTING-%EC%84%9C%EB%B2%84%EB%A6%AC%EC%8A%A4-%EC%82%AC%EC%A7%84%EC%B2%A9</guid>
            <pubDate>Fri, 03 Feb 2023 15:00:16 GMT</pubDate>
            <description><![CDATA[<h3 id="과제의-목표">과제의 목표</h3>
<p>서버리스 사진첩 서비스는, 여느 클라우드 사진 저장 서비스들처럼 단순히 사진을 업로드하는 것 외에도, 인증 기능과 썸네일 생성 기능을 제공합니다.</p>
<h3 id="최소-요구-조건">최소 요구 조건</h3>
<blockquote>
<ul>
<li>이미지가 업로드되면, 원본과 별도로 썸네일을 생성하고, 이를 별도의 버킷에 저장해야 합니다.</li>
</ul>
</blockquote>
<ul>
<li>썸네일 이미지는 가로 200px의 크기를 가집니다.</li>
<li>썸네일을 저장할 별도의 버킷은 람다 함수의 환경 설정으로 구성되어야 합니다.</li>
<li>썸네일 생성이 완료되면, 메일로 해당 썸네일 URL과 함께 전송이 되어야 합니다.</li>
<li>Amazon SNS를 활용합니다.</li>
</ul>
<h4 id="시작-세팅">시작 세팅</h4>
<p>sam init을 통해 Quick Start Template으로부터 Standalone function을 선택했다.
아래는 Standalone function에 대한 정의다.</p>
<blockquote>
<p>독립 실행형 함수는 특정 작업을 수행하고 클래스나 객체의 일부가 되지 않고 독립적으로 실행될 수 있는 독립적인 코드 조각입니다. 다양한 프로그래밍 언어로 정의할 수 있으며 입력 매개변수를 수락하고 출력 값을 반환할 수 있습니다. 독립 실행형 기능은 코드를 구성하고 재사용하는 방법을 제공하여 유지 관리 및 디버그를 더 쉽게 만듭니다.</p>
</blockquote>
<hr>
<h3 id="처음-만나게-된-문제">처음 만나게 된 문제</h3>
<p>시작 세팅을 완료한 후 Lambda 함수에 대해 간단한 정의 후 빌드 및 배포를 진행했다.</p>
<pre><code>시작 코드
exports.helloFromLambdaHandler = async (event, context) =&gt; {
    console.log(event)

    console.log(context)

    return &#39;Hello from Lambda!&#39;;
}</code></pre><p>몇 몇 권한을 추가 한 후 이미지를 업로드 할 S3 버킷을 생성해 트리거로 연결해주었고 테스트까지는 어렵지 않게 성공할 수 있었다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/43543e2c-a641-469a-b514-2c5d2aa9b669/image.png" alt=""></p>
<p>하지만 CloudWatch에 s3 업로드 후 Lambda 실행에 대한 로그 기록이 쌓이지 않는 것을 확인했고 
Lambda 함수가 실행되면 로그 기록이 생성되고 추가 된다는 것을 확인 한 후 문제를 두 가지로 압축할 수 있었다.</p>
<blockquote>
<ol>
<li>Lambda 함수 자체에 문제가 있다.</li>
<li>S3 버킷과 Lambda 사이의 문제다.</li>
</ol>
</blockquote>
<p>해당 문제를 해결하기 위해 꽤 오랜시간을 썼는데... 일단 테스트는 문제 없이 진행되었기 때문에 1번은 아니라고 생각하고 2번을 전제로 문제를 해결하기 위해 노력했다.</p>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/10b287c9-6138-4607-a364-970d7958e4c0/image.png" alt=""></p>
<p>문제는 Lambda를 뒤져보면서 아무렇지 않게 지나친 그 곳에 있었다.
문제를 해결하지 못해 엔지니어님께 문의 한 결과 Lambda의 실행권한에 대한 문제일 것 같다는 이야기를 전달받았고 Lambda의 실행권한에서 S3에 접근할 수 있는 권한을 추가한 후 문제를 해결할 수 있었다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/6243513d-55ed-4483-a1f1-d83a5a06d610/image.png" alt=""></p>
<hr>
<h3 id="두-번째-만나게-된-문제">두 번째 만나게 된 문제</h3>
<p>두 번째 문제는 S3 버킷에 업로드한 이미지를 리사이징하여 새로운 버킷에 자동으로 업로드하는 과정에서 일어났다.</p>
<p>실습의 진행을 위해 간단한 Snippet code가 주어졌는데.</p>
<pre><code>// 원본 버킷으로부터 파일 읽기
const s3Object = await s3.getObject({
  Bucket: 원본_버킷_이름,
  Key: 업로드한_파일명
}).promise()

// 이미지 리사이즈, sharp 라이브러리가 필요합니다.
const data = await sharp(s3Object.Body)
    .resize(200)
    .jpeg({ mozjpeg: true })
    .toBuffer()

// 대상 버킷으로 파일 쓰기
const result = await s3.putObject({
  Bucket: 대상_버킷_이름, 
  Key: 업로드한_파일명과_동일,
  ContentType: &#39;image/jpeg&#39;,
  Body: data,
  ACL: &#39;public-read&#39;
}).promise()</code></pre><p>해당 코드와 <a href="https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/with-s3-example.html">S3 트리거를 활용해 Lambda 함수를 호출하는 AWS 공식문서</a>를 활용해 기존 파일을 아래와 같이 수정했다.</p>
<pre><code>const aws = require(&#39;aws-sdk&#39;);
const s3 = new aws.S3({ apiVersion: &#39;2006-03-01&#39; });

exports.helloFromLambdaHandler = async (event, context) =&gt; {
    console.log(event)

    console.log(context)

    const bucket = event.Records[0].s3.bucket.name;
    const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, &#39; &#39;));


    // 원본 버킷으로부터 파일 읽기
    const s3Object = await s3.getObject({
        Bucket: bucket,
        Key: key
    }).promise()

    // 이미지 리사이즈, sharp 라이브러리가 필요합니다.
    const data = await sharp(s3Object.Body)
      .resize(200)
      .jpeg({ mozjpeg: true })
      .toBuffer()

    // 대상 버킷으로 파일 쓰기
    const result = await s3.putObject({
        Bucket: &quot;리사이징 버킷 이름&quot;, 
        Key: key,
        ContentType: &#39;image/jpeg&#39;,
        Body: data,
        ACL: &#39;public-read&#39;
    }).promise()

    return result;</code></pre><p>배포 후 콘솔에서 테스트를 진행했을때 처음 마주하게 된 문제는 key값을 정의 할 수 없다는 것이었다. 
위의 코드에서 소스를 불러올 버킷과 이미지의 이름은 정의하였지만 그 불러온 이미지를 리사이징하여 보내줄 버킷과 key가 설정되지 않아서 발생한 문제였다. 
코드를 아래와 같이 수정함으로써 해당 문제는 해결할 수 있었다.
<a href="https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/with-s3-tutorial.html#with-s3-tutorial-create-policy">트리거를 사용해 썸네일을 생성하는(리사이징 할 수 있는) AWS 공식문서</a></p>
<pre><code>const aws = require(&#39;aws-sdk&#39;);
const s3 = new aws.S3({ apiVersion: &#39;2006-03-01&#39; });

exports.helloFromLambdaHandler = async (event, context) =&gt; {
    console.log(event)

    console.log(context)

    const bucket = event.Records[0].s3.bucket.name;
    const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, &#39; &#39;));
    const rzbucket = bucket + &quot;-resize&quot;
    const rzkey = key + &quot;-resize&quot; 


    // 원본 버킷으로부터 파일 읽기
    const s3Object = await s3.getObject({
        Bucket: bucket,
        Key: key
    }).promise()

    // 이미지 리사이즈, sharp 라이브러리가 필요합니다.
    const data = await sharp(s3Object.Body)
      .resize(200)
      .jpeg({ mozjpeg: true })
      .toBuffer()

    // 대상 버킷으로 파일 쓰기
    const result = await s3.putObject({
        Bucket: rzbucket, 
        Key: rzkey,
        ContentType: &#39;image/jpeg&#39;,
        Body: data,
        ACL: &#39;public-read&#39;
    }).promise()

  return result;
}</code></pre><hr>
<h3 id="세-번째-만나게-된-문제">세 번째 만나게 된 문제</h3>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/2e3cef83-a906-44b8-ab72-50dfc5d2ca0e/image.png" alt=""></p>
<p>코드를 수정한 후 다시 빌드하여 배포를 진행했다. 세번째 문제가 날 기다리고 있었다 ㅎㅎ
Sharp 모듈을 찾을 수 없다는 내용의 문제였다.
사실 이 문제는 큰 문제는 아니었다. 오류 내용? <strong>알고 있고</strong> 해결방법? <strong>역시 알았다.</strong></p>
<h4 id="처음-시도한-방법">처음 시도한 방법</h4>
<p>페어와 함께 작업을 진행하면서 처음 이 문제의 해결방법에 대해 <a href="https://stackoverflow.com/questions/62039078/error-darwin-x64-binaries-cannot-be-used-on-the-linux-x64-platform-aws-lamb">Stackoverflow 형님들</a>의 이 해결 사례를 보면서 문제를 해결하려 했다.</p>
<blockquote>
<p>우리의 첫 생각은 이거였다. </p>
</blockquote>
<ul>
<li>로컬에서 함수를 작동시킬게 아니라! </li>
<li>Lambda에서 작동시킬 거니까!!</li>
<li>Lambda는 linux 환경에서 작동하니까!!</li>
</ul>
<p>이렇게 해줘야 하지 않을까? 라고 생각하고 문제에 접근했다.</p>
<pre><code>npm install --arch=x64 --platform=linux sharp

그리고 소스코드에 
const sharp = require(&#39;sharp&#39;);를 추가로 선언해줬다.</code></pre><p>이후는 혼돈의 카오스였다. 내 페어는 문제 없이 작동했지만 난 아니었다. 계속해서 Cannot find module 에러를 생산해냈다... 정말 머리가 복잡해져서 서로 코드도 비교해가면서 문제를 찾아보려 했지만 찾지를 못했다.</p>
<h4 id="해결한-방법">해결한 방법?</h4>
<p>일단 먼저 해결한 방법에 대해 이야기 하자면 <code>$ npm i sharp</code>를 통해 의존성을 설치 해주니 문제가 해결됐다...</p>
<h4 id="문제-원인에-대한-고민">문제 원인에 대한 고민</h4>
<p>처음에 Lambda에서 작동시킬 거니까 무조건 linux 환경에 맞추어서 구성을 해야겠다는 생각을 했던 게 지금에 와서는 문제의 원인이 되지 않았나 싶다. 
나의 페어는 mac을 사용하고 나는 window를 사용하는데 사용자의 OS 환경에 미치는 영향을 고려해가면서 문제를 바라볼 필요가 있다는 생각이 들었다.  </p>
<hr>
<h3 id="네-번째-만나게-된-문제">네 번째 만나게 된 문제</h3>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/7909ebc1-2aed-4eec-aa34-ef92912813d3/image.png" alt=""></p>
<p>다음 문제는 AccessDenied 였다. 이 문제는 공식문서에 그 답이 완전히 나와있었다.
<a href="https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/with-s3-tutorial.html#with-s3-tutorial-create-policy">Amazon S3 트리거를 사용하여 썸네일 생성 AWS 공식문서</a></p>
<p>여기에 위에 내가 작성한 코드를 실행시키기 위한 IAM 정책을 생성할 필요가 있다고 이미 적혀 있었는데.... 제대로 안보고 넘어갔다..</p>
<p>아무튼 공식 문서를 통해 Lambda 실행 역할에 만든 정책을 추가한 후 문제를 해결할 수 있었다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/b3da29f4-6450-44b6-88ee-db69e75e166a/image.png" alt=""></p>
<hr>
<h3 id="다섯-번째-만나게-된-문제">다섯 번째 만나게 된 문제</h3>
<p>여기서부턴 진짜 힘들었다. 오늘은 금요일 하기도 너무 싫은데 하필 금요일이다. 와우
거기다 Console의 테스트는 계속해서 통과하는데 로그가 쌓이지 않는다......
그리고 S3에 이미지를 업로드해도 리사이징 버킷에 이미지가 생성되지를 않았다.
하지만 테스트는 계속해서 통과하는 아이러니한 상황이었다.</p>
<p>뭐가 문제일지 정말 여러 방면으로 찾아봤지만 에러메세지 조차 남겨주질 않으니 찾기가 너무 힘들었다 ㅠㅠ</p>
<h4 id="처음-시도한-방법-1">처음 시도한 방법</h4>
<p>함수의 문제인지를 확인하기 위해 디버깅을 해봤다. 내가 디버깅에 대한 지식은 전무하기에...
페어의 도움을 받아 디버깅을 진행할 수 있었다.</p>
<pre><code>(async () =&gt; {
    const a = await require (&#39;./handlers/hello-from-lambda&#39;).helloFromLambdaHandler()
    console.log(a)
})();</code></pre><p>디버깅을 위한 app.js 파일을 생성 후 위의 코드를 넣었고 소스코드를 아래와 같이 수정했다.</p>
<pre><code>const aws = require(&#39;aws-sdk&#39;);
const s3 = new aws.S3({ apiVersion: &#39;2006-03-01&#39; });
const sharp = require(&#39;sharp&#39;);

exports.helloFromLambdaHandler = async (/*event, context*/) =&gt; {
    console.log(JSON.stringify(event))

    console.log(JSON.stringify(context))

    const bucket = &lt;&quot;소스를 불러올 버킷 이름&quot;&gt;
    const key = &lt;&quot;소스를 불러올 이미지 ID&quot;&gt;
    const rzbucket = bucket + &quot;-resize&quot;
    const rzkey = &quot;resize-&quot; + key


    // 원본 버킷으로부터 파일 읽기
    const s3Object = await s3.getObject({
        Bucket: bucket,
        Key: key
    }).promise()

    // 이미지 리사이즈, sharp 라이브러리가 필요합니다.
    const data = await sharp(s3Object.Body)
      .resize(200)
      .jpeg({ mozjpeg: true })
      .toBuffer()

    // 대상 버킷으로 파일 쓰기
    const result = await s3.putObject({
        Bucket: rzbucket, 
        Key: rzkey,
        ContentType: &#39;image/jpeg&#39;,
        Body: data,
        ACL: &#39;public-read&#39;
    }).promise()
}

</code></pre><p>이후 로컬에서 접근해야 하기에 S3 버킷을 퍼블릭으로 전환 및 정책 생성 후 <code>$ node app.js</code>로 디버깅을 진행했다.</p>
<p>그리고 이미지 리사이징에 성공했다... ㅠㅠ 일단 다행히도 함수는 문제가 없었다.</p>
<h4 id="어떻게-해결했나">어떻게 해결했나?</h4>
<p>디버깅까지 진행 후에도 문제는 여전했다. S3에 이미지를 업로드해도 로컬에서 처럼 리사이징이 되질 않았다.
이제는 진짜 방법을 모르겠어서 멍해져갈때쯤 엔지니어님이 갑자기 방문해주셨다. 디버깅을 통해 로컬에서 함수에는 문제가 없는 상황을 말해드렸고 배포를 통해 콘솔에서의 테스트에서도 문제가 없는 상황을 말씀드렸다.</p>
<p>엔지니어님께서 여러 화면을 슥 슥 보시더니 .jpg로 되어 있는 확장자를 가르켜 주셨다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/320732a8-d925-48ff-ae44-020c1ffdd631/image.png" alt=""></p>
<p>내가 구성한 S3 트리거의 구성 정보이다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/7a03b280-1c3f-4c3a-9ddb-d99678ac5e99/image.png" alt=""></p>
<p>.jpeg 확장자를 올려야 이미지를 .jpg로 계속 올리고 있었던 것이다....
그러다보니 트리거에서 해당 조건에 부합하지 않으니 Lambda 함수를 실행시키지 않았던 것이다.</p>
<p>그렇게 3시간은 &#39;e&#39; 하나에 날아갔다. 영상 편집자로 2년을 일하면서 수많은 jpg와 jpeg를 봤는데 그 차이 한번을 검색해보지 않은 스노우볼이 오늘의 3시간을 날렸다...</p>
<h3 id="마지막-문제">마지막 문제</h3>
<p>마지막은 리사이징한 이미지를 이메일을 통해 보내주는 작업을 구성하는 일이었다.
AWS SNS를 통해 해당 구성에 대한 세팅을 마쳤다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/9b1bbc14-876f-4ac2-b9e7-42606de9aad9/image.png" alt=""></p>
<p>이후 메일이 전송되어 확인을 했는데
<img src="https://velog.velcdn.com/images/son_doobu96/post/b232b9ae-991e-4e79-9c26-596eecc481fe/image.png" alt=""></p>
<p>이렇게 복잡한 문자로 오는 것을 확인할 수 있었다. 이 문제를 콘솔에서 해결 할 수 있는 방법을 찾다가 도저히 찾지 못해서 소스코드를 추가해 해당 문제를 해결할 수 있었다.</p>
<pre><code>const region = &quot;ap-northeast-2&quot;

    const sns = new aws.SNS(region);
    const massage = await sns.publish({
        Message: &quot;URL : &quot; + `https://${rzbucket}.s3.${region}.amazonaws.com/${rzkey}`,
        Subject: &quot;썸네일 도착&quot;,
        TopicArn: &lt;&quot;TopicArn&quot;&gt;
    }).promise()
}</code></pre><h4 id="아직-해결되지-않은-문제">아직 해결되지 않은 문제?</h4>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/4e50087b-7f90-4f33-a597-1578f7981ed4/image.png" alt=""></p>
<p>수정한 소스코드를 배포한 함수로 메일이 하나 오고 이전 소스코드로도 메일이 오는 현상이 있었다.
처음에는 조금 당황했으나 이전 소스코드의 아래의 지워진 링크를 클릭하여 구독을 취소하고
맨 처음에 온 메일로 다시 구독을 수락하니 해당 문제를 해결할 수 있었다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/74882561-cc79-4ceb-aeb3-e8a1a3939b55/image.png" alt=""></p>
<h4 id="진짜-마지막-문제">진짜 마지막 문제!</h4>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/4e6b1c55-e8a8-4e26-83e4-0e12893bcece/image.png" alt=""></p>
<p>CloudWatch로 들어오는 로그가 객체 형식으로 들어오다 보니 너무 보기가 불편했다.
마지막을 소스코드를 수정해 JSON 형식으로 로그가 들어올 수 있게 수정하였다.</p>
<p><strong>최종 소스코드</strong></p>
<pre><code>const aws = require(&#39;aws-sdk&#39;);
const s3 = new aws.S3({ apiVersion: &#39;2006-03-01&#39; });
const sharp = require(&#39;sharp&#39;);
const region = &quot;ap-northeast-2&quot;

exports.helloFromLambdaHandler = async (event, context) =&gt; {
    console.log(JSON.stringify(event))

    console.log(JSON.stringify(context))

    const bucket = event.Records[0].s3.bucket.name;
    const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, &#39; &#39;));
    const rzbucket = bucket + &quot;-resize&quot;
    const rzkey = &quot;resize-&quot; + key


    // 원본 버킷으로부터 파일 읽기
    const s3Object = await s3.getObject({
        Bucket: bucket,
        Key: key
    }).promise()

    // 이미지 리사이즈, sharp 라이브러리가 필요합니다. 
    const data = await sharp(s3Object.Body)
      .resize(200)
      .jpeg({ mozjpeg: true })
      .toBuffer()

    // 대상 버킷으로 파일 쓰기
    const result = await s3.putObject({
        Bucket: rzbucket, 
        Key: rzkey,
        ContentType: &#39;image/jpeg&#39;,
        Body: data,
        ACL: &#39;public-read&#39;
    }).promise()

    const sns = new aws.SNS(region);
    const massage = await sns.publish({
        Message: &quot;URL : &quot; + `https://${rzbucket}.s3.${region}.amazonaws.com/${rzkey}`,
        Subject: &quot;썸네일 도착&quot;,
        TopicArn: &lt;&quot;TopicArn&quot;&gt;
    }).promise()
}</code></pre><p>이제 로그도 이쁘게 들어온다!
<img src="https://velog.velcdn.com/images/son_doobu96/post/87a559bc-9b11-4756-8195-e816fb8623ff/image.png" alt=""></p>
<hr>
<h3 id="이번-트러블-슈팅을-하면서-느낀-점">이번 트러블 슈팅을 하면서 느낀 점</h3>
<p>디버깅의 중요성을 깨닫게 되었다. 소스코드를 배포하고 콘솔에서 테스트 하는 작업이 여간 불편한 게 아니었다.. 그리고 문제의 원인의 가짓수를 지워나가는 데에도 정말 도움이 안됐던것 같다.
디버깅을 앞으로 잘 할 수 있는 능력을 키워야 겠다는 생각이 강하게 들었다.</p>
<p>그리고 조금 더 꼼꼼하게 공식문서를 보는 능력을 키워야겠다는 생각이 들었다.</p>
<p>마지막으로 ChatGTP를 잘 활용하면 좋을것 같았다. AWS SNS 서비스를 nodejs로 어떻게 표현해야 할까라고 질문을 했었는데
<img src="https://velog.velcdn.com/images/son_doobu96/post/2f5fd0e0-f0b9-40c9-97c2-19a3d7e8c16c/image.png" alt="">위의 사진처럼 정말 자세하게 알려주었다. AI에 잘 질문하는 게 정말 앞으로는 중요해 지겠다는 생각이 들었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS Lambda와 API Gateway]]></title>
            <link>https://velog.io/@son_doobu96/AWS-Lambda%EC%99%80-API-Gateway</link>
            <guid>https://velog.io/@son_doobu96/AWS-Lambda%EC%99%80-API-Gateway</guid>
            <pubDate>Thu, 02 Feb 2023 14:00:07 GMT</pubDate>
            <description><![CDATA[<h3 id="◎-aws-lambda">◎ AWS Lambda</h3>
<h4 id="■-aws-lambda란">■ AWS Lambda란?</h4>
<p>Lambda는 AWS가 제공하는 서버리스 FaaS 솔루션으로, 함수의 인스턴스를 실행하여 이벤트를 처리한다.</p>
<h4 id="■-faas란">■ FaaS란?</h4>
<blockquote>
<ul>
<li>FaaS는 자체 서버 시스템이나 수명이 긴 서버 애플리케이션을 관리하지 않고 백엔드 코드를 실행하는 것</li>
</ul>
</blockquote>
<ul>
<li>FaaS는 런타임(node.js, Java 등)에 대한 사전 준비가 필요하지 않음</li>
<li>FaaS 기능에는 특히 상태 및 실행 기간과 관련하여 상당한 아키텍처 제한이 있음</li>
<li>수평적 확장은 완전 자동이며 탄력적이며 공급자가 관리함</li>
<li>FaaS의 기능은 일반적으로 공급자가 정의한 이벤트 유형에 의해 트리거됨<ul>
<li>HTTP 요청에 대한 응답으로 트리거되도록 만들 수 있음</li>
</ul>
</li>
</ul>
<h4 id="■-lambda의-특징">■ Lambda의 특징</h4>
<ul>
<li>서버를 프로비저닝하거나 관리할 필요 없이 작성한 코드를 백엔드 서비스로서 배포할 수 있게 해준다.</li>
<li>Lambda 함수를 실행하려면, 애플리케이션 또는 백엔드 서비스의 코드를 작성한 뒤 이벤트 트리거만 정의하면 됩니다.<ul>
<li>이벤트 트리거의 대표적인 예: Amazon S3 업로드나 DynamoDB 업데이터, Kinesis 스트리밍, API 게이트웨이 요청</li>
<li>이벤트 주도 아키텍처(Event Driven Architecture)를 구성할 수 있다.</li>
</ul>
</li>
<li>높은 가용성을 제공한다.</li>
</ul>
<h4 id="■-aws-lambda의-단점">■ AWS Lambda의 단점</h4>
<ul>
<li>24시간 돌아가지 않는다!!<ul>
<li>Lambda는 요청이 오면 함수를 실행하는 형태로 이루어져있다. 때문에 요청이 들어오면 Lambda함수를 실행하는데 시간을 사용하기 때문에 응답의 속도에 차이가 생긴다.
이러한 차이는 요청이 적을때는 크게 의미가 없지만 <strong>요청이 많을때 큰 차이로 나타난다.</strong></li>
</ul>
</li>
<li>특정 함수에 대해 대기 기능이 존재하지만 속도 문제는 여전하다.</li>
<li>벤더에 종속될 여지가 있다 : 
백엔드 전체를 Lambda에 배포한 경우 한 서버리스 환경에서 다른 서버리스 환경으로 마이그레이션 하는 작업이 쉽지 않다.  </li>
</ul>
<blockquote>
<h4 id="●-cold-start">● Cold Start</h4>
</blockquote>
<ul>
<li>앱이 처음부터 시작하는 것</li>
<li>기기가 부팅되거나 시스템에서 앱이 종료되고 나서 처음으로 시작</li>
<li>시스템과 앱이 다른 시작 상태보다 더 많은 작업을 실행해야 하므로 시작 시간을 최소화하는 데 큰 어려움</li>
</ul>
<blockquote>
<h4 id="●-warm-start">● Warm Start</h4>
</blockquote>
<ul>
<li>콜드 스타트 시 발생하는 작업의 일부 하위 집합을 포함</li>
<li>애플리케이션을 실행하고 난 후, 장시간 사용되지 않아 
프로세스를 안드로이드가 후순위로 밀어낸 후에 
(그러나 아직 프로세스가 kill 되지 않은 순간) 
다시 애플리케이션을 실행할 때 warm start로 인식</li>
</ul>
<h4 id="■-aws-lambda는-어떻게-작동-되나">■ AWS Lambda는 어떻게 작동 되나?</h4>
<p>AWS Lambda에서 실행하는 코드는 &#39;Lambda 함수&#39;로 업로드 된다. 각 함수에는 이름과 설명, 진입점, 리소스 요구 사항 등 연관된 구성 정보가 포함되어 있다.</p>
<blockquote>
<p>※ 유의점</p>
</blockquote>
<ul>
<li>Lambda의 함수는 Stateless여야 한다.<ul>
<li>무상태성을 유지해야만이 요청이 많아 졌을때 Lambda에서 필요한 만큼 함수 사본을 빠르게 시작하여 자동 스케일링이 가능하다.</li>
</ul>
</li>
<li>Lambda의 프로그래밍 모델은 상태 비저장이지만 코드 또는 S3, DynamoDB 등 다른 웹 서비스를 호출하여 상태 저장 데이터에 엑세스 할 수 있다.</li>
</ul>
<h4 id="■-aws-lambda의-체크포인트">■ AWS Lambda의 체크포인트?</h4>
<p>▶ 트리거</p>
<ul>
<li>트리거는 Lambda 함수를 호출하는 리소스 또는 구성이다. 트리거에는 이벤트 소스를 제공하는 AWS 서비스가 포함될 수 있다.</li>
</ul>
<p>▶ 함수 로깅 하는 방법</p>
<ul>
<li>AWS CloudWatch를 통해 확인 할 수 있다. </li>
</ul>
<hr>
<h3 id="◎-aws-lambda-1">◎ AWS Lambda</h3>
<h4 id="■-api-gateway는-무엇일까">■ API Gateway는 무엇일까?</h4>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/a7c88bbc-c6e0-4036-b771-2d28d51c32f5/image.png" alt=""></p>
<ul>
<li>경로와 엔드포인트로 구성되어 정의된 HTTP 서버를 말한다.</li>
<li>각 경로는 해당 경로를 처리하는 리소스와 연결되며, 서버리스 아키텍처에서 이러한 핸들러는 FaaS 기능을 종종 사용한다. </li>
<li><strong>즉, API Gateway는 각 API 요청의 관문의 역할이다!</strong></li>
</ul>
<blockquote>
<p>이러한 이유로 모놀리틱 아키텍처에서 마이클서비스 아키텍처로의 전환이 가속화되고 있는 요즘
API Gateway의 중요성이 대두되고 있다. </p>
</blockquote>
<ul>
<li>각 서비스들을 연결하고 요청에 대한 단일 진입점으로써 아키텍처의 복잡도를 줄여주며 전달의 정확성과 그 결과에 대한 로깅까지도 가능하다!</li>
<li>API Gateway를 통해 마이크로서비스들을 연결하고 마이크로서비스의 단점을 보완할 수 있다.
<a href="https://velog.io/@son_doobu96/%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4%EC%99%80-%EC%84%9C%EB%B2%84%EB%A6%AC%EC%8A%A4">마이크로서비스에 대해서?</a></li>
</ul>
<hr>
<h4 id="■-api-gateway는-어떤-기능들을-할까">■ API Gateway는 어떤 기능들을 할까?</h4>
<ul>
<li><p>IP 화이트리스팅
IP 주소 범위를 제한하거나 화이트리스트에 추가한다. 액세스는 일반적으로 액세스 정책 관리자를 통해 구성된다. 액세스 정책 관리자는 Gateway가 처리하는 방법 및 제한 사항을 사용자에게 표시하는 방법과 함께 제한 사항을 구현할 수 있다. AWS의 API Gateway를 예로 들면, 자원 정책 기능은 사용자가 리소스에 대한 JSON 정책 문서를 할당하고 IAM 그룹, 사용자 또는 역할이 그들에 액세스 할 수 있는지 확인할 수 있다.</p>
</li>
<li><p>전송 메세지 암호화
API Gateway를 통과하는 서비스 간의 메시지를 보호하는 역할을 하며 전송 중인 모든 정보에 공통 표준이 적용되도록 한다. Amazon API Gateway의 경우 적절한 최소 보안 표준을 설정하는 암호화되지 않은 워크로드를 지원하지 않는다.</p>
</li>
<li><p>속도 제한
스로틀링 메커니즘을 사용하여 대기열 및 경우에 따라 요청 우선순위를 관리하게 된다. 속도 제한은 요청 우선순위에 따라 API에 대한 요청 수를 제한하는 기능이다.
정책 수준으로 구성이 정의되며 모든 요청 및 프로토콜에 적용된다. 속도 제한은 DDOS 공격의 영향을 잠재적으로 제한하기 때문에 보안 기능으로도 간주된다.</p>
</li>
<li><p>API 구성 및 라우팅
API Gateway의 작업은 서비스에서 서비스로 요청을 라우팅하는 것 뿐만 아니라 성능과 효율성도 고려한다. API 구성 또는 집계는 서로 다른 서비스로의 쿼리 결과를 단일 응답으로 결합하는 프로세스다. 이것은 대규모 데이터 세트에 이상적이지는 않지만 요청자에서 서비스까지 이상적인 경로를 제공하는 매우 간단하고 효율적인 방법이다.</p>
</li>
<li><p>캐싱
TTL이 만료될 때까지 요청이 서비스를 모두 적중할 필요가 없도록 API Gateway 수준에서 캐싱할 수 있다. 캐싱은 대부분의 API Gateway에 공통적인 기본 기능이지만 구성의 세분성은 제품 제공 및 구현에 따라 다르다.</p>
</li>
<li><p>로깅 추적
즉시 사용 가능한 로깅 기능을 제공하여 API Gateway를 통과하는 모든 API 트래픽의 추적을 가능하게 하고 Gateway에서 들어오고 나가는 요청, URL, 관점에서 수행하는 방식과 함께 요청되는 매개변수에 대한 지표를 표시한다.
Gateway의 다른 모니터링 기능도 중요하다. 예를 들어 AWS Cloudwatch 를 사용 하여 Rest API 실행과 같은 API Gateway의 지표를 표시한다. 이는 서로 다른 API 간에 비교 지표를 제공할 수 있기 때문에 많은 가치가 있다.</p>
</li>
<li><p>API 버전 관리
다양한 버전의 API를 처리하는 것은 API Gateway의 중요한 기능이다.
API 버전 관리는 소비자와 함께 기존 API에 대한 코드 해독 변경을 방지하는 데 도움이 되는 중요한 전략으로 개발자가 사용자에게 미치는 영향을 최소화하면서 여전히 사용 중인 API의 이전 버전을 천천히 감가상각할 수 있다. 이는 내부 API 사용자에게 중요하지만 실제 개발자와 같이 도메인 소유자에게 직접 액세스할 수 없는 API의 제3자 소비자와 작업할 때는 훨씬 더 중요하다.</p>
</li>
</ul>
<hr>
<h4 id="■-aws-api-gateway는-어떤-장점이-있을까">■ AWS API Gateway는 어떤 장점이 있을까?</h4>
<p>Amazon API Gateway는 트래픽 관리, 권한 부여 및 액세스 제어, 모니터링, API 버전 관리를 비롯해 최대 수십만 건의 동시 API 호출을 수락 및 처리하는 데 관련된 모든 작업을 처리합니다.</p>
<ul>
<li>트래픽 측정<ul>
<li>API 키 별로 일련의 플랜을 정의하고, 제한과 할당량 한도를 구성할 수 있다.</li>
<li>API에 대한 트래픽을 측정하기 때문에 각 API 키에 대한 사용률 데이터를 추출할 수 있다.</li>
</ul>
</li>
</ul>
<ul>
<li>보안<ul>
<li>AWS IAM과 Amazon Cognito 같은 AWS관리 및 보안 도구를 사용해서 API 에 대한 액세스 권한을 부여할 수 있다.</li>
<li>AWS에서 자체 API를 확인할 때 사용한 것과 동일한 방법론을 사용하여 서명된 API 호출을 확인한다.</li>
<li>AWS Lambda 함수로 작성된 사용자 정의 권한 부여자를 사용하여, 수신되는 전달자 토큰을 확인하도록 지원하여 백엔드 코드의 인증 문제를 해결한다.</li>
</ul>
</li>
</ul>
<ul>
<li><p>복원력</p>
<ul>
<li>조절을 통해 트래픽을 관리하므로 트래픽 스파이크가 발생해도 백엔드에서 처리할 수 있다.</li>
<li>매번 백엔드를 호출하지 않도록 API 호출 결과를 캐싱하여 최종 사용자가 경험하는 API 성능 및 지연 시간을 개선할 수 있다.</li>
</ul>
</li>
<li><p>작업모니터링</p>
<ul>
<li>API가 게시되고 사용되면 API Gateway에서 서비스에 대한 호출을 모니터링할 수 있는 지표 대시보드를 제공한다.</li>
<li>대시보드는 Amazon CloudWatch와 통합을 통해 API 호출, 지연 시간 데이터, 오류 발생률 등 백엔드 성능 지표를 제공한다.</li>
<li>API의 각 메서드에 대한 상세 지표를 활성화하고 CloudWatch log에 있는 오류, 액세스 또는 디버그 로그로 수신 할 수 있다.</li>
</ul>
</li>
</ul>
<ul>
<li>수명 주기 관리<ul>
<li>API를 게시한 후에는 이전 버전을 개선하거나 새로운 기능을 추가한 새로운 버전을 구축 및 테스트해야 하는 경우가 많다.<br>API Gateway를 사용하면 여러 API 버전과 각 버전의 여러 스테이지를 동시에 운영할 수 있으므로 새로운 API 버전이 게시된 후에도 기존 애플리케이션에서 이전 버전을 계속 호출할 수 있다.</li>
</ul>
</li>
</ul>
<ul>
<li>개발자를 위한 설계<ul>
<li>API를 신속하게 생성하고 API 응답에 대한 정적 콘텐츠를 할당하여 팀 간 개발 노력을 줄이고 애플리케이션 출시 시간을 단축할 수 있습니다.</li>
<li>개발자의 API를 사용하는 팀은 개발자가 백엔드 프로세스를 구축하는 동안 개발을 시작할 수 있다.</li>
</ul>
</li>
</ul>
<ul>
<li>실시간 양방향 통신<ul>
<li>서버를 실행하거나 관리할 필요 없이 채팅 앱, 스트리밍 대시보드 및 알림 같은 실시간의 양방향 통신 애플리케이션을 구축할 수 있다.</li>
<li>API Gateway를 사용하면 연결된 사용자 간에 영구 연결이 유지되며 이러한 사용자 간에 메시지를 전송할 수 있다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TROUBLESHOOTING] API Gateway로 서버리스 애플리케이션 호출하기]]></title>
            <link>https://velog.io/@son_doobu96/API-Gateway%EB%A1%9C-%EC%84%9C%EB%B2%84%EB%A6%AC%EC%8A%A4-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%ED%98%B8%EC%B6%9C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@son_doobu96/API-Gateway%EB%A1%9C-%EC%84%9C%EB%B2%84%EB%A6%AC%EC%8A%A4-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%ED%98%B8%EC%B6%9C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 02 Feb 2023 13:21:46 GMT</pubDate>
            <description><![CDATA[<h3 id="◎-이번-실습의-달성-목표">◎ 이번 실습의 달성 목표</h3>
<blockquote>
<ul>
<li>아키텍처로 구성</li>
</ul>
</blockquote>
<ul>
<li>API Gateway - Lambda - DynamoDB<ul>
<li>직접 API Gateway로 실행</li>
<li>API Gateway의 인증 기능을 이용해서, HTTP 요청에 특정 API Key를 사용하는 방법 구현</li>
<li>API Key</li>
<li>권한 부여자</li>
<li>CloudWatch Logs를 통해서 API 호출을 모니터링</li>
</ul>
</li>
</ul>
<h4 id="■-step-12-api-gateway와--lambda를-같이-배포">■ Step 1~2. API Gateway와  Lambda를 같이 배포</h4>
<p>본 실습은 SAM 템플릿을 통해 진행됐습니다.
자료 출처 : <a href="https://serverlessland.com/patterns/lambda-dynamodb">https://serverlessland.com/patterns/lambda-dynamodb</a></p>
<h4 id="sam을-활용-전-알아야-할-것">SAM을 활용 전 알아야 할 것?</h4>
<p>아래 두 가지만 잘 기억하시면 SAM 관련 문제는 왠만해서는 해결하실 수 있을겁니다.</p>
<blockquote>
<ol>
<li>aws-cli에서 사용하고 있는 사용자에 적절한 권한이 없다면 많은 오류가 날 것이다.</li>
<li>적절한 권한을 주었음에도 진행이 되지 않을 경우 CloudFormation에서 스택을 삭제 후 다시 진행해 볼것을 추천합니다.</li>
</ol>
</blockquote>
<h4 id="▶-사용한-권한-목록">▶ 사용한 권한 목록</h4>
<p>다른 권한도 들어있으니 참고로만 봐주시면 감사 하겠습니다!
<img src="https://velog.velcdn.com/images/son_doobu96/post/8fc9819f-34b3-4a6f-95a5-63cadeaa50dc/image.png" alt=""></p>
<pre><code>sam deploy --guided</code></pre><p>를 활용해 Lambda의 배포에 성공했다면 트리거를 추가해 API Gateway를 생성 해준다.
API Gateway를 사용하는 기본적인 이유는 Lambda의 백엔드 서비스 함수를 호출하고 그 결과를 집계하기 위해서이다. 
자세한 이유는 <a href="https://velog.io/@son_doobu96/AWS-Lambda%EC%99%80-API-Gateway">이 포스팅</a>을 확인해주시면 감사합니다!</p>
<hr>
<h4 id="■-step-3-api-gateway-제한하기">■ Step 3. API Gateway 제한하기</h4>
<p>API Gateway를 제한하는 이유는 보안, 속도, 용도의 제한등 다양한 이유가 있다.</p>
<blockquote>
<p>이번 실습에서 API Gateway를 제한하는 목표에는 아래 4가지가 있었다.</p>
</blockquote>
<ul>
<li>POST 전용으로만 작동하게 만들기</li>
<li>본문만 저장하도록 만들기</li>
<li>API 키를 이용한 인증 추가하기</li>
<li>권한 부여자를 이용한 인증 부여하기 (optional)</li>
</ul>
<hr>
<h4 id="post-전용으로만-작동하게-만들기">POST 전용으로만 작동하게 만들기</h4>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/0c58f744-07eb-4107-ae63-488756b67082/image.png" alt=""></p>
<p>처음 API 게이트웨이 UI를 확인하면 정돈되지 못한 느낌을 받게 된다. 조금 옛날스런 느낌도 있다.
맨 처음 화면을 보게 되었을때는 빨간색으로 표시된 POST 부분이 ANY로 지정되어 있다. 
<a href="https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/api-gateway-method-settings-method-request.html">AWS API Gateway HTTP Method 설정 공식 문서</a></p>
<p>해당 문서의 HTTP 메서드 설정을 확인하면 ANY는 모든 HTTP 메서드를 지원한다고 되어있다.
우리의 목표인 POST 전용으로만 작동하게 만들기에 부합하지 않으므로 새로운 메서드를 생성해 주었다.</p>
<p>어렵지 않게 메서드를 생성한 후 Postman을 이용해 해당 변경 사항이 잘 적용되었는지 체크하려 했지만 문제가 발생했다.</p>
<blockquote>
<ol>
<li>POST 요청이 안된다.</li>
<li>근데 GET 요청도 문제가 없다.</li>
</ol>
</blockquote>
<p>이건 사실 당연한 문제이다. AWS Console을 통해 진행 하다보니 Local 진행과 다른 점을 눈치채지 못했었는데 POST 메소드를 생성하고 ANY 메소드를 제거한 것이 &quot;소스 코드를 변경한 행위&quot;와 같다는 점을 인식하지 못했었다. </p>
<p>우리가 Local에서 소스코드를 변경했다면 당연히 다시 배포를 해야 그 변경 사항이 적용이 될 것이다. AWS Console에서의 진행도 크게 다르지 않았다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/3c6edf24-adb4-49d5-9e61-123156b1f002/image.png" alt=""></p>
<blockquote>
<p>변경 사항이 있다면 반드시 다시 배포를 통해 변경 사항을 적용해줘야 한다는 것을 머리에 심어뒀다!</p>
</blockquote>
<p>다음은 Postman으로 확인해야 할 엔드포인트이다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/27d5bdae-f2c9-4641-bbea-e0b5487c88b7/image.png" alt=""></p>
<p>그건 AWS Lambda의 트리거에서 쉽게 확인할 수 있다.
하지만 쉬운 확인 보다 중요했던 것이... 이 엔드포인트를 분석하는 것이었다.</p>
<p><strong>API endpoint의 구성은</strong></p>
<pre><code>https://&lt;api-id&gt;.execute-api.&lt;region&gt;.amazonaws.com/&lt;stage_name&gt;/&lt;호출하려는 API 리소스 이름&gt;</code></pre><p>으로 되어 있다. 여기서 확인할 수 있는 점은 Lambda는 함수이기에 <strong>호출 하려는 API 리소스의 이름은 백엔드 Local 서버 에서 구성하던 하나의 &quot;함수&quot;인 것이고</strong>
<strong>스테이지는 그 상위의 디렉토리다!</strong> 라는 것을 알게 되었다.</p>
<p>실제로 필요에 따라 Local에서 엔드포인트를 구성하듯이 스테이지를 구성할 수 있다는 것을 팀원이 아니었다면 아마 끝까지 못 찾았을 수도 있을것 같다...
<strong>하나하나 잘 뜯어보자!</strong>
<img src="https://velog.velcdn.com/images/son_doobu96/post/82a924db-8d9d-4e53-b049-dba040fcbe44/image.png" alt=""></p>
<hr>
<h4 id="본문만-저장하도록-만들기">본문만 저장하도록 만들기</h4>
<p>내가 이해한 내용으로 이 내용은 DB에 body만이 저장되도록 만들라는 것으로 이해했다.
이 부분을 확인하기 위해 DynamoDB에 데이터가 어떻게 들어오는지를 먼저 확인하려 했다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/7dad674a-cffc-44fb-8a7b-6d9d8f71b1b0/image.png" alt="">
항목 탐색 탭에서 해당 내용을 확인해 볼 수 있었다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/aa0ceea0-9957-4c04-b1be-72380aaa2350/image.png" alt="">
잘 들어온다 .. 개꿀?</p>
<hr>
<h4 id="api-키를-이용한-인증-추가하기">API 키를 이용한 인증 추가하기</h4>
<p>이 부분은 공식문서에 매우 자세하게 나와있었다.
<a href="https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/api-gateway-setup-api-key-with-console.html">AWS API Gateway 콘솔에서 API key를 설정하는 방법</a>
공식 문서대로 API Key를 생성한 후
<img src="https://velog.velcdn.com/images/son_doobu96/post/a40968c3-a1fc-45a9-bc17-2e820d8b06bd/image.png" alt=""></p>
<p>사용량 계획을 생성해줬다. 사용량 계획을 생성하는 이유는 API Key와 연동하여 
설정한 스테이지 엔드포인트에 대한 보안 능력을 높이고 성능 수준을 향상시키기 위해서이다.
<img src="https://velog.velcdn.com/images/son_doobu96/post/c147218a-d7e4-4f08-8207-9747950b9fd6/image.png" alt=""></p>
<h4 id="◆-발생한-문제">◆ 발생한 문제</h4>
<p>이후 포스트맨으로 인증 암호 없이 POST요청을 보냈는데 너무 잘 작동해서 한참동안의 삽질을 시작했다.  </p>
<p>문제의 원인은 리소스에서 API 키에 대한 설정을 안해줬었기 때문이다..
<img src="https://velog.velcdn.com/images/son_doobu96/post/bb94c736-ef01-451a-b784-b27bd818dbbb/image.png" alt=""></p>
<p>설정 해준 후 문제 없이 잘 작동했다. 사실 요청헤더에 x-api-key가 아닌 Authorization을 넣어서 몇번 삽질을 더 하긴 했지만 말이다 ㅋㅋㅋㅋ
나와 같은 문제를 겪으신 분들은 아래 공식문서를 확인하시면 좋을 것 같다.
<a href="https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/api-gateway-api-key-source.html">AWS 키 소스 선택에 관한 공식문서</a></p>
<hr>
<h4 id="권한-부여자를-이용한-인증-부여하기-optional">권한 부여자를 이용한 인증 부여하기 (optional)</h4>
<p>이 부분은 AWS가 정말 더 없이 친절하다.
<a href="https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html">AWS 권한 부여자 사용에 관련된 공식문서</a></p>
<p>AWS 공식 문서의 내용대로 권한부여자의 역할을 파악하고 따라서 진행한 것이 정말 도움이 많이 됐다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[메시지 브로커]]></title>
            <link>https://velog.io/@son_doobu96/%EB%A9%94%EC%8B%9C%EC%A7%80-%EB%B8%8C%EB%A1%9C%EC%BB%A4</link>
            <guid>https://velog.io/@son_doobu96/%EB%A9%94%EC%8B%9C%EC%A7%80-%EB%B8%8C%EB%A1%9C%EC%BB%A4</guid>
            <pubDate>Wed, 01 Feb 2023 12:00:22 GMT</pubDate>
            <description><![CDATA[<h4 id="■-메시지-브로커-message-broker">■ 메시지 브로커 (message broker)</h4>
<p>송신자의 메시지 프로토콜 형식으로부터의 메시지를 수신자의 메시지 프로토콜 형식으로 변환하는 중간 프로그램 모듈이다.</p>
<h4 id="■-메시지-브로커의-두-가지-방식">■ 메시지 브로커의 두 가지 방식</h4>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/71dff66d-0b4a-4193-b63d-1fb94fabf8e7/image.png" alt=""></p>
<blockquote>
<p>● Topic
JMS 에서 Topic은 발행(publish)과 구독(subscribe)을 구현한 것이다.
메시지를 발행(publish)하면 관심있어하는 모든 구독자(subscribers)에게 전달된다.</p>
</blockquote>
<ul>
<li>따라서 다수의 구독자가 메시지 카피본을 받을 수 있게 된다.
브로커가 메시지를 받는 시점에 활성 구독(active subscription)이 있는 구독자만 메시지를 받을 수 있다.</li>
</ul>
<blockquote>
<p>● Queue
JMS 에서 Queue 는 로드 발란서를 구현한 것이다.
하나의 메시지는 정확히 하나의 소비자(consumer)에게 전달된다.</p>
</blockquote>
<ul>
<li>메시지를 보내는 시점에 소비자(consumer)가 하나도 없는 경우 소비자가 메시지 처리가 가능해 질 때까지 보관된다.</li>
<li>소비자가 메시지를 받았는데, closing 전까지 인지(acknowledge)되지 않을 경우 메시지는 다른 소비자에게 재전송된다.</li>
</ul>
<h4 id="■-대표적인-메시지-브로커-3가지">■ 대표적인 메시지 브로커 3가지</h4>
<p><strong>Apache Kafka (Topic)</strong>
<img src="https://velog.velcdn.com/images/son_doobu96/post/a5895430-6a3f-48db-8286-570e5cef8cba/image.png" alt="">
실시간 스트리밍 데이터 파이프라인 및 애플리케이션을 구축하기 위한 오픈 소스. 고성능, 내결함성 및 확장 가능한 플랫폼이다.
Kafka는 스트리밍 데이터 저장소로 스트리밍 데이터를 생산하는 애플리케이션(생산자)을 데이터 저장소에서 스트리밍 데이터를 소비하는 애플리케이션(소비자)와 데이터 저장소로 분리한다.</p>
<blockquote>
<p>■ Apache kafka 구성요소</p>
</blockquote>
<ul>
<li>레코드 : 키(선택 사항), 값 및 타임스탬프가 있을 수 있다, 한 번 작성되면 수정이 불가능하다.</li>
<li>주제 : 레코드의 스트림이며 피드 이름으로 생각할 수 있다.</li>
<li>로그 : 디스크에 있는 주제의 저장소</li>
<li>파티션 및 세그먼트 : 각 토픽 로그를 세분화하는 역할을 한다.</li>
</ul>
<blockquote>
<p>■ Kafka의 네 가지 주요 API</p>
</blockquote>
<ul>
<li>Producer API : 데이터 스트림을 Kafka 클러스터의 주제로 보낸다.</li>
<li>Consumer API : Kafka 클러스터의 주제에서 데이터 스트림을 읽는다.</li>
<li>Streams API : 데이터 스트림을 입력 주제에서 출력 주제로 변환한다.</li>
<li>Connect API : 일부 소스 시스템 또는 앱에서 Kafka로 일관디게 가져오거나 Kafka에서 다른 것으로 푸쉬하는 커텍터를 구현한다.</li>
</ul>
<blockquote>
<p>■ Kafka의 핵심 기능 요약</p>
</blockquote>
<ul>
<li>높은 처리량: 대기시간이 2ms 미만인 머신 클러스터를 사용하여 제한된 네트워크 처리량으로 메시지를 전달한다.</li>
<li>확장성</li>
<li>영구 저장소</li>
<li>고가용성</li>
<li>파이프라인의 관리를 쉽게 해줌
<img src="https://velog.velcdn.com/images/son_doobu96/post/57022bd0-a46e-4cac-8b7a-548af4d86412/image.png" alt=""></li>
</ul>
<hr>
<p><strong>Amazon Kinesis (Topic)</strong>
<img src="https://velog.velcdn.com/images/son_doobu96/post/477efbfa-8a72-4c60-b3d9-33e261f85aae/image.png" alt=""></p>
<p>AWS Kinesis Streams를 사용하면 스트리밍 데이터의 대규모 데이터 수집 및 실시간 처리가 가능하다. 동일한 순서로 레코드를 읽거나 재생하는 기능뿐만 아니라 레코드의 순서를 보장한다.</p>
<p>Kinesis 고유의 주요 기능은 시간당 수백 테라바이트의 대용량 데이터 스트림을 처리하는 기능이다.
이는 운영 로그, 소셜 미디어 피드, 게임 내 소액 거래 또는 플레이어 활동 또는 금융 거래와 같은 소스에서 지속적으로 캡쳐될 수 있다. 이전 기술에 비해 장점은 특정 앱의 개발 프로세스를 단순화할 수 있다는 것이다.</p>
<blockquote>
<p>■ Amazon Kinesis 구성요소</p>
</blockquote>
<ul>
<li>샤드 : Amazon Kinesis 데이터 스트림의 기본 처리량 단위</li>
<li>데이터 생산자 : 데이터 레코드가 생성될 때 내보낸다.</li>
<li>데이터 소비자 :  스트림의 모든 샤드에서 데이터가 생성될 때 데이터를 검색한다.</li>
<li>스트림 : 샤드의 논리적 그룹</li>
<li>레코드 : Amazon Kinesis 스트림에 저장된 데이터 단위</li>
<li>파티션 키 : 일반적으로 사용자 ID 또는 타임 스탬프와 같은 의미 있는 식별자</li>
<li>시퀀스 번호 : 각 데이터 레코드에 대한 고유 식별자</li>
</ul>
<blockquote>
<p>■ Amazon Kinesis의 특징</p>
</blockquote>
<ul>
<li>최대 1MB의 레코드 크기 허용</li>
<li>메시지 수준이 아닌 샤드 수준에서 작동한다.</li>
<li>Auto Scaling이 없다, 개발자는 샤드 사용량을 추적하고 필요할 때 Kinesis 스트림을 다시 샤딩 해야한다.</li>
<li>제한된 읽기 처리량 (샤드당 초당 5개의 트랜잭션)</li>
<li>스트림의 샤드 수가 최대 처리량을 결정한다.</li>
<li>여러 소비자를 단일 스트림에 연결할 수 있으며 각 소비자는 모든 레코드를 개별적으로 처리할 수 있다.
(shard-iterator 덕분에) 빅데이터를 실시간으로 처리하는 AWS이다.</li>
</ul>
<hr>
<p><strong>Amazon SQS (Queue)</strong>
AWS Simple Queue Service(SQS)는 분산 애플리케이션 구성 요소 간에 메시지를 젖아하고 데이터를 쉽게 이동할 수 있도록 안정적이고 확장성이 뛰어난 서버리스 호스팅 대기열을 제공한다.</p>
<blockquote>
<p>■ Amazon SQS의 특징</p>
</blockquote>
<ul>
<li>최대 256KB의 비교적 작은 메시지 크기 허용</li>
<li>각 메시지는 독립적으로 처리될 수 있다.</li>
<li>대기열에서 읽는 작업의 수를 조정하여 읽기 처리량을 동적으로 증가시키는 자동 조정이 가능하다.</li>
<li>메시장 시맨틱(예: 메시지 수준 승인/실패) 및 메시지 가시성 시간 초과를 제공한다.</li>
<li>표준 대기열의 경우 인플라이트 메시지 수에 대한 120,000개 제한 및 FIFO 대기열의 경우 20,000개 제한</li>
</ul>
<hr>
<h4 id="■-메시지-브로커-비교">■ 메시지 브로커 비교</h4>
<p><strong>Apache Kafka vs AWS Kinesis</strong></p>
<table>
<thead>
<tr>
<th align="left">-</th>
<th align="left">Apache Kafka</th>
<th align="left">AWS Kinesis</th>
</tr>
</thead>
<tbody><tr>
<td align="left">비용</td>
<td align="left">비용이 많이 든다</td>
<td align="left">온디맨드</td>
</tr>
<tr>
<td align="left">사용의 용의성</td>
<td align="left">설정에 많은 시간과 비용을 들여야 한다.</td>
<td align="left">몇 분안에 시작 가능하다.</td>
</tr>
<tr>
<td align="left">관리 오버헤드</td>
<td align="left">높다. (자율성이 높다.)</td>
<td align="left">낮다. (자율성이 낮다.)</td>
</tr>
<tr>
<td align="left">확장성</td>
<td align="left">확장하기 힘들다.</td>
<td align="left">몇 초만에 확장이 가능하다.</td>
</tr>
<tr>
<td align="left">처리량</td>
<td align="left">무한</td>
<td align="left">샤드로 확장한다, 최대 1mb 페이로드를 지원한다.</td>
</tr>
<tr>
<td align="left">내구성</td>
<td align="left">구성이 가능하다.</td>
<td align="left">기본값의 3배정도의 내구성을 보장한다.</td>
</tr>
<tr>
<td align="left">하부구조</td>
<td align="left">직접 관리</td>
<td align="left">AWS의 관리</td>
</tr>
<tr>
<td align="left">쓰기-읽기 대기시간</td>
<td align="left">100ms 미만 달성 가능</td>
<td align="left">100ms(HTTP/2 사용)</td>
</tr>
<tr>
<td align="left">오픈 소스</td>
<td align="left">O</td>
<td align="left">X</td>
</tr>
</tbody></table>
<hr>
<p><strong>AWS SQS vs AWS Kinesis</strong></p>
<table>
<thead>
<tr>
<th align="left"></th>
<th align="left">AWS SQS</th>
<th align="left">AWS Kinesis</th>
</tr>
</thead>
<tbody><tr>
<td align="left">메시지 내구성</td>
<td align="left">보관기간 또는 삭제시까지 유지</td>
<td align="left">보간기간까지 유지 (삭제 불가)</td>
</tr>
<tr>
<td align="left">메시지 보관기간</td>
<td align="left">60초 ~ 최대 14일</td>
<td align="left">24시간 ~ 최대 7일</td>
</tr>
<tr>
<td align="left">순서 보장</td>
<td align="left">표준 순서 보장</td>
<td align="left">Shard 내 순서 보장</td>
</tr>
<tr>
<td align="left">메시지 전달</td>
<td align="left">Queue 당 여러 consumer</td>
<td align="left">shard당 여러 Application 별 consumer</td>
</tr>
<tr>
<td align="left">Scaling</td>
<td align="left">자동</td>
<td align="left">shard별 리샤딩이 필요하다.</td>
</tr>
<tr>
<td align="left">메시지 간 iterate</td>
<td align="left">No</td>
<td align="left">shard iterators</td>
</tr>
<tr>
<td align="left">전달 목적지/방식</td>
<td align="left">SQS Consumers / pull</td>
<td align="left">Kinesis Consumers / pull</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[CQRS]]></title>
            <link>https://velog.io/@son_doobu96/CQRS</link>
            <guid>https://velog.io/@son_doobu96/CQRS</guid>
            <pubDate>Wed, 01 Feb 2023 10:49:39 GMT</pubDate>
            <description><![CDATA[<h4 id="■-cqrs란">■ CQRS란?</h4>
<p>CQRS는 Command Query Responsibility Segregation(명령과 조회의 책임 분리)의 약자로 
이름처럼 명령을 처리하는 책임과 조회를 처리하는 책임을 분리하는 것이 CQRS의 핵심입니다.</p>
<h4 id="■-cqrs의-시작">■ CQRS의 시작?</h4>
<p>CQRS는 초기 CQS에서 시작되어 확장되었습니다.
CQS는 Command Query Separation의 약자로 시스템에서 처리되는 명령과 조회, 
이 두 작업을 정의하는 핵심 개념이자, 이 둘을 분리시키는 디자인 패턴입니다.</p>
<blockquote>
<p>◐ CQRS에서 명령과 조회</p>
</blockquote>
<ul>
<li>명령 : 상태를 변경하는 작업 (Create, Update, Delete)</li>
<li>조회 : 상태를 반환하는 작업 (Read)</li>
</ul>
<hr>
<h4 id="■-cqrs-패턴을-마이크로서비스에-적용">■ CQRS 패턴을 마이크로서비스에 적용</h4>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/7bdfa9f6-7268-423e-ae0c-b88b3d4b3e25/image.png" alt=""></p>
<p>마이크로서비스의 핵심은 서비스별 데이터 저장소를 
각기 다르게 채택한다는 점입니다.</p>
<blockquote>
<p>■ 발생하는 문제점</p>
</blockquote>
<ul>
<li>성능 향상을 위한 스케일 아웃시 빈번한 명령과 조회 작업으로 리소스 교착 상태가 발생한다.</li>
<li>명령보다 조회요청이 훨씬 많이 사용되기에 조회 요청의 빈도가 증가함에 따라 스케일 아웃시 명령기능도 함께 확장해야 하기에 도메인의 복잡도가 높아진다.</li>
</ul>
<blockquote>
<p>■ 해결책</p>
</blockquote>
<ul>
<li>CQRS 패턴 적용 : 
명령(Create, Update, Delete)과 조회(Read)로 나누어 처리</li>
</ul>
<p>■ 여전한 문제?</p>
<ul>
<li>단순히 모델을 나누는 형태로 명령과 조회를 분리한 것으로 하나의 데이터 저장소를 
사용한다는 면에서 완벽하게 마이크로서비스 설계 철학과 부합하는 것은 아니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/5e2a94a5-93bb-44b5-8782-b0919d13c38e/image.png" alt=""></p>
<blockquote>
<p>■ 또 다른 해결책</p>
</blockquote>
<ul>
<li>아예 물리적으로 명령 작업과 조회 작업을 위한 저장소를 따로 준비한다. </li>
<li>이러한 방법으로 명령과 조회를 분리하면 명령(쓰기) 요청의 부하를 줄이고 조회 대기 시간을 줄이는 등의 다양한 이점이 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/son_doobu96/post/dfa9a890-55cb-4832-8aa9-1e6aaa20eab6/image.png" alt=""></p>
<p>■ 이벤트 소싱 패턴
CQRS 패턴은 이벤트 소싱 패턴과 함께 사용되기도 합니다. </p>
<p>■ 이벤트 소싱 패턴의 흐름</p>
<ol>
<li>이벤트 발생</li>
<li>발생한 이벤트에 대해 수정 혹은 업데이트, 삭제 없이 입력만을 진행해 저장소에 이벤트를 저장해둔다. (쓰기)</li>
<li>필요한 데이터가 생기는 시점에 축적된 에벤트를 조회(읽기)하는 형태</li>
</ol>
<blockquote>
<p>▶ 이벤트 주도 아키텍처?
메세지 브로커를 이용한 이벤트 주도의 아키텍처의 경우, 이벤트로 인해 상태가 변경된 경우, 이를 데이터 모델로 처리하고 최종값을 반영하는 형태</p>
</blockquote>
<blockquote>
<p>▶ 이벤트 소싱 패턴?
이벤트 소싱 패턴의 경우, 이를 데이터로 저장하는 것이 아니라 
상태 변경 이벤트 자체를 저장하는 기법이다.</p>
</blockquote>
<blockquote>
<p>■ 이벤트 소싱 패턴의 장점? </p>
</blockquote>
<ul>
<li>메시지 브로커와 데이터 저장소 분리할 필요 X</li>
<li>이벤트를 데이터로 변경하는 복잡한 과정이 없어 쓰기 속도가 빠르다.</li>
<li>이벤트에 따른 CRUD를 전부 처리할 필요 없이, 이벤트 발생/조회만 처리하면 되어 서비스 확장이 용이하다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>