[gVisor] The True Cost of Containing : A g Visor Case Study

The True Cost of Containing : A g Visor Case Study

Containerization (OS-level Virtualization) 는 Guest OS 없이 Application 에 필요한 라이브러리를 하나의 이미지로 패키징 해주므로 오버헤드가 줄어들고, 관리하기 편해진다. 이러한 장점에도 VM 의 각 APP 들은 자신들의 Guest OS 에서 실행되는 반면, Container 는 공통의 OS (Common OS) 에서 실행되기 때문에 효율적이지만, 고립성(Isolation) 및 보안에 취약하다.

gVisor 는 보안 중심으로 설계한 컨테이너 시스템이다. 예를 들어 인터넷 웹상에서 돌아다니는 코드를 다운받았다고 하자. 이는 Untrusted Code (신뢰할수 없는 코드) 이다. gVisor 는 이러한 코드를 실행하여도 Host 운영체제가 공격당하거나 스파이 당하지 않도록 설계되었다. 즉,

  • 호스트에 접근할 수 없음. (단일 서브 시스템)

  • GCF(Google Cloud Function) 에도 isolation mechanism 을 제공한다.

Docker 같은 경우, Container Engine 으로 Docker Daemon 이 들어가고, gVisor 는 Container Engine 으로 Sentry 가 있다. Sentry 는 "초소, 보초막" 이라는 뜻인데, Sentry 의 역할을 잘 나타내는 단어 같다. Sentry 는 리눅스이며, 그 자신도 리눅스 위에서 돌아간다. 즉 Linux-on-Linux Architecture 가 더 깊은 층을 쌓으면서 보안성을 높인다고 생각할 수 있다. Sentry 는 2가지 모드를 가진다.

  • ptrace mode : ptrace(process trace) 하나의 프로세스가 다른 프로세스들을 감시하면서 호스트 접근을 가로막는 유형이다.

  • KVM mode : Sentry 를 게스트 OS 처럼 역할하게끔 하는 것이다.

gVisor 는 다음 2가지로 구성되어 있다.

  • Sentry

  • Gofer

Sentry

System Call 이란 어플리케이션이 운영체제에게 하는 하드웨어 접근(프로세스 생성)을 요청하기 위해 추상화된 함수의 집합이다. 예를 들어 open(file 이름) 하면 해당 파일이 열리도록 구현되있다. 일반적으로 다른 함수와 크게 다를게 없어보이지만, 결론적으로 운영체제에 System Interrupt 가 발생하여 모드 전환(사용자모드 -> 커널 모드)이 일어난다.

Sentry 는 조금 특이하다. 보통 시스템콜은 커널에 구현되어 있는데 Sentry 는 User space Kernel 이다. "사용자 공간"에 구현된 커널이다. 특징을 보자면,

  • Reduce Syscall : 총 319개의 리눅스 시스템콜 중에 211개만 사용하도록 하며, 211개의 시스템 콜을 55개로 구현하여서, 더 적은 시스템 콜의 사용으로 더 적은 확률로 호스트에 접근하도록 하였다.

  • Filtering Syscall : 특히나 seccomp filter 를 사용하여서 open, socket 은 common attack vectors 로 정의되어 실행 불가능하다. Sentry 의 Memory 크기는 15MB 이다.

File Handling

  • Internally serve file systems: 호스트에게 요청할 필요없이 자체적으로 I/O 를 구현하였다. (ex tmpfs, procfs, overlayfs)

  • Service open w/ Gofer : Gofer 가 Sentry 를 대신하여 외부 파일을 연 후 Plan 9 Channel 에 전달한다.

Mode Switch

Sentry 가 KVM 모드로 실행될 경우, GR0 (Guest Ring) 에서 HR3 (Host Ring) 로 전환되어야 한다. 파일을 읽은 후 read, write 와 같은 시스템콜이 GR0에서 직접 호출하지 않기 위해서이다.

Networking

Sentry 의 Network 는 GO 로 구현되어 있다. 보통은 패킷의 헤드는 응용 계층에서 수정 불가능하다. 커널모드에서만 수정 가능하다.

