이 레포가 무엇을 하는 물건인가.
기존 도구는 "아파트 한 동(공유 리눅스 VM)에 모든 세대(컨테이너)를 입주"시킨다. 한 세대만 쓰려고 해도 건물 전체를 지어야 하고, 옆집과 벽을 공유한다. container는 "세대마다 독채(전용 VM)를 짓는" 방식이다. 독채는 필요한 가구(코어 유틸·동적 라이브러리)만 들여놓아 작고 빠르며, 옆집과 물리적으로 분리돼 보안·프라이버시가 강하다.
이 '독채를 순식간에 짓는 기술'의 정수는 별도 오픈소스 패키지 apple/containerization에 있고, 이 apple/container 레포는 그것을 사람이 쓰도록 감싼 CLI + 백그라운드 데몬 + XPC 헬퍼들이다.
container는 macOS에서 리눅스 컨테이너를 경량 VM으로 실행하는 커맨드라인 도구다. container run docker.io/python:alpine처럼 익숙한 Docker식 명령을 쓰지만, 내부 동작은 전혀 다르다. 컨테이너를 만들 때마다 Virtualization 프레임워크로 전용 마이크로 VM을 부팅하고, 그 안에서 OCI 이미지의 프로세스를 띄운다. OCI 표준을 따르므로 Docker Hub 등 어떤 레지스트리와도 pull/push가 호환된다.
전체는 한 덩어리 프로그램이 아니라 여러 프로세스의 협업이다. CLI(container)가 명령을 받으면, 백그라운드 launchd 에이전트인 container-apiserver에 XPC로 요청을 보낸다. apiserver는 다시 이미지 관리 헬퍼(container-core-images), 네트워크 헬퍼(container-network-vmnet), 그리고 컨테이너 하나당 하나씩의 런타임 헬퍼(container-runtime-linux)를 띄워 일을 나눈다.
트렌딩 이유 · Docker Desktop·OrbStack·colima 대비 장점.
주목 이유는 셋이다. 첫째, "Apple이 공식으로 만든 컨테이너 런타임"이라는 무게감 — 그동안 맥의 컨테이너는 전부 서드파티(Docker·OrbStack 등)였고, OS를 만드는 회사가 직접 Virtualization·vmnet을 가장 깊이 다뤄 내놓은 건 처음이다. 둘째, 컨테이너당 1 VM이라는 격리 모델이 보안/프라이버시 면에서 공유 VM 방식보다 근본적으로 강하다. 셋째, 100% Swift로 쓰인 완성도 높은 멀티프로세스 시스템 프로그래밍 교과서라 학습 가치가 크다.
| 비교 항목 | Docker Desktop / OrbStack / colima | apple/container |
|---|---|---|
| 격리 단위 | 공유 리눅스 VM 1대에 모든 컨테이너 | 컨테이너마다 전용 경량 VM |
| 격리 강도 | 컨테이너끼리 같은 커널 공유 | 커널까지 분리(VM 경계) |
| 호스트 데이터 노출 | 쓸 가능성 있는 데이터를 공유 VM에 미리 마운트 | 컨테이너별로 필요한 것만 마운트(프라이버시) |
| 구현 언어 / 타깃 | Go 등 / 범용 | Swift / Apple Silicon arm64 최적화 |
| OS 통합 | 자체 가상화 계층 | Virtualization·vmnet·XPC·launchd·Keychain 네이티브 |
| 이미지 호환 | OCI | OCI(완전 호환 — pull/run/push) |
| 제공처 | 서드파티(일부 유료) | Apple 공식 · Apache-2.0 |
Docker Desktop류는 거대한 리눅스 VM 한 대 안에 모든 컨테이너를 넣는다. 이러면 ① 컨테이너끼리 같은 커널을 공유해 한 컨테이너의 커널 익스플로잇이 옆을 위협할 수 있고, ② 호스트의 어떤 폴더든 쓸지 모르니 공유 VM에 미리 다 마운트해 둬야 해 데이터 노출면이 넓어지며, ③ VM 한 대가 항상 큰 메모리를 점유한다.
컨테이너마다 전용 VM이므로 커널이 분리돼 공격 표면이 좁아지고, 그 컨테이너에 꼭 필요한 폴더만 그 VM에 마운트하면 되니 프라이버시가 좋다. VM이 최소 구성이라 부팅이 컨테이너급으로 빠르고 메모리는 앱이 실제 쓰는 만큼만 쓴다. Apple이 OS 레벨 프레임워크를 직접 다뤄 이 모델을 현실적인 성능으로 구현했다는 점이 핵심이다.
100% Swift 6.2 · Apple 프레임워크 네이티브 · gRPC/Protobuf.
container는 단일 언어(Swift) 프로젝트지만, 그 아래로 Apple OS 프레임워크와 Swift 서버 생태계 패키지가 촘촘히 깔려 있다. Package.swift(swift-tools 6.2)는 20여 개의 라이브러리 타깃과 6개의 실행 타깃(container, container-apiserver, container-core-images, container-network-vmnet, container-runtime-linux, machine-apiserver)으로 쪼개져 있다.
| 구분 | 패키지 / 프레임워크 — 역할 |
|---|---|
| 저수준 컨테이너 엔진 | apple/containerization — VM·이미지·프로세스 관리의 심장부(별도 레포). container는 이걸 감싸는 껍데기다. |
| CLI 파싱 | swift-argument-parser — 서브커맨드 트리(run/build/image/network…) 구성 |
| 비동기 네트워킹 | swift-nio · async-http-client — 레지스트리 통신, 소켓 포워딩 |
| RPC / 직렬화 | grpc-swift-2 · grpc-swift-nio-transport · swift-protobuf — 빌더(buildkit shim)와의 gRPC 통신 |
| 시스템 호출 | swift-system — 파일·경로·POSIX 래퍼 |
| 설정·로깅 | swift-configuration(+TOML) · swift-log · Yams(YAML) |
| 자료구조 | swift-collections — 큐·순서맵 등 |
container의 진짜 차별점은 외부 라이브러리가 아니라 OS에 붙는 깊이다. README/기술개요가 직접 꼽는 통합 지점은 다음과 같다.
| 프레임워크 | container에서의 역할 |
|---|---|
| Virtualization.framework | 컨테이너별 리눅스 VM 생성·부팅·디바이스 부착의 핵심 엔진 |
| vmnet.framework | 컨테이너가 붙는 가상 네트워크 관리(container-network-vmnet 헬퍼) |
| XPC | CLI ↔ apiserver ↔ 헬퍼들 사이 프로세스 간 통신(IPC) |
| launchd | apiserver를 launch agent로 등록/시작/중지(container system start/stop) |
| Keychain Services | 레지스트리 자격증명 안전 보관 |
| Unified Logging (os_log) | 애플리케이션 통합 로깅(container system logs) |
| Rosetta | --rosetta로 컨테이너 안 x86 바이너리 실행 지원 |
container system start를 치면 container-apiserver가 launch agent로 떠서 명령을 받을 준비를 한다.멀티프로세스 데몬 군집 · 컨테이너당 1 VM · vminitd 부트.
핵심 통찰은 "한 프로그램이 다 하지 않는다"는 점이다. 사용자가 container run을 치면, 그 일은 CLI → apiserver → 헬퍼들로 XPC를 타고 흩어져 처리된다. 그리고 각 컨테이너는 자기만의 런타임 헬퍼 + 자기만의 VM을 갖는다.
container-runtime-linux가 지시를 내리면 vminitd가 VM 내부에서 실행한다.container run 한 줄이 일으키는 일 (생명주기)모든 기능을 한 데몬에 몰지 않고 이미지·네트워크·런타임을 별도 헬퍼 프로세스로 분리했다(Sources/Plugins/의 CoreImages·NetworkVmnet·RuntimeLinux). 한 헬퍼가 죽어도 전체가 안 무너지고, 각자 필요한 권한만 갖는다. CLI와 서비스 사이도 ContainerAPIClient(클라) / ContainerAPIService(서버)로 깔끔히 갈라 둔다.
각 서비스가 Client/와 Server/ 한 쌍으로 설계된다(예: ContainerAPIService/Client + /Server, RuntimeLinux/Client + /Server). 클라는 XPC 메시지를 보내고, 서버는 받아 실제 일을 한다. Sources/ContainerXPC/가 이 통신의 공통 토대(XPCClient/XPCServer/XPCMessage)를 제공한다.
공유 데몬이 모든 컨테이너를 직접 관장하지 않는다. 컨테이너마다 전용 container-runtime-linux가 떠서 그 컨테이너의 VM 하나만 책임진다. 한 컨테이너의 문제가 다른 컨테이너로 번지지 않는 격리의 물리적 토대다.
container build는 특별한 호스트 코드가 아니라 "빌더"라는 유틸리티 컨테이너(buildkit shim, builderShimVersion)를 경량 VM으로 띄워 거기서 Dockerfile을 빌드한다. 그래서 빌드도 --cpus/--memory로 자원을 조절(container builder start)한다. 빌더와는 gRPC/Protobuf로 통신한다(Sources/ContainerBuild/Builder.grpc.swift).
Sources를 읽는 법 — 라이브러리 타깃 vs 실행 헬퍼 타깃.
① docs/technical-overview.md로 큰 그림 → ② Sources/CLI/Container/에서 익숙한 run 명령이 어떻게 정의됐는지 → ③ Sources/Services/ContainerAPIService/{Client,Server}로 "CLI가 보낸 요청이 서버에서 어떻게 처리되나" → ④ Sources/ContainerXPC/로 그 사이 통신 토대 → ⑤ Sources/Services/RuntimeLinux/Server/로 실제 VM·게스트 제어. Client/Server 쌍을 짝지어 읽는 것이 이 코드베이스의 독해 열쇠다.
이 레포에서 무엇을 배우고, 무엇을 실습할까.
대부분은 "컨테이너는 가볍고 VM은 무겁다"까지만 안다. 이 레포는 "컨테이너의 편의 + VM의 격리"를 동시에 얻는 마이크로 VM 모델이 어떻게 가능한지 실물로 보여준다. 왜 Apple은 공유 VM이 아니라 컨테이너당 VM을 택했나(보안·프라이버시), 그 대가는 무엇인가(메모리 밸룬 한계 등)를 같이 읽으면 가상화의 트레이드오프 감각이 생긴다.
이 레포는 Apple 프레임워크로 진짜 시스템 소프트웨어를 짜는 법의 보기 드문 공개 사례다. Virtualization으로 VM을 부팅하고, vmnet으로 네트워크를 깔고, XPC로 프로세스를 잇고, launchd로 데몬을 관리하는 코드를 한곳에서 볼 수 있다. Sources/ContainerXPC/·Sources/APIServer/·Sources/Plugins/를 읽어라.
XPCMessage/XPCClient 시그니처를 참고만 할 것."왜 한 프로그램이 다 하지 않고 헬퍼로 쪼갰나"를 코드로 이해할 수 있다. 권한 분리·장애 격리·확장성을 위해 기능마다 Client(요청) / Server(처리) 쌍으로 가른 설계는 컨테이너뿐 아니라 모든 데몬형 시스템에 적용되는 패턴이다.
ContainerAPIService/Client/ContainerClient.swift의 한 메서드를 골라, 그에 대응하는 Server/Containers/ContainersService.swift 쪽 처리를 찾아 "요청→처리" 한 경로를 추적해 그려 보라.OCI 이미지/콘텐츠 스토어를 다루는 법(ContainerImagesService, ClientImage.swift)과, 빌더 컨테이너와 gRPC/Protobuf로 통신하는 법(ContainerBuild/Builder.grpc.swift, Protobuf.Makefile)을 같이 볼 수 있다. ".proto에서 코드 생성 → 클라이언트 호출"의 전형을 익힌다.
container build가 왜 "빌더 컨테이너"를 따로 띄우는지 설명하고, container builder start --cpus 8 --memory 32g가 무엇을 바꾸는지 한 단락으로 정리하라.swift-nio·grpc-swift·swift-argument-parser·swift-log 등 Swift 서버 생태계의 실전 조합을 한 프로젝트에서 본다. Swift 6.2의 동시성(actor·structured concurrency)이 시스템 데몬에서 어떻게 쓰이는지 살펴볼 가치가 있다.
swift-argument-parser로 서브커맨드가 있는 작은 CLI(mytool image ls 같은)를 만들어, 이 레포의 명령 트리 구성 방식을 흉내 내 보라.Apple Silicon 필수 · macOS 26 권장.
| 항목 | 요구사항 |
|---|---|
| CPU / 머신 | Apple Silicon Mac 필수(arm64). Intel Mac 미지원. |
| 운영체제(권장) | macOS 26 — Virtualization·vmnet의 새 기능을 활용. 유지보수자는 macOS 26에서만 재현되는 이슈를 다룬다. |
| 운영체제(제한적 동작) | macOS 15에서도 돌지만 네트워크 제약: 컨테이너 간 통신 불가, 다중 네트워크 불가(container network 명령 비활성), IP 할당 이슈. |
| 설치 | GitHub 릴리스의 서명된 .pkg 설치(파일은 /usr/local에 배치, 관리자 암호 필요) |
| 서비스 기동 | container system start (launchd 에이전트 기동) / container system stop |
| 컨테이너 기본 자원 | VM당 기본 RAM 1 GiB · CPU 4개(--memory/--cpus로 조절). 빌더는 기본 RAM 2 GiB · CPU 2개. |
| 빌드(소스에서) | Swift 6.2 툴체인(BUILDING.md 참조). 의존성 apple/containerization. |
Virtualization 프레임워크의 메모리 밸룬(ballooning) 지원이 부분적이라, 컨테이너 안 프로세스가 해제한 메모리가 호스트로 곧장 돌아오지 않을 수 있다. 메모리 집약 컨테이너를 많이 돌리면 가끔 재시작이 필요하다. --memory 16g로 띄워도 실제 사용량은 그보다 훨씬 작게 보일 수 있다.
난이도별 5단계 — 첫 컨테이너부터 코드 추적까지.
설치 후 container system start → container run --rm docker.io/python:alpine python -V로 첫 컨테이너 실행. 이어 container ls·container system logs로 무슨 프로세스가 떴는지 관찰한다. (Activity Monitor에서 container-runtime-linux가 컨테이너 수만큼 보이는지도 확인.)
--volume ~/work:/app로 호스트 폴더를 마운트하고, -p 8080:80으로 포트를 노출해 간단한 웹 서버 이미지를 띄운다. "필요한 폴더만 그 컨테이너 VM에 마운트된다"는 프라이버시 모델을 체감할 것.
Dockerfile을 작성해 container build -t myimg .로 빌드(빌더 컨테이너가 어떻게 뜨는지 관찰), 그다음 자신의 레지스트리에 container image push. OCI 호환이라 그 이미지를 Docker로도 받아 돌려 본다.
container run의 한 옵션(예: --memory)이 CLI 파싱 → APIClient → XPC → apiserver → runtime-linux까지 어떻게 흐르는지 코드를 따라간다. Sources/CLI/Container/에서 시작해 ContainerResource의 설정 모델, ContainerAPIService/{Client,Server}로 이어 읽는다.
BUILDING.md를 따라 직접 빌드한다(Swift 6.2 + containerization 의존성). 그다음 이 레포의 ContainerXPC 패턴을 참고해, 작은 launchd 데몬 + XPC 클라이언트로 "메시지 보내면 서버가 처리해 응답"하는 최소 예제를 직접 구현해 본다.
6주 코스 — 컨테이너 사용에서 시스템 프로그래밍까지.
| 주차 | 주제 | 실습 · 참고 |
|---|---|---|
| 1주차 | 컨테이너·이미지·OCI 기초, container 설치/첫 실행 | technical-overview.md · how-to.md · 과제 1~2 |
| 2주차 | 가상화 vs 컨테이너 격리, 마이크로 VM 모델 | technical-overview "How does container run" · Virtualization.framework 문서 |
| 3주차 | 멀티프로세스 데몬·XPC IPC·launchd 서비스 | Sources/APIServer · Sources/ContainerXPC · 과제 4 |
| 4주차 | Client/Server 분리 설계, 컨테이너당 런타임 헬퍼 | Sources/Services/{ContainerAPIService,RuntimeLinux} |
| 5주차 | vmnet 네트워킹·DNS·소켓 포워딩 | Plugins/NetworkVmnet · DNSServer · SocketForwarder |
| 6주차 | OCI 이미지 빌드(gRPC 빌더)·레지스트리·소스 빌드 | ContainerBuild · Protobuf.Makefile · BUILDING.md · 과제 3·5 |
본문·소스에 나온 용어 빠른 참조.
| 용어 | 의미 |
|---|---|
| container (CLI) | 사용자가 치는 명령줄 도구(실행 타깃 container). run/build/image/network 등 서브커맨드를 가진다. |
| Containerization | VM·이미지·프로세스 관리의 저수준 엔진을 담은 별도 Swift 패키지(apple/containerization). container의 심장부. |
| container-apiserver | launchd로 뜨는 백그라운드 데몬. 클라이언트 API를 제공하고 자원 수명주기를 관리. system start/stop로 제어. |
| container-core-images | 이미지 관리 + 로컬 콘텐츠 스토어를 담당하는 XPC 헬퍼. |
| container-network-vmnet | vmnet 기반 가상 네트워크를 관리하는 XPC 헬퍼. |
| container-runtime-linux | 컨테이너 1개당 하나 뜨는 런타임 헬퍼. 그 컨테이너의 경량 VM 한 대를 책임진다. |
| vminitd | 각 경량 VM 안에서 PID 1로 뜨는 게스트 init 데몬. rootfs 마운트·프로세스 실행·신호 전달을 담당. |
| 경량 VM (micro VM) | 컨테이너 하나만 돌리는 최소 구성 리눅스 가상머신. 컨테이너당 1대씩 부팅. |
| OCI | Open Container Initiative. 이미지·런타임 표준. container가 이를 지켜 Docker 등과 호환. |
| Virtualization.framework | VM 생성·부팅·디바이스 부착을 담당하는 Apple 프레임워크. container의 VM 엔진. |
| vmnet.framework | 컨테이너가 붙는 가상 네트워크를 만드는 Apple 프레임워크. |
| XPC | macOS의 프로세스 간 통신. CLI↔apiserver↔헬퍼 사이 메시지 전달의 토대. |
| launchd | macOS 서비스 관리자. apiserver를 launch agent로 등록/기동. |
| 빌더 (builder) | container build가 띄우는 유틸리티 컨테이너(buildkit shim). Dockerfile 빌드를 VM 안에서 수행. |
| Rosetta | --rosetta로 arm64 컨테이너 안에서 x86_64 바이너리를 실행하게 해주는 변환 계층. |