eBPF 살펴보기

최대한·2023년 3월 31일
2
post-thumbnail

목표

eBPF 의 컨셉 및 eBPF 프로그램과 이를 활용한 모던 소프트웨어 개발의 이점들에 대한 간략한 이해

목차


  1. eBPF 란?
    1. 사용되는 영역
    2. Benefits and Impact of eBPF
  2. Background: BPF (Berkeley Packet Filter)
  3. eBPF Architecture
    1. eBPF 프로그램 작성
      1. High-Level Language Programming
      2. Compilation
    2. Loader and Verifier
    3. JIT (Just In Time) Compilation
    4. Maps
  4. eBPF Tools and Ecosystem
    1. eBPF 개발 도구들
      1. BCC (BPF Compiler Collection)
      2. libbpf
    2. bpftrace: high-level tracing language
    3. Networking: XDP (eXpress Data Path), traffic control
    4. Cilium: eBPF-based networking, load balancing, and security project
  5. 결론

1. eBPF 란?


  • eBPF 란 리눅스 커널에서 일어나는 다양한 이벤트들에 대해 사용자 정의된 함수들이 커널 내 샌드박스 환경에서 동작하게끔 하는 기술
  • 커널 소스 코드를 바꾸거나 모듈을 추가할 필요 없이 프로그램을 운영체제의 커널 공간에서 실행하는 기술

1.1 사용되는 영역

  • Networking
  • Security
  • Tracing and Profiling
  • Observability and Monitoring

1.2 Benefits and Impact of eBPF

1.2.1. 성능 향상

  • 사용자 정의 함수를 커널단에서 실행시켜 system call 과 context switch 의 횟수를 감소시켜 성능 향상을 기대할 수 있음

1.2.2. Real-time visibility and monitoring

  • eBPF 의 tracing & profiling 능력은 실시간 visibility 를 제공하여, 발생하는 이슈를 쉽게 확인하고 해소할 수 있도록 도와줌

1.2.3. 향상된 보안

  • 커스텀된 보안규칙과 필터를 적용하면 특정 요구사항에 맟는 보안 시스템을 만들어 보안을 향상시킬 수 있음

1.2.4. Customizability and programmability

  • 개발자가 커널 기능을 이용해 특정 Use Case 를 위한 코드를 직접 작성할 수 있음

1.3 How it’s used?

eBPF는 다양한 종류의 트래픽에 걸쳐 작동하므로 통합 관찰가능성이라는 기업의 목표를 달성하는 데 큰 도움이 된다. 예를 들어, 데브옵스 엔지니어는 전체 트레이스 요청, 데이터베이스 질의, HTTP 요청, gRPC 스트림 수집은 물론 CPU 사용량이나 전송 바이트 수와 같은 자원 활용 지표(메트릭스) 수집에도 eBPF를 활용할 수 있다. 따라서 해당 기업은 관련 통계를 산출하고 데이터의 개요를 파악해 다양한 기능의 자원 소모 현황을 이해할 수 있다. 또한, eBPF는 암호화된 트래픽을 처리할 수 있다.

2. Background: BPF (Berkeley Packet Filter)


  • BPF (cBPF, classic BPF) 란
    • Berkeley Packet Filter 의 약자로, 1992 년에 Berkeley 연구실에서 패킷 필터링과 캡처링을 위한 목적으로 Steven McCanne 과 Van Jacobson 에 의해 처음 개발되었음
  • eBPF 란
    • Extended BPF 의 약자로, 2014 년에 기존 BPF 기술의 개선된 버전으로써 도입됨
    • 기존 BPF 프로그램에 비해 더많은 기능들을 제공
    • 개선된 C 계열 언어로 작성하여 LLVM 같은 컴파일러에 의해 바이트코드로 컴파일
    • 리눅스 커널 안에 안전하고, 샌드박스된 환경에서 동작
  • 현재는 eBPF 를 사용하기 때문에 BPF 나 eBPF 모두 같은 개념이라고 생각하면 됨

3. eBPF Architecture


3.1 eBPF 프로그램 작성

3.1.1 High-Level Language Programming

  • C 계열 언어 혹은 Go, Rust, Lua 및 Python 과 같은 High Level 언어를 이용하여 작성
#include <linux/sched.h>
#include <linux/fs.h>

struct key_t {
    u32 pid;
    u64 inode;
};

BPF_HASH(counts, struct key_t, u64, 256);

int trace_read(struct pt_regs *ctx, struct file *file, char __user *buf, size_t count)
{
    struct key_t key = {};
    struct task_struct *task = (struct task_struct *)bpf_get_current_task();

    key.pid = bpf_get_current_pid_tgid();
    key.inode = file->f_inode->i_ino;

    u64 *val = counts.lookup(&key);
    if (val) {
        (*val)++;
    } else {
        u64 zero = 0;
        counts.update(&key, &zero);
    }

    return 0;
}
from bcc import BPF

