Firecracker
고친 사람 github-actions[bot]
Firecracker 는 가상 머신을 아주 가볍게 띄워 주는 프로그램입니다. 함수 한 번을 부를 때마다 가상 머신을 한 대씩 새로 켜도 될 만큼 빨리 뜹니다. 메모리도 적게 먹습니다. 그 대신 보통 가상 머신이 갖춘 장치 대부분을 덜어 냈습니다.
쉽고 빠른 이해
- 무슨 일을 하나 — 장치를 덜어 낸 작은 가상 머신을 1초도 안 걸려 띄워 줍니다. 남이 올린 코드를 받아 돌려 주는 클라우드 서비스가 이것으로 호출마다 가상 머신을 켜고 그 안에서 코드를 돌립니다.
- 왜 있나 — 서로 모르는 고객의 코드를 한 서버에서 돌리려면 벽이 두꺼워야 합니다. 컨테이너는 빠릅니다. 하지만 운영체제의 핵심을 나눠 쓰므로 벽이 얇습니다. 보통 가상 머신은 벽이 두껍습니다. 대신 켜는 데 오래 걸리고 무겁습니다. Firecracker 는 그 둘 사이의 빈칸을 메웁니다.
- 어떻게 도나
- 프로세서와 메모리를 나누는 일은 리눅스 커널에 든 가상화 기능에 맡깁니다
- 흉내 내는 장치는 디스크와 네트워크 등 몇 개로 줄입니다. 켤 때 거치는 점검 단계도 건너뜁니다
- 가상 머신 한 대마다 프로그램 하나를 띄웁니다. 그 프로그램을 한 겹 더 가둡니다
- 대가 — 화면이나 키보드 같은 장치가 없습니다. 윈도우나 데스크톱을 올리는 데는 못 씁니다. 호스트가 리눅스여야 합니다. 프로세서의 가상화 기능도 켜져 있어야 합니다.
상세
Firecracker 는 AWS(Amazon Web Services, 아마존 웹 서비스)가 만들어 오픈 소스로 공개한 프로그램입니다. Rust 로 짰습니다. AWS 의 두 서비스가 고객의 코드를 더 빨리, 더 적은 비용으로 돌리려고 만들었습니다.
그 둘은 AWS Lambda 와 AWS Fargate 입니다. Lambda 는 함수 하나를 맡겨 두면 부를 때만 돌려 주는 서비스입니다. Fargate 는 서버를 직접 빌리지 않고 컨테이너만 맡겨 돌리는 서비스입니다.
가상 머신과 컨테이너 사이의 빈칸
가상 머신은 컴퓨터 한 대를 여러 대처럼 나눠 쓸 때의 한 몫입니다. 몫마다 운영체제를 한 벌씩 올립니다. 이렇게 나눠 주는 소프트웨어를 하이퍼바이저라고 부릅니다.
가상 머신 안에서 도는 운영체제를 게스트라고 부릅니다. 가상 머신들을 얹고 있는 바깥 기계와 그 운영체제는 호스트라고 부릅니다.
컨테이너는 다른 길을 갑니다. 운영체제의 핵심인 커널은 호스트의 것 하나만 둡니다. 대신 프로그램마다 보이는 파일과 프로세스를 갈라 놓습니다. 켤 때 커널을 부팅하지 않으니 빠르고 가볍습니다.
두 방식은 벽의 두께가 다릅니다. 컨테이너들은 커널 하나를 나눠 씁니다. 그 커널에 구멍이 하나 있으면 이웃 컨테이너까지 닿을 수 있습니다. 가상 머신은 게스트마다 커널이 따로입니다. 게스트 하나가 뚫려도 하이퍼바이저를 한 번 더 넘어야 이웃에 닿습니다.
flowchart TD
subgraph CT["컨테이너 · 커널을 나눠 쓴다"]
C1["컨테이너 A"]
C2["컨테이너 B"]
CK["호스트 커널 하나"]
C1 --> CK
C2 --> CK
end
subgraph VT["가상 머신 · 커널이 따로다"]
V1["게스트 A · 자기 커널"]
V2["게스트 B · 자기 커널"]
HV["하이퍼바이저"]
HK["호스트"]
V1 --> HV
V2 --> HV
HV --> HK
end
CK ~~~ V1
위쪽은 컨테이너 A 가 커널을 뚫으면 곧장 B 와 같은 커널 안에 있습니다. 아래쪽은 게스트 A 가 자기 커널을 뚫어도 하이퍼바이저가 한 번 더 막아섭니다.
여러 고객이 기계 한 대를 나눠 쓰는 구조를 멀티테넌시라고 부릅니다. AWS Lambda 처럼 남이 올린 코드를 받아 돌려 주는 서비스가 그런 구조입니다. 이런 서비스는 두꺼운 벽이 필요해서 가상 머신을 써야 합니다.
그런데 함수 한 번 부를 때마다 보통 가상 머신을 켜면 켜는 데만 한참 걸립니다. 메모리도 운영체제 한 벌치를 따로 먹습니다. Firecracker 는 벽을 가상 머신만큼 두껍게 둡니다. 그러면서 켜는 속도와 몸집을 컨테이너 쪽으로 끌어옵니다.
이렇게 덜어 낸 가상 머신을 마이크로VM이라고 부릅니다. VM 은 Virtual Machine(가상 머신)을 줄인 말입니다. 보통 가상 머신은 켤 때 펌웨어가 먼저 돕니다. 펌웨어는 운영체제보다 먼저 떠서 장치를 점검하는 작은 프로그램입니다.
| 컨테이너 | 보통 가상 머신 | Firecracker 마이크로VM | |
|---|---|---|---|
| 커널 | 호스트와 나눠 쓴다 | 게스트마다 따로 | 게스트마다 따로 |
| 켤 때 거치는 것 | 프로세스 하나 띄우기 | 펌웨어의 장치 점검 → 운영체제 부팅 | 리눅스 커널 부팅만 |
| 흉내 내는 장치 | 없다 | 많다 | 몇 개뿐 |
KVM 위에 얹힌 얇은 모니터
Firecracker 는 하이퍼바이저의 일을 혼자 다 하지 않습니다. 프로세서와 메모리를 나누는 일은 KVM(Kernel-based Virtual Machine, 커널 기반 가상 머신)에 맡깁니다. KVM 은 리눅스 커널을 하이퍼바이저로 만들어 주는 커널 부품입니다. 게스트의 코드를 진짜 프로세서에서 돌려 줍니다.
KVM 이 맡지 않는 일도 있습니다. 디스크나 네트워크 카드 같은 장치를 흉내 내는 일과 가상 머신을 조립하는 일입니다. 이 일은 커널 밖에서 평범한 프로그램이 도는 영역, 곧 사용자 공간의 프로그램이 맡습니다.
그 프로그램을 VMM(Virtual Machine Monitor, 가상 머신 모니터)이라고 부릅니다. Firecracker 가 바로 이 VMM 입니다.
같은 일을 오래 맡아 온 VMM 으로 QEMU(Quick Emulator, 빨리 도는 에뮬레이터)가 있습니다. 에뮬레이터는 다른 기계를 소프트웨어로 흉내 내는 프로그램입니다. QEMU 는 기계 한 대를 통째로 흉내 낼 만큼 장치를 많이 갖췄습니다. Firecracker 는 같은 일을 훨씬 작게 다시 만들었습니다.
마이크로VM 한 대는 호스트에서 Firecracker 프로세스 하나입니다. 열 대를 켜면 Firecracker 프로세스가 열 개 뜹니다. 하나가 죽어도 나머지 마이크로VM 은 영향을 받지 않습니다.
flowchart TD
subgraph U["사용자 공간"]
subgraph F1["Firecracker 프로세스 하나 · 마이크로VM 한 대"]
G1["게스트 리눅스 커널 · 고객 코드"]
D1["흉내 내는 장치 몇 개"]
end
F2["또 다른 Firecracker 프로세스 · 마이크로VM 한 대"]
end
subgraph K["호스트 리눅스 커널"]
KV["KVM"]
end
HW["프로세서"]
F1 -->|"「돌려라」를 건다"| KV
F2 -->|"「돌려라」를 건다"| KV
KV -->|"게스트 코드는 진짜 프로세서에서 돈다"| HW
Firecracker 가 하는 일은 「Firecracker 프로세스 하나」 상자 안쪽뿐입니다. 장치를 흉내 냅니다. 가상 머신을 조립해 KVM 에 「돌려라」를 겁니다. 프로세서 시간과 메모리를 나누는 일은 아래의 KVM 과 호스트 커널이 합니다.
덜어 낸 장치
보통 가상 머신은 진짜 컴퓨터를 최대한 닮게 만듭니다. 켜면 BIOS(Basic Input/Output System, 기본 입출력 시스템) 같은 펌웨어가 먼저 돌아 장치를 점검합니다. 그다음에 운영체제를 불러옵니다. 화면, 키보드, USB(Universal Serial Bus, 범용 직렬 버스) 같은 장치도 흉내 냅니다.
Firecracker 는 이것을 대부분 버렸습니다. 펌웨어 단계를 건너뛰고 리눅스 커널을 곧장 메모리에 올려 시작합니다. 흉내 내는 장치도 게스트가 일하는 데 꼭 필요한 몇 가지로 줄였습니다.
| 남긴 장치 | 하는 일 |
|---|---|
| 블록 장치 | 게스트의 디스크입니다. 호스트의 파일 하나를 디스크처럼 보여 줍니다 |
| 네트워크 장치 | 게스트의 네트워크 카드입니다 |
| vsock(VM socket, 가상 머신 소켓) | 게스트와 호스트가 네트워크를 거치지 않고 주고받는 통로입니다 |
| 직렬 콘솔 | 게스트가 찍는 글자를 호스트에서 받아 보는 창입니다 |
위 장치들은 virtio 라는 방식으로 흉내 냅니다. virtio 는 게스트가 가상 머신 안에 있다는 것을 알고 하이퍼바이저와 약속된 방식으로 주고받는 장치 규격입니다. 진짜 장치의 동작을 하나하나 흉내 내지 않아도 되니 가볍습니다.
장치를 덜어 낸 것은 속도 때문만이 아닙니다. 장치를 흉내 내는 코드는 게스트가 건드릴 수 있는 호스트 쪽 코드입니다. 게스트가 벽을 넘으려 할 때 두드리는 곳이 바로 이 코드입니다. 흉내 내는 장치가 적으면 뚫릴 수 있는 코드도 적습니다.
Rust 로 짠 까닭도 같은 쪽에 있습니다. Rust 는 메모리를 잘못 건드리는 실수를 컴파일할 때 많이 막아 주는 언어입니다. 호스트 쪽 코드의 흔한 구멍이 그런 실수에서 나옵니다.
리눅스 커널은 부팅을 마치면 맨 처음 /sbin/init 이라는 프로그램을 실행합니다. 켜라는 요청을
받은 때부터 이 프로그램이 뜨기까지 125 밀리초 이하로 잡혀 있습니다. 직렬 콘솔을 끄고 최소한으로
줄인 커널과 파일 시스템으로 잰 값입니다.
메모리도 적게 씁니다. Firecracker 프로세스 안에는 따로 도는 실행 흐름인 스레드가 여럿 있습니다. 그중 장치를 흉내 내고 가상 머신을 조립하는 스레드들을 VMM 스레드라고 부릅니다.
VMM 스레드가 쓰는 메모리는 5 MiB(메비바이트) 이하로 잡혀 있습니다. 게스트에게 내준 메모리는 빼고 센 값입니다. 돌리는 일에 따라 달라집니다.
잃은 것도 분명합니다. 화면과 키보드가 없으니 데스크톱을 올릴 수 없습니다. 펌웨어 없이 리눅스 커널을 곧장 올리는 방식이라 게스트는 리눅스를 전제로 합니다. 윈도우 게스트는 돌리지 못합니다.
한 대를 켜는 순서
Firecracker 는 설정 파일을 읽어 켜지 않습니다. 떠 있는 Firecracker 프로세스에 요청을 보내 한 부분씩 조립합니다. 이렇게 프로그램이 다른 프로그램에게 일을 시키는 약속된 통로를 API(Application Programming Interface, 응용 프로그램 인터페이스)라고 부릅니다.
이 API 를 부르는 쪽은 대개 사람이 아니라 프로그램입니다. 이 편에서는 그 프로그램을 관리 프로그램이라고 부릅니다. AWS Lambda 같은 서버리스 서비스의 제어 코드가 그런 관리 프로그램입니다. 컨테이너를 띄우는 도구도 이 몫을 맡습니다. 여러 대를 켜고 지우는 일도 관리 프로그램이 합니다.
관리 프로그램이 Firecracker 프로세스를 띄우면 그 프로세스가 호스트에 유닉스 도메인 소켓 하나를 엽니다. 유닉스 도메인 소켓은 같은 기계 안의 프로그램끼리만 주고받는 통로입니다. 관리 프로그램은 이 소켓으로 HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜) 요청을 보냅니다.
sequenceDiagram
participant M as 관리 프로그램
participant F as Firecracker 프로세스
participant K as KVM
Note over M,F: 관리 프로그램이 프로세스를 띄우면 소켓이 열린다
M->>F: 요청 1 · 게스트 커널 파일은 이것이다
M->>F: 요청 2 · 디스크로 쓸 파일과 네트워크 장치를 붙여라
M->>F: 요청 3 · 프로세서 수와 메모리 크기는 이만큼이다
M->>F: 요청 4 · 켜라
F->>K: 가상 머신과 가상 프로세서를 만든다
F->>K: 돌려라
Note over K: 게스트 커널이 부팅한다
요청 넷은 모두 같은 소켓으로 들어갑니다. 조립이 끝나야 마지막 「켜라」가 먹힙니다. 그 뒤로 KVM 에 가상 머신을 만들고 돌리게 하는 일은 Firecracker 가 알아서 합니다.
jailer 로 한 겹 더 가두기
Firecracker 에는 jailer 라는 작은 프로그램이 딸려 있습니다. 운영 환경에서는 Firecracker 를 직접 띄우지 않고 jailer 를 거쳐 띄웁니다.
벽이 두 겹이기 때문입니다. 첫 겹은 KVM 이 세운 가상 머신의 벽입니다. 게스트가 그 벽을 넘으면 닿는 곳이 Firecracker 프로세스입니다. 그 프로세스가 호스트 전체를 만질 수 있으면 벽이 한 겹뿐인 것과 같습니다. 둘째 겹은 Firecracker 프로세스 자신을 가두는 울타리입니다.
jailer 가 치는 울타리는 리눅스 커널의 기능 넷으로 짭니다.
| 기능 | 무엇을 좁히나 |
|---|---|
| chroot | 프로세스가 볼 수 있는 파일 시스템을 한 디렉터리 아래로 좁힙니다 |
| 네임스페이스 | 프로세스가 볼 수 있는 다른 프로세스와 네트워크를 갈라 놓습니다 |
| cgroup(control group, 컨트롤 그룹) | 프로세스가 쓸 수 있는 프로세서 시간과 메모리에 상한을 겁니다 |
| 권한 내려놓기 | 관리자 권한을 버린 평범한 사용자로 Firecracker 를 실행합니다 |
프로그램이 커널에게 일을 시킬 때 부르는 함수를 시스템 콜이라고 부릅니다. 파일을 여는 것도 프로세스를 만드는 것도 시스템 콜입니다.
Firecracker 는 뜬 뒤에 스스로 seccomp 필터도 겁니다. seccomp 는 프로세스가 부를 수 있는 시스템 콜을 목록으로 좁히는 커널 기능입니다. 목록 밖의 것을 부르면 커널이 그 프로세스를 멈춥니다.
sequenceDiagram
participant M as 관리 프로그램
participant J as jailer
participant F as Firecracker
M->>J: Firecracker 를 띄워 달라
Note over J: chroot 로 보이는 디렉터리를 좁힌다
Note over J: 네임스페이스로 갈라 놓는다
Note over J: cgroup 으로 상한을 건다
Note over J: 관리자 권한을 버린다
J->>F: 울타리 안에서 Firecracker 를 실행한다
Note over F: 스스로 seccomp 필터를 건다
울타리는 Firecracker 가 뜨기 전에 다 쳐집니다. 그래서 Firecracker 는 처음부터 좁혀진 세상에서 시작합니다.
flowchart TD
subgraph U["사용자 공간"]
subgraph J["둘째 겹 · jailer 의 울타리와 seccomp 필터"]
subgraph P["Firecracker 프로세스"]
subgraph V["첫 겹 · KVM 이 세운 가상 머신의 벽"]
C["게스트 커널 · 고객 코드"]
end
end
end
end
H["호스트 리눅스 커널과 나머지 호스트"]
U -->|"두 겹을 다 넘어야 닿는다"| H
고객 코드가 호스트에 닿으려면 가상 머신의 벽을 넘어야 합니다. 그다음 울타리를 또 넘어야 합니다. 둘 중 하나만 뚫려서는 호스트에 닿지 못합니다.
고르는 때와 피하는 때
Firecracker 가 맞는 일은 짧게 살고 자주 켜고 끄는 일입니다. 남이 올린 코드를 받아 돌리는 서버리스 함수가 그렇습니다. 믿을 수 없는 코드를 따로 떼어 돌리는 샌드박스도 그렇습니다. 한 번 쓰고 버릴 가상 머신이 많이 필요할수록 켜는 속도와 몸집이 값을 합니다.
반대로 한 대를 오래 크게 굴리는 서버라면 보통 가상 머신으로 충분합니다. 켜는 시간은 한 번만 치르면 됩니다. 덜어 낸 장치가 아쉬워질 일이 더 많습니다. 게스트가 윈도우이거나 화면이 필요해도 다른 하이퍼바이저를 골라야 합니다.
호스트 쪽 조건도 있습니다. KVM 을 쓰므로 호스트가 리눅스여야 합니다. 프로세서의 가상화 기능도 켜져 있어야 합니다.
클라우드에서 빌린 가상 머신 안에서 Firecracker 를 돌리려면 그 가상 머신 안에서 다시 가상 머신을 켤 수 있어야 합니다. 이것을 중첩 가상화라고 부릅니다.
중첩 가상화가 안 되면 베어 메탈 서버를 빌립니다. 베어 메탈은 가상화하지 않은 물리 서버를 통째로 빌리는 방식입니다.
flowchart TD
A{"호스트가 리눅스이고 KVM 을 쓸 수 있나"} -->|아니오| X["Firecracker 는 못 쓴다"]
A -->|예| B{"게스트가 리눅스이고 화면이 필요 없나"}
B -->|아니오| Y["보통 가상 머신"]
B -->|예| C{"짧게 살고 자주 켜고 끄나"}
C -->|아니오| Y
C -->|예| F["Firecracker"]
관련 항목
Firecracker 가 기대는 리눅스 가상화 부품
KVM · 하이퍼바이저 · 가상 머신 · 하드웨어 가상화 확장 · vCPU · 게스트 · 호스트 · 커널 · 사용자 공간 · 중첩 가상화
Firecracker 가 흉내 내는 장치 규격
virtio · vsock · 블록 장치 · 직렬 콘솔 · 가상 디바이스 · 펌웨어 · BIOS
Firecracker 와 같은 몫을 맡는 다른 VMM
VMM · QEMU · Cloud Hypervisor · crosvm · rust-vmm
Firecracker 가 프로세스를 가둘 때 쓰는 커널 기능
chroot · 네임스페이스 · cgroup · seccomp · 시스템 콜 · 최소 권한 원칙
Firecracker 와 벽의 두께를 두고 겨루는 격리 방식
컨테이너 · gVisor · 샌드박스 · 멀티테넌시 · 마이크로VM · 베어 메탈
Firecracker 를 밑에 깔고 코드를 돌리는 서비스와 도구
AWS Lambda · AWS Fargate · Kata Containers · firecracker-containerd · 서버리스 · AWS
Firecracker 를 조립하고 켜는 통로
API · REST API · 유닉스 도메인 소켓 · HTTP · 프로세스 · 스레드
Firecracker 를 짠 언어와 그 성질
다른 이름: firecracker · 파이어크래커 · Firecracker microVM · 파이어크래커 마이크로VM