아티팩트 기반 빌드 시스템

문제 신고 소스 보기 1박 · 7.2 · 7.1 · 7.0 · 6.5 · 6.4

이 페이지에서는 아티팩트 기반 빌드 시스템과 이 시스템에 관한 철학을 다룹니다. 만들 수 있습니다. Bazel은 아티팩트 기반 빌드 시스템입니다. 작업 기반 빌드가 빌드 스크립트보다 좋은 단계이기 때문에 작업을 실행하기에 개별 엔지니어가 작업을 정의할 수 있습니다

아티팩트 기반 빌드 시스템에는 시스템이 정의하는 소수의 작업이 있음 엔지니어가 제한된 방식으로 구성할 수 있습니다 엔지니어들은 여전히 시스템에 빌드할 방법빌드 시스템이 결정합니다. 마찬가지로 아티팩트 기반 빌드 시스템(예: Bazel)에서 빌드 파일이 있지만 이러한 빌드 파일의 콘텐츠는 매우 다릅니다. 오히려 더 이상 튜링이 완성되지 않은 스크립트 언어에서 명령적인 명령 집합이 되는 것보다 출력 생성 방법을 설명하면 Bazel의 빌드 파일은 선언적 빌드할 아티팩트 집합과 그 종속 항목을 설명하는 매니페스트 빌드 방법에 영향을 미치는 제한된 옵션 집합일 수 있습니다. 엔지니어가 bazel를 실행할 때 명령줄에서 빌드할 대상 집합 (무엇)을 지정합니다. Bazel은 컴파일의 구성, 실행, 예약을 담당합니다. 단계 (방법)를 참조하세요. 빌드 시스템이 이제 빌드 시스템에서 실행할 수 있게 해주기 때문에 훨씬 더 강력한 보장을 통해 보다 효율적이면서도 정확성을 보장합니다.

기능적인 관점

아티팩트 기반 빌드 시스템과 기능적 빌드 시스템을 비유하기도 쉽습니다. 프로그래밍에 대해 알아보겠습니다. Java, C 및 Python)에서 차례로 실행할 문 목록을 지정하고 프로그래머가 작업 기반 빌드 시스템을 사용하여 일련의 단계를 정의하는 것과 같은 방식으로 정의할 수 있습니다 함수형 프로그래밍 언어 (예: Haskell 및 ML)는 일련의 수학 방정식처럼 더 구성됩니다. 포함 프로그래머는 수행할 계산을 설명하지만 계산이 실행되는 시점과 방법에 대한 세부 정보를 합니다.

이는 아티팩트 기반 빌드 시스템에서 매니페스트를 선언하는 아이디어에 매핑됩니다. 시스템이 빌드 실행 방법을 파악하도록 할 수 있습니다. 많은 문제에서 함수 프로그래밍을 사용하여 쉽게 표현할 수 있지만, 언어가 이러한 입력 문장을 쉽게 병렬 처리할 수 있는 정확성을 강력하게 보장합니다. 불가능에 가깝습니다. 다음을 사용하여 표현할 수 있는 가장 쉬운 문제입니다. 함수 프로그래밍은 한 조각을 간단히 변환하는 데이터를 다른 규칙 또는 함수로 변환할 수 있습니다. 이게 바로 시스템 전체가 사실상 수학적 함수입니다. 소스 파일 (및 컴파일러와 같은 도구)을 입력으로 사용하여 바이너리를 출력으로 사용할 수 있습니다. 따라서 빌드의 기반이 잘 작동한다는 것은 당연한 일입니다. 시스템을 살펴봤습니다

아티팩트 기반 빌드 시스템 이해

Google의 빌드 시스템인 Blaze는 최초의 아티팩트 기반 빌드 시스템이었습니다. 바젤 Blaze의 오픈소스 버전입니다.

Bazel에서 빌드 파일 (일반적으로 BUILD)은 다음과 같이 표시됩니다.

java_binary(
    name = "MyBinary",
    srcs = ["MyBinary.java"],
    deps = [
        ":mylib",
    ],
)
java_library(
    name = "mylib",
    srcs = ["MyLibrary.java", "MyHelper.java"],
    visibility = ["//java/com/example/myproduct:__subpackages__"],
    deps = [
        "//java/com/example/common",
        "//java/com/example/myproduct/otherlib",
    ],
)

