cgroups

3 개의 포스트

gitlab

Kubernetes에서 Gitaly로 GitLab 스택 통합하기 (새 탭에서 열림)

GitLab 18.11부터 Gitaly on Kubernetes가 정식 지원되면서, GitLab의 모든 구성 요소를 Kubernetes에서 운영할 수 있게 되었습니다. 기존의 Kubernetes와 VM을 함께 사용하는 하이브리드 구조를 없애고, 인프라 운영과 모니터링을 단일 Kubernetes 환경으로 통합할 수 있습니다. 다만 현재 Gitaly Cluster(Praefect)는 Kubernetes를 아직 지원하지 않아 완전한 고가용성은 제공되지 않습니다. ### Kubernetes 환경에 맞춘 Gitaly의 변화 - Git 작업은 메모리 사용량이 크고 패턴을 예측하기 어렵습니다. - Gitaly는 Git 프로세스를 별도 cgroup에서 실행해 메모리 초과로 프로세스가 종료되더라도 주 Gitaly 프로세스가 영향을 받지 않도록 합니다. - Kubernetes에서 이 구조를 구현하기 위해 다음 작업이 필요했습니다. - `containerd` 환경에서 cgroupfs에 쓰기 권한을 부여 - init container로 `/sys/fs/cgroup`를 마운트 - 해당 경로를 쓰기 가능하도록 설정 - 이를 통해 Git 프로세스의 OOM(메모리 부족) 종료가 Gitaly 전체 장애로 이어지는 것을 방지합니다. ### Pod 재시작과 서비스 중단 문제 - VM에서는 Omnibus가 Gitaly 바이너리를 교체하고 소켓을 유지한 채 graceful reload를 수행할 수 있습니다. - Kubernetes에서는 Helm 업그레이드, 노드 드레이닝, 설정 변경 등으로 StatefulSet Pod가 교체되면 프로세스가 강제 종료된 뒤 재시작됩니다. - Gitaly Sharded처럼 자체 고가용성을 제공하지 않는 구성에서는 이 방식이 일시적인 중단을 일으킬 수 있습니다. - 이를 보완하기 위해 Rails 등 Gitaly 클라이언트의 요청 재시도 시간을 설정할 수 있게 했습니다. - Gitaly가 재시작되는 동안 요청을 재시도 - 사용자는 짧은 시간 동안 약간 높은 지연을 경험할 수 있음 - 재시작이 완료되면 요청은 최종적으로 성공 ### 업그레이드 중에도 높은 성공률 유지 - VM 기반 Gitaly와 Kubernetes 기반 Gitaly에서 일반적인 Git 작업을 실행한 뒤, 테스트 중간에 업그레이드를 수행하는 벤치마크를 진행했습니다. - 두 환경의 요청 성공률은 거의 동일했습니다. - Kubernetes에서는 Pod 종료 시 프로세스가 즉시 끝나고 소켓도 닫히지만, 클라이언트 재시도 덕분에 대부분의 요청이 성공했습니다. - 모든 작업에서 100% 성공률을 보장하려면 Gitaly Cluster(Praefect)가 필요합니다. - 그러나 Praefect는 현재 Kubernetes를 지원하지 않으며, Kubernetes 지원의 정식 제공을 준비 중입니다. ### 인프라 통합의 효과 - 기존 하이브리드 배포 사용자는 Gitaly를 VM에서 Kubernetes 클러스터로 이전할 수 있습니다. - 별도의 Gitaly VM fleet를 유지·모니터링할 필요가 없어집니다. - GitLab 전체를 Kubernetes가 관리하는 단일 환경으로 통합할 수 있습니다. - Kubernetes를 이미 운영 중인 신규 GitLab 사용자도 Helm chart를 통해 Kubernetes 네이티브 방식으로 GitLab을 배포할 수 있습니다. ### 설치 방법과 배포 형태 - 권장 설치 방법은 GitLab Helm chart를 사용하는 것입니다. - 설치 전 Gitaly on Kubernetes 공식 문서를 확인해 주요 설정과 일반적인 문제를 검토해야 합니다. - Gitaly는 두 가지 형태로 배포할 수 있습니다. - 전체 GitLab 설치의 일부로 배포 - 외부 Gitaly 구성 요소로 별도 배포 - 구체적인 설정 방법과 주의사항은 Gitaly on Kubernetes 문서에서 각 시나리오별로 제공합니다. ### 실용적인 결론 GitLab을 Kubernetes 중심으로 운영하는 팀이라면 Gitaly를 클러스터로 통합하는 것이 운영 복잡성을 줄이는 현실적인 선택입니다. 다만 무중단 고가용성이 반드시 필요한 환경은 Praefect의 Kubernetes 지원 여부를 확인한 뒤 도입을 결정하는 것이 좋습니다.

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 기반 필터링은 숨은 의존성과 간접 의존성을 찾아내는 방어 계층으로 활용할 수 있다. - 다만 실제 운영에서는 차단 정책을 적용하기 전에 관찰 모드로 트래픽을 수집하고, 정상적인 고객 트래픽과 배포 트래픽이 정확히 분리되는지 검증하는 것이 바람직하다.

figma

