작업 기반 빌드 시스템

문제 신고 소스 보기 Nightly · 7.3 · 7.2 · 7.1 · 7.0 · 6.5

이 페이지에서는 태스크 기반 빌드 시스템, 작동 방식, 태스크 기반 시스템에서 발생할 수 있는 몇 가지 문제에 대해 설명합니다. 셸 스크립트 후, 작업 기반 빌드 시스템은 빌딩의 논리적 발전입니다.

태스크 기반 빌드 시스템 이해

작업 기반 빌드 시스템에서 작업의 기본 단위는 작업입니다. 각 태스크는 모든 종류의 로직을 실행할 수 있는 스크립트이며 태스크는 다른 태스크를 그보다 먼저 실행해야 하는 종속 항목으로 지정합니다. 사용 중인 대부분의 주요 빌드 시스템 Ant, Maven, Gradle, Grunt 및 Rake와 같은 오늘날은 작업 기반입니다. 대신 셸 스크립트, 대부분의 최신 빌드 시스템에서는 엔지니어가 빌드 파일을 작성해야 함 확인할 수 있습니다

이 예를 개미 설명서:

<project name="MyProject" default="dist" basedir=".">
   <description>
     simple example build file
   </description>
   <!-- set global properties for this build -->
   <property name="src" location="src"/>
   <property name="build" location="build"/>
   <property name="dist" location="dist"/>

   <target name="init">
     <!-- Create the time stamp -->
     <tstamp/>
     <!-- Create the build directory structure used by compile -->
     <mkdir dir="${build}"/>
   </target>
   <target name="compile" depends="init"
       description="compile the source">
     <!-- Compile the Java code from ${src} into ${build} -->
     <javac srcdir="${src}" destdir="${build}"/>
   </target>
   <target name="dist" depends="compile"
       description="generate the distribution">
     <!-- Create the distribution directory -->
     <mkdir dir="${dist}/lib"/>
     <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
     <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
   </target>
   <target name="clean"
       description="clean up">
     <!-- Delete the ${build} and ${dist} directory trees -->
     <delete dir="${build}"/>
     <delete dir="${dist}"/>
   </target>
</project>

빌드 파일은 XML로 작성되며 작업 목록(XML의 <target> 태그)과 함께 빌드에 관한 간단한 메타데이터를 정의합니다. Ant는 작업을 나타내기 위해 타겟이라는 단어를 사용하고 명령어를 나타내기 위해 작업이라는 단어를 사용합니다. 각 태스크는 Ant에서 정의한 가능한 명령어 목록을 실행합니다. 여기에는 디렉터리 생성 및 삭제, javac 실행, JAR 파일 생성이 포함됩니다. 이 명령어 세트는 사용자 제공 플러그인으로 확장하여 모든 종류의 로직을 처리할 수 있습니다. 각 태스크는 depends 속성을 통해 종속되는 태스크를 정의할 수도 있습니다. 이러한 종속 항목은 비순환 그래프를 형성하므로 Cloud CDN을 사용 설정합니다

종속 항목을 보여주는 아크릴 그래프

그림 1. 종속 항목을 보여주는 비순환 그래프

사용자는 Ant의 명령줄 도구에 작업을 제공하여 빌드를 수행합니다. 예를 들어 사용자가 ant dist를 입력하면 Ant는 다음 단계를 따릅니다.

  1. 현재 디렉터리에 있는 build.xml라는 파일을 로드하고 파싱하여 그림 1과 같은 그래프 구조를 만듭니다.
  2. 명령줄에 제공된 dist라는 작업을 찾습니다. compile라는 작업에 종속 항목이 있음을 발견합니다.
  3. compile라는 작업을 찾아 다음 항목에 종속 항목이 있음을 발견합니다. init라는 작업을 실행합니다.
  4. init라는 작업을 찾아 종속 항목이 없음을 발견합니다.
  5. init 작업에 정의된 명령어를 실행합니다.
  6. compile 작업에 정의된 명령어를 실행합니다. 이 모든 것이 주어진 경우 태스크의 종속 항목이 실행됩니다.
  7. dist 작업에 정의된 명령어를 실행합니다. 이 모든 것이 주어진 경우 태스크의 종속 항목이 실행됩니다.

결국 dist 작업을 실행할 때 Ant에서 실행되는 코드는 동일합니다. 다음 셸 스크립트에 추가합니다.

