<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ops Insights – 커넥션 게이트웨이</title><link>https://docs.makgol.com/gateway/</link><description>Recent content in 커넥션 게이트웨이 on Ops Insights</description><generator>Hugo -- gohugo.io</generator><language>ko-KR</language><copyright>© 2026 Mont</copyright><lastBuildDate>Thu, 06 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://docs.makgol.com/gateway/index.xml" rel="self" type="application/rss+xml"/><item><title>문제의 형태 — API Gateway를 떠나는 진짜 이유</title><link>https://docs.makgol.com/gateway/01-problem-shape/</link><pubDate>Thu, 06 Aug 2026 00:00:00 +0000</pubDate><guid>https://docs.makgol.com/gateway/01-problem-shape/</guid><description>
&lt;h1&gt;01 · 문제의 형태 — API Gateway를 떠나는 진짜 이유&lt;/h1&gt;&lt;div class="hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200"&gt;
&lt;div class="hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2"&gt;&lt;svg height=1.2em class="hx:inline-block hx:align-middle" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;div class="hx:w-full hx:min-w-0 hx:leading-7"&gt;
&lt;div class="hx:mt-6 hx:leading-7 hx:first:mt-0"&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Connection duration for WebSocket API: 2 hours&lt;/code&gt; · &lt;code&gt;Can be increased: No&lt;/code&gt;. 이 전환의 유일한 논거입니다. 상시 연결이 전제인 단말에 2시간마다 강제 종료가 걸립니다.&lt;/li&gt;
&lt;li&gt;비용 논거는 성립하지 않습니다. 5만 대 상시 연결의 connection-minutes는 월 $540입니다. &amp;ldquo;비싸서 나간다&amp;quot;고 쓰면 첫 질문에서 무너집니다. &lt;em&gt;싸지만 맞지 않습니다.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;2시간 종료의 비용은 쿼터에서 나오지 않습니다. 재접속률 자체는 초당 7건으로 500/s 쿼터의 1.4%에 불과합니다. 재개 메커니즘이 없으면 2시간마다 5만 대가 전량 재동기화합니다. 그게 실제 비용입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Idle Connection Timeout: 10 minutes&lt;/code&gt;도 상향 불가라 하트비트를 뺄 선택지가 없습니다. 자체 구현으로 옮겨도 이 제약은 없어지지 않고 LB idle timeout으로 이름만 바뀝니다.&lt;/li&gt;
&lt;li&gt;프레임 32KB / 페이로드 128KB. 초과하면 에러 응답 대신 close code 1009로 연결이 끊깁니다. 페이로드가 자랄 여지가 있으면 이 상한에 언제 걸릴지 알 수 없습니다.&lt;/li&gt;
&lt;li&gt;흐름이 일방향이면 WebSocket의 양방향성은 안 쓰는 기능입니다. SSE로 내려가면 재연결과 재개(&lt;code&gt;Last-Event-ID&lt;/code&gt;)가 프로토콜에 내장되어 따라옵니다. WebSocket에서는 전부 직접 만들어야 하는 것들입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;왜 이 문서인가. 전환 검토서의 첫 문장이 &amp;ldquo;API Gateway가 비싸서&amp;quot;이면 그 검토는 거기서 끝납니다. 실제로 계산해보면 쌉니다. 이 문서는 떠나야 하는 이유를 하나로 좁히고 자체 구현으로 넘어갔을 때 &lt;em&gt;따라오는 제약&lt;/em&gt;과 &lt;em&gt;없어지는 제약&lt;/em&gt;을 구분합니다. 검증 기준: AWS 공식 쿼터 표, API Gateway 요금표, WHATWG HTML 명세의 server-sent events 절.&lt;/p&gt;</description></item><item><title>수립 방향이라는 분기점</title><link>https://docs.makgol.com/gateway/02-connect-direction/</link><pubDate>Thu, 06 Aug 2026 00:00:00 +0000</pubDate><guid>https://docs.makgol.com/gateway/02-connect-direction/</guid><description>
&lt;h1&gt;02 · 수립 방향이라는 분기점&lt;/h1&gt;&lt;div class="hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200"&gt;
&lt;div class="hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2"&gt;&lt;svg height=1.2em class="hx:inline-block hx:align-middle" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;div class="hx:w-full hx:min-w-0 hx:leading-7"&gt;
&lt;div class="hx:mt-6 hx:leading-7 hx:first:mt-0"&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&amp;ldquo;연결을 누가 거느냐&amp;quot;와 &amp;ldquo;데이터가 어디로 흐르느냐&amp;quot;는 별개입니다.&lt;/strong&gt; 중앙 → 단말 일방 푸시라는 요구는 두 분기가 모두 만족합니다. SSE·WebSocket·long-polling이 존재하는 이유 자체가 &lt;strong&gt;클라이언트가 건 연결 위에서 서버가 일방적으로 미는 것&lt;/strong&gt;이기 때문입니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;현행 구성이 이미 답을 알고 있습니다.&lt;/strong&gt; API Gateway WebSocket API에는 &lt;strong&gt;클라이언트 개시 외의 모드가 없습니다.&lt;/strong&gt; 지금 돌아간다면 POS가 outbound로 걸고 있다는 뜻입니다 — 서비스 제약이 그것 말고는 허용하지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;판정은 액세스 로그 한 줄이면 끝납니다.&lt;/strong&gt; &lt;code&gt;$context.identity.sourceIp&lt;/code&gt;가 매장 회선의 NAT 공인 IP로 찍히면 분기 A가 확정됩니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;분기는 링의 유무 말고도 여럿을 바꿉니다.&lt;/strong&gt; 전송 프로토콜(SSE가 맞는가), k8s 워크로드 종류(Deployment냐 StatefulSet이냐), 장애 감지 주체가 전부 뒤집힙니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;분기 B가 네트워크상 가능해도 보통은 A를 택합니다.&lt;/strong&gt; 5만 대의 주소 인벤토리, 단말별 TLS 서버 인증서와 그 갱신, 매장 방화벽 인바운드 규칙이 전부 새 운영 비용으로 붙기 때문입니다. &lt;strong&gt;B는 &amp;ldquo;가능한가&amp;quot;가 아니라 &amp;ldquo;그 값을 치를 만한가&amp;quot;로 판정해야 합니다.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;혼재는 A로 수렴시킵니다.&lt;/strong&gt; 일부 매장이 전용망이라 해서 두 경로를 함께 유지하면 장애 모드가 두 배가 됩니다. 전용망 매장도 outbound로 걸게 하면 A 하나로 끝납니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;왜 이 문서인가.&lt;/strong&gt; 이 질문 하나가 &lt;a href="https://docs.makgol.com/gateway/04-branch-a-client-dials/"&gt;04&lt;/a&gt;와 &lt;a href="https://docs.makgol.com/gateway/05-branch-b-pod-dials/"&gt;05&lt;/a&gt; 중 어느 쪽을 읽어야 하는지를 정합니다. 그런데 실무에서는 이 질문이 &lt;strong&gt;&amp;ldquo;중앙에서 단말로 밀어야 하니 중앙이 연결을 걸어야 하는 것 아닌가&amp;rdquo;&lt;/strong&gt; 라는 형태로 자주 뒤섞입니다. 여기서는 두 방향을 분리하고 판정을 코드 한 줄 짜지 않고 끝내는 절차를 남깁니다.&lt;/p&gt;</description></item><item><title>플랫폼 선례 해부 — 그들은 왜 링을 쓰는가</title><link>https://docs.makgol.com/gateway/03-platform-precedents/</link><pubDate>Thu, 06 Aug 2026 00:00:00 +0000</pubDate><guid>https://docs.makgol.com/gateway/03-platform-precedents/</guid><description>
&lt;h1&gt;03 · 플랫폼 선례 해부 — 그들은 왜 링을 쓰는가&lt;/h1&gt;&lt;div class="hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200"&gt;
&lt;div class="hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2"&gt;&lt;svg height=1.2em class="hx:inline-block hx:align-middle" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;div class="hx:w-full hx:min-w-0 hx:leading-7"&gt;
&lt;div class="hx:mt-6 hx:leading-7 hx:first:mt-0"&gt;&lt;ul&gt;
&lt;li&gt;링을 쓰는 이유는 다섯 시스템이 전부 다릅니다. &amp;ldquo;분산 시스템이니까 링&amp;quot;은 이유가 못 됩니다. 각자 못 피한 제약 하나를 링으로 우회했을 뿐입니다. 그 제약이 우리에게 없으면 링도 필요 없습니다.&lt;/li&gt;
&lt;li&gt;Loki/Mimir ingester가 링을 쓰는 진짜 이유는 쿼리입니다. flush 전 chunk가 그 파드 메모리에만 있어서 querier가 그 파드를 찾아가야 합니다. 링은 &amp;ldquo;누가 갖고 있나&amp;quot;를 O(1)로 답하는 주소록이고 부하 분산은 부수 효과입니다.&lt;/li&gt;
&lt;li&gt;vmagent는 링을 쓰지 않습니다. &lt;code&gt;hash(target) % membersCount&lt;/code&gt;이고 &lt;code&gt;memberNum&lt;/code&gt;은 플래그로 고정됩니다. 멤버십을 알지 못하고 죽은 멤버의 몫을 아무도 인수하지 않습니다 — 대신 &lt;code&gt;replicationFactor&lt;/code&gt;로 미리 중복 스크랩해 덮습니다.&lt;/li&gt;
&lt;li&gt;Alloy는 반대를 택했습니다. consistent hashing으로 노드당 512 토큰을 돌리고 문서가 트레이드오프를 명시합니다 — hashmod는 &lt;em&gt;fully consistent&lt;/em&gt;지만 재분배가 전량이고 consistent hashing은 이동이 1/N이지만 &lt;em&gt;&lt;strong&gt;eventually consistent&lt;/strong&gt;&lt;/em&gt;입니다. &amp;ldquo;eventually&amp;quot;가 곧 일시적 중복 소유입니다. 이게 분기 B의 최대 위험입니다.&lt;/li&gt;
&lt;li&gt;Promtail은 분배 문제를 배치로 없앴습니다. DaemonSet으로 노드마다 하나 두면 &amp;ldquo;누가 무엇을 맡나&amp;quot;가 질문조차 되지 않습니다. 우리 분기 A에서는 로드밸런서가 이 역할을 합니다.&lt;/li&gt;
&lt;li&gt;Thanos compactor는 링을 안 쓰고 상호배제를 사람에게 떠넘깁니다. &lt;em&gt;&amp;ldquo;you need to ensure on your own that only a single Compactor is running against a single stream&amp;rdquo;&lt;/em&gt; — 링의 한계를 가장 정직하게 드러낸 문장입니다. 링은 배정을 나눌 뿐 상호배제를 보장하지 않습니다.&lt;/li&gt;
&lt;li&gt;판정 축은 셋입니다 — ① 휘발성 상태가 파드 메모리에 있는가, ② 죽은 멤버의 몫을 인수해야 하는가, ③ 중복 소유가 사고인가. 우리 워크로드는 ①에 아니오, ②는 분기에 따라, ③은 분기 B에서만 예입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&amp;ldquo;Loki도 링을 쓰니 우리도 링&amp;quot;은 근거가 아닙니다. 이 문서는 다섯 시스템에서 링을 채택하게 만든 제약을 하나씩 뽑아내고 그 제약이 POS 게이트웨이에 있는지를 대조합니다. 미리 말해 두면 분기 A에는 다섯 개 중 어느 제약도 없습니다. 검증 기준은 각 프로젝트 공식 문서와 CLI 플래그 정의입니다.&lt;/p&gt;</description></item><item><title>분기 A — POS가 건다: 라우팅을 없애는 설계</title><link>https://docs.makgol.com/gateway/04-branch-a-client-dials/</link><pubDate>Thu, 06 Aug 2026 00:00:00 +0000</pubDate><guid>https://docs.makgol.com/gateway/04-branch-a-client-dials/</guid><description>
&lt;h1&gt;04 · 분기 A — POS가 건다: 라우팅을 없애는 설계&lt;/h1&gt;&lt;div class="hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200"&gt;
&lt;div class="hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2"&gt;&lt;svg height=1.2em class="hx:inline-block hx:align-middle" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;div class="hx:w-full hx:min-w-0 hx:leading-7"&gt;
&lt;div class="hx:mt-6 hx:leading-7 hx:first:mt-0"&gt;&lt;ul&gt;
&lt;li&gt;커넥션 레지스트리를 만들지 않습니다. push를 pull로 뒤집으면 &amp;ldquo;어느 파드가 이 단말을 들고 있나&amp;quot;라는 질문이 사라집니다. 발신 서비스는 스트림에 &lt;code&gt;XADD&lt;/code&gt;만 합니다. 파드는 자기 커넥션에 해당하는 것만 골라 흘립니다.&lt;/li&gt;
&lt;li&gt;Redis Stream ID를 SSE &lt;code&gt;id:&lt;/code&gt;에 그대로 싣습니다. 그러면 &lt;code&gt;Last-Event-ID&lt;/code&gt;가 곧 &lt;code&gt;XREAD&lt;/code&gt;의 시작점이 되고 재개 로직이 따로 필요하지 않습니다. ID 체계를 어떻게 잡느냐로 푸는 문제입니다.&lt;/li&gt;
&lt;li&gt;샤딩은 파드가 받는 양을 줄이지 않습니다. 파드가 랜덤 분산이면 모든 샤드를 구독하므로 파드당 수신량은 총 이벤트량 그대로입니다. 샤딩이 줄이는 건 Redis 노드당 송신량입니다. 둘을 섞어 말하면 용량 산정이 틀립니다.&lt;/li&gt;
&lt;li&gt;탈출 임계는 &lt;code&gt;R × S × P ≤ 예산&lt;/code&gt; 하나입니다. 파드를 늘리면 Redis 송신량도 선형으로 같이 오릅니다 — 스케일아웃이 백엔드 부하를 키우는 구조이므로 용량 계획에 반영해야 합니다.&lt;/li&gt;
&lt;li&gt;재개 창은 명시적 계약입니다. &lt;code&gt;XADD ... MINID ~&lt;/code&gt;로 스트림을 시간 기준으로 자르고 창을 벗어난 재접속은 정상 경로로 &lt;code&gt;event: resync&lt;/code&gt;를 받아 전체 재동기화합니다. 장시간 오프라인 매장은 상시 발생합니다.&lt;/li&gt;
&lt;li&gt;브로드캐스트를 별도 스트림에 두지 않습니다. ID 공간이 둘로 나뉘어 &lt;code&gt;Last-Event-ID&lt;/code&gt; 계약이 깨집니다. 발신 측에서 샤드마다 &lt;code&gt;XADD&lt;/code&gt;를 반복하는 편이 훨씬 쌉니다.&lt;/li&gt;
&lt;li&gt;at-least-once는 클라이언트 규약으로 만듭니다. POS가 처리를 끝낸 뒤에 last event ID를 갱신하면 중간에 끊겨도 그 이벤트가 재개 시 다시 옵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://docs.makgol.com/gateway/02-connect-direction/"&gt;02&lt;/a&gt;가 분기 A로 판정되면 이 문서가 설계 전문입니다. 주장은 하나입니다 — 재개 보장을 요구한 대가로 얻은 &amp;ldquo;이벤트가 파드 밖에 있다&amp;quot;는 성질을 끝까지 활용하면 API Gateway가 대신 해주던 커넥션 레지스트리를 만들 필요가 아예 없어집니다. &lt;a href="https://docs.makgol.com/gateway/03-platform-precedents/"&gt;03&lt;/a&gt;의 판정 축 ①(파드 메모리의 휘발성 상태)이 &amp;ldquo;없음&amp;quot;이기 때문에 성립하는 설계입니다.&lt;/p&gt;</description></item><item><title>분기 B — pod가 건다: 링을 만들어야 하는 경우</title><link>https://docs.makgol.com/gateway/05-branch-b-pod-dials/</link><pubDate>Thu, 06 Aug 2026 00:00:00 +0000</pubDate><guid>https://docs.makgol.com/gateway/05-branch-b-pod-dials/</guid><description>
&lt;h1&gt;05 · 분기 B — pod가 건다: 링을 만들어야 하는 경우&lt;/h1&gt;&lt;div class="hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200"&gt;
&lt;div class="hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2"&gt;&lt;svg height=1.2em class="hx:inline-block hx:align-middle" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;div class="hx:w-full hx:min-w-0 hx:leading-7"&gt;
&lt;div class="hx:mt-6 hx:leading-7 hx:first:mt-0"&gt;&lt;ul&gt;
&lt;li&gt;먼저 &amp;ldquo;연결을 상시 유지해야 하나&amp;quot;부터 물어야 합니다. POS에 우리가 닿을 수 있다면 보낼 게 생겼을 때 아무 파드나 &lt;code&gt;POST&lt;/code&gt;하면 됩니다. 상시 연결을 유지하지 않기로 하면 링도 lease도 통째로 필요 없어집니다 — B의 링 요구는 상당 부분 스스로 만든 문제입니다.&lt;/li&gt;
&lt;li&gt;링만으로는 중복 다이얼을 못 막습니다. &lt;a href="https://grafana.com/docs/alloy/latest/configure/clustering/distribute-prometheus-scrape-load/"target="_blank" rel="noopener"&gt;Alloy 문서&lt;/a&gt;가 consistent hashing을 &lt;em&gt;eventually consistent&lt;/em&gt;라고 명시하고 &lt;a href="https://thanos.io/tip/components/compact.md/"target="_blank" rel="noopener"&gt;Thanos&lt;/a&gt;는 상호배제를 아예 운영자에게 떠넘깁니다. 링은 후보를 좁히고 소유는 lease가 확정합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hash % membersCount&lt;/code&gt;(vmagent 방식)는 우리 요구와 맞지 않습니다. 그 방식은 멤버십을 모르는 대신 죽은 몫 인수를 포기합니다. &amp;ldquo;죽은 파드의 몫을 남은 파드가 주워간다&amp;quot;가 요구사항이면 처음부터 배제됩니다.&lt;/li&gt;
&lt;li&gt;참고 원형은 Loki가 아니라 Kafka consumer group입니다. 중복이 사고인 도메인에서 검증된 방식은 재배정 순간에 소유권 공백을 만들어 겹침을 원천 차단하는 쪽입니다. 잘 나누는 것만으로는 부족합니다.&lt;/li&gt;
&lt;li&gt;단말별 lease는 비쌉니다. 샤드별 lease로 내려야 합니다. 5만 개 키를 5초마다 갱신하면 초당 1만 명령입니다. 소유 단위를 샤드(64개)로 올리면 키가 64개가 되고 갱신 부하가 세 자릿수 줄어듭니다.&lt;/li&gt;
&lt;li&gt;lease TTL이 곧 최대 무연결 시간입니다. TTL 15초면 파드가 죽고 최대 15초간 그 단말들은 끊겨 있습니다. 줄이면 갱신 부하가 오릅니다 — 이 트레이드오프가 B의 SLO를 정합니다.&lt;/li&gt;
&lt;li&gt;B에서도 StatefulSet은 근거가 약합니다. ordinal이 필요한 건 &lt;code&gt;hash % N&lt;/code&gt; 방식뿐인데 그건 이미 배제됐습니다. 멤버십을 Redis나 EndpointSlice에서 읽으면 Deployment로 충분합니다.&lt;/li&gt;
&lt;li&gt;B를 택하면 SSE는 폐기됩니다. pod가 걸고 POS가 SSE를 서빙하면 데이터가 반대로 흐릅니다. 전송 프로토콜을 처음부터 다시 골라야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;왜 이 문서인가. &lt;a href="https://docs.makgol.com/gateway/02-connect-direction/"&gt;02&lt;/a&gt;가 분기 B로 판정됐을 때의 설계입니다. 그런데 이 문서의 절반은 B를 축소하는 데 씁니다 — 링과 lease와 멤버십 발견을 전부 만들기 전에 그게 정말 필요한지를 세 번 되묻습니다. &lt;a href="https://docs.makgol.com/gateway/03-platform-precedents/"&gt;03&lt;/a&gt;의 판정 축 ②(죽은 몫 인수)와 ③(중복 소유가 사고인가)이 둘 다 &amp;ldquo;예&amp;quot;일 때만 이 문서 전체가 필요합니다.&lt;/p&gt;</description></item><item><title>k8s 형태 판정 — Deployment로 끝나는 이유와 종료 설계</title><link>https://docs.makgol.com/gateway/06-k8s-shape/</link><pubDate>Thu, 06 Aug 2026 00:00:00 +0000</pubDate><guid>https://docs.makgol.com/gateway/06-k8s-shape/</guid><description>
&lt;h1&gt;06 · k8s 형태 판정 — Deployment로 끝나는 이유와 종료 설계&lt;/h1&gt;&lt;div class="hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200"&gt;
&lt;div class="hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2"&gt;&lt;svg height=1.2em class="hx:inline-block hx:align-middle" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;div class="hx:w-full hx:min-w-0 hx:leading-7"&gt;
&lt;div class="hx:mt-6 hx:leading-7 hx:first:mt-0"&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;두 분기 모두 Deployment로 끝납니다.&lt;/strong&gt; StatefulSet이 주는 안정적 ordinal·네트워크 ID·PVC·순차 롤링 중 이 워크로드가 쓰는 것이 하나도 없습니다. Loki ingester가 StatefulSet인 이유는 WAL용 PVC인데, &lt;strong&gt;우리는 디스크에 아무것도 쓰지 않습니다.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;headless service는 분기 A에서 필요 없습니다.&lt;/strong&gt; 파드 간 직접 통신이 생기는 건 &lt;a href="https://docs.makgol.com/gateway/04-branch-a-client-dials/"&gt;04 §6&lt;/a&gt;의 레지스트리 방식으로 전환할 때뿐입니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt; 기본 30초는 이 워크로드에 짧습니다.&lt;/strong&gt; 파드 하나가 2,500개 SSE 연결을 들고 있고 그 연결을 흩어서 끊어야 하므로 종료 절차가 분 단위입니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSE의 &lt;code&gt;retry:&lt;/code&gt; 필드가 재접속 폭풍의 유일한 프로토콜 레벨 해법입니다.&lt;/strong&gt; 종료 직전에 커넥션마다 다른 값을 밀어넣으면 2,500대의 재접속이 원하는 창에 흩어집니다. WebSocket에는 등가물이 없습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;순서가 중요합니다&lt;/strong&gt; — readiness 실패 → LB 드레이닝 대기 → &lt;code&gt;retry:&lt;/code&gt; 배포 → 점진적 종료. 이 순서가 틀리면 방금 끊은 연결이 다시 이 파드로 꽂힙니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPU는 이 워크로드의 HPA 지표로 최악입니다.&lt;/strong&gt; idle SSE 연결은 CPU를 거의 안 씁니다. &lt;strong&gt;파드당 활성 커넥션 수&lt;/strong&gt;를 커스텀 메트릭으로 쓰고 &lt;strong&gt;scale-in은 그 자체가 재접속을 유발하므로&lt;/strong&gt; 안정화 창을 길게 잡습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ALB idle timeout 기본 60초가 하트비트 주기를 정합니다.&lt;/strong&gt; API Gateway의 10분 제약이 사라지는 대신 이 값이 들어옵니다 — &lt;strong&gt;없어지는 게 아니라 이름이 바뀝니다.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;압축과 버퍼링을 꺼야 합니다.&lt;/strong&gt; 경로상 어디든 응답을 버퍼링하면 SSE는 &amp;ldquo;연결은 됐는데 아무것도 안 오는&amp;rdquo; 상태가 됩니다. 가장 흔한 SSE 사고입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&amp;ldquo;StatefulSet이나 headless service로 정의해야 하나&amp;quot;라는 질문에 답하고 그보다 훨씬 자주 사고를 내는 &lt;strong&gt;종료 설계&lt;/strong&gt;를 다룹니다. 장수명 연결 게이트웨이에서 배포는 평시 최대 부하 이벤트입니다 — 아무것도 안 하면 배포할 때마다 5만 대가 동시에 재접속합니다.&lt;/p&gt;</description></item><item><title>Kotlin 구현 노트 — 무엇이 실제로 어려운가</title><link>https://docs.makgol.com/gateway/07-kotlin-notes/</link><pubDate>Thu, 06 Aug 2026 00:00:00 +0000</pubDate><guid>https://docs.makgol.com/gateway/07-kotlin-notes/</guid><description>
&lt;h1&gt;07 · Kotlin 구현 노트 — 무엇이 실제로 어려운가&lt;/h1&gt;&lt;div class="hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200"&gt;
&lt;div class="hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2"&gt;&lt;svg height=1.2em class="hx:inline-block hx:align-middle" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;div class="hx:w-full hx:min-w-0 hx:leading-7"&gt;
&lt;div class="hx:mt-6 hx:leading-7 hx:first:mt-0"&gt;&lt;ul&gt;
&lt;li&gt;런타임 선택은 성능 문제가 아닙니다. WebFlux·Ktor·(MVC + 가상 스레드) 셋 다 파드당 2,500 연결을 여유 있게 감당합니다. 차이는 백프레셔를 어떤 모델로 표현하느냐에서 생깁니다. 팀이 이미 쓰는 것을 고르는 게 대개 옳습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;XREAD BLOCK&lt;/code&gt;은 커넥션을 독점합니다. Lettuce 문서가 명시하듯 &lt;em&gt;&amp;ldquo;the connection will no longer respond to any other commands until XREAD completes&amp;rdquo;&lt;/em&gt; — tail 전용 커넥션을 따로 잡아야 하고 일반 명령용 커넥션과 절대 섞으면 안 됩니다.&lt;/li&gt;
&lt;li&gt;한 번의 &lt;code&gt;XREAD&lt;/code&gt;로 64개 스트림을 전부 읽을 수 있습니다. 샤드마다 커넥션을 만들 필요가 없습니다 — 스트림 하나당 스레드 하나를 붙이는 순진한 구현이 이 설계에서 가장 흔한 낭비입니다.&lt;/li&gt;
&lt;li&gt;그런데 Redis Cluster에서는 그게 &lt;code&gt;CROSSSLOT&lt;/code&gt;으로 깨집니다. 해시 태그로 샤드를 슬롯 그룹에 묶어 그룹당 &lt;code&gt;XREAD&lt;/code&gt; 하나로 만들거나, Cluster 대신 인스턴스를 애플리케이션 레벨로 나누는 편이 단순합니다.&lt;/li&gt;
&lt;li&gt;느린 소비자가 이 시스템의 조용한 킬러입니다. POS 하나가 TCP 수신 버퍼를 비우지 않으면 그 커넥션의 큐가 무한히 자랍니다. 커넥션당 큐 상한과 초과 시 연결 종료가 없으면 힙이 샙니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Sinks.Many&lt;/code&gt;든 &lt;code&gt;Channel&lt;/code&gt;이든 상한 없는 버퍼를 쓰지 마세요. 기본값이 무제한인 API가 많습니다.&lt;/li&gt;
&lt;li&gt;POS 클라이언트를 우리가 만든다는 사실이 설계 자산입니다. last event ID를 처리 완료 후에 커밋하게 하면 서버 코드 변경 없이 at-least-once가 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://docs.makgol.com/gateway/04-branch-a-client-dials/"&gt;04&lt;/a&gt;의 설계를 JVM/Kotlin으로 옮길 때 실제로 발목을 잡는 것들만 모았습니다. 프레임워크 튜토리얼이 다루지 않는 blocking 명령의 커넥션 독점, Cluster의 CROSSSLOT, 느린 소비자가 본론입니다.&lt;/p&gt;</description></item></channel></rss>