서버 측 샌드박싱 (새 탭에서 열림)

컨테이너와 seccomp는 가상 머신보다 가볍게 서버 측 작업을 격리할 수 있지만, 기본 설정만으로 안전한 샌드박스가 되는 것은 아니다. 컨테이너 탈출 위험은 런타임 구현, 운영체제 커널 기능, 런타임 설정에 좌우되며, 침해된 컨테이너가 호스트나 다른 시스템에 미치는 영향까지 함께 제한해야 한다. Figma는 컨테이너의 OS 수준 격리 기능과 seccomp를 활용해 성능과 보안 사이의 균형을 맞추고 있다. ## 컨테이너와 가상 머신의 차이 - 가상 머신은 하이퍼바이저를 통해 운영체제 수준에서 격리한다. - 컨테이너는 호스트 OS의 커널 기능을 공유하며, 다음과 같은 기능을 이용해 격리한다. - Linux namespaces - cgroups - 권한 강등(privilege dropping) - seccomp - SELinux, AppArmor 같은 필수 접근 제어(MAC) - 컨테이너는 VM보다 가볍고 효율적이지만, 격리 수준을 사용자가 직접 세밀하게 설정해야 한다. - 설정 자유도가 높은 만큼 잘못된 구성으로 인해 보안 취약점이 발생할 가능성도 크다. ## 샌드박싱이 필요한 이유 - 이미지 처리와 데이터 파싱처럼 애플리케이션에 필수적인 기능은 외부 입력을 다루므로 취약점 위험을 포함한다. - 모든 보안 취약점을 사전에 제거하는 것은 비용이 많이 들고 현실적으로 어렵다. - 따라서 취약한 작업이 실행되더라도 호스트와 다른 시스템으로 피해가 확산되지 않도록 서버 측 샌드박싱, 즉 workload isolation을 적용한다. - 격리 설계에서는 두 가지 질문을 중심으로 보안성을 평가한다. - 악성 작업이 컨테이너에서 탈출해 호스트를 변경하거나 장악할 수 있는가? - 컨테이너를 탈출하지 못하더라도 컨테이너 권한으로 다른 시스템에 접근하거나 피해를 줄 수 있는가? ## 컨테이너에서 호스트로 이어지는 공격 표면 컨테이너 탈출 가능성은 컨테이너 자체보다 여러 구성 요소의 조합에 의해 결정된다. - 주요 공격 표면은 다음 세 가지다. - 컨테이너 런타임 구현의 버그 - 런타임이 사용하는 운영체제 커널 기능과 인터페이스 - 런타임 보안 설정 및 구성 오류 - Linux 환경에서 Docker는 일반적으로 runC 런타임을 사용한다. - runC와 Docker는 namespaces, cgroups, 권한 강등, seccomp, SELinux, AppArmor 등을 조합해 격리를 제공한다. - 커널 취약점이나 런타임 구현 오류, 잘못된 설정이 있으면 악성 작업이 다음과 같은 행위를 할 수 있다. - 호스트 파일 수정 - 호스트에서 코드 실행 - 컨테이너 탈출 - 컨테이너의 기본 설정이 과거보다 안전해졌더라도, 특정 샌드박싱 용도에 충분한지는 사용자가 직접 검증해야 한다. ## 침해된 컨테이너의 피해 제한 컨테이너 내부에서 작업이 침해되었다고 가정하고, 호스트뿐 아니라 주변 시스템에 대한 접근도 차단해야 한다. - 컨테이너 설정을 강화해 호스트 장악을 방지해야 한다. - 컨테이너에 다음 자원을 불필요하게 제공하지 않는 것이 중요하다. - 네트워크 장치 - 인증 자격 증명 - 다른 작업의 데이터 - 컨테이너를 별도의 격리된 네트워크에 배치할 수 있다. - 오케스트레이션 시스템을 사용해 입력은 통제된 채널로 전달하고, 출력도 검증·제한된 방식으로 수집한다. - 핵심은 “컨테이너가 침해되지 않을 것”이 아니라 “침해되더라도 피해 범위를 제한할 것”이라는 방어적 설계다. ## seccomp의 역할 - seccomp(secure computing mode)는 프로그램이 호출할 수 있는 시스템 콜을 제한하는 Linux 기능이다. - 시스템 콜을 허용 목록 또는 제한 정책으로 관리하면, 컨테이너 프로세스가 커널과 상호작용할 수 있는 범위를 줄일 수 있다. - 컨테이너 격리는 런타임, 커널 기능, 설정의 결합으로 구현되므로 seccomp 역시 다른 보안 기능과 함께 사용해야 한다. - 제공된 글 내용은 seccomp 보안 모델을 소개하는 부분에서 중단되어 있어, 이후 Figma의 구체적인 nsjail 구성과 운영 방식은 확인할 수 없다. 컨테이너를 서버 측 샌드박스로 사용할 때는 기본 설정을 그대로 신뢰하지 말고, 최소 권한·제한된 시스템 콜·격리된 네트워크·자격 증명 차단을 함께 적용하는 것이 좋다. 특히 컨테이너 탈출 방지와 침해 이후 피해 제한을 별개의 보안 목표로 보고 설계해야 한다.