./createTimestamp.sh
mkdir build/
javac src/* -d build/
mkdir -p dist/lib/
jar cf dist/lib/MyProject-$(date --iso-8601).jar build/*

문법을 제거하면 빌드 파일과 빌드 스크립트는 실제로 크게 다르지 않습니다. 하지만 이렇게 하여 이미 많은 이점을 얻었습니다. 다른 디렉터리에서 새 빌드 파일을 만들고 서로 연결할 수 있습니다. 쉽게 임의의 복잡한 방법으로 기존 작업에 의존하는 새 작업을 추가합니다. 단일 작업의 이름을 ant 명령줄 도구에 전달하기만 하면 실행해야 하는 모든 작업이 결정됩니다.

Ant는 원래 2000년에 출시된 오래된 소프트웨어입니다. 기타 도구 Maven과 Gradle은 그 사이에 Ant를 개선했으며 기본적으로 외부 IP 주소 자동 관리와 같은 종속 항목, XML 없이 더 깔끔한 문법을 제공합니다. 하지만 이러한 최신 시스템의 본질은 동일합니다. 엔지니어가 작업으로서 원칙적이고 모듈식 방식으로 빌드 스크립트를 작성하고 이러한 작업을 실행하고 그 간의 종속 항목을 관리하는 도구를 제공할 수 있습니다.

태스크 기반 빌드 시스템의 어두운 측면

이러한 도구를 사용하면 기본적으로 엔지니어가 모든 스크립트를 작업으로 정의할 수 있기 때문에 매우 강력해서 상상할 수 있는 거의 모든 것을 할 수 있습니다. 공유할 수 있습니다. 하지만 이러한 힘에는 단점이 수반되고 작업 기반 빌드 시스템은 빌드 스크립트가 더 복잡해짐에 따라 작업하기가 더 어려워집니다. 이 이러한 시스템의 문제는 실제로 컴퓨터에 너무 많은 전력을 공급하여 시스템에 충분한 전원이 부족할 수 있습니다. 시스템은 스크립트가 무엇을 하는지 알 수 없으므로 빌드 단계를 예약하고 실행하는 방식이 매우 보수적이어야 하므로 성능이 저하됩니다. 또한 시스템에서 각 스크립트가 제대로 작동하는지 확인할 방법이 없으므로 스크립트가 점점 복잡해지고 디버깅이 필요한 또 다른 항목이 될 수 있습니다.

빌드 단계 병렬화 어려움

최신 개발 워크스테이션은 매우 강력하며 여러 빌드 단계를 동시에 실행할 수 있습니다. 하지만 작업 기반 시스템은 작업 실행을 병렬화할 수 있을 것 같아도 실제로는 병렬화하지 못하는 경우가 많습니다. 작업 A가 작업 B와 C에 종속되어 있다고 가정해 보겠습니다. 태스크 B와 C는 서로 종속되지 않으므로 시스템이 태스크 A에 더 빨리 도달할 수 있도록 동시에 실행해도 안전한가요? 아마도, 아무 것도 터치하지 않으면 동일한 리소스를 사용할 수 있습니다 그렇지 않을 수도 있습니다. 둘 다 동일한 파일을 사용하여 동시에 실행하면 충돌이 발생합니다. 없음 이러한 충돌을 야기하거나 위험을 감수해야 하기 때문에 (드물지만 디버그하기 매우 어려운 빌드 문제를 초래하거나) 전체 빌드가 단일 프로세스의 단일 스레드에서 실행되도록 제한할 수 있습니다. 이는 강력한 개발자 머신을 낭비하게 만들 수 있으며 빌드를 여러 머신에 배포할 가능성을 완전히 배제합니다.

증분 빌드 수행의 어려움

우수한 빌드 시스템을 사용하면 엔지니어가 소규모 변경사항으로 전체 코드베이스를 처음부터 다시 빌드할 필요가 없도록 안정적인 증분 빌드를 실행할 수 있습니다. 이는 빌드 시스템이 느리고 앞서 언급한 이유로 빌드 단계를 병렬화할 수 없는 경우에 특히 중요합니다. 하지만 안타깝게도 여기에도 작업 기반 빌드 시스템이 어렵습니다. 작업은 무엇이든 할 수 있으므로 일반적으로 작업이 이미 완료되었는지 확인할 방법이 없습니다. 많은 작업은 단순히 소스 파일 세트를 가져와 컴파일러를 실행하여 바이너리 세트를 만듭니다. 따라서 기본 소스 파일이 변경되지 않은 경우 다시 실행할 필요가 없습니다. 하지만 추가 정보가 없으면 시스템은 이러한 내용을 말할 수 없습니다. 변경되었을 수 있는 파일을 다운로드하거나 각 실행마다 다를 수 있는 타임스탬프를 기록합니다. 정확성을 보장하기 위해 시스템은 일반적으로 각 빌드 중에 모든 태스크를 다시 실행해야 합니다. 다소 유용함 증분 빌드를 사용하려면 엔지니어가 작업을 재실행해야 하는 조건으로 인해 발생할 수 있습니다. 일부 경우에는 가능하지만 종종 보이는 것보다 훨씬 까다로운 문제입니다. 예를 들어 파일을 다른 파일에 직접 포함할 수 있도록 하는 C++처럼 변경사항을 감시해야 하는 전체 파일 세트를 결정하는 것은 불가능합니다. 소스 코드를 생성합니다. 엔지니어는 종종 지름길을 택하고, 이러한 단축키는 작업 결과가 나오지 않게 되어 적절하지 않은 경우에도 재사용됨 이러한 문제가 자주 발생하면 엔지니어는 매 빌드 전에 클린을 실행하여 최신 상태를 가져오는 습관을 갖게 되며, 이는 애초에 증분 빌드를 사용하는 목적을 완전히 무시하는 것입니다. 작업을 재실행해야 할 때를 파악하는 일은 놀라울 정도로 미묘하며 작업이 인간보다 기계가 더 잘 처리합니다.

스크립트 유지관리 및 디버깅의 어려움

마지막으로 작업 기반 빌드 시스템에서 적용하는 빌드 스크립트는 사용하기 어렵습니다. 빌드 스크립트는 빌드 중인 시스템과 같은 코드이며 버그를 쉽게 숨길 수 있습니다. 다음은 태스크 기반 빌드 시스템을 사용할 때 매우 흔히 발생하는 버그의 예입니다.

  • 태스크 A는 특정 파일을 출력으로 생성하기 위해 태스크 B에 종속됩니다. 작업 B의 소유자가 다른 작업에서 이 작업을 사용하고 있다는 사실을 모르고 다른 위치에서 출력을 생성하도록 변경합니다. 이는 누군가 태스크 A를 실행하려고 시도했을 때 실패하는 것으로 확인될 때까지 감지할 수 없습니다.
  • 작업 A는 작업 B에 종속되며, 이는 작업 B에 종속되며, 이는 작업 B에 종속되어 특정 파일을 태스크 A에 필요한 출력으로 만듭니다. 태스크 B의 소유자가 더 이상 태스크 C에 종속될 필요가 없다고 결정하면 태스크 B가 태스크 C를 전혀 신경 쓰지 않더라도 태스크 A가 실패하게 됩니다.
  • 새로운 작업의 개발자는 실수로 머신이 실행 중인 작업, 즉 툴의 위치나 특정 환경 변수입니다 태스크가 개발자의 머신에서는 작동하지만 다른 개발자가 시도할 때마다 실패합니다.
  • 태스크에는 인터넷에서 파일을 다운로드하거나 빌드에 타임스탬프를 추가하는 것과 같은 비결정론적 구성요소가 포함됩니다. 이제 사람들은 결과를 얻지 못할 수 있습니다. 즉, 엔지니어들이 항상 서로의 오류를 재현하고 고칠 수 있는 것은 장애를 예측하지 못할 수 있습니다
  • 종속 항목이 여러 개인 태스크는 경합 상태를 일으킬 수 있습니다. 작업 A인 경우 작업 B와 작업 C에 모두 종속되며 작업 B와 C는 모두 작업 A는 작업 B와 C 중 어느 것이 무엇인지에 따라 다른 결과를 얻게 됨 있습니다.

여기에 설명된 태스크 기반 프레임워크 내에서 이러한 성능, 정확성 또는 유지보수성 문제를 해결하는 범용적인 방법은 없습니다. 엔지니어가 빌드 중에 실행되는 임의의 코드를 작성할 수 있는 한 시스템은 항상 빌드를 빠르고 정확하게 실행하기에 충분한 정보를 보유할 수 없습니다. 이 문제를 해결하려면 엔지니어의 일부 권한을 시스템에 다시 부여하고 시스템의 역할을 작업 실행이 아닌 아티팩트 생성으로 재개념화해야 합니다.

이 접근 방식으로 Blaze와 같은 아티팩트 기반 빌드 시스템을 만들었습니다. Bazel이 있습니다