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

종속 항목 관리

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

이전 페이지를 살펴보면 한 가지 테마가 반복적으로 발생합니다. 자체 코드를 관리하는 것은 상당히 간단하지만 종속 항목을 관리하는 것이 훨씬 더 어렵습니다. 다양한 종속 항목이 있습니다. 예를 들어 '버전을 완료로 표시하기 전에 문서를 푸시'하는 등 작업에 종속 항목이 있고 아티팩트에 다음과 같은 종속 항목이 있는 경우가 있습니다. 코드를 빌드하려면 최신 버전의 컴퓨터 비전 라이브러리가 있어야 합니다.” 코드베이스의 다른 부분에 내부 종속 항목이 있고 코드나 데이터에 관한 외부 종속 항목이 있는 경우도 있습니다. 다른 팀(조직 내 또는 타사)에서 소유하는 경우 하지만 어떤 경우든 '이 작업을 하기 전에 필요하다'는 생각은 빌드 시스템 설계에서 반복적으로 발생하는 것이며, 종속 항목 관리는 빌드 시스템

모듈 및 종속 항목 처리

Bazel과 같은 아티팩트 기반 빌드 시스템을 사용하는 프로젝트는 모듈 집합으로 분류되며 모듈은 BUILD 파일을 통해 서로에 대한 종속 항목을 표현합니다. 이러한 모듈과 종속 항목을 적절하게 구성하면 빌드 시스템의 성능과 유지관리 작업에 상당한 영향을 미칠 수 있습니다.

세분화된 모듈 및 1:1:1 규칙 사용

아티팩트 기반 빌드를 구성할 때 첫 번째로 던지는 질문은 개별 모듈이 포함해야 하는 기능의 양을 결정하는 것입니다. Bazel에서 모듈java_library 또는 go_binary과 같이 빌드 가능한 단위를 지정하는 타겟으로 표시됩니다. 극단적으로 하나의 루트에 BUILD 파일을 포함하고 그 프로젝트의 모든 소스 파일을 반복적으로 함께 배치하여 전체 프로젝트를 단일 모듈에 포함할 수 있습니다. 다른 상황에서는 거의 모든 소스 파일을 자체 모듈로 만들 수 있으므로 각 파일이 사용하는 다른 모든 파일을 BUILD 파일에 나열해야 합니다.

대부분의 프로젝트는 이러한 극한의 어 somewhere가에 속하며, 성능과 유지보수 가능성 사이에 장단점이 있습니다. 전체 프로젝트에 단일 모듈을 사용하면 외부 종속 항목을 추가하는 경우를 제외하고 BUILD 파일을 건드리지 않아도 되지만 빌드 시스템에서 항상 전체 파일을 빌드해야 함을 의미합니다. 동시에 확인할 수 있습니다. 즉, 빌드의 일부를 병렬화하거나 배포할 수 없으며 이미 빌드된 부분을 캐시할 수도 없습니다. 파일당 모듈 1개는 반대입니다. 빌드 시스템이 빌드의 단계를 캐싱 및 예약할 때 최대의 유연성을 발휘하지만 엔지니어가 종속 항목 목록을 변경할 때마다 유지관리에 더 많은 노력을 기울여야 합니다. 파일을 참조하는 파일.

