# Kubernetes를 브라우저로 포팅함

> Clean Markdown view of GeekNews topic #31014. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=31014](https://news.hada.io/topic?id=31014)
- GeekNews Markdown: [https://news.hada.io/topic/31014.md](https://news.hada.io/topic/31014.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-07-02T02:40:59+09:00
- Updated: 2026-07-02T02:40:59+09:00
- Original source: [ngrok.com](https://ngrok.com/blog/i-ported-kubernetes-to-the-browser)
- Points: 1
- Comments: 1

## Topic Body

- **webernetes**는 Kubernetes 일부를 TypeScript로 옮겨 브라우저 안에서 클러스터를 실행하게 만든 프로젝트로, 2개월 동안 552개 커밋·629개 파일·거의 10만 줄 규모로 만들어짐
- WebAssembly로 Kubernetes를 그대로 컴파일한 방식이 아니라, **kubelet 일부**, 여러 컨트롤러, 브라우저 기반 CNI와 컨테이너 런타임, 클러스터 조작 API를 새로 구현함
- 실제 이미지 레지스트리에서 이미지를 가져오지 않고 TypeScript API로 이미지를 정의하며, 목표는 프로덕션 배포판이 아니라 **인터랙티브 Kubernetes 콘텐츠** 제작임
- 코드 대부분은 LLM이 작성했지만 모든 줄을 사람이 리뷰했고, k3s와 같은 테스트를 실행하는 **204개 통합 테스트**와 Kubernetes Go 코드베이스에서 포팅한 1,855개 단위 테스트로 검증함
- LLM은 포팅 중 축약, 임의 헬퍼 생성, 테스트 누락을 반복했으며, 빠른 코드 생성의 이점을 얻으려면 **리뷰와 테스트**를 함께 적용해야 함

---

### webernetes가 브라우저에서 실행하는 것
- [webernetes](https://github.com/ngrok/webernetes)는 Kubernetes를 TypeScript로 부분 포팅해 브라우저에서 클러스터를 실행할 수 있게 만든 프로젝트임
- 데모 클러스터는 전부 브라우저 안에서 동작하며, 실제 Kubernetes 클러스터가 하는 작업 중 상당 부분을 수행함
  - ### Pod 생명주기
    - 클러스터 DNS와 네트워킹
    - 컨테이너 가비지 컬렉션
    - IP 할당
    - `Deployment`와 `ReplicaSet` 추적
    - 데모의 파란 점은 Pod들이 서로 요청을 보내는 모습을 나타냄

### WebAssembly 컴파일 대신 선택한 부분 포팅
- Kubernetes를 **WebAssembly**로 컴파일한 것이 아님
- 단순한 Go `hello, world!` 프로그램을 WebAssembly로 컴파일해도 gzip 기준 약 **540KiB**이며, webernetes는 gzip 기준 약 **140KiB**임
- Kubernetes 전체를 WebAssembly로 컴파일하면 메가바이트 단위 전송이 필요할 가능성이 있고, 브라우저에서 쓸 수 없는 시스템 수준 API 호출 때문에 컴파일 시점 오류도 발생함
- webernetes는 다음 요소로 구성됨
  - Pod 실행과 프로브에 필요한 Kubernetes **kubelet** 바이너리의 부분 포팅
  - Pod 스케줄러, namespace controller, kube-proxy, deployment controller 등 여러 Kubernetes 컨트롤러 포팅
  - Pod 간 통신을 위한 브라우저 기반 컨테이너 네트워크 인터페이스(CNI)
  - kubelet이 컨테이너 런타임 인터페이스(CRI)를 통해 컨테이너를 실행하게 하는 브라우저 기반 컨테이너 런타임
  - manifest 적용과 리소스 watch 같은 작업을 위한 webernetes 클러스터 API

### 이미지 정의와 API 사용 방식
- webernetes는 크기를 작게 유지하기 위해 Docker Hub 같은 레지스트리에서 실제 이미지를 가져오지 않음
- 대신 브라우저 기반 레지스트리를 갖고 있으며, 이미지는 **TypeScript API**로 정의함
- 예시 이미지 `HelloWorld`는 `w8s.BaseImage`를 상속하고, `exec` 안에서 8080 포트 HTTP 요청에 `"Hello, world!"`를 반환함
- 클러스터 사용 흐름은 다음과 같음
  - `new w8s.Cluster()`로 클러스터를 생성함
  - `cluster.registerImage(HelloWorld)`로 이미지를 등록함
  - `cluster.apply()`로 `apps/v1` `Deployment` manifest를 적용함
  - `cluster.api.corev1.listNamespacedPod()`로 Pod 목록을 조회함
  - `cluster.informer("pods", ...)`로 Pod 변경을 감시함
  - `cluster.on("request")`, `cluster.on("response")`로 Pod 간 요청과 응답 이벤트를 관찰함
  - `cluster.fetch()`로 클러스터 네트워크를 통해 Pod IP에 HTTP 요청을 보낼 수 있음
- 더 많은 예시는 [webernetes repository examples](https://github.com/ngrok/webernetes/tree/main/examples)에 있음

### 용도와 현재 한계
- webernetes의 목적은 **인터랙티브 Kubernetes 콘텐츠**를 만드는 것임
- 프로덕션 준비가 된 Kubernetes 배포판이 아니며, 실제 이미지를 실행할 필요도 없음
- 콘텐츠 제작자가 설명하려는 Kubernetes 개념을 보여주기 위해 특정 워크로드를 설정할 수 있으면 충분함
- 현재 지원하지 않는 기능에는 다음이 포함됨
  - ConfigMaps
  - Secrets
  - Pod resources
  - persistent volumes
  - 아직 필요하지 않았던 여러 Kubernetes 기능
- 향후 콘텐츠 제작 과정에서 필요한 Kubernetes 기능을 더 구현할 계획임

### LLM으로 만들었지만 그대로 맡기지 않은 이유
- webernetes 코드의 거의 전부는 **LLM**이 작성함
- 프로젝트 신뢰성을 위해 두 가지를 병행함
  - 모든 코드 줄을 직접 리뷰함
  - webernetes가 실제 클러스터와 같은 동작을 하는지 확인하는 수백 개 테스트를 만듦
- 수동 리뷰를 통해 코드 대부분이 Kubernetes Go 코드베이스와 줄 단위로 동일하다는 확신을 얻음
- 테스트는 이런 어휘적 유사성이 실제 동작의 동일성으로 이어지는지 확인하는 역할을 함
- 리뷰 후 남아 있는 실수는 프로젝트 작성자의 책임이며, 문제를 찾으면 [issue](https://github.com/ngrok/webernetes/issues/)를 열어 달라고 요청함

### 포팅 코드에 리뷰가 필요했던 이유
- LLM으로 C 컴파일러를 작성하거나 Bun을 Zig에서 Rust로 포팅한 사례는 자동으로 정확성을 검증할 방법이 있었음
  - Anthropic은 비교할 기존 C 컴파일러들이 있었음
  - Bun은 수동 리뷰 없이 100만 줄 이상의 새 Rust 코드를 병합할 만큼 신뢰하는 대규모 테스트 스위트가 있었음
- webernetes에는 그런 기반이 없었음
  - 테스트 스위트가 필요하면 직접 작성해야 했음
  - 실제 Kubernetes와 비교하려면 비교 방법도 직접 마련해야 했음
- 대부분의 webernetes 코드는 Kubernetes Go 코드베이스에서 포팅됐고, 손으로 입력하는 것보다 빠를 것으로 보고 LLM을 사용함
- 포팅 과정에서 LLM은 반복적으로 실수를 냄
  - **축약**: Kubernetes의 LRU cache, expiring cache, FIFO cache, transforming cache 등을 제대로 구현하지 않고 `Map`으로 대체해 잘못된 동작을 만들 수 있었음
  - **과도한 정리**: 원본 Go 코드에 없는 헬퍼 함수를 만들어 리뷰를 어렵게 하거나 미묘한 차이를 만들 수 있었음
  - **누락**: Go의 table test를 포팅할 때 테스트 케이스를 임의로 빼먹는 일이 자주 있었음
- LLM 포팅 결과를 신뢰하려면 출력물을 리뷰해야 하며, 프로젝트 작성자는 자신이 쓸 수 있는 지름길을 알지 못한다고 봄

### 실제 클러스터와 비교한 테스트
- 코드가 원본과 나란히 비슷해도 Go와 JavaScript 런타임은 다르기 때문에 동작이 달라질 수 있음
- webernetes에는 JavaScript 버전의 **channels**, **mutexes**, Go의 `select` 문, 기타 Go식 동작도 필요했음
- 같은 테스트 코드를 webernetes와 [k3s](https://k3s.io/) 클러스터 양쪽에서 실행해 동작을 비교함
- API 호환 대상으로는 TypeScript 타입이 있는 공식 JavaScript Kubernetes 클라이언트 [kubernetes-client/javascript](https://github.com/kubernetes-client/javascript)를 선택함
- 테스트 하네스는 `kubernetes.describe(..)`를 통해 실행 환경을 바꿈
  - `pnpm test:node`는 Node 환경에서 k3s를 대상으로 테스트함
  - `pnpm test:browser`는 headless browser에서 webernetes를 대상으로 테스트함
- 통합 테스트는 포팅된 코드뿐 아니라 커스텀 브라우저 기반 [container runtime](https://github.com/ngrok/webernetes/blob/9594c1b688015f79d9af33ce13e30674c562db7a/src/cluster/cri/runtime.ts#L129)과 [cluster network](https://github.com/ngrok/webernetes/blob/9594c1b688015f79d9af33ce13e30674c562db7a/src/cluster/cni/network.ts#L161)가 실제 클러스터와 맞게 동작하는지도 확인함
- 버그를 발견하면 먼저 k3s에서는 통과하고 webernetes에서는 실패하는 테스트를 만든 뒤, 그 피드백 루프로 LLM의 도움을 받아 원인을 이해하고 수정함
- 작성 시점 기준 webernetes에는 **204개 통합 테스트**와 **1,855개 단위 테스트**가 있음

### 리뷰와 테스트를 함께 써야 하는 이유
- LLM이 만든 코드에도 사람이 작성한 PR과 마찬가지로 좋은 테스트와 좋은 코드가 필요함
- 2026년의 차이는 사람 동료에게는 어느 정도 좋은 작업을 기대했지만, LLM에는 좋은 작업을 하지 않을 것이라고 가정하는 편이 안전하다는 점임
- 테스트 코드조차 리뷰하지 않으면 LLM이 어떤 성공 기준을 대상으로 작업하는지 알기 어려움
- 모든 코드를 리뷰하더라도 테스트가 없으면 사람이 모든 가능성을 추론한다고 믿기 어려움
- LLM은 지치지 않고 빠르게 입력하므로, 사람이 생각하지 못한 edge case를 제안하게 하고 타당하면 테스트를 작성시키는 방식이 유용함
- 사람의 취향과 이해, LLM의 빠른 작성 능력을 결합한 방식이 2012년 커리어 시작 이후 가장 큰 변화로 느껴졌다고 봄

### 프로젝트 규모와 토큰 사용량
- 첫 커밋은 4월 21일에 현재의 [webernetes repository](https://github.com/ngrok/webernetes)에 들어감
- 초기 작업 일부는 이 블로그 사이트 뒤의 저장소 브랜치에서 진행됐기 때문에 그래프가 전체 현실을 완전히 담지는 않음
- 코드 라인 그래프는 6월 15일 주 기준 **126,642줄**을 표시함
- 처음에 말한 약 10만 줄은 TypeScript가 아닌 코드, 주석, [demo app](https://webernetes-demo.ngrok.app/)을 제외한 수치임
- 주별 총 라인 수는 다음처럼 증가함
  - 4월 20일 주: 11,640줄
  - 4월 27일 주: 20,660줄
  - 5월 4일 주: 25,048줄
  - 5월 11일 주: 30,417줄
  - 5월 18일 주: 42,301줄
  - 5월 25일 주: 54,155줄
  - 6월 1일 주: 79,704줄
  - 6월 8일 주: 98,532줄
  - 6월 15일 주: 126,642줄
- Codex와 Claude 세션의 토큰 사용량은 cached input token이 다른 토큰보다 훨씬 컸고, 긴 컨텍스트 창을 자주 채울수록 특히 두드러짐
- 6월 15일 주에는 uncached input token **104,155,857개**, cached input token **2,196,467,968개**, output token **6,420,826개**가 사용됨

### 마지막 주의 급증과 비용
- 마지막 주에는 [demo app](https://webernetes-demo.ngrok.app/)에 Deployments 지원을 넣으려다 예상보다 작업이 커짐
- LLM의 첫 포팅 시도는 필요한 구성요소의 많은 기능을 누락함
- 이후 여러 에이전트를 동원해 의존성 체인을 식별하고, 각 구성요소를 더 많은 하위 에이전트로 포팅했으며, 또 다른 하위 에이전트들로 리뷰를 수행함
- 이 방식은 수동 작업보다 빠르게 일을 끝냈지만 토큰 효율은 매우 나빴고, 마지막에는 여전히 수동 리뷰가 필요했음
- API 환산 LLM 토큰 비용은 주별로 증가했으며, 6월 15일 주에는 **$1,811.64**였음
- 프로젝트 전체에서 작성자의 시간은 마지막까지도 가장 비싼 항목이었음

### 마무리 자료와 참여 방법
- 제작 과정을 기록한 영상 시리즈도 있음
  - [Part 1](https://www.youtube.com/watch?v=xckIMSFaB3s)
  - [Part 2](https://www.youtube.com/watch?v=wRSyz0_bhPI)
  - Part 3는 곧 공개 예정임
- 영상에서는 초기의 잘못된 낙관과, 음성 제어와 눈 추적으로 대부분 hands-free로 작업하는 방식도 볼 수 있음
- 프로젝트를 사용해 보고 [issues](https://github.com/ngrok/webernetes/issues/)를 열거나, 무언가 만들었거나 막혔을 때 `s.rose@ngrok.com`으로 연락해 달라고 요청함

## Comments



### Comment 60986

- Author: neo
- Created: 2026-07-02T02:41:00+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=48738985) 
* 가르치는 것과 만드는 것을 좋아하는 입장에서, 이런 건 꽤 유용할 수 있음  
  예를 들어 이걸 만들었는데 [https://kubernetes-made-simple.vercel.app/](<https://kubernetes-made-simple.vercel.app/>) 이제 여기에 추가할 수 있겠음
  * iPhone에서 기대 **Pod 수**를 바꾸면 reconciliation 시뮬레이터가 이상하게 폭주함  
    그래도 사이트는 멋짐
  * 전체를 다 읽었고 내용이 좋음  
    **Gateway**를 더 확장하고, 가능하면 **CRD**도 언급하면 좋겠음
  * 멋진 사이트임  
    대부분의 사람들이 **k8s**에 대해 잘못 이해해서 학습을 불필요하게 어렵게 만드는 것 하나를 꼽자면 무엇일까?
* 멋짐. 예전에 Kubernetes 교육 콘텐츠를 만들었던 입장에서, 이런 걸 만들고 싶은 매력이 분명히 보임  
  기억하기로는 처음엔 **Katacoda**를 썼고 이후 비슷한 다른 플랫폼을 썼는데, 각 사용자에게 특정 설정이 된 새 인스턴스를 즉석에서 띄워줘서 매우 유용했음  
  다만 지금은 개념이나 아키텍처 교육에 더 잘 맞아 보임. 진짜 재미는 **kubectl**을 능숙하게 다루기 시작할 때부터임
  * 예전 직장에서라면 **컨트롤 플레인**이 어떻게 동작하는지 설명하는 다이어그램, 성능 저하와 장애 모드, k8s에 배포하는 여러 아키텍처나 방식 비교에 정말 좋았을 것 같음
  * 아쉽게도 **Katacoda**는 유료 장벽 뒤로 들어갔음. 왜 그렇게 했는지는 완전히 이해함. 이런 것들은 비용이 드니까  
    비슷한 다른 플랫폼들도 비용을 대줄 사람이 없어져 사라진 것 같고, 안타까움  
    이게 대안이 되길 바람. 현실과 달라져 낡아질 위험은 있지만, 그래도 핵심은 거의 항상 유효할 것임
* 우선 정말 멋짐  
  이게 **LLM 보조 엔지니어링**을 바라보는 올바른 방식처럼 느껴짐. AI는 놀랄 만큼 많은 코드를 생성할 수 있지만, 실제 가치는 리뷰 규율과 그 주변의 테스트에 있음  
  브라우저에서 Kubernetes를 다루는 각도도 멋지지만, 더 흥미로운 건 워크플로, 특히 “그럴듯해 보인다”를 믿는 대신 k8s에 대해 동작을 테스트하는 부분임. 이미 얼마나 많은 팀이 AI가 작성한 코드에 대해 이 정도 검증을 하고 있을지 궁금하고, 앞으로 몇 년간 모두가 가게 될 방향일 수도 있음
  * 이건 말 그대로 맞춰 구현할 **명세**가 있는 특수한 경우임  
    안타깝게도 모든 코딩 작업이 그런 기회를 갖는 건 아님
* Nate Abele가 작년 **Carolina Code Conference**에서 이 주제로 발표했음  
  [https://youtu.be/t7L2iROVaRg?is=xoV4aiCXcYMVvVDL](<https://youtu.be/t7L2iROVaRg?is=xoV4aiCXcYMVvVDL>)
* 진짜로 꽤 멋짐  
  추가 복잡도나 성능 손실에는 어느 정도 정당화가 필요하겠지만, 일부 사용 사례에서는 충분히 보상받을 수 있어 보임
* kube 복잡도에 대한 농담들이 나오기 전에 말하자면, kube 같은 것은 kube가 의도한 작업들을 수행하기 위해 필요한 수준의 복잡도라는 흥미로운 논리가 가능해 보임  
  Fred Brooks의 **본질적 복잡도**와 우발적 복잡도 구분과 비슷함  
  물론 더 단순하게 할 수 있는 일에 kube를 쓰면, kube는 빠르게 우발적 복잡도가 됨
* 1. 실제로 브라우저에서 컨테이너를 실행하는 건 아닌 것 같음. 모든 서비스에 커스텀 커넥터가 필요해 보이고, 더 중요하게는…  
  2. …렌더러도 필요하지 않나? 그렇지 않으면 “브라우저로 포팅됐다”는 게 무슨 뜻인지 모르겠음  
  비유하자면 누군가 DOOM을 브라우저로 포팅했다면 이제 브라우저에서 플레이할 수 있다는 뜻임. 하지만 탭에 보이는 데이터베이스들을 실제로 브라우저에서 실행할 수 있는 건 아니지 않나?  
  ruby2d를 띄우면 갑자기 클라이언트 쪽 Ruby 지원이 생긴다고 말할 수는 없음. 브라우저 탭에서 실제로 돌아가게 하려면 온갖 커스텀 작업이 필요함  
  반면 일반적인 백엔드 컨테이너 서비스는 정말로 여기저기로 옮겨 어디서든 실행할 수 있음  
  그래서 요점을 잘 모르겠고, 내가 틀렸다면 바로잡아 달라. 주장하는 바 자체와도 맞지 않아 보임
  * 그 지점들은 제목에는 없지만 본문에서 다뤄짐  
    실제 **컨테이너 이미지**를 실행하는 건 아님. “시뮬레이션된 Kubernetes”라고 부르는 편이 더 나을 수도 있음  
    포팅된 것은 컨트롤 플레인임. 스케줄러, kube-proxy, deployment controller 등이 실제 Go 소스에서 옮겨졌고, 같은 클라이언트 API를 써서 k3s와의 동작 일치성을 테스트함. “렌더링”은 Pod 간 요청을 움직이는 점으로 시각화하는 데모 앱임
  * “Webernetes는 **대화형 Kubernetes 콘텐츠**를 만들기 위한 용도임”
* 데모는 멋짐. 이걸 어디에 쓰면 될까?
* 관련해서 며칠 전에 재미로 이걸 vibe coding으로 만들었음: [https://imjasonh.github.io/kubescheduler-the-game/](<https://imjasonh.github.io/kubescheduler-the-game/>)  
  재미있었음
  * 이 게임은 딱 내 취향임
