github

GitHub가 eBPF를 사용하여 배포 안전성을 향상시키는 방법 (새 탭에서 열림)

GitHub는 장애 상황에서도 배포를 계속할 수 있도록 배포 코드가 GitHub 자체나 다른 내부 서비스에 의존하는 순환 의존성을 차단하려 한다. 이를 위해 배포 프로세스만 별도 Linux cGroup에 배치하고, eBPF의 BPF_PROG_TYPE_CGROUP_SKB를 이용해 해당 프로세스의 네트워크 송신을 선택적으로 감시·차단하는 방식을 검토했다. 이 접근법은 호스트 전체의 네트워크를 막지 않으면서 배포 과정의 외부·내부 의존성을 검증할 수 있다는 점이 핵심이다.

GitHub 배포의 순환 의존성

  • GitHub는 자체 소스 코드를 github.com에 보관하므로, GitHub 장애 시 소스 코드 접근과 복구 배포가 동시에 어려워질 수 있다.
  • 이를 완화하기 위해 다음을 유지한다.
    • 장애 시 수정 배포를 위한 코드 미러
    • 롤백에 사용할 사전 빌드된 배포 자산
  • 하지만 배포 스크립트가 새롭게 순환 의존성을 만들 가능성은 여전히 남아 있다.
    • 내부 서비스 호출
    • GitHub에서 바이너리 다운로드
    • 실행 중인 도구의 자동 업데이트 확인 등

순환 의존성의 세 가지 유형

직접 의존성

  • 배포 스크립트가 GitHub에서 오픈 소스 도구의 최신 릴리스를 직접 다운로드한다.
  • GitHub 장애로 릴리스 데이터를 제공할 수 없으면 배포 스크립트도 완료되지 않는다.

숨은 의존성

  • 필요한 도구가 이미 호스트 디스크에 존재하더라도, 실행 시 업데이트 가능 여부를 확인할 수 있다.
  • 이때 도구가 GitHub에 접속하지 못하면:
    • 오류를 반환하고 실패하거나
    • 네트워크 타임아웃으로 멈출 수 있다.
  • 코드만 검토해서는 이런 런타임 의존성을 발견하기 어렵다.

일시적·간접 의존성

  • 배포 스크립트가 내부 마이그레이션 서비스 같은 다른 서비스를 API로 호출한다.
  • 해당 서비스가 다시 GitHub에서 최신 바이너리를 받으려 하면, 의존성이 여러 단계 뒤에서 발생한다.
  • 최종적으로 GitHub 장애가 내부 서비스와 배포 스크립트까지 연쇄적으로 실패시킨다.

기존 검증 방식의 한계

  • 기존에는 각 상태 저장 호스트를 담당하는 팀이 배포 스크립트를 검토해 순환 의존성을 찾아야 했다.
  • 실제로는 많은 의존성이 장애가 발생한 뒤에야 드러난다.
  • 가장 단순한 검증 방법은 호스트에서 github.com 접근을 전부 차단하는 것이다.
  • 그러나 해당 호스트는 롤링 배포, 드레인, 재시작 중에도 고객 트래픽을 처리하므로 호스트 전체의 네트워크를 차단하면 운영 기능까지 손상된다.

cGroup과 eBPF를 이용한 선택적 네트워크 차단

  • eBPF는 Linux 커널에 사용자 정의 프로그램을 로드하고 네트워크 같은 핵심 시스템 동작에 연결할 수 있다.
  • GitHub가 주목한 프로그램 유형은 BPF_PROG_TYPE_CGROUP_SKB다.
    • 특정 cGroup의 네트워크 ingress/egress에 연결 가능
    • 특히 프로세스 그룹의 외부 송신 트래픽을 제어할 수 있음
  • cGroup은 프로세스 집합에 리소스 제한과 격리를 적용하는 Linux 기능이다.
    • Docker 전용 기능이 아니며 직접 생성·구성할 수 있다.
  • 따라서 다음 구조가 가능하다.
    • 배포 스크립트만 별도 cGroup에 배치
    • 해당 cGroup의 egress 트래픽만 eBPF로 감시
    • GitHub나 특정 내부 서비스로 향하는 연결만 차단
    • 같은 호스트에서 실행 중인 고객 트래픽 처리 프로세스는 계속 네트워크 사용

Go와 cilium/ebpf를 이용한 구현

  • GitHub는 Go 기반 proof of concept을 만들고 cilium/ebpf 라이브러리를 사용했다.
  • 이 라이브러리는 다음 작업을 단순화한다.
    • eBPF 프로그램과 맵을 읽고 수정
    • 프로그램을 컴파일·로드
    • 커널의 다양한 hook에 연결
  • Go 코드에서는 다음 흐름으로 cGroup에 eBPF 프로그램을 연결한다.
    • 사전 컴파일된 eBPF 오브젝트와 맵을 커널에 로드
    • /sys/fs/cgroup/system.slice 같은 cGroup 경로 지정
    • ebpf.AttachCGroupInetEgress를 사용해 송신 트래픽 hook에 연결
    • eBPF 맵에서 패킷 수 등 관측 데이터를 주기적으로 조회
  • 예제 eBPF 프로그램은 BPF_MAP_TYPE_ARRAY 맵에 값을 저장하고, cgroup_skb/egress hook이 호출될 때마다 송신 패킷 수를 증가시키는 구조다.
  • 이처럼 먼저 트래픽을 관찰한 뒤, 특정 목적지에 대한 연결을 허용하거나 차단하는 정책으로 확장할 수 있다.

실용적인 의미

  • 배포 시스템은 “호스트 전체를 격리”하는 대신 “배포 프로세스만 제한”할 수 있다.
  • 장애 대응 전에 GitHub, 내부 API, 자동 업데이트 서버 등 필수 경로에 대한 의존성을 실제 실행 환경에서 검증할 수 있다.
  • eBPF 기반 필터링은 숨은 의존성과 간접 의존성을 찾아내는 방어 계층으로 활용할 수 있다.
  • 다만 실제 운영에서는 차단 정책을 적용하기 전에 관찰 모드로 트래픽을 수집하고, 정상적인 고객 트래픽과 배포 트래픽이 정확히 분리되는지 검증하는 것이 바람직하다.