strace로 컨테이너 시스템콜 추적하기

strace를 이용해 Docker 컨테이너 내 프로세스의 시스템콜을 추적하는 방법, seccomp 우회, 파일/네트워크/프로세스 추적 시나리오, perf와 eBPF 도구 활용법을 설명합니다.

· 6 min read · PALDYN Team

지난 글에서 셸 접속 방법을 다뤘다. 셸로 진입해도 원인을 파악하기 어려운 경우가 있다. 예를 들어 어떤 파일을 읽으려다 실패하는지, 어떤 네트워크 주소로 연결을 시도하는지가 로그에 남지 않을 때 strace가 가장 직접적인 해답을 준다.

strace란

strace는 리눅스 ptrace 시스템콜을 사용해 대상 프로세스의 모든 시스템콜(시스템 호출)을 가로채 출력하는 도구다. 프로세스가 OS 커널에 요청하는 파일 I/O, 네트워크, 프로세스 생성 등 모든 상호작용을 볼 수 있다.

strace 컨테이너 시스템콜 추적

seccomp 프로파일 주의사항

Docker는 기본적으로 seccomp 프로파일을 적용해 일부 시스템콜을 차단한다. ptrace도 기본 프로파일에서는 제한된다. strace를 사용하려면 제한을 해제해야 한다.

# 방법 1: --cap-add SYS_PTRACE
docker run -d --cap-add SYS_PTRACE --name myapp myimage

# 방법 2: seccomp 비활성화 (더 넓은 허용)
docker run -d --security-opt seccomp=unconfined --name myapp myimage

# 실행 중인 컨테이너에는 적용 불가 — 재실행 필요
docker rm -f myapp
docker run -d --cap-add SYS_PTRACE --name myapp myimage

프로덕션에서는 seccomp=unconfined 대신 커스텀 프로파일에서 필요한 syscall만 허용하는 것이 안전하다.

기본 사용법

# 컨테이너 내부에서 (strace가 이미지에 있는 경우)
docker exec myapp strace -p 1

# 호스트에서 (nsenter 방식)
PID=$(docker inspect --format '{{.State.Pid}}' myapp)
sudo strace -p "$PID"

# 새 프로세스 추적 (시작부터)
docker exec myapp strace /app/server --config /etc/app/config.yaml

시나리오별 syscall 필터

디버깅 시나리오별 syscall 필터

파일 접근 문제

어떤 설정 파일을 찾지 못하는지 확인할 때 유용하다.

# 파일 시스템 관련 syscall만 추적
sudo strace -p "$PID" -e trace=openat,open,read,write,close -s 256 2>&1 | \
  grep -v ENOENT | head -30

# 실패한 open만 보기
sudo strace -p "$PID" -e trace=openat 2>&1 | grep "= -1 ENOENT"
# openat(AT_FDCWD, "/etc/app/secret.key", O_RDONLY) = -1 ENOENT (No such file or directory)

네트워크 연결 문제

어떤 IP/포트로 연결을 시도하는지 추적한다.

# 네트워크 syscall 추적
sudo strace -p "$PID" -e trace=network 2>&1 | grep -v EAGAIN

# connect 실패만 보기
sudo strace -p "$PID" -e trace=connect 2>&1 | grep "= -1"
# connect(3, {sa_family=AF_INET, sin_addr=10.0.0.5, sin_port=5432}, ...) = -1 ECONNREFUSED

이 출력으로 앱이 DB에 접근할 때 어떤 IP를 사용하는지 직접 확인할 수 있다. DNS 설정 오류나 잘못된 환경변수를 찾는 데 효과적이다.

성능 분석

# syscall 통계 (-c)
sudo strace -p "$PID" -c &
STRACE_PID=$!
sleep 30
kill -SIGINT $STRACE_PID

# 예시 출력:
# % time  seconds  calls  errors  syscall
# 68.12   0.043234   1024       0  epoll_wait
# 15.23   0.009671    512      12  futex
#  8.45   0.005362    256       0  read

자식 프로세스 포함 추적

# -f: fork/exec된 자식 프로세스도 추적
sudo strace -p "$PID" -f -e trace=execve 2>&1

# 프로세스별로 출력 분리
sudo strace -p "$PID" -f -e trace=execve -o /tmp/strace.txt
grep "execve" /tmp/strace.txt

eBPF 기반 도구: perf와 bpftrace

strace는 ptrace 기반이라 성능 오버헤드가 크다(최대 100배 느려짐). 프로덕션에서는 eBPF 기반 도구가 더 적합하다.

# bpftrace: 특정 syscall 이벤트 추적 (오버헤드 매우 낮음)
bpftrace -e 'tracepoint:syscalls:sys_enter_openat /comm == "myapp"/ { printf("%s\n", str(args->filename)); }'

# perf: 컨테이너 내 프로세스 성능 프로파일링
sudo perf stat -p "$PID" sleep 10
sudo perf record -p "$PID" sleep 10
sudo perf report

# pidstat: CPU/메모리를 프로세스별로 추적
pidstat -p "$PID" 1 10

실전 사례: 설정 파일 경로 오류

# 앱이 시작하자마자 종료됨 - strace로 원인 찾기
docker run --rm --cap-add SYS_PTRACE \
  --entrypoint strace myapp:latest \
  -e trace=openat -s 200 /app/server 2>&1 | head -20

# 출력:
# openat(AT_FDCWD, "/etc/myapp/config.yml", O_RDONLY) = -1 ENOENT
# openat(AT_FDCWD, "/app/config.yml", O_RDONLY) = -1 ENOENT
# 
# → 앱이 /etc/myapp/config.yml을 찾고 있었으나 /app/config/config.yml로 마운트됨
# 수정: 올바른 경로로 마운트
docker run -v $(pwd)/config.yml:/etc/myapp/config.yml myapp:latest

seccomp 프로파일 생성에 strace 활용

strace 출력으로 앱이 실제로 사용하는 syscall 목록을 만들어 최소 권한 seccomp 프로파일을 만들 수 있다.

# 앱이 사용하는 syscall 목록 추출
sudo strace -p "$PID" -c 2>&1 | \
  awk 'NR>3 && $5 != "syscall" {print $5}' | sort -u

# oci-seccomp-bpf-hook으로 자동 생성
docker run --annotation io.containers.trace-syscall=of:/tmp/seccomp.json \
  myapp:latest

지난 글: 컨테이너에 셸로 접속하기: exec, attach, nsenter

다음 글: tcpdump로 컨테이너 네트워크 패킷 분석하기


읽어주셔서 감사합니다. 😊