원문: https://blog.bytebytego.com/p/ep221-how-docker-works-under-the
이번 주 시스템 디자인 요점
- Docker가 내부에서 동작하는 방식
- git merge vs git rebase
- 12가지 인기 벡터 데이터베이스
- 대용량 시스템의 페이지네이션 전략
- LLM이 AI 에이전트로 딥 리서치를 수행하는 방법
Docker의 내부 동작 방식
Docker 컨테이너는 단 하나의 명령으로 시작되지만, 그 명령이 실제로 실행 중인 Linux 프로세스가 되기까지는 여러 단계를 거친다.
실제로 어떤 일이 벌어지는가:
-
Docker CLI가 명령을 받아 호스트에서 실행 중인 **Docker 데몬(dockerd)**으로 API 호출을 전송한다.
-
dockerd는 nginx 이미지가 이미 디스크에 있는지 확인한다. 없다면 Docker Hub나 ECR 같은 레지스트리에서 이미지를 가져온다. 그런 다음 컨테이너 설정을 준비한다. -
dockerd는 컨테이너를 직접 실행하지 않는다. 컨테이너 라이프사이클을 관리하는 containerd에 요청을 전달한다. -
containerd는 런타임 파일을 준비하고 OCI(Open Container Initiative) 설정과 루트 파일시스템으로 구성된 번들을 조립한다. -
containerd는 그 다음 runc를 호출한다. -
runc는 번들을 읽고, 설정에 정의된 Linux 네임스페이스와 마운트를 생성한 뒤, 그 안에서 프로세스를 시작한다. -
프로세스가 실행되면 runc는 종료된다.
실행 중인 컨테이너는 자체 PID, 네트워크, 마운트 네임스페이스를 가진 일반 Linux 프로세스다. 파일시스템은 읽기 전용 이미지 레이어 위에 쓰기 가능한 레이어가 쌓인 구조로, 컨테이너 내부의 변경사항이 이미지를 수정하지 않는다.
격리는 커널 기능에서 나온다:
- 네임스페이스(Namespaces): 프로세스 분리
- cgroups: CPU와 메모리 제한
- 네트워크 네임스페이스: 컨테이너 자체 인터페이스 제공
게스트 OS도, 하이퍼바이저도 없다.
여러분은 어디서 컨테이너 문제를 가장 많이 만나는가 — 이미지, 네트워킹, 아니면 리소스 제한?
git merge vs git rebase
두 명령 모두 변경 사항을 main에 통합하지만, 기록(history)에 하는 일이 다르다.
git merge는 원래의 브랜치 구조를 보존한다. main과 feature 브랜치가 분기된 경우, Git은 머지 커밋을 생성하고 두 개발 흐름을 모두 유지한다. 어디서 브랜치가 갈라졌는지, 무엇이 머지됐는지, 언제 합쳐졌는지를 쉽게 볼 수 있다.
git rebase는 다른 접근법을 취한다. 피처 커밋들을 최신 main 브랜치 위에 새 커밋으로 재적용한다. 결과는 **선형 히스토리(linear history)**다. 단, 리베이스된 커밋은 새로운 ID를 갖기 때문에 공유 브랜치를 리베이스하면 보통 강제 푸시(force-push)가 필요하고, 다른 사람들이 리라이트된 히스토리에 동기화해야 한다.
그래서 팀들은 공유 브랜치에는 merge를, 개인 브랜치 정리에는 rebase를 사용하는 경우가 많다.
| 방법 | 특징 | 언제 사용 |
|---|---|---|
git merge |
브랜치 구조 보존, 머지 커밋 생성 | 공유 브랜치 통합 |
git rebase |
선형 히스토리, 커밋 ID 변경 | 개인 브랜치 정리 후 PR |
12가지 인기 벡터 데이터베이스
LLM 애플리케이션에서 적절한 컨텍스트를 모델에 전달하려면 벡터 데이터베이스가 필수다.
| DB | 특징 | 최적 사용 케이스 |
|---|---|---|
| Pinecone | 완전 관리형, 서버리스, 하이브리드 검색(dense+sparse) 내장 | 인프라 오버헤드 없애고 싶을 때 |
| Weaviate | 내장 벡터화, BM25+벡터 하이브리드 검색 | 키워드+시맨틱 검색 동시 필요 시 |
| Milvus | 분산형, 수평 확장, GPU 가속 지원 | 초대규모 워크로드 |
| Qdrant | Rust 기반, dense/sparse/메타데이터 필터링, 프로덕션급 | 랭킹·레이턴시 제어가 중요할 때 |
| Chroma | 임베디드, Python 앱 내부 직접 실행 | 로컬 RAG 프로토타입·실험 |
| Pgvector | PostgreSQL 확장, HNSW·IVF 인덱스 | 이미 PostgreSQL을 쓰고 있을 때 |
| FAISS | Meta의 ANN 라이브러리, GPU 가속, 높은 커스터마이징 | 커스텀 검색 시스템 |
| Vespa | dense 벡터·sparse 검색·구조화 데이터 통합 | 대규모 검색·랭킹 시스템 |
| MongoDB Atlas Vector Search | MongoDB 컬렉션에 벡터 검색 통합 | 앱이 MongoDB를 이미 사용 중일 때 |
| Redis Vector | 저레이턴시 벡터 검색 + 캐싱·pub/sub·스트리밍 | 실시간 AI 앱·시맨틱 캐싱 |
| Elasticsearch | BM25+벡터 검색 단일 쿼리 엔진 | Elastic로 검색·분석 중일 때 |
| LanceDB | 인-프로세스, zero-copy 디스크 읽기, 서버 없음, 멀티모달 지원 | 로컬 AI, 엣지 배포, 데이터 레이크 |
프로덕션에서는 여러 DB를 조합해서 사용하는 경우가 많다.
대용량 시스템의 페이지네이션 전략
테이블에 수천 개 행이 있을 때는 단순해 보이지만, 데이터가 커지면 페이지네이션은 진짜 문제가 된다.
오프셋 기반 페이지네이션 (Offset Pagination)
?page=3&size=10→ DB가 20개 행을 건너뛰고 10개 반환- 만들기 쉽고 설명하기도 쉬움
- 문제: DB가 건너뛰는 행도 모두 읽는다. 5페이지는 빠르지만 5000페이지는 느리다.
키셋 페이지네이션 (Keyset Pagination)
- "ID 101 이후 항목을 반환하라"고 서버에 요청
- 인덱스를 통해 바로 해당 지점으로 이동하므로 1페이지와 10,000페이지가 같은 속도
- 딥 페이지 처리에 훨씬 유리
컨티뉴에이션 토큰 (Continuation Tokens)
- S3, YouTube 같은 대형 API에서 사용
- 서버가 토큰을 반환하면, 다음 요청에 그 토큰을 보내 마지막 응답이 끝난 곳에서 재개
- 중요: 토큰은 원래 요청의 필터 및 정렬 기준에 묶여 있음
시간 기반 페이지네이션 (Time-Based Pagination)
- 피드·로그에 적합
- 타임스탬프 이전 항목을 요청하여 역순으로 읽음
- 주의: 같은 타임스탬프를 가진 행을 위해 보조 필드(예: ID) 추가 필요 → 없으면 레코드 누락 가능
LLM이 AI 에이전트로 딥 리서치를 수행하는 방법
Claude, ChatGPT, Gemini 같은 LLM에 복잡한 주제의 딥 리서치를 요청하면, 단 하나의 모델이 모든 작업을 하는 게 아니다. 전문화된 AI 에이전트들이 협력하는 시스템이다.
동작 방식:
Step 1: 질문 이해 및 계획 수립 "2026년 AI 에이전트 경쟁 구도를 분석해줘"라는 쿼리로 시작한다. 시스템은 무작정 달려들지 않는다. 먼저 필요한 것을 정확히 파악하기 위한 명확화 질문을 할 수 있다. 그런 다음 계획을 세우고 큰 질문을 작고 관리 가능한 태스크로 분해한다.
Step 2: 서브 에이전트 작업 시작 각 소규모 태스크는 서브 에이전트 — 특정 역할을 가진 미니 AI 워커 — 에 할당된다. 예를 들어, 한 서브 에이전트는 최신 Nvidia 실적을 찾는 임무를 맡을 수 있다. 사용할 도구(웹 검색, 특정 페이지 탐색, 데이터 분석 코드 실행 등)를 스스로 결정하고, AI를 외부 세계와 연결하는 보안 API·서비스 레이어를 통해 이를 수행한다.
Step 3: 모든 것을 종합하기 모든 서브 에이전트가 태스크를 완료하면, **합성 에이전트(Synthesizer Agent)**가 인계받는다. 모든 내용을 집계하고, 핵심 테마를 식별하며, 개요를 계획하고, 중복·불필요한 정보를 제거한다. 동시에 **인용 에이전트(Citation Agent)**가 모든 주장이 출처와 연결되고 적절히 포맷되도록 보장한다. 최종 결과물은 잘 인용된 완성된 아웃풋이다.
핵심 요약 (20줄)
- Docker 동작 원리: Docker CLI → dockerd → containerd → runc → Linux 프로세스. 게스트 OS 없음, 커널 기능(네임스페이스·cgroups)으로 격리.
- runc의 역할: 컨테이너 번들 읽기, Linux 네임스페이스 생성, 프로세스 시작 후 즉시 종료.
- 컨테이너 파일시스템: 읽기 전용 이미지 레이어 + 쓰기 가능 레이어 스택 → 이미지 변경 없음.
- git merge: 브랜치 구조 보존, 머지 커밋 생성. 공유 브랜치에 적합.
- git rebase: 선형 히스토리 생성, 커밋 ID 변경. 개인 브랜치 정리에 적합.
- 벡터 DB 선택 기준: 인프라 없이 → Pinecone, 대규모 → Milvus, 로컬 RAG → Chroma, PostgreSQL 환경 → Pgvector.
- Qdrant: Rust 기반, 프로덕션급 랭킹·레이턴시 제어 필요 시 최적.
- LanceDB: 인-프로세스, 서버 없음, 멀티모달 지원 — 엣지/로컬 AI에 적합.
- 오프셋 페이지네이션: 구현 쉬움, 딥 페이지에서 성능 저하.
- 키셋 페이지네이션: 인덱스 직접 접근으로 딥 페이지도 동일 속도.
- 컨티뉴에이션 토큰: S3·YouTube 방식, 서버가 토큰 반환 → 클라이언트가 다음 요청에 전송.
- 시간 기반 페이지네이션: 피드·로그에 적합, 보조 필드 없으면 레코드 누락 위험.
- LLM 딥 리서치 구조: 단일 모델이 아닌 멀티 에이전트 시스템.
- 딥 리서치 Step 1: 질문 이해 → 명확화 → 태스크 분해 계획.
- 딥 리서치 Step 2: 서브 에이전트 병렬 작업 — 웹 검색, 페이지 탐색, 코드 실행.
- 딥 리서치 Step 3: 합성 에이전트(집계·테마 파악) + 인용 에이전트(출처 연결).
- 프로덕션 현실: 벡터 DB 하나로 해결 안 됨 → 여러 DB 조합이 일반적.
- 네임스페이스 격리: PID/네트워크/마운트 네임스페이스로 컨테이너 간 완전 분리.
- 페이지 5000 문제: 오프셋 방식은 건너뛴 행도 모두 읽어 딥 페이지가 느려짐.
- 핵심 인사이트: Docker는 VM이 아닌 Linux 커널 기능의 조합 — runc가 핵심 실행자.