정확한 세부사항은 언어마다 다르지만 때로는 언어 내에서도 다르지만 Google은 일반적으로 작업 기반 빌드 시스템에서 작성하는 것보다 훨씬 작은 모듈을 선호합니다. Google의 일반적인 프로덕션 바이너리는 수만 개의 타겟을 사용하는 경우가 많으며, 보통 규모의 팀에서도 코드베이스 내에 수백 개의 타겟을 보유할 수 있습니다. 기본적으로 강력한 패키징 개념이 있는 자바의 경우 각 디렉터리에는 일반적으로 단일 패키지, 대상 및 BUILD 파일 (Bazel을 기반으로 하는 또 다른 빌드 시스템, 이를 1:1:1 규칙이라고 부릅니다. 약한 패키징 규칙이 있는 언어는 종종 BUILD 파일당 여러 대상을 정의합니다.

소규모 빌드 타겟의 이점은 대규모 분산 빌드를 얻고 타겟을 다시 빌드해야 할 필요성이 줄어들기 때문에 대규모로 나타납니다. 테스트 대상을 세부적으로 정립하면 테스트의 이점이 더욱 강력해집니다. 이는 특정 대상의 영향을 받을 수 있는 제한된 테스트 하위 집합만 빌드하는 것이 빌드 시스템을 훨씬 더 스마트하게 만들 수 있기 때문입니다. 이동합니다. Google은 소규모 타겟 사용의 시스템적 이점이 있다고 믿기 때문에 개발자의 부담을 피하기 위해 BUILD 파일을 자동으로 관리하는 도구에 투자하여 단점을 완화할 수 있습니다.

buildifierbuildozer와 같은 일부 도구는 buildtools 디렉터리에서 Bazel과 함께 사용할 수 있습니다.

모듈 공개 상태 최소화

Bazel 및 기타 빌드 시스템을 사용하면 각 대상에 공개 상태를 지정할 수 있습니다. 이 속성에서는 종속 항목에 액세스할 수 있는 다른 대상을 지정할 수 있습니다. 대상은 공개될 수 있으며, 이 경우 작업공간의 다른 대상에서 참조될 수 있습니다. private: 이 경우 동일한 BUILD 파일 내에서만 참조할 수 있습니다. 명시적으로 정의된 다른 타겟 목록에만 표시됩니다. 가시성은 본질적으로 종속 항목과 반대입니다. 즉, 타겟 A가 타겟 B에 종속되기를 원하는 경우 타겟 B가 타겟 A에 노출되도록 해야 합니다. 대부분의 프로그래밍 언어와 마찬가지로 최대한 가시성을 최소화하는 것이 좋습니다. 일반적으로 Google의 팀은 대상이 Google의 모든 팀에서 사용할 수 있는 널리 사용되는 라이브러리를 나타내는 경우에만 공개합니다. 코드를 사용하기 전에 다른 사람이 조율해야 하는 팀은 타겟의 공개 상태를 고객 허용 목록으로 유지합니다. 각 팀의 내부 구현 대상은 팀이 소유한 디렉터리로 제한됩니다. 또한 대부분의 BUILD 파일에는 비공개가 아닌 디렉터리가 하나만 있습니다.

종속 항목 관리

모듈은 서로를 참조할 수 있어야 합니다. 코드베이스를 세분화된 모듈로 분할하면 해당 모듈 간의 종속 항목을 관리해야 한다는 단점이 있습니다 (이 도구를 자동화하는 데 도움이 될 수 있음). 이러한 종속 항목을 표현하는 것은 일반적으로 BUILD 파일에 있는 콘텐츠의 대부분이 됩니다.

내부 종속 항목

대규모 모듈로 세분화된 대규모 프로젝트에서는 대부분 종속 항목이 내부에 있을 가능성이 높습니다. 즉, 동일한 소스 저장소에서 정의되고 빌드된 다른 대상에 적용됩니다. 내부 종속 항목은 빌드를 실행하는 동안 사전 빌드된 아티팩트로 다운로드되는 것이 아니라 소스에서 빌드된다는 점에서 외부 종속 항목과 다릅니다. 즉, 내부 종속 항목의 '버전' 개념이 없습니다. 대상과 모든 내부 종속 항목이 항상 저장소에서 동일한 커밋/버전으로 빌드됩니다. 내부 종속 항목과 관련하여 신중하게 처리해야 하는 문제 중 하나는 전이 종속 항목을 처리하는 방법입니다 (그림 1). 타겟 A가 공통 라이브러리 타겟 C에 종속되는 타겟 B에 종속된다고 가정해 보겠습니다. 타겟 A가 타겟 C에 정의된 클래스를 사용할 수 있어야 하나요?

전이 종속 항목

그림 1. 전이 종속 항목

근본적인 도구는 문제가 되지 않습니다. B와 C는 모두 빌드될 때 타겟 A에 연결되므로 C에 정의된 모든 기호는 A에 알려져 있습니다. Bazel은 수년간 이 문제를 허용했습니다. 하지만 Google이 성장하면서 문제를 볼 수 있었습니다. B가 더 이상 C에 종속될 필요가 없도록 리팩터링되었다고 가정합니다. 그런 다음 C에 대한 B의 종속 항목이 삭제되면 A와 B의 종속 항목을 통해 C를 사용한 다른 대상이 중단됩니다. 실질적으로 대상의 종속 항목은 공개 계약의 일부가 되어 절대 안전하게 변경할 수 없었습니다. 즉, 시간이 지나면서 종속 항목이 누적되고 Google에서 빌드가 느려지기 시작했습니다.

Google은 최종적으로 Bazel에 '엄격한 전이 종속 모드'를 도입하여 이 문제를 해결했습니다. 이 모드에서 Bazel은 대상이 기호에 직접 의존하지 않고 기호를 참조하려고 하는지 시도하며, 실패하려는 경우 오류와 함께 종속 항목을 자동으로 삽입하고 종속 항목을 자동으로 삽입하는 데 사용할 수 있는 셸 명령어를 감지합니다. 에서 확인할 수 있습니다. Google의 전체 코드베이스에 이 변경사항을 적용하고 종속 항목을 명시적으로 나열하기 위해 수백만 개의 빌드 타겟을 하나씩 리팩터링하기는 수년이 걸렸지만 그만한 가치가 있었습니다. 타겟의 불필요한 종속 항목이 줄어들어 빌드 속도가 훨씬 빨라졌으며 엔지니어는 종속된 종속 항목을 손상할 염려 없이 불필요한 종속 항목을 삭제할 수 있습니다.

그렇듯이 엄격한 전이 종속 항목에 대해서는 절충점을 찾아야 합니다. 그 결과 빌드 파일을 더 간결하게 만들 수 있게 되었습니다. 자주 사용하는 라이브러리를 우발적으로 가져오는 대신 여러 곳에 명시적으로 나열해야 하며 엔지니어는 BUILD 파일에 종속 항목을 추가하는 데 더 많은 노력을 들여야 했습니다. 에서 확인할 수 있습니다. 이후 Google에서는 개발자가 개입하지 않아도 누락된 종속 항목을 자동으로 감지하여 BUILD 파일에 추가하여 이러한 수고를 줄여주는 도구를 개발했습니다. 하지만 이러한 도구가 없어도 코드베이스가 확장됨에 따라 이 단점을 감당할 수 있는 것으로 나타났습니다. 종속 항목을 BUILD 파일에 명시적으로 추가하는 것은 일회성 비용이지만 암시적 전이 종속 항목은 빌드 타겟이 존재하는 동안 지속적인 문제를 일으킬 수 있습니다. Bazel은 기본적으로 자바 코드에 엄격한 전이 종속 항목을 적용합니다.

외부 종속 항목

종속 항목이 내부가 아닌 경우 외부에 있어야 합니다. 외부 종속 항목은 빌드 시스템 외부에서 빌드되고 저장된 아티팩트의 종속 항목입니다. 종속 항목은 일반적으로 인터넷을 통해 액세스되는 아티팩트 저장소에서 직접 가져오고, 소스에서 빌드하지 않고 그대로 사용합니다. 외부 종속 항목과 내부 종속 항목의 가장 큰 차이점은 외부 종속 항목이 버전이 있고 프로젝트의 소스 코드와 독립적으로 존재한다는 것입니다.

자동 종속성 관리와 수동 종속 항목 관리 비교

빌드 시스템을 사용하면 외부 종속 항목의 버전을 수동 또는 자동으로 관리할 수 있습니다. 수동으로 관리하는 경우 빌드 파일은 아티팩트 저장소에서 다운로드하려는 버전을 명시적으로 나열하며 주로 1.1.4와 같은 시맨틱 버전 문자열을 사용합니다. 자동으로 관리되는 경우 소스 파일은 허용되는 버전의 범위를 지정하며 빌드 시스템은 항상 최신 버전을 다운로드합니다. 예를 들어 Gradle은 종속 버전이 '1.+'로 선언되어 주 버전이 1인 한 종속 항목의 부 버전 또는 패치 버전을 허용할 수 있습니다.

소규모 프로젝트에서는 자동 관리형 종속 항목이 편리할 수 있지만 일반적으로 대규모 프로젝트 또는 2명 이상의 엔지니어가 작업하는 프로젝트에 재해가 발생할 수 있습니다. 자동 관리형 종속 항목의 문제는 버전이 업데이트되는 시점을 제어할 수 없다는 점입니다. 시맨틱 버전 관리를 사용한다고 주장하는 경우에도 외부 당사자가 브레이킹 체인지를 하지 않도록 보장할 수 있는 방법이 없습니다. 따라서 하루만에 제대로 작동하는 빌드가 손상되어 어떤 의미가 있는지 쉽게 감지할 수 없을 것입니다. 변경되거나 작동 상태로 롤백될 수 있습니다. 빌드가 중단되지 않더라도 추적할 수 없는 미묘한 동작 또는 성능 변경사항이 있을 수 있습니다.

반면 수동으로 관리되는 종속 항목은 소스 제어를 변경해야 하므로 손쉽게 검색하고 롤백할 수 있으며 이전 종속 항목을 사용하여 빌드할 이전 버전의 저장소를 확인할 수 있습니다. Bazel을 사용하려면 모든 종속 항목의 버전을 수동으로 지정해야 합니다. 적당한 규모에서도 수동 버전 관리의 오버헤드는 제공하는 안정성에 충분합니다.

단일 버전 규칙

라이브러리의 여러 버전은 일반적으로 서로 다른 아티팩트로 표시되므로 이론적으로 동일한 외부 종속 항목의 여러 버전을 빌드 시스템에서 서로 다른 이름으로 선언할 수 없는 이유는 없습니다. 그러면 각 타겟에서 사용할 종속 항목의 버전을 선택할 수 있습니다. 이는 실제로 많은 문제를 발생하므로 Google은 코드베이스의 모든 타사 종속 항목에 엄격한 단일 버전 규칙을 적용합니다.

여러 버전 허용 시 가장 큰 문제는 다이아몬드 종속 문제입니다. 대상 A가 대상 B와 외부 라이브러리의 v1에 종속된다고 가정해 보겠습니다. 동일한 외부 라이브러리의 v2에 종속 항목을 추가하기 위해 타겟 B를 리팩터링한 후 타겟 A는 동일한 라이브러리의 두 가지 버전에 암시적으로 의존하기 때문에 중단됩니다. 타겟의 사용자가 이미 다른 버전에 종속될 수 있기 때문에 실질적으로 타겟의 새 종속 항목을 여러 버전의 타사 라이브러리에 추가하는 것은 안전하지 않습니다. 단일 버전 규칙을 따르면 이러한 충돌이 불가능해집니다. 즉, 타겟이 타사 라이브러리에 종속 항목을 추가하면 기존의 종속 항목은 동일한 버전에 이미 존재하므로 원활하게 공존할 수 있습니다.

전이 외부 종속 항목

외부 종속 항목의 전이 종속 항목을 처리하는 것은 특히 어려울 수 있습니다. Maven Central과 같은 많은 아티팩트 저장소를 사용하면 아티팩트가 저장소에서 다른 아티팩트의 특정 버전에 대한 종속 항목을 지정할 수 있습니다. Maven 또는 Gradle과 같은 빌드 도구는 기본적으로 각 전이 종속 항목을 재귀적으로 다운로드하는 경우가 많습니다. 즉, 프로젝트에 단일 종속 항목을 추가하면 전체 수십 개의 아티팩트가 다운로드될 수 있습니다.

이 기능은 매우 편리합니다. 새 라이브러리에 종속 항목을 추가할 때 각 라이브러리의 전이 종속 항목을 모두 수동으로 추가해야 한다면 큰 어려움이 될 수 있습니다. 하지만 큰 단점도 있습니다. 다양한 라이브러리가 동일한 타사 라이브러리의 여러 버전에 종속될 수 있으므로 이 전략은 반드시 One-Version 규칙을 위반하고 다이아몬드 종속성 문제를 초래합니다. 대상이 동일한 종속 항목의 서로 다른 버전을 사용하는 외부 라이브러리 두 개에 종속되는 경우 어떤 버전을 가져올지 알 수 없습니다. 즉, 외부 종속 항목을 업데이트하면 새로운 버전의 일부 종속 항목 버전이 충돌하기 시작하면 코드베이스 전체에서 서로 관련 없는 오류가 발생할 수 있습니다.

따라서 Bazel은 전이 종속 항목을 자동으로 다운로드하지 않습니다. 또한 안타깝게도 아무 효과가 없습니다. Bazel의 대안은 저장소 외부 종속 항목 모두와 저장소 전반의 종속 항목에 사용되는 명시적 버전을 나열하는 전역 파일을 요청하는 것입니다. 다행히 Bazel은 Maven 아티팩트 집합의 전이 종속 항목이 포함된 이러한 파일을 자동으로 생성할 수 있는 도구를 제공합니다. 이 도구를 한 번 실행하면 프로젝트의 초기 WORKSPACE 파일을 생성할 수 있습니다. 그런 다음 이 파일을 수동으로 업데이트하여 각 종속 항목의 버전을 조정할 수 있습니다.

하지만 여기서도 편의성과 확장성 중에서 선택할 수 있습니다. 소규모 프로젝트는 전이 종속 항목 관리를 걱정할 필요가 없고 자동 전이 종속 항목을 사용할 필요가 없습니다. 이 전략은 조직과 코드베이스가 증가함에 따라 점점 더 매력적이지 않게 되며, 충돌과 예기치 않은 결과가 더 자주 발생합니다. 대규모에서는 수동으로 종속 항목을 관리하는 비용이 자동 종속 항목 관리로 인해 발생하는 문제를 처리하는 비용보다 훨씬 적습니다.

외부 종속 항목을 사용하여 빌드 결과 캐싱

외부 종속 항목은 소스 코드를 제공하지 않고도 안정적인 버전의 라이브러리를 출시하는 타사에서 주로 제공하는 경우가 많습니다. 일부 조직에서는 자체 코드의 일부를 아티팩트로 제공하여 다른 코드가 내부 종속 항목이 아닌 타사에 종속되도록 할 수도 있습니다. 이렇게 하면 아티팩트가 빌드 속도가 느리지만 다운로드 속도가 빠른 경우 이론적으로 빌드 속도가 빨라질 수 있습니다.

하지만 이 과정에서 많은 오버헤드와 복잡성이 발생합니다. 즉, 각 아티팩트를 빌드하고 아티팩트 저장소에 업로드해야 할 필요가 있으며, 클라이언트는 { 101}최신 버전 또한 시스템의 여러 부분이 저장소의 다양한 지점에서 빌드되며 소스 트리의 일관된 뷰가 없으므로 디버깅이 훨씬 더 어려워집니다.

빌드에 오랜 시간이 걸리는 아티팩트 문제를 해결하는 더 좋은 방법은 앞서 설명한 대로 원격 캐싱을 지원하는 빌드 시스템을 사용하는 것입니다. 이러한 빌드 시스템은 모든 빌드의 결과 아티팩트를 엔지니어들이 공유하는 위치에 저장하므로, 개발자가 최근에 다른 사람이 빌드한 아티팩트에 의존하는 경우 빌드 시스템은 자동으로 다운로드합니다. 있습니다. 이렇게 하면 아티팩트에 따라 직접적으로 달라지는 모든 성능 이점을 누릴 수 있으며 빌드가 항상 동일한 소스에서 빌드된 것처럼 일관성이 보장됩니다. 이는 Google에서 내부적으로 사용하는 전략이며, Bazel이 원격 캐시를 사용하도록 구성할 수 있습니다.

외부 종속 항목의 보안 및 안정성

타사 소스의 아티팩트에 따라 본질적으로 위험합니다. 아티팩트 저장소와 같은 타사 소스가 다운되면 전체 종속 항목이 중단되어 외부 종속 항목을 다운로드할 수 없기 때문에 가용성 위험이 있습니다. 또한 보안 위협도 있습니다. 제3자 시스템이 공격자에 의해 손상된 경우 공격자는 참조된 아티팩트를 자체 설계 중 하나로 바꿔 빌드에 임의의 코드를 삽입할 수 있습니다. 에서 확인할 수 있습니다. 종속된 아티팩트를 제어하는 서버에 미러링하고 빌드 시스템이 Maven Central과 같은 타사 아티팩트 저장소에 액세스하는 것을 차단함으로써 두 가지 문제를 완화할 수 있습니다. 단점은 이러한 미러가 유지 관리에 필요한 노력과 리소스를 낭비하므로 종종 프로젝트 규모에 따라 선택 여부가 달라집니다. 보안 문제는 소스 저장소에 각 타사 아티팩트의 해시를 지정하도록 요구하여 오버헤드가 거의 없이 완전히 방지될 수 있으며, 아티팩트가 조작되면 빌드가 실패합니다. 이 문제를 완전히 회피하는 또 다른 방법은 프로젝트의 종속 항목을 벤더링하는 것입니다. 프로젝트는 공급업체에서 종속 항목을 만들 때 프로젝트의 소스 코드와 함께 소스 또는 바이너리로 소스 컨트롤에 체크인됩니다. 즉, 프로젝트의 모든 외부 종속 항목이 내부 종속 항목으로 변환됩니다. Google은 이 방법을 내부적으로 사용하여 Google 전체에서 참조되는 모든 타사 라이브러리를 Google 소스 트리의 루트에 있는 third_party 디렉터리로 확인합니다. 하지만 Google의 소스 제어 시스템은 대규모 대규모 모노레코드를 처리하도록 커스텀되어 있기 때문에 Google에서 이 방법을 사용할 수 있기 때문에 일부 조직에서는 벤더링을 수행할 수 없습니다.