BazelCon 2022는 11월 16~17일에 뉴욕과 온라인에서 개최됩니다.
지금 등록하기

분산 빌드

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

대규모 코드베이스를 사용하면 종속 항목 체인이 매우 깊이 있게 유지될 수 있습니다. 단순한 바이너리도 수만 개의 빌드 대상에 종속될 수 있습니다. 이 규모에서는 단일 머신에서 적절한 시간 내에 빌드를 완료할 수 없습니다. 즉, 빌드 시스템이 머신의 하드웨어에 적용된 물리학의 근본적인 법칙을 해결할 수 없습니다. 이 작업을 진행하는 유일한 방법은 시스템에서 수행하는 작업 단위가 임의적이고 확장 가능한 머신 전체에 분산되는 분산 빌드를 지원하는 빌드 시스템을 사용하는 것입니다. 시스템의 작업을 충분히 작은 단위 (자세한 내용은 뒷부분)로 나'다고 가정하면 이제 원하는 만큼 모든 크기의 빌드를 완료할 수 있습니다. 이러한 확장성은 Google이 아티팩트 기반 빌드 시스템을 정의하기 위해 노력해 왔습니다.

원격 캐싱

분산형 빌드의 가장 간단한 유형은 그림 1에 표시된 원격 캐싱만 활용하는 것입니다.

원격 캐싱이 포함된 분산형 빌드

그림 1. 원격 캐싱을 보여주는 분산형 빌드

개발자 워크스테이션과 지속적 통합 시스템을 포함하여 빌드를 수행하는 모든 시스템은 공통 원격 캐시 서비스에 대한 참조를 공유합니다. 이 서비스는 Redis와 같은 빠른 로컬 단기 스토리지 시스템이거나 Google Cloud Storage와 같은 클라우드 서비스일 수 있습니다. 사용자가 직접 또는 종속 항목으로 아티팩트를 빌드해야 할 때마다 시스템은 먼저 원격 캐시를 통해 아티팩트가 이미 존재하는지 확인합니다. 그렇다면 아티팩트를 빌드하는 대신 아티팩트를 다운로드할 수 있습니다. 그렇지 않으면 시스템이 아티팩트 자체를 빌드하고 결과를 캐시에 다시 업로드합니다. 즉, 자주 변경되지 않는 하위 수준의 종속 항목은 각 사용자가 다시 빌드할 필요 없이 한 번 빌드되어 사용자 간에 공유할 수 있습니다. Google에서는 수많은 아티팩트가 처음부터 빌드되지 않고 캐시에서 제공되므로 빌드 시스템 실행 비용을 크게 줄일 수 있습니다.

원격 캐싱 시스템이 작동하려면 빌드 시스템에서 빌드를 완전히 재현할 수 있도록 보장해야 합니다. 즉, 동일한 빌드 집합이 모든 머신에서 정확히 동일한 출력을 생성하도록 모든 빌드 대상의 입력 집합을 확인할 수 있어야 합니다. 이는 아티팩트를 다운로드하는 결과가 자체적으로 빌드한 결과와 동일하도록 하는 유일한 방법입니다. 이를 위해서는 캐시의 각 아티팩트가 대상과 입력의 해시 모두에 키가 지정되어야 합니다. 이렇게 하면 다양한 엔지니어가 동시에 동일한 대상을 서로 다르게 수정할 수 있습니다. 원격 캐시는 결과 아티팩트를 모두 저장하고 충돌 없이 적절하게 제공합니다.

물론 원격 캐시의 이점을 누리려면 아티팩트를 빌드하는 것보다 아티팩트를 다운로드해야 합니다. 항상 그럴 필요는 없습니다. 특히 캐시 서버가 빌드를 수행하는 머신과 멀리 떨어져 있는 경우 더욱 그렇습니다. Google의 네트워크와 빌드 시스템은 빌드 결과를 신속하게 공유할 수 있도록 세심하게 조정되었습니다.

원격 실행

원격 캐싱은 진정한 분산 빌드가 아닙니다. 캐시가 손실되었거나 모든 사항을 다시 빌드해야 하는 하위 수준 변경사항이 발생하더라도 여전히 머신에서 로컬로 전체 빌드를 수행해야 합니다. 진정한 목표는 원격 실행을 지원하는 것입니다. 원격 실행에서는 빌드 작업을 실제 작업자 수에 상관없이 분산할 수 있습니다. 그림 2는 원격 실행 시스템을 보여줍니다.

원격 실행 시스템

그림 2. 원격 실행 시스템

