사전 Firecracker
구현체

Firecracker

gabury1고친 사람 github-actions[bot]

Firecracker 는 가상 머신을 아주 가볍게 띄워 주는 프로그램입니다. 함수 한 번을 부를 때마다 가상 머신을 한 대씩 새로 켜도 될 만큼 빨리 뜹니다. 메모리도 적게 먹습니다. 그 대신 보통 가상 머신이 갖춘 장치 대부분을 덜어 냈습니다.

쉽고 빠른 이해
  • 무슨 일을 하나 — 장치를 덜어 낸 작은 가상 머신을 1초도 안 걸려 띄워 줍니다. 남이 올린 코드를 받아 돌려 주는 클라우드 서비스가 이것으로 호출마다 가상 머신을 켜고 그 안에서 코드를 돌립니다.
  • 왜 있나 — 서로 모르는 고객의 코드를 한 서버에서 돌리려면 벽이 두꺼워야 합니다. 컨테이너는 빠릅니다. 하지만 운영체제의 핵심을 나눠 쓰므로 벽이 얇습니다. 보통 가상 머신은 벽이 두껍습니다. 대신 켜는 데 오래 걸리고 무겁습니다. Firecracker 는 그 둘 사이의 빈칸을 메웁니다.
  • 어떻게 도나
    1. 프로세서와 메모리를 나누는 일은 리눅스 커널에 든 가상화 기능에 맡깁니다
    2. 흉내 내는 장치는 디스크와 네트워크 등 몇 개로 줄입니다. 켤 때 거치는 점검 단계도 건너뜁니다
    3. 가상 머신 한 대마다 프로그램 하나를 띄웁니다. 그 프로그램을 한 겹 더 가둡니다
  • 대가 — 화면이나 키보드 같은 장치가 없습니다. 윈도우나 데스크톱을 올리는 데는 못 씁니다. 호스트가 리눅스여야 합니다. 프로세서의 가상화 기능도 켜져 있어야 합니다.

상세

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 를 짠 언어와 그 성질

Rust · 메모리 안전성 · 오픈 소스

다른 이름: firecracker · 파이어크래커 · Firecracker microVM · 파이어크래커 마이크로VM