한 줄짜리 eBPF 로 운영 질문에 답한다
이 실습은 VM 에서 돕니다
우분투 24.04 VM 한 대입니다. bpftrace 는 BPF 프로그램을 커널에 올리므로 CAP_BPF 와 CAP_PERFMON 이 필요합니다. 실습 파드에는 그 권한이 없고, 줄 수도 없습니다. 그래서 VM 에서 돕니다.
목표
bpftrace 한 줄로 tracepoint·kprobe·kretprobe·uprobe 에 프로그램을 붙이고, 결과를
커널 안의 맵에 모아서 끝날 때 한 번에 받습니다. 가게 프로그램 kx-shop 이 연
파일을 세고, 읽기 크기와 지연을 히스토그램으로 그리고, 함수 인자로 매출을 합칩니다.
그리고 "누가 내 프로세스를 죽였나" 라는 운영 질문에 신호 tracepoint 로 답합니다.
왜 중요한가
1단원에서 strace 가 프로그램을 수십 배 느리게 만드는 것을 쟀습니다. 사건마다
프로세스를 멈추고 사용자 공간으로 넘겨 주기 때문입니다. bpftrace 는 반대로 합니다.
사건이 일어난 자리(커널 안)에서 작은 프로그램이 돌고, 그 자리에서 세고 더해 맵에
넣고, 사용자 공간으로는 마지막 결과만 넘깁니다. 그래서 운영 중인 서버에서도 짧게
켜 볼 수 있습니다. 물어볼 질문이 정해지면 한 줄이면 됩니다.
단계
openat관련 syscall tracepoint 목록을/root/kx/bt/01-list.txt에 저장하세요.kx-shop이 연 파일을 이름별로 세어/root/kx/bt/02-open.txt에 저장하세요.kx-shop의vfs_read요청 크기를 히스토그램@bytes로 그려/root/kx/bt/03-hist.txt에 저장하세요.vfs_read의 지연을 재는 스크립트/root/kx/bt/vfs_lat.bt를 쓰고 결과를/root/kx/bt/04-lat.txt에 저장하세요.- uprobe 로
kx_checkout의 호출 수와 금액 합을/root/kx/bt/05-uprobe.txt에 저장하세요. kx-victim을 누가 죽이는지 신호 tracepoint 로 잡아/root/kx/bt/06-signal.txt에 저장하세요.- 다시 쓸 수 있는 스크립트
/root/kx/bt/top-open.bt를 만드세요. - 정리를
/root/kx/bt/08-report.md에 쓰세요.
참고
bpftrace -l '<패턴>'은 붙일 수 있는 자리를 찾습니다.-e '<프로그램>'은 한 줄 실행,-c '<명령>'은 그 명령이 끝날 때까지만 추적,-o <파일>은 출력을 파일로 보냅니다.- 맵은
@이름[키] = count()처럼 씁니다. 프로그램이 끝나면 남은 맵을 전부 출력합니다. 쓰고 나서 지우지 않은 맵도 출력됩니다. kx-shop이 처리하는 주문 수는 VM 마다 다릅니다. 숫자를 추측하지 말고 세세요.- 흔한 실수:
-c없이 실행하고 Ctrl-C 를 기다리지 않는 것. 결과는 끝날 때 나옵니다.
붙일 자리를 찾는다
bpftrace -l 로 이름이 sys_enter_open 으로 시작하는 syscall tracepoint 를 찾아 목록을 /root/kx/bt/01-list.txt 에 저장하세요.
패턴에 별표를 쓸 수 있습니다. 자리의 이름은 종류:분류:이름 꼴이고, 시스템 콜 진입 tracepoint 는 syscalls 분류에 있습니다. 목록에 sys_enter_openat 이 보여야 합니다.
연 파일을 센다
tracepoint:syscalls:sys_enter_openat 에 붙어 comm 이 kx-shop 인 것만 골라 파일 이름별로 세는 한 줄 프로그램을 -c /usr/local/bin/kx-shop 으로 실행하고, 출력을 /root/kx/bt/02-open.txt 에 저장하세요.
tracepoint 의 인자는 args 로 읽습니다. 파일 이름은 사용자 공간 포인터라 str() 로 감싸야 글자가 됩니다. /조건/ 으로 거르고 @[키] = count() 로 셉니다. 설정 파일이 몇 번 열렸는지가 이 VM 의 주문 수입니다.
읽기 크기를 히스토그램으로
kprobe:vfs_read 에서 comm 이 kx-shop 인 것만 골라 세 번째 인자(요청 크기)를 맵 @bytes 의 2의 거듭제곱 히스토그램으로 모으세요. -c /usr/local/bin/kx-shop 으로 실행하고 출력을 /root/kx/bt/03-hist.txt 에 저장하세요.
kprobe 의 인자는 arg0, arg1, … 입니다. vfs_read(file, buf, count, pos) 이니 크기는 몇 번째인지 세어 보세요. hist() 는 2의 거듭제곱 구간으로 나눠 막대를 그립니다.
들어갈 때와 나올 때
스크립트 /root/kx/bt/vfs_lat.bt 를 쓰세요. kprobe:vfs_read 에서 kx-shop 의 스레드별 시작 시각을 @start[tid] 에 적고, kretprobe:vfs_read 에서 걸린 나노초를 @lat_ns 히스토그램에 넣은 뒤 @start[tid] 를 지우게 하세요. -c /usr/local/bin/kx-shop 으로 실행한 출력을 /root/kx/bt/04-lat.txt 에 저장하세요.
nsecs 가 현재 시각(나노초)입니다. kretprobe 에서는 kprobe 에서 적어 둔 값이 있을 때만 계산하세요(/@start[tid]/). 다 쓴 칸을 delete() 로 지우지 않으면 맵이 계속 자라고, 끝날 때 남은 칸까지 출력됩니다.
사용자 함수의 인자를 읽는다
uprobe:/usr/local/bin/kx-shop:kx_checkout 에 붙어 호출 수를 @n 에, 첫 번째 인자(금액)의 합을 @total 에 모으고, -c /usr/local/bin/kx-shop 으로 실행한 출력을 /root/kx/bt/05-uprobe.txt 에 저장하세요.
uprobe 는 사용자 공간 함수의 첫 명령에 붙으므로 인자를 arg0 으로 읽습니다. 반환값(세금이 붙은 금액)을 보려면 uretprobe 의 retval 이지만 이 단계는 넘겨받은 금액을 합칩니다. count() 와 sum() 을 쓰세요.
누가 죽였나
tracepoint:signal:signal_generate 에서 대상 이름(args->comm)이 kx-victim 이고 신호가 SIGTERM(15)인 것을 잡아 신호 번호, 대상 이름과 PID, 보낸 프로세스의 이름과 PID, 그 부모의 이름을 한 줄로 찍고 끝나는 프로그램을 쓰세요. 그다음 kx-victim 을 백그라운드로 띄우고, 잡은 줄을 /root/kx/bt/06-signal.txt 에 저장하세요.
이 tracepoint 의 comm·pid 는 신호를 받는 쪽이고, bpftrace 의 내장 변수 comm·pid 는 지금 실행 중인(보내는) 쪽입니다. 부모 이름은 curtask->real_parent->comm 으로 읽습니다. kx-victim 은 자식 sleep 이 끝날 때마다 SIGCHLD(17)도 받으니 신호 번호로 거르세요. 첫 사건에서 exit() 하면 됩니다. bpftrace 가 붙은 다음에 kx-victim 을 띄우세요.
다시 쓸 스크립트로 남긴다
스크립트 /root/kx/bt/top-open.bt 를 쓰세요. sys_enter_openat 에서 kx-shop 의 것만 골라 키가 (comm, 파일 이름) 두 개인 맵 @opens 에 세고, END 블록에서 @opens 의 상위 5개만 출력한 뒤 그 맵을 비워야 합니다. 채점기는 이 스크립트를 -c /usr/local/bin/kx-shop 으로 직접 실행합니다.
맵 키는 @opens[comm, str(args->filename)] 처럼 쉼표로 여럿을 줄 수 있습니다. print(@맵, 개수) 는 상위 몇 개만 찍고, clear(@맵) 은 비웁니다. 비우지 않으면 bpftrace 가 끝날 때 전부를 한 번 더 찍습니다.
무엇을 어디에 붙였나
/root/kx/bt/08-report.md 에 ## 무엇을 붙였나·## 맵에 모으는 이유·## 운영에서 조심할 것 세 절을 쓰세요. 본문에 tracepoint·kprobe·uprobe 가 나오고, 6단계에서 잡은 보낸 프로세스 이름을 그대로 인용해야 합니다.
첫 절에는 붙인 자리 네 종류(tracepoint, kprobe, kretprobe, uprobe)가 각각 무엇을 보는지, 둘째 절에는 strace 와 무엇이 다른지, 셋째 절에는 kprobe 가 커널 판에 따라 깨질 수 있다는 것과 맵을 지우지 않을 때의 문제를 적습니다.