Bazel에서 BUILD 파일은 대상을 정의합니다. 여기서 두 가지 유형의 대상은 다음과 같습니다. java_binaryjava_library. 모든 대상은 이전에 발생했던 바이너리 타겟은 시스템에 의해 생성될 수 있는 바이너리를 생성하며 직접 실행되며 라이브러리 타겟은 바이너리 또는 기타 라이브러리를 사용합니다. 모든 표적에는 다음이 포함됩니다.

  • name: 대상이 명령줄에서 참조되는 방식 및 기타 대상 대상
  • srcs: 타겟의 아티팩트를 만들기 위해 컴파일할 소스 파일입니다.
  • deps: 이 타겟 전에 빌드되어 연결되어야 하는 다른 대상 이 항목

종속 항목은 동일한 패키지 (예: MyBinary) 내에 있을 수 있습니다. :mylib의 종속 항목) 또는 동일한 소스 계층 구조의 다른 패키지에 있는 경우 (예: //java/com/example/common에 관한 mylib의 종속 항목).

작업 기반 빌드 시스템과 마찬가지로 Bazel의 명령줄 있습니다. MyBinary 타겟을 빌드하려면 bazel build :MyBinary를 실행합니다. 후(After) 클린 저장소인 Bazel에 이 명령어를 처음으로 입력합니다.

  1. 작업공간의 모든 BUILD 파일을 파싱하여 종속 항목 그래프를 만듭니다. 살펴봤습니다
  2. 그래프를 사용하여 MyBinary의 전이 종속 항목을 확인합니다. 저것 MyBinary이(가) 의존하는 모든 타겟과 이들이 사용하는 모든 타겟 대상은 재귀적으로 종속됩니다
  3. 각 종속 항목을 순서대로 빌드합니다. Bazel은 먼저 각 다른 종속 항목이 없고 어떤 종속 항목을 추적하며 여전히 각 대상에 대해 빌드되어야 합니다 모든 표적의 공격이 빌드되면 Bazel이 해당 대상을 빌드하기 시작합니다. 이 프로세스 MyBinary의 모든 전이 종속 항목이 빌드되었습니다.
  4. MyBinary를 빌드하여 빌드한 종속 항목을 빌드합니다.

기본적으로 이런 일이 벌어지고 있는 것처럼 보일 수는 있지만, 작업 기반 빌드 시스템을 사용할 때와 어떻게 다른지 살펴보겠습니다. 실제로 최종 결과는 동일한 바이너리이고 이를 생성하는 프로세스는 여러 단계를 분석하여 그 사이에 종속 항목을 찾은 다음 정리해 보겠습니다 하지만 중요한 차이점이 있습니다. 첫 번째가 나타납니다. 각 타겟이 Java 라이브러리만 생성한다는 것을 알고 있으므로 Bazel은 임의의 언어가 아닌 Java 컴파일러를 실행하기만 하면 된다는 것을 알고 있습니다. 이러한 단계를 병렬로 실행해도 안전하다는 것을 인지하고 있습니다. 이를 통해 기존 모델을 빌드하는 것보다 훨씬 더 많은 하나의 멀티코어 머신에서 한 번에 하나씩 타겟팅하고 아티팩트 기반 접근 방식으로 빌드 시스템이 자체 실행을 담당하게 됨 동시 로드를 보다 강력하게 보장할 수 있습니다

동시 로드를 넘어서는 이점이 있습니다. 다음으로 할 일은 개발자가 아무것도 변경하지 않고 bazel build :MyBinary를 두 번 입력하면 Bazel이 더 적은 시간에 종료됩니다. 타겟이 최신 상태라는 메시지가 표시됩니다. 이것은 앞서 말씀드린 함수적 프로그래밍 패러다임 덕분에 가능한 일이죠. Bazel은 각 대상이 단지 Java API를 실행한 결과라는 것을 알고 있습니다. Java 컴파일러의 출력이 입력이 변경되지 않는 한 출력을 재사용할 수 있습니다. 이러한 분석은 모든 수준에서 효과가 있습니다. MyBinary.java가 변경되면 Bazel은 MyBinary를 다시 빌드하지만 mylib를 재사용합니다. 소스 파일이 //java/com/example/common가 변경되면 Bazel은 해당 라이브러리를 다시 빌드하는 방법을 알고 있습니다. mylibMyBinary이지만 //java/com/example/myproduct/otherlib는 재사용합니다. Bazel은 모든 단계에서 실행되는 도구의 속성을 알고 있기 때문에 매번 최소한의 아티팩트 집합만 재빌드할 수 있는 동안 오래된 빌드를 생성하지 않습니다

작업이 아닌 아티팩트 측면에서 빌드 프로세스를 재구성하는 것이 미묘함 강력한 힘이 있습니다. 프로그래머에게 노출되는 유연성을 줄여서 빌드는 시스템은 빌드의 모든 단계에서 수행 중인 작업에 대해 더 많이 알 수 있습니다. 가능 이 지식을 활용하여 빌드를 동시에 로드함으로써 빌드를 훨씬 효율적으로 만들 수 있습니다. 그 출력을 재사용할 수 있습니다 하지만 이것은 실제로 첫 번째 단계일 뿐이며 이러한 구성 요소 및 재사용을 통해 분산된 일괄 처리의 기반을 형성합니다 빌드 시스템을 빌드합니다.

Bazel의 다른 멋진 트릭

아티팩트 기반 빌드 시스템으로 근본적으로 동시 로드 문제 해결 재사용 및 재사용이 가능합니다. 하지만 여전히 이전에 제시되었지만 해결하지 못한 몇 가지 문제가 있습니다. Bazel은 똑똑하고 먼저 이에 대해 논의한 후 계속 진행하세요.

종속 항목로서의 도구

앞서 겪었던 한 가지 문제는 빌드가 설치된 도구에 따라 달라진다는 것이었습니다. 여러 시스템 간에 빌드를 재현하기가 어려울 수 있는데, 사용할 수 있습니다. 문제가 더욱 어려워짐 프로젝트에 적합한 도구를 기반으로 한 다른 도구가 필요한 언어를 빌드되거나 컴파일되는 플랫폼 (예: Windows 대 Linux) 플랫폼마다 조금씩 다른 도구를 사용해야 동일한 작업을 수행할 수 있습니다

Bazel은 도구를 종속 항목으로 취급하여 이 문제의 첫 번째 부분을 해결합니다. 확인할 수 있습니다 작업공간의 모든 java_library는 암시적으로 Java에 종속됨 잘 알려진 컴파일러를 기본적으로 사용합니다. Bazel이 빌드한 java_library: 지정된 컴파일러를 사용할 수 있는지 확인합니다. 위치를 확인할 수 있습니다 다른 종속 항목과 마찬가지로 Java 컴파일러가 이에 의존하는 모든 아티팩트가 다시 빌드됩니다.

Bazel은 첫 번째 부분인 플랫폼 독립성을 빌드 구성을 참조하세요. 오히려 구성 유형에 따라 달라집니다.

  • 호스트 구성: 빌드 중에 실행되는 빌드 도구
  • 대상 구성: 최종적으로 요청한 바이너리 빌드

빌드 시스템 확장

Bazel은 다양한 프로그래밍 언어에서 엔지니어는 항상 더 많은 작업을 하길 원합니다. 이는 작업 기반 모든 종류의 빌드 프로세스를 지원하는 유연성이며 아티팩트 기반 빌드 시스템에서 포기하지 않는 것이 더 낫습니다. 다행히 Bazel을 사용하면 지원되는 대상 유형을 맞춤 규칙 추가에 대해 자세히 알아보세요.

Bazel에서 규칙을 정의하기 위해 규칙 작성자는 규칙이 필수 (BUILD 파일에 전달된 속성 형식) 및 수정된 출력 집합의 일종입니다. 또한 작성자는 사용자가 해당 규칙에 의해 생성됩니다. 각 작업은 입력과 출력을 선언하며 특정 실행 파일을 실행하거나 특정 문자열을 파일에 쓸 수 있으며 입력과 출력을 통해 다른 작업에 연결됩니다. 즉, 빌드 시스템의 최하위 수준의 컴포저블 단위로, 작업은 선언된 입력과 출력만 사용하는 한 원하는 것이 무엇이든 간에 Bazel은 적절히 작업을 예약하고 결과를 캐시합니다.

개발자의 작업을 막을 방법이 없으므로 시스템이 완벽하지는 않습니다. 비결정적 프로세스를 도입하는 것과 같은 작업을 파악할 수 있습니다. 그러나 실제로 자주 발생하지는 않으며, 행동 수준까지 악용될 가능성은 크게 감소합니다. 기회를 제공합니다. 많은 공통 언어 및 도구를 지원하는 규칙은 온라인에서 널리 사용되고 있으며 대부분의 프로젝트에서 있습니다. 사용하는 경우에도 규칙 정의는 한 가지로만 정의하면 중앙의 위치에 배치되므로 대부분의 엔지니어는 걱정할 필요 없이 해당 규칙을 적용할 수 있습니다.

환경 격리

작업이 다른 작업의 작업과 동일한 문제를 마주할 수 있을 것 같음 동일한 시스템에 쓰는 작업을 파일이 서로 충돌하게 되면 어떻게 될까요? 실제로 Bazel은 충돌이 발생할 경우 샌드박스를 사용하면 불가능할 수 있습니다. 지원됨 모든 작업이 파일 시스템을 통해 다른 모든 작업과 격리됩니다. 있습니다. 사실상 각 작업에서는 선언한 입력과 해당 출력이 있는 출력을 포함하는 파일 시스템 있습니다. 이는 Linux의 LXC와 같은 시스템에서 시행하는 살펴봤습니다 즉, 작업이 서로 충돌할 수 없으며 다른 하나는 그들이 선언하지 않은 파일을 읽을 수 없기 때문이며 작성하지만 선언하지 않은 파일은 있습니다. 또한 Bazel은 샌드박스를 사용하여 않습니다.

외부 종속 항목을 확정적으로 만들기

아직 한 가지 문제가 남아 있습니다. 빌드 시스템은 (도구든 라이브러리든 상관없이) 외부 소스에서 빌드할 수 있습니다 이는 다음 예에서 JAR 파일을 다운로드하는 @com_google_common_guava_guava//jar 종속 항목 볼 수 있습니다

현재 작업공간 외부의 파일에 의존하는 것은 위험합니다. 이러한 파일은 빌드 시스템이 지속적으로 업데이트 사항을 확인해야 할 수 있습니다. 참신한 앱인지 아닌지 판단해야 합니다. 상응하는 변경사항 없이 원격 파일이 변경되는 경우 문제가 있으면 재현 불가능한 빌드(빌드)가 일어날 수 있습니다. 어떤 날에는 효과가 있었고 다음 날은 눈에 띄지 않게 되어 명백한 이유 없이 종속 항목 변경을 지원합니다. 마지막으로, 외부 의존은 엄청난 보안을 위험: 공격자가 회사 또는 조직의 보안에 침투할 수 있는지 종속 항목 파일을 그들만의 고유한 디자인을 가질 수 있고, 잠재적으로는 빌드를 완전히 제어할 수 있습니다. 살펴봤습니다

근본적인 문제는 빌드 시스템이 이러한 파일을 소스 제어에 체크인하지 않아도 됩니다 종속 항목 업데이트 의식적인 선택이지만 그러한 선택은 중앙에서 한 번 이루어져야 합니다. 개별 엔지니어가 자동으로 관리하거나 있습니다. 'Live at Head' 모델을 사용하더라도 여전히 확정적입니다. 즉, 마지막 커밋에서 커밋을 체크아웃하는 경우 종속 항목이 이전 상태가 아닌 이전 상태 그대로 표시됩니다. 있습니다.

Bazel 및 다른 빌드 시스템은 모든 외부 항목에 대한 암호화 해시를 나열하는 작업공간 전체 매니페스트 파일입니다. 종속 항목을 설치합니다 해시는 전체 파일을 소스 제어에 체크인하지 않고 검증할 수 있습니다 새 외부 종속 항목이 작업공간에서 참조되면 해당 종속 항목의 해시는 수동으로 또는 자동으로 매니페스트에 추가됩니다. Bazel이 캐시된 종속 항목의 실제 해시를 예상되는 해시를 다시 다운로드하고 해시가 다른 경우에만 파일을 다시 다운로드합니다.

다운로드한 아티팩트에 매니페스트의 경우, 매니페스트의 해시가 업데이트되지 않으면 빌드가 실패합니다. 이 자동으로 적용될 수 있지만, 해당 변경사항을 승인하고 소스 제어가 새 종속 항목을 수락하기 전에 코드를 실행합니다 즉, 종속 항목이 업데이트된 시점과 작업공간 소스에 상응하는 변경사항이 없으면 종속 항목을 변경할 수 없습니다. 또한 이전 버전의 소스 코드를 체크아웃할 때 빌드 시점에 사용하던 것과 동일한 종속 항목을 사용하도록 보장 또는 해당 종속 항목이 체크인된 경우 실패합니다. 더 이상 사용할 수 없음).

물론 원격 서버를 사용할 수 없게 되거나 손상된 데이터를 제공하기 시작합니다. 이로 인해 모든 빌드가 실패하기 시작할 수 있습니다 이 문제를 해결할 수 있습니다 이를 방지하려면 문제가 있는 경우, 중요한 프로젝트의 경우 해당 프로젝트의 모든 데이터를 신뢰할 수 있고 제어하는 서버 또는 서비스에 종속 항목을 가져올 수 있습니다 그렇지 않은 경우 빌드 시스템의 하드웨어는 항상 서드 파티에 의해 안전할 수 있으므로 체크인 해시가 보안을 보장하더라도 마찬가지입니다.