에이전트 SDK 없이 /proc에서 서비스 간 의존성을 매핑하는 방법
어떤 서비스가 어떤 서비스와 통신하는지 알기 위해 계측이 필요하지는 않습니다. 커널은 이미 그것을 기록해두고 있습니다.
2026년 7월 10일 · 8분 읽기
서비스 디스커버리에 대해 대부분의 옵저버빌리티 도구가 제시하는 답은 "모든 서비스에 에이전트를 추가하라"입니다. 모든 배포 매니페스트를 건드리고, 모든 프로세스를 재시작하고, 자신에게 보고하지 않는 사람들이 소유한 수십 개 서비스에서 SDK 버전을 계속 맞춰나갈 의향이 있다면 합리적인 답입니다. 하지만 아무도 설정 한 줄 바꾸는 것에 동의하지 않은 상태에서, 제품을 쓰기 시작한 첫 5분 안에 시스템을 매핑하려 한다면 훨씬 덜 합리적인 답입니다.
커널은 이미 어떤 소켓이 열려 있고 누가 소유하고 있는지 알고 있습니다. 그저 가서 읽기만 하면 됩니다.
/proc 안에 실제로 들어있는 것
리눅스의 모든 프로세스는 /proc/<pid> 아래에 디렉터리를 가지며, 그 안의 net/tcp(IPv6는 net/tcp6)는 해당 프로세스의 네트워크 네임스페이스에서 보이는 모든 TCP 소켓을 나열합니다: 로컬 주소, 원격 주소, 연결 상태, 그리고 — 여기서 핵심이 되는 필드인 — inode 번호까지. 이 inode는 실제로 그 소켓인 파일 디스크립터의 심볼릭 링크 대상으로 /proc/<pid>/fd/에서도 동일하게 나타납니다. 모든 프로세스의 파일 디스크립터를 순회하며 inode를 소켓 테이블과 매칭시키면, 어떤 프로세스에도 허락을 구하지 않고 프로세스-연결 매핑을 얻을 수 있습니다.
여기서부터는 혁신이 아니라 덧셈에 가깝습니다: 각 PID를 컨테이너에 매핑하면(cgroup 경로나 Docker API가 이를 직접 알려줍니다), "프로세스 4821이 10.0.4.12:5432와 연결되어 있다"는 날것의 목록이 "orders 컨테이너가 postgres 컨테이너와 통신하고 있다"로 바뀝니다. orders 안에도, postgres 안에도 에이전트가 없고, 어느 쪽 코드 경로도 자신이 감시당하고 있다는 걸 알지 못합니다.
실제로 반복 개선이 필요했던 부분
테이블을 한 번 읽는 건 쉬운 80%에 불과합니다. 문제는 /proc/net/tcp가 스냅샷이라는 점입니다: 두 번의 폴링 사이에 열리고 닫히는 연결은 어느 샘플에도 나타나지 않습니다. 60초 폴링 간격 대비 45초마다 발동하는 헬스체크, 200ms 만에 끝나는 아웃바운드 호출, 5분짜리 관찰 윈도우 동안 딱 한 번만 연결하고 다시는 연결하지 않는 배치 작업 — 이 모두가 실제 의존성이며, 순진한 스냅샷 비교 방식은 이들을 끊임없이 놓칩니다. 트래픽이 많은 서비스에서는 흥미로운 트래픽 대부분이 바로 이런 짧은 수명의 연결이며, 토폴로지 뷰가 이미 완전하다고 확신을 준 뒤에야 이 사실을 깨닫는 건 꽤나 당혹스러운 일입니다.
저희가 택한 해결책은 단순 스냅샷이 아니라 슬라이딩 보존 윈도우입니다: 최근 다섯 번의 폴링 주기 중 한 번이라도 관찰된 엣지는 그래프에 남아 있고, 그 전체 구간 동안 완전히 관찰되지 않아야만 제거됩니다. 영리한 알고리즘은 아닙니다 — "필요하다고 생각하는 것보다 조금 더 오래 기억해두기"에 가깝습니다 — 하지만 그럴듯해 보이는 토폴로지와 실제로 정확한 토폴로지의 차이를 만드는 지점입니다.
이 접근법의 한계
한계에 대해서는 솔직하게 밝힐 필요가 있습니다. 컨테이너별 네임스페이스 구성(Docker와 Kubernetes에서 흔한 경우)에서는 호스트 전체를 위한 하나의 공유 테이블이 아니라 각 컨테이너의 네트워크 네임스페이스를 개별적으로 읽어야 하는데, 관리해야 할 대상이 늘어날 뿐 근본적으로 더 어려워지지는 않습니다. 암호화된 트래픽도 연결 자체를 숨기지는 못합니다 — 여기서 필요한 건 페이로드가 아니라 TCP 메타데이터뿐이니까요 — 다만 두 컨테이너가 통신 중이라는 것만 알 수 있을 뿐, 어떤 프로토콜로 어떤 엔드포인트와 통신하는지는 알 수 없습니다. 그건 Beyla 기반 RED 메트릭 레이어가 담당하는 영역이며, 둘은 서로를 대체하기보다는 함께 읽히도록 설계되어 있습니다. 그리고 이 방식은 근본적으로 TCP 레이어 관점입니다: UDP 기반 프로토콜은 완전히 다른 방식의 읽기가 필요하며, 이는 아직 에이전트에 구현되지 않은, 앞으로의 계획 목록에 있는 항목입니다.
