1. 개요
gVisor 는 컨테이너 안의 앱이 부르는 시스템 콜을 호스트 커널 대신 받아 처리하는 사용자 공간 커널이다. Google 이 Go 로 만들었고, OCI 런타임 runsc 로 쓴다.
일반 컨테이너(runc)는 Namespace 와 cgroup 으로 칸을 나눌 뿐, 앱의 시스템 콜은 모두 호스트 커널이 그대로 처리한다. 커널 취약점 하나로 컨테이너를 빠져나갈 수 있는 이유다. gVisor 는 앱과 호스트 커널 사이에 커널을 하나 더 끼워, 앱이 호스트 커널에 직접 닿는 길을 줄인다.
runc gVisor (runsc)
앱 앱
│ 시스템 콜 │ 시스템 콜
▼ ▼
호스트 커널 Sentry (gVisor 커널, 사용자 공간)
│ 꼭 필요한 일부 시스템 콜만
▼
호스트 커널
컨테이너 안에서 uname -r 을 찍으면 호스트 커널이 아니라 gVisor 가 보여 주는 커널 버전이 나온다.
2. 구성 요소
| 구성 요소 | 하는 일 |
|---|---|
runsc | OCI 런타임. runc 자리에 들어가 샌드박스를 만들고 컨테이너를 띄운다 |
| Sentry | gVisor 의 커널. 앱의 시스템 콜·메모리·시그널·파일 시스템을 처리한다 |
| Gofer | 호스트 파일 시스템 접근을 대신 맡는 별도 프로세스 |
| Netstack | gVisor 자체 TCP/IP 스택. 앱의 네트워크를 호스트 커널 스택 대신 처리한다 |
containerd-shim-runsc-v1 | containerd 가 runsc 를 부를 때 쓰는 shim |
최근 릴리스 묶음은 runsc 실행 파일 하나가 아니라, runsc 옆의 gvisor-bin/ 디렉터리에 Sentry 등 보조 프로그램을 따로 둔다. runsc 만 설치하면 샌드박스가 만들어지지 않는다.
gvisor.tar.bz2
├── runsc
├── containerd-shim-runsc-v1
└── gvisor-bin/
├── gvisor_sentry
├── gvisor-sentry-prewarmer
├── checkpointgofer
├── runsc-fd-parking
└── runsc-metric-server
3. 플랫폼
플랫폼은 앱의 시스템 콜을 Sentry 로 가로채는 방법이다.
| 플랫폼 | 방식 | 쓰는 곳 |
|---|---|---|
| systrap | seccomp 의 trap 으로 시스템 콜을 가로챈다 | 기본값. 일반 VM·클라우드 인스턴스 |
| KVM | 하드웨어 가상화로 가로챈다 | KVM 을 쓸 수 있는 bare metal 등 |
| ptrace | ptrace 로 가로챈다 | 예전 기본값. 느려서 권장하지 않음 |
AWS 의 일반 EC2 인스턴스(t3, m5a 등)는 중첩 가상화를 주지 않으므로 systrap 으로 돈다.
4. 쿠버네티스 연결
쿠버네티스는 RuntimeClass 로 Pod 마다 런타임을 고른다.
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc # containerd 설정의 런타임 이름
---
apiVersion: v1
kind: Pod
spec:
runtimeClassName: gvisor
노드의 containerd 에는 같은 이름의 런타임을 등록한다.
# containerd 2.x (config version 3)
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1"
runtimeClassName 이 없는 Pod 는 그대로 runc 로 돈다. 핸들러는 있는데 runsc 바이너리가 없으면 그 Pod 는 뜨지 않는다(FailedCreatePodSandBox).
관리형 지원은 클라우드마다 다르다.
| 클라우드 | gVisor 지원 |
|---|---|
| GKE | GKE Sandbox (노드 풀 옵션) |
| EKS | 관리형 옵션 없음. 노드 부팅 스크립트나 AMI 로 직접 설치 |
5. seccomp 와의 관계
gVisor Pod 안에서는 seccomp 필터가 적용되지 않는다(/proc/self/status 의 Seccomp: 0). 시스템 콜을 Sentry 가 받으므로 seccomp 가 막을 대상이 호스트 커널까지 내려가지 않는다. gVisor 자체가 그 역할을 한다.
다만 Pod Security restricted 는 seccompProfile 설정을 요구한다. 설정을 지우면 Pod 가 거부되므로, 실제로 적용되지 않더라도 RuntimeDefault 를 남겨 둔다.
6. 비용과 제약
| 항목 | 내용 |
|---|---|
| 성능 | 시스템 콜이 많은 작업(파일 I/O, 많은 작은 네트워크 요청)이 느려진다. CPU 만 쓰는 계산은 차이가 작다 |
| 호환성 | 모든 리눅스 시스템 콜을 구현하지는 않는다. 드문 기능을 쓰는 앱은 실패할 수 있다 |
| 네트워크 | Netstack 을 거치므로 CNI 의 일부 소켓 단계 기능(예: Cilium 소켓 LB)이 적용되지 않는다 |
| 운영 | 관리형이 아니면 버전 관리·설치를 직접 한다 |
7. 다른 샌드박스와 비교
| 방식 | 격리 수준 | 필요한 것 |
|---|---|---|
| runc | Namespace·cgroup. 커널 공유 | 없음 |
| gVisor | 사용자 공간 커널 | 일반 VM 에서 동작 |
| Kata Containers | 경량 VM | 하드웨어 가상화 (클라우드에서는 보통 bare metal) |
| Firecracker | microVM | 하드웨어 가상화 |
| AWS Fargate | Pod 마다 microVM | 관리형. DaemonSet 불가 |
8. 관련 문서
- CNI — Pod 네트워크를 붙이는 규격. gVisor Pod 도 CNI 가 만든 인터페이스를 쓴다
- Cilium — gVisor 와 함께 쓸 때
socketLB.hostNamespaceOnly가 필요하다 - IMDS — 컨테이너 탈출이 노리는 노드 IAM 권한의 출처
- CRI — kubelet 이 containerd 를 부르는 규격
- seccomp (예정) — 커널이 허용할 시스템 콜을 거르는 필터
- Pod Security (예정) —
restricted수준의 요구 사항