하지만, raw socket 위의 기존 네트워크 스택을 거치지 않고 유저 스페이스로 바로 패킷을 올려버리기 때문에 헤더 수정이 가능하다. Sentry 는 유저 공간에 구현된 커널이고 user-space networking stack(netstack) 때문에 raw socket 을 사용하는 것 같다.

Performance

runc (based on Linux namespaces and cgroups) : 리눅스 환경에서 지원하는 컨테이너(LXC)로

  • namespace, cgroups : 리소스 관리 장치를 사용하여 분리된 환경을 만들고,

  • chroot : 특정 디렉토리를 루트 디렉토리를 사용하여 분리된 환경을 만든다.

runsc (gVisor runtime engine)

하나의 컨테이너를 실행 종료하는 데 걸리는 시간은 gVisor 가 10% 더 오래 걸렸지만, 그렇게 나쁘지는 않아 보인다.

runc (1.014s) < gVisor + KVM (1.117s) < gVisor + ptrace (1.181s)

System Call Performance

gVisor 는 SysCall 을 호출할 때, 어떤 시스템콜인지에 따라서

  • sentry : File I/O 처럼 내부에서 처리할 수도 있고

  • host : 호스트에 있는 명령을 호출할 수도 있고

  • gofer : Gofer 프로세스에 도움을 받을 수도 있다.

gettimeofday(2) 라는 시스템콜을 3가지 버전으로 나누어 실행해봤고 리눅스 컨테이너보다 gVisor 가 더 느린것을 확인했다. KVM 모드는 9배 느렸으며, ptrace 모드는 72배 느렸다.

Memory Performance

1000kb 를 malloc() 하였을 시,

  • native 보다 절반 정도의 성능을 보였으며,

  • 할당하고자 하는 메모리 크기가 클수록 성능이 감소되어, 1MB allocation 시 gVisor 은 0에 가까워진다. 즉, gVisor 는 대용량 메모리 할당(Big memory allocation) 이슈가 있다고 볼 수 있다.

Network Performance

wget 을 사용하여 netstack 을 평가하였을 시, 적은 용량의 데이터는 잘 다운되었지만, 용량이 클수록 성능이 좋지 않았다.

Storage : File Performance

Gofer 에 도움을 받는다. tmpfs(Temp File Storage)는 리눅스의 파일 스토리지로 /dev/shm 안에 디렉토리를 만들어서 사용한다. 재부팅 되면 사라지는 휘발성 메모리이다.

  • Sentry's Internal tmpfs :

  • External tmpfs (on host) :

파일을 열고 종료하는데 gV 는 12배 느리다.

또한 용량을 다르게 해가면서 읽는 처리량을 보았을 때 적은 사이즈는 처리량이 작았지만, 사이즈가 커질수록 처리량이 개선되는 것을 보였다. 이것은 RAM(Memory-based) file (ex. tmpfs)이라서 그렇고, Disk 파일은 아니다.

Write Performance

쓰기 성능도 읽기 성능과 비슷하다. 하지만 Read 와 다르게 메모리 사이즈가 큰경우(64kb ~ 1MB) gV+Int 가 runc 보다 성능이 좋다. (why??)

gVisor I/O 는 모드와 프로세서 스위치되면서 오버헤드가 있다. 작은 데이터를 처리할 경우, 이 오버헤드가 증가한다. Sentry 내부에서 처리하는 경우, 스위칭이 필요없기 때문에 성능향상이 크게 나타난다.

KVM 을 Exit 할 경우 두가지 케이스가 있는데

  • Userspace Exit : 유저 모드에서 돌고 있는 모드를 떠나서 Host 시스템콜을 호출하는 상황

    • Sentry 내부에서 파일 읽기를 수행하면 구지 Host Syscall 을 할 필요가 없으므로 메모리 사이즈가 클수록 감소

    • External 로 할경우 메모리 사이즈랑 관계 없이 동일함.

  • Pagefault : 프로그램 실행 도중 RAM(물리메모리) 에서 블록 발견 못하고 디스크(가상메모리)에서 찾아야 되는 상황

    • 메모리 사이즈가 클수록 Pagefault 가 증가되는 경향을 보임.

Critique Point

  • No I/O Evaluation on disk (Done with tmpfs)

  • Network evaluation limited (Only downloading)

  • Not comprehensive

Last updated