# define the eBPF program
program = """
#include <linux/sched.h>
#include <linux/fs.h>

struct key_t {
    u32 pid;
    u64 inode;
};

BPF_HASH(counts, struct key_t, u64, 256);

int trace_read(struct pt_regs *ctx, struct file *file, char __user *buf, size_t count)
{
    struct key_t key = {};
    struct task_struct *task = (struct task_struct *)bpf_get_current_task();

    key.pid = bpf_get_current_pid_tgid();
    key.inode = file->f_inode->i_ino;

    u64 *val = counts.lookup(&key);
    if (val) {
        (*val)++;
    } else {
        u64 zero = 0;
        counts.update(&key, &zero);
    }

    return 0;
}
"""

# create the BPF module
b = BPF(text=program)

# attach the eBPF program to a tracepoint
b.attach_kprobe(event="vfs_read", fn_name="trace_read")

# display the map contents every second
while True:
    print("PID   INODE   COUNT")
    counts = b["counts"]
    for k, v in counts.items():
        print(f"{k.pid}   {k.inode}   {v.value}")
    print("")
    counts.clear()
    time.sleep(1)

잠깐!

Kprobe 란?: Kernel code 에 동적으로 중단점을 삽입하여 사용자가 정의하는 핸들러 함수가 실행되도록 하는 강력한 도구
Uprobe 란?: User-level 에 동작하는 function 에 삽입하는 것

  • 동적으로 dynamic instrumentation 코드를 삽입하여 모니터링

✓ 같이 알아보기

Tracepoint 란?: 소스코드의 특정 위치(커널 이벤트)에 정적으로 마킹하여 디버깅을 위해 데이터를 수집하게 해주는 것. 커널 소스코드에 미리 정의된 매크로를 사용하고, trace-cmd 툴 사용.
USDT 란: User Space Tracing Dynamic Tracing 의 약자로, 유저레벨의 이벤트를 관측하고 profiling 가능하게 해준다. 컴파일된 코드와 dtrace 툴 사용

  • 디버깅 및 프로파일링 용도

3.1.2 Compilation

  • BCC(BPF Compiler Collection) 와 같은 툴킷을 사용하여 byte code 로 컴파일
    • BCC 는 backend compiler 로 LLVM (low-level virtual machine) 을 사용
    • LLVM 의 frontend compiler 로는 Clang 을 사용

3.2 Loader and Verifier

eBPF 프로그램이 커널로 로드될 때 Loader 와 Verifier 를 거치게 됨

  • Loader: eBPF 바이트 코드를 파싱하고 커널단으로 로드
  • Verifier: eBPF 프로그램이 충분한 권한이 있는지 확인 및 Kernel 에 치명적인 문제가 발생할 수 있는지 등등을 확인
    • loop 혹은 bounded execution

3.3 JIT (Just In Time) Compiler

  • bytecode 는 기계어가 아닌만큼 해당 cpu 아키텍처 친화적이지 않기 때문에 약간 느릴 수 있음
  • JIT 컴파일러는 이를 해결하기 위해 eBPF 의 bytecode를 런타임에 동적으로 기계어로 변환

3.4 Maps

eBPF 는 리눅스 VM 에서 돌아가는 프로그램이지만 안전하고 효율적인 사용자 정의 함수들을 실행시키기 위해 Heap 영역을 사용하지 않음. (Stack size: 512 bytes per program)

  • 성능: 동적으로 할당 및 할당 해제를 할 경우, 네트워크 모니터링이나 보안같은 고성능 앱에 성능 이슈를 야기할 수 있음.
  • 보안: 버퍼 오버플로우나 메모리 누수같은 공격에 취약

이에 대해 eBPF 는 Maps 이라는 특수한 형태의 메모리 관리 메카니즘을 을 제공. 이는 유저영역 앱과 공유될 수 있다.

3.5 And more...

4. eBPF Tools and Ecosystem


4.1 eBPF 개발 도구들

유저레벨에서 eBPF 프로그램을 개발하고, 컴파일 및 로딩하게끔 도와주는 프로그램들

4.1.1 BCC (BPF Compiler Collection)

  • BPF 프로그램을 개발하는 오래된 방법 중 하나. 개발, 디버깅, eBPF 프로파일링 및 다양한 pre-built 툴과 script 를 제공
  • 런타임에 컴파일
    • LLVM / Clang 사용

  • high-level library
  • Python 과 Lua 의 바인딩을 지원
    • C 이외에도 다른 언어로도 eBPF 프로그램 작성 가능
  • 보통 더 작성하기 쉽고 간편
  • 런타임 컴파일때문에 오버헤드가 발생할 수 있음
  • 타켓 호스트 머신에 설치되어야하는 커널 헤더 패키지에 의존함
    • 대부분의 경우 문제되지 않으나, 여러 머신에 작업할 때 셋업과 유지보수가 번거로울 수 있음