각 사용자의 머신에서 실행되는 빌드 도구 (사용자가 인간 엔지니어이거나 자동화된 빌드 시스템임)는 중앙 빌드 마스터로 요청을 전송합니다. 빌드 마스터는 요청을 구성요소 작업으로 나누고 확장 가능한 작업자 풀을 통해 작업 실행을 예약합니다. 각 작업자는 사용자가 지정한 입력으로 요청된 작업을 실행하고 결과 아티팩트를 작성합니다. 이러한 아티팩트는 최종 출력이 생성되어 사용자에게 전송될 때까지 필요한 작업을 실행하는 다른 머신에서 공유됩니다.

이러한 시스템을 구현하는 데 있어 가장 까다로운 부분은 작업자, 마스터, 사용자의 로컬 머신 간 통신을 관리하는 것입니다. 작업자는 다른 작업자가 생성한 중간 아티팩트에 종속될 수 있으며 최종 출력은 사용자의 로컬 머신으로 다시 전송되어야 합니다. 이를 위해 각 작업자가 결과를 쓰고 캐시에서 종속 항목을 읽도록 하여 이전에 설명한 분산 캐시를 기반으로 빌드할 수 있습니다. 마스터는 종속된 모든 작업이 완료될 때까지 작업자를 진행할 수 없으며, 이 경우 캐시에서 입력을 읽을 수 있습니다. 최종 제품도 캐시되어 로컬 머신에서 다운로드할 수 있습니다. 또한 작업자가 빌드 전에 변경사항을 적용할 수 있도록 사용자의 소스 트리에서 로컬 변경사항을 내보내는 별도의 방법이 필요합니다.

이를 위해 앞서 설명한 아티팩트 기반 빌드 시스템의 모든 부분을 하나로 결합해야 합니다. 빌드 환경은 사람의 개입 없이 작업자를 실행할 수 있도록 완전히 자기 기술되어야 합니다. 각 프로세스는 다른 머신에서 실행될 수 있으므로 빌드 프로세스 자체는 완전히 독립적이어야 합니다. 각 작업자가 다른 작업자에서 수신하는 결과를 신뢰할 수 있도록 출력은 완전히 확정적이어야 합니다. 이러한 보장은 태스크 기반 시스템이 제공하기 매우 어려우며, 따라서 안정적인 원격 실행 시스템을 기반으로 빌드할 수 없습니다.

Google의 분산형 빌드

Google은 2008년부터 그림 3과 같이 원격 캐싱과 원격 실행을 모두 사용하는 분산 빌드 시스템을 사용해 왔습니다.

높은 수준의 빌드 시스템

그림 3. Google의 분산 빌드 시스템

Google의 원격 캐시를 ObjFS라고 합니다. 이 파일은 프로덕션 머신 전체에 분산된 Bigtable에 빌드 출력을 저장하는 백엔드와 각 개발자의 머신에서 실행되는 objfsd라는 프런트엔드 FUSE 데몬으로 구성됩니다. FUSE 데몬을 사용하면 엔지니어가 마치 워크스테이션에 저장된 일반 파일인 것처럼 빌드 출력을 탐색할 수 있지만, 파일 콘텐츠는 사용자가 직접 요청한 몇 개의 파일에 대해서만 주문형으로 다운로드됩니다. 에서 확인할 수 있습니다. 주문형 파일 콘텐츠를 제공하면 네트워크 및 디스크 사용량이 모두 크게 줄어들며 시스템은 개발자의 로컬 디스크에 모든 빌드 출력을 저장할 때보다 두 배 빠르게 빌드할 수 있습니다.

Google의 원격 실행 시스템을 Forge라고 합니다. 배포자라고 하는 Blaze(Bazel 내부)의 Forge 클라이언트는 작업마다 요청을 스케줄러라는 데이터 센터에서 실행되는 작업에 전송합니다. 스케줄러는 작업 결과의 캐시를 유지하여 시스템의 다른 사용자가 이미 작업을 생성한 경우 즉시 응답을 반환할 수 있습니다. 그렇지 않은 경우 작업을 대기열에 배치합니다. 대규모 Executor 작업 풀은 이 큐에서 작업을 지속적으로 읽고 실행하고 결과를 ObjFS Bigtable에 직접 저장합니다. 이러한 결과는 실행자가 향후 작업에 사용하거나 objfsd를 통해 최종 사용자가 다운로드할 수 있습니다.

결과적으로 Google에서 수행된 모든 빌드를 효율적으로 지원하도록 확장되는 시스템이 탄생합니다. Google 빌드의 규모는 정말로 방대한 규모입니다. Google은 매일 수백만 개의 테스트 사례를 실행하고 수십억 개의 소스 코드로부터 페타바이트 규모의 빌드 출력을 생성하는 수많은 빌드를 실행합니다. 이러한 시스템을 통해 엔지니어는 복잡한 코드베이스를 신속하게 빌드할 수 있을 뿐만 아니라 빌드를 기반으로 하는 자동화된 도구 및 시스템도 대량으로 구현할 수 있습니다.