4.1.2 libbpf

  • 비교적 최근에 개발된 BPF 개발 library
  • low-level library
  • 리눅스 커널의 프로젝트며 유지보수되고 있음
  • BTF(BPF type format) 을 사용하여 llvm 의 의존도를 없애서 바로 커널에 로드될 수 있음
    • 더 직접적인 인터페이스를 제공하여 더 나은 성능과 유연함을 이끌어낼 수 있음
  • Python 이나 Lua 등의 지원은 불가능하나, libbpf-rs 같은 러스트용 라이브러리로는 바인딩 가능

개인적인 비교

4.2 bpftrace: high-level tracing language

  • eBPF 를 기반으로 한 리눅스의 high-level tracing 트레이서
  • One line script 작성으로 eBPF 프로그램을 사용하고 데이터 수집
# bpftrace -e 'kprobe:vfs_read /pid == 30153/ { @start[tid] = nsecs; }
kretprobe:vfs_read /@start[tid]/ { @ns = hist(nsecs - @start[tid]); delete(@start[tid]); }'
Attaching 2 probes...
^C

@ns:
[256, 512)         10900 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                      |
[512, 1k)          18291 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[1k, 2k)            4998 |@@@@@@@@@@@@@@                                      |
[2k, 4k)              57 |                                                    |
[4k, 8k)             117 |                                                    |
[8k, 16k)             48 |                                                    |
[16k, 32k)           109 |                                                    |
[32k, 64k)             3 |                                                    |

bpftrace 를 이용하여 생성된 Flame Graph

4.3 Networking: XDP (eXpress Data Path), traffic control

XDP 는 리눅스 커널에서 아주 고속으로 패킷들을 처리할 수 있는 방법을 제공하는 기술

XDP는 eBPF Hook의 한 종류로 네트워크 스택에서 패킷을 인터셉트하고 처리. 이러한 XDP 기능을 지원하기 위해 네트워크 장치 드라이버 내에는 XDP 관련 코드가 구현이 되어있다.

  • XDP는 Kernel network stack을 거치지 않고 User space로 frames를 이동
  • Kernel을 완전히 우회(bypass) 하는 것은 아니지만 최대한 짧은 시간내에 Kernel내에서의 빠른 경로(in-kernel fast path)를 생성
    • 커널 바이패스가 필요하지 않음. XDP는 패킷이 커널 네트워킹 스택에 도달하기 전에 실행
  • Kernel space와 User space간 zero copy 및 XDP bytecode를 NIC에서 offloading
  • 특별한 하드웨어가 필요하지 않음. XDP는 모든 하드웨어를 실행할 수 있다. (최적화된 장치나 드라이버가 없으면 속도가 저하될 수 있지만 테스트 목적으로 특수 하드웨어가 필요하지 않음)
  • 베어메탈 패킷 처리

단점

  • NIC 에서 XDP offloading 을 지원하지 않을 경우 퍼포먼스가 잘 안나옴
    • 따라서 tc(traffic control) ingress/egress hook에만 bpf 붙여서 쓰기도 함 (cilium 기준)

XDP 와 eBPF 의 Use Case

  • 디도스 공격
  • 로드 밸런서
  • 방화벽

4.4 Cilium: eBPF-based networking, load balancing, and security project

  • Linux 컨테이너 관리 플랫폼을(Docker, Kubernetes) 사용하여 배포된 애플리케이션 서비스 간 네트워크 연결을 보호하는 오픈 소스 소프트웨어
  • 네트워킹, 로드 밸런싱, 보안을 위해 eBPF를 활용하는 오픈 소스 프로젝트로, 컨테이너 및 클라우드 네이티브 환경을 위한 강력한 기능을 제공
  • eBPF 의 특성 덕분에 application 코드나 컨테이너 구성을 변경하지 않고도 Cilium 보안 정책 적용 가능

5. 결론

  • eBPF 는 개발자가 Linux 커널 내부에서 실행되는 사용자 정의 함수/코드를 만들 수 있는 강력한 기술
  • 이 코드는 네트워크 패킷을 필터링하거나 시스템 성능을 프로파일링하는 등 시스템 동작을 모니터링하고 수정하는 데 사용
  • 유연성과 효율성이 매우 뛰어나고 C, Python, Go 등 다양한 프로그래밍 언어로 작성 가능
  • eBPF는 네트워크 모니터링 및 보안에서 성능 분석 및 디버깅에 이르기까지 다양한 잠재적 사용 사례가 있다
  • 업계와 학계에서 eBPF의 채택이 증가함에 따라 eBPF가 Linux 시스템 개발 및 관리 분야에서 점점 더 중요한 기술이 될 것임이 분명해졌습니다.

References


profile
Awesome Dev!

0개의 댓글