8.7

View in English

8.7 빌드 시스템과 산출물 관리

개요와 동기

빌드는 소스 코드가 출하할 수 있는 무엇이 되는 곳입니다. 8.1장의 모든 파이프라인이 여기서 시작합니다. 무언가를 테스트하고, 스캔하고, 배포하고, 승격하기 전에, 빌드 시스템이 소스 파일 트리를 구체적인 산출물, 곧 컴파일된 바이너리, 패키지, 컨테이너 이미지, 정적 자산 묶음으로 바꿔야 합니다. 그 첫 단계가 느리거나, 불안정하거나, 재현할 수 없다면 이후의 모든 단계가 그 피해를 물려받습니다. 두 기계에서 다른 출력을 내는 빌드는 돌리는 모든 테스트와 모으는 모든 승인을 훼손합니다. 검증한 것이 출하하는 것이라고 증명할 수 없기 때문입니다.

이 장은 그 첫 단계와 그 출력에 관한 것입니다. 산출물을 구성하는 빌드 시스템, 그리고 그것을 저장, 버전 관리, 보호, 승격하는 산출물 관리입니다. 전체 지속적 통합 및 지속적 전달(CI/CD) 파이프라인을 다루는 8.1장보다 의도적으로 좁습니다. 여기서의 주제는 빌드 자체와 그것이 내보내는 산출물입니다. 입력을 추적하고 통제하는 방식을 다스리는 소프트웨어 형상 관리의 2.10장, 끌어오는 제3자 코드를 다스리는 의존성 및 공급망 관리의 2.18장을 보완합니다. 빌드는 그 입력이 만나는 곳입니다. 소스, 의존성, 구성이 모두 하나의 불변 출력으로 수렴합니다.

큰 팀에게 판돈은 구체적입니다. 수백 명의 엔지니어가 하루에도 여러 번 몇 분짜리 빌드를 기다릴 때, 합산된 잃어버린 시간은 거의 모든 다른 엔지니어링 비용을 압도합니다. 산출물이 가변적이거나, 추적되지 않거나, 환경마다 다시 빌드되면 프로덕션에서 무엇이 돌아가는지 확신을 갖고 말할 능력을 잃습니다. 기업과 정부 환경에서 그 추적 가능성은 선택이 아닙니다. 감사자와 보안 담당자는 프로덕션의 바이너리가 리뷰된 소스에서, 신뢰할 수 있는 시스템이 빌드했으며, 기록된 보관 연쇄가 있다는 증거를 필요로 합니다. 규율 있는 빌드와 산출물 실천은 그 증거를 감사 전마다의 허둥지둥이 아니라 일반 작업의 부산물로 바꿉니다.

핵심 원칙

  • 빌드는 전달의 첫 단계입니다. 그 속도와 정확성을 프로덕션 관심사로 다루십시오.
  • 재현 가능하고, 가능한 곳에서는 밀폐된 빌드를 목표로 하십시오. 같은 입력, 늘 같은 출력.
  • 산출물을 한 번 빌드하고 정확히 그 산출물을 환경 전반에 승격하십시오.
  • 산출물을 불변이고 콘텐츠 주소 지정되게 만들고, 의미 있게 버전을 관리하십시오.
  • 산출물을 보존, 접근 통제, 출처가 있는 관리형 저장소에 보관하십시오.
  • 공격적으로 캐시하되 캐시를 속도의 요령만이 아니라 보안 경계로 다루십시오.
  • 출처, 서명, 자재 명세서를 사후가 아니라 빌드 시점에 포착하십시오.

권장 사항

빌드를 전달의 첫 단계로 다룬다

빌드 시스템은 프로덕션 인프라이므로 그렇게 자금을 대고 유지해야 합니다. 산출물의 빠르고 올바른 구성은 CI/CD(8.1장)가 의존하는 기초입니다. 팀이 빌드를 사후 고려, 곧 아무도 소유하지 않는 셸 스크립트 더미로 다루면, 불안정한 파이프라인, 수수께끼 같은 “내 기계에서는 되는데” 결함, 11부에 기술된 엔지니어링 흐름 전체를 침식하는 느린 피드백으로 값을 치릅니다. 빌드에 소유자, 코드와 나란히 버전 관리에 보관되는 정의(저장소 구조의 2.14장), 다른 핵심 시스템과 같은 리뷰 규율을 주십시오.

빌드를 재현 가능하고, 가능하면 밀폐되게 만든다

재현 가능한 빌드는 같은 소스에서 비트 단위로 동일한 출력을 만들어, 누구나 독립적으로 다시 빌드해 산출물이 소스와 일치함을 검증할 수 있습니다. 이것이 커밋과 배포 사이에 바이너리가 변조되지 않았다고 신뢰하게 해 주는 속성입니다. 거기에 이르려면 비결정성의 원천을 없애야 합니다. 내장된 타임스탬프, 절대 파일 경로, 빌드 순서 무작위성, 결과가 시간에 따라 벌어지는 네트워크 가져오기입니다.

밀폐된 빌드는 모든 입력을 앞서 선언하고 네트워크나 호스트의 주변 상태에 닿을 수 없는 격리된 환경에서 실행해 한 걸음 더 나아갑니다. 선언하지 않은 것은 아무것도 빌드에 들어가지 않습니다. 고정된 툴체인 버전, 고정된 의존성, 명시적 소스 파일입니다. 밀폐성이 재현성을 운이 아니라 믿을 만하게 만듭니다. 완전한 밀폐성은 도구와 규율에서 실제 비용이 들므로, 이분법이 아니라 방향으로 다루십시오. 컴파일러 버전 고정, 의존성 벤더링이나 잠금, 타임스탬프 제거 같은 부분적 진전만으로도 적은 노력에 신뢰의 대부분을 얻습니다.

잠금 파일로 의존성을 결정적으로 해결한다

모든 빌드는 제3자 코드를 끌어오며, 그것을 어떻게 해결하느냐가 빌드가 결정적인지를 정합니다. 잠금 파일은 모든 직접 및 전이 의존성의 정확한 해결된 버전과 암호학적 해시를 기록해, 몇 달 뒤의 빌드가 정확히 같은 그래프로 해결됩니다. 잠금 파일을 커밋하고, 그에 대한 변경을 리뷰 가능한 사건으로 다루고, 변조된 상류 패키지가 눈에 띄지 않고 끼어들지 못하도록 가져올 때마다 해시를 검증하십시오. 이것이 2.18장의 공급망 규율의 빌드 시점 얼굴입니다. 잠금 파일이 없으면 “어제 빌드됐다”는 오늘 무엇이 빌드될지 아무것도 알려 주지 않습니다. 떠다니는 버전 범위가 새 릴리스를 조용히 끌어오거나, 공격자가 악성 릴리스를 게시할 수 있기 때문입니다.

로컬과 원격에서 증분 빌드와 캐싱을 쓴다

아무도 바뀌지 않은 것을 다시 빌드해서는 안 됩니다. 증분 빌드는 어느 입력이 어느 출력에 공급되는지 추적하고 변경이 영향을 주는 부분만 다시 빌드합니다. 빌드 캐시는 입력의 해시를 키로 이전 작업의 출력을 저장해, 바뀌지 않은 대상을 다시 계산하는 대신 가져옵니다. 로컬 캐시는 한 개발자의 루프를 빠르게 하고, 원격 또는 분산 빌드 캐시는 팀 전체와 CI 군단에 결과를 공유해, 주어진 입력을 처음 빌드하는 사람이 비용을 치르고 나머지 모두는 캐시 적중을 얻습니다. 큰 모노레포에서 이것이 10분짜리 빌드와 10초짜리 빌드의 차이입니다.

보답은 개발자 피드백 속도이며, 이것이 할 수 있는 가장 지렛대 효과가 큰 투자의 하나입니다. 빠르고 올바른 피드백은 엔지니어를 몰입 상태로 유지하고 코드를 쓰는 일과 그것이 동작하는지 아는 일 사이의 루프를 단축합니다. 그래도 캐시의 정확성은 신중히 지키십시오. 실제 입력(환경 변수, 도구 버전)을 빠뜨린 캐시 키는 디버깅하기 미칠 듯한 낡은 결과를 만듭니다. 캐시는 입력 해싱의 완전성만큼만 신뢰할 수 있습니다.

규모에 맞는 빌드 도구를 고른다

빌드 도구는 스펙트럼 위에 있습니다. 가벼운 쪽 끝에서 Make와 언어 네이티브 도구는 단순한 의존성 그래프를 모델링하며 단일 서비스나 작은 저장소에는 충분합니다. 중간에는 자바 세계의 Gradle과 Maven, 또는 Go, Rust, JavaScript의 표준 툴체인 같은 생태계 도구가 의존성 해결과 관례를 더합니다. 무거운 쪽 끝에서 Bazel과 비슷한 모노레포 빌드 도구 같은 그래프 기반 시스템은 전체 빌드를 대상의 세밀하고 밀폐된 방향 비순환 그래프로 모델링해, 큰 코드베이스 전반에 걸쳐 정밀한 증분성, 원격 캐싱, 원격 실행을 가능하게 합니다.

더 무거운 도구는 서로 의존하는 많은 프로젝트, 큰 모노레포(2.14장), 팀을 짓누르는 빌드 시간이 있을 때 보답합니다. 실제 투자가 듭니다. 더 가파른 학습 곡선, 이전 노력, 빌드 정의를 유지할 전담 팀입니다. 유행이라는 이유로 Bazel급 도구를 채택하지 마십시오. 빌드 그래프가 충분히 커서 세밀한 캐싱과 병렬성이 도구를 운영하는 비용보다 더 많은 엔지니어링 시간을 되찾을 때 채택하십시오. 대부분의 작고 중간 규모의 시스템에서는 원격 캐시가 있는 좋은 생태계 도구가 최적 지점입니다.

관리형 저장소에 산출물을 보관한다

산출물을 빌드하면 그것에는 집이 필요합니다. 산출물 저장소(레지스트리라고도 함)는 패키지, 컨테이너 이미지, 바이너리를 버전 관리, 접근 통제, 메타데이터와 함께 저장합니다. 소스 저장소의 짝입니다. 소스가 들어가고 산출물이 나오며, 둘 다 관리됩니다. 좋은 저장소는 내부 산출물을 게시하고 가져오는 단일 신뢰할 수 있는 장소를 주고, 빌드마다 공개 인터넷에 손을 뻗지 않도록 외부 것을 프록시하고 캐시하며, 누가 무엇을 언제 게시했는지 기록합니다. 컨테이너 이미지에는 고유한 레지스트리 관례가 있고 다른 패키지 유형에도 있지만, 규율은 같습니다. 관리되고 접근이 통제되는 저장소에서 오지 않은 것은 프로덕션에서 돌지 않습니다.

산출물을 버전 관리하고 불변이고 콘텐츠 주소 지정되게 만든다

모든 산출물에 의미 있는 버전을 주십시오. 시맨틱 버전 관리(major.minor.patch)는 변경의 성격을 소비자에게 전달합니다. 메이저 증가는 호환성을 깨는 변경을, 마이너는 호환되는 기능 추가를, 패치는 버그 수정을 알립니다. 사람이 읽을 수 있는 버전과 나란히, 각 산출물을 콘텐츠의 암호학적 해시로 식별해 콘텐츠 주소 지정되게 하십시오. 흔히 다이제스트라 부르는 콘텐츠 주소는 한 바이트만 바뀌어도 바뀌는 지문으로, 정확한 산출물을 모호하지 않게 가리키고 어떤 변조든 탐지하게 해 줍니다.

게시된 산출물을 불변으로 만드십시오. 버전이 게시되면 결코 바뀌지 않습니다. 같은 버전으로 다른 바이트를 다시 게시하는 것은 공급망 위험이자 디버깅 악몽입니다. 두 사람이 “버전 1.4.2”를 갖고 있는데 다른 소프트웨어일 수 있기 때문입니다. “latest” 같은 가변 태그는 사람에게 편리하지만, 중요한 모든 것에서는 항상 기록한 특정 불변 다이제스트로 해결되어야 합니다. 떠다니는 태그가 아니라 다이제스트로 배포해, 테스트한 것이 실행하는 것임을 증명할 수 있게 하십시오.

한 번 빌드하고 어디서나 승격한다

산출물을 한 번 빌드한 뒤 그 같은 산출물을 개발, 스테이징, 프로덕션 환경을 통해 옮기십시오. 이 “한 번 빌드, 어디서나 승격” 규칙이 가장 중요한 단일 산출물 관리 실천입니다. 환경마다 다시 빌드하면 테스트한 산출물이 배포된 산출물이라는 보장을 버린 것입니다. 각 재빌드가 다른 의존성을 끌어오거나 약간 다른 기계에서 돌 수 있기 때문입니다. 승격은 메타데이터 작업입니다. 이미 빌드되고 테스트된 다이제스트를 다음 환경에 승인된 것으로 표시하고, 다시 빌드하는 대신 외부화된 구성(2.10장)으로 그 환경에 맞게 설정합니다. 이는 바이너리를 일정하게, 구성을 가변으로 유지하며, 신뢰성과 감사 가능성 모두에 바로 원하는 분리입니다.

출처를 포착하고, 산출물에 서명하고, SBOM을 생성한다

빌드 시점에 산출물이 어디서 왔는지 기록하고 변조되지 않았음을 증명하십시오. 출처는 산출물이 어떻게 빌드되었는지에 대한 서명된 진술입니다. 어느 소스 커밋, 어느 빌더, 어느 입력입니다. 산출물에 서명하면 소비자가 실행하기 전에 진위와 무결성을 검증할 수 있고, 배포 시점의 서명 검증이 고리를 닫습니다. 소프트웨어 자재 명세서(SBOM), 곧 산출물 안의 구성 요소와 의존성의 완전한 목록은 새 취약점이 공개될 때 빌드 로그를 며칠 뒤지는 대신 몇 분 안에 “우리가 영향받는가?”에 답하게 해 줍니다.

SLSA(소프트웨어 산출물을 위한 공급망 수준) 같은 틀은 빌드 시점 무결성의 등급 모델을 줍니다. 더 높은 수준은 밀폐되고 격리된 빌드와 위조할 수 없는 출처를 요구합니다. 이 모든 것을 정보가 권위 있고 수집이 싼 빌드에서 생성하십시오. 비싸고 믿을 수 없을 때 사후에 재구성하지 마십시오. 이 작업은 4.9장의 보안 소프트웨어 개발 수명주기와 2.18장의 공급망 관심사에 직접 봉사합니다.

캐시를 보호하고 보존과 비용을 관리한다

공유 빌드 캐시는 공유된 신뢰 경계입니다. 공격자가 오염된 항목을 쓸 수 있다면 그것을 가져오는 모든 소비자가 침해된 코드를 실행하고, 속도 이점이 공격 표면이 됩니다. 캐시를 인증으로 보호하고, 쓰기 접근을 좁게 제한하고(흔히 신뢰할 수 있는 CI에만, 개발자 노트북에는 절대), 오염되거나 낡은 항목이 정당한 것으로 위장하지 못하도록 캐시 키가 모든 실제 입력을 해시하게 하십시오. 특히 팀 간에 공유되는 원격 캐시에 대해 캐시 오염을 실제 위협 모델로 다루십시오.

산출물도 비용을 쌓습니다. 컨테이너 이미지와 빌드 출력은 크며, 한도 없는 레지스트리는 저장 청구서와 느린 조회가 문제를 강제할 때까지 자랍니다. 보존 정책을 정의하십시오. 프로덕션으로 승격된 모든 산출물과 실행 중인 시스템이 참조하는 모든 것을 보관하고, 오래된 개발 및 풀 리퀘스트 빌드는 자동으로 만료시키고, 삭제한 것을 기록하십시오. 목표는 재현성과 감사를 위해 필요한 것을 유지하면서 잡음을 털어 내는 저장소이며, 놀라게 하는 비용이 아니라 의식적으로 고른 비용입니다.

장단점

결정장점단점
무거운 그래프 빌드 도구 (Bazel급)세밀한 증분성, 원격 캐시와 실행, 거대한 모노레포로 확장가파른 학습 곡선, 이전 비용, 전담 빌드 팀 필요
가벼운 빌드 도구 (Make, 네이티브)단순하고 오버헤드가 낮고 도입이 빠름규모에서 약한 증분성과 캐싱, 약한 밀폐성
원격/분산 빌드 캐시공유된 결과, 군단 전반의 극적인 속도 향상캐시 오염 표면, 정확성이 완전한 입력 해싱에 달림
완전 밀폐 빌드믿을 만한 재현성, 강한 출처실제 도구와 규율 비용, 더 어려운 로컬 워크플로
한 번 빌드, 어디서나 승격테스트한 산출물이 곧 출하한 산출물, 깨끗한 감사 추적외부화된 구성과 규율 있는 승격 필요
불변이고 콘텐츠 주소 지정된 산출물변조 흔적이 드러남, 모호하지 않은 참조떠다니는 태그보다 불편, 관리할 저장소가 더 많음
긴 산출물 보존완전한 재현성과 감사 이력저장 비용, 정리 정책 없이는 느린 조회

되풀이되는 긴장은 속도 대 신뢰입니다. 캐싱, 공유 빌드 팜, 떠다니는 태그는 모두 빌드를 더 빠르고 편리하게 하며, 각각은 부주의하게 쓰면 정확히 무엇을 빌드했는지 말하고 변조되지 않았음을 증명할 능력을 약화시킵니다. 신뢰할 수 있는 길을 빠른 길로 만들어 해결하십시오. 완전한 입력 해시는 캐시를 빠르고 올바르게 만듭니다. 다이제스트로 배포하는 것은 태그로 배포하는 것만큼 빠르고 훨씬 안전합니다. 빌드에서 SBOM을 생성하는 데는 몇 초가 들고 며칠을 아낍니다. 무결성을 처음부터 빠른 길에 엔지니어링해 넣으면 속도와 무결성 중 하나를 고를 일이 거의 없습니다.

팀과 논의할 질문

  1. 지난 분기의 프로덕션 산출물을 오늘 다시 빌드해 같은 바이트를 얻을 수 있으며, 그렇지 않다면 무엇이 빠져 있습니까? 이것은 빌드 규율의 가장 날카로운 시험입니다. 재현성이 고정된 툴체인, 잠긴 의존성, 제거된 비결정성이 모두 함께 동작하는 데 달려 있기 때문입니다. 몇 달 전에 출하된 특정 산출물 하나를 골라 기록된 소스 커밋에서 실제로 다시 빌드해 보십시오. 시도에서 배우는 것이 어떤 정책 문서보다 가치 있습니다. 의존성 범위가 떠다녔을 수도, 컴파일러 버전이 고정된 적이 없을 수도, 타임스탬프가 구워져 있을 수도 있습니다. 찾은 간극이 재현성 백로그이며, 그것을 닫는 것이 감사한 것이 실행하는 것이라고 신뢰하게 해 줍니다. 그 보관 연쇄가 법적 요건인 규제 및 정부 환경에서 이는 엄청나게 중요합니다.

  2. 각 산출물을 한 번 빌드하고 승격합니까, 환경마다 다시 빌드합니까? 어느 쪽인지 어떻게 증명하겠습니까? 많은 팀이 단일 산출물을 승격한다고 믿지만, 자세히 보면 스테이징과 프로덕션이 각각 미묘하게 다른 입력으로 새 빌드를 촉발함을 발견합니다. 실제 릴리스 하나를 커밋에서 프로덕션까지 추적해, 정확히 같은 다이제스트가 모든 환경을 거쳤는지 길에서 새 바이트가 만들어졌는지 확인하십시오. 재빌드를 발견한다면, 테스트한 산출물과 배포된 산출물이 증명 가능하게 동일하지 않기 때문에 테스트 보장이 생각보다 약한 곳을 찾은 것입니다. 해법, 곧 설정이 달라지는 동안 바이너리가 일정하게 유지되도록 구성을 외부화하는 것은 신뢰성과 훨씬 깨끗한 감사 이야기 모두에서 보답합니다.

  3. 내일 흔한 라이브러리에서 치명적 취약점이 발표된다면, 그것을 담은 모든 산출물을 얼마나 빨리 열거할 수 있습니까? 이 질문은 빌드 시점 출처와 SBOM 실천이 실제인지 열망인지 시험합니다. 널리 쓰이는 구성 요소가 악용 가능하다고 드러나면, 몇 시간 만에 복구하는 조직은 빌드 시점에 자재 명세서를 생성해 각 산출물과 함께 저장하는 곳이고, 몇 주가 걸리는 곳은 빌드 로그를 grep하고 엔지니어를 인터뷰하는 곳입니다. 실제로 의존하는 라이브러리로 시나리오를 구체적으로 짚어 보고 오늘 답이 얼마나 걸릴지 시간을 재 보십시오. 그 시간과 “몇 분” 사이의 간극이 공급망 노출의 직접 척도이며, 4.9장의 보안 개발 수명주기 작업에 곧장 연결됩니다.

  4. 빌드가 매일 엔지니어링 시간으로 얼마를 치르며, 빠르게 만드는 비즈니스 사례는 무엇입니까? 빌드 지연은 모든 엔지니어가 모든 변경에 치르는 세금이며, 큰 팀 규모에서는 어느 한 번의 대기도 비싸게 느껴지지 않으므로 합산을 과소평가하기 쉽습니다. 측정에 합의하면 막연한 불평이 원격 캐시, 더 나은 증분성, 더 무거운 빌드 도구의 비용에 견주어 저울질할 수 있는 숫자로 바뀝니다. 로컬과 CI 빌드 시간의 중앙값과 최악의 경우, 하루 빌드 횟수, 느린 빌드가 누군가를 몰입에서 맥락 전환으로 밀어내는 빈도의 정직한 추정을 가져오십시오. 경쟁하는 고려는 더 빠른 빌드가 공짜가 아니라는 것입니다. 원격 캐시와 분산 실행은 운영하고 보호할 인프라를 더하고, 더 무거운 도구는 유지보수 팀을 더합니다. 기업이나 정부 조직에서는 많은 팀에 걸친 느린 피드백의 처리량과 사기 비용을 세어 보십시오. 이는 보통 인프라 청구서를 압도하며 리더십이 이미 자금을 대는 바로 그 틀입니다.

  5. 공유 빌드 캐시에는 누가 쓸 수 있으며, 오염된 항목이 프로덕션에 닿는 것을 무엇이 막습니까? 공유 캐시는 속도 이득을 새로운 신뢰 경계와 맞바꾸며, 한 엔지니어의 결과가 군단 전체에 봉사하게 하는 같은 메커니즘이 하나의 손상되거나 악의적인 항목이 그것을 가져오는 모두를 침해하게 합니다. 큰 팀에서 피해 범위는 조직 전체이므로, 도구의 기본값이 우연히 정하는 것이 아니라 의도적인 결정이 필요합니다. 각 캐시에 대한 쓰기 접근을 쥔 사람과 무엇의 목록, 쓰기가 개발자 노트북이 아니라 신뢰할 수 있는 CI로 한정되는지, 낡거나 오염된 항목이 정당한 것으로 위장하지 못하도록 캐시 키가 모든 실제 입력을 해시하는지 가져오십시오. 긴장은 가장 엄격한 통제가 개발자가 자기 기계에서 캐시 항목을 밀어 넣는 편리한 경로를 늦춘다는 것입니다. 기업과 정부 환경에서는 캐시 오염을 공급망 모델의 명시적 위협으로 다루고, 릴리스에 코드를 주입할 수 있는 다른 프로덕션 시스템에 적용하는 것과 같은 접근 통제, 로깅, 리뷰를 요구하십시오.

  6. 빌드 그래프는 어느 시점에 더 무거운 도구를 정당화하며, 그 선을 넘었음을 어떻게 알겠습니까? 가벼운 생태계 도구와 Bazel 같은 그래프 기반 시스템 사이의 선택은 이 영역에서 가장 비싸고 되돌리기 어려운 결정의 하나입니다. 큰 코드베이스를 세밀한 빌드 정의로 이전하는 데 몇 달과 전담 팀이 들기 때문입니다. 임계값을 미리 정하면 유행이라서 필요 없는 복잡성을 채택하거나, 빌드 시간이 모든 팀을 짓누른 지 한참 지나서도 가벼운 도구에 매달리는 것을 모두 막습니다. 빌드 그래프의 크기와 상호 의존성, 현재 빌드 및 캐시 적중 지표, 도구가 되찾을 엔지니어링 시간에 견준 이전 및 지속적 유지보수 비용의 현실적 추정을 가져오십시오. 경쟁하는 끌림은 무거운 도구가 규모에서 다른 어떤 것도 맞먹지 못하는 정밀한 증분성과 원격 실행을 주지만, 그래프가 보답할 만큼 정말 클 때만이라는 것입니다. 큰 기업이나 기관에서는 도구의 밀폐성과 출처 보장이 감사 및 공급망 요건을 충족하는 데 도움이 되는지도 저울질하십시오. 이는 원시 속도를 넘어 계산을 바꿀 수 있습니다.

분야별 관점

스타트업. 팀이 아주 작고 빌드 인프라에 쓸 여유가 없다면 가볍게 유지하십시오. 언어 네이티브 빌드 도구를 쓰고, 첫날부터 잠금 파일을 채택하고, 컨테이너 이미지를 “latest” 태그가 아니라 다이제스트로 배포하십시오. 이 습관은 거의 비용이 들지 않고 나중에 한 부류의 “내 기계에서는 되는데” 고통을 면하게 해 줍니다. 무거운 그래프 빌드 도구에는 저항하십시오. 가장 희소한 자원은 엔지니어링의 주의입니다. 빌드가 몇 분을 넘기기 시작하면 원격 빌드 캐시가 손을 뻗을 만한 단 하나의 업그레이드입니다.

소기업. 전담 빌드 엔지니어가 없으니 직접 산출물 인프라를 운영하는 대신 관리형 서비스에 기대십시오. 호스팅 레지스트리와 CI 제공자의 내장 캐시가 플랫폼 팀 없이 버전 관리, 보존, 접근 통제를 줍니다. 선택을 만들기보다 사기로 구성하고, 저장 비용이 예측 가능하도록 자동 만료 정책을 설정하고, 기본, 곧 잠긴 의존성과 불변이고 다이제스트에 고정된 배포가 갖춰져 있는지 확인하십시오. 아무도 파이프라인을 상시 지켜보지 않을 때도 이것들이 여러분을 보호하기 때문입니다.

대기업. 많은 팀에 걸쳐 문제는 일관성입니다. 공유되고 소유되는 빌드 플랫폼, 공통 산출물 저장소, 어떤 그룹도 믿을 수 없는 파이프라인을 다시 발명하지 않도록 하는 잠금 파일, 서명, SBOM, 한 번 빌드 후 승격의 시행되는 표준입니다. 원격 캐시에 투자하고, 빌드 그래프가 정당화하는 곳에서는 그래프 기반 도구에 투자하고, 캐시를 쓰기 접근이 한정되고 감사 로깅이 있는 다스려지는 신뢰 경계로 다루십시오. 산출물을 보존 정책과 출처가 있는 통제된 영역으로 관리해, 어떤 프로덕션 구성 요소든 요청 시 리뷰된 소스까지 추적되게 하십시오.

정부. 조달 규칙, 투명성, 공적 책임이 빌드 시점 무결성을 호의가 아니라 컴플라이언스 요건으로 만듭니다. 파이프라인을 SLSA 같은 등급 틀에 맞추고, 고정된 툴체인에서 격리되고 네트워크가 제한된 환경에서 빌드를 돌리고, 제3자 의존성을 사용 전에 스캔하고 승인하는 내부 저장소를 통해 프록시하십시오. 서명된 SBOM과 출처를 기록 보존법이 요구하는 햇수 동안 불변으로 저장하고, 서명되고 다이제스트로 식별된 산출물만 배포하며, 프로덕션의 소프트웨어가 리뷰되고 승인된 바로 그것임을 감사자와 대중에게 증명할 준비를 하십시오.

사례

스타트업. 작은 모노레포를 운영하는 열다섯 명의 스타트업이 그 규모에서 옳은 선택인 언어 네이티브 빌드 도구와 빠른 피드백으로 시작합니다. 성장하면서 빌드 시간이 5분을 넘어가고 엔지니어가 기다리는 동안 맥락 전환을 합니다. 무거운 그래프 빌드 도구로 뛰는 대신 개발자 기계와 CI 사이에 공유되는 원격 빌드 캐시를 더해, 바뀌지 않은 대상이 다시 빌드되지 않고 가져와지므로 대부분의 빌드를 몇 초로 줄입니다. 모든 언어에 잠금 파일을 채택하고, 컨테이너 이미지를 “latest” 태그 대신 다이제스트로 배포하고, 레지스트리 청구서가 평평하게 유지되도록 풀 리퀘스트 이미지 빌드에 자동 만료를 켭니다. 전체 노력은 몇 주가 걸리고 매일 몇 시간의 엔지니어링 시간을 되찾습니다.

기업. 한 글로벌 금융 서비스 회사가 수백 명의 엔지니어에 걸친 큰 모노레포를 운영하며, 그 규모에서는 세밀한 증분성이 빌드 팀의 비용보다 훨씬 많은 엔지니어링 시간을 되찾으므로 원격 캐싱과 원격 실행이 있는 그래프 기반 빌드 시스템을 채택합니다. 모든 산출물은 격리된 환경에서 밀폐되게 빌드되고, 서명되고, SBOM과 서명된 출처가 첨부된 채 관리형 레지스트리에 게시됩니다. 배포는 콘텐츠 다이제스트로 이루어지고, 정책 엔진은 서명이 검증되지 않는 이미지의 실행을 거부합니다. 산출물은 스테이징에서 프로덕션으로 다시 빌드되지 않고 승격되므로, 테스트를 통과한 바이너리가 고객에게 서비스하는 바로 그것임이 증명됩니다. 감사자가 프로덕션 구성 요소를 리뷰된 소스까지 추적해 달라고 하면, 보관 연쇄는 조사가 아니라 질의입니다.

정부. 시스템을 현대화하는 한 국세청이 빌드 시점 공급망 무결성을 컴플라이언스 요건으로 다루며, 파이프라인을 SLSA 같은 등급 틀에 맞춥니다. 빌드는 고정된 툴체인과 잠긴 의존성으로 격리되고 네트워크가 제한된 환경에서 돌아, 출력이 재현 가능하고 독립적으로 검증 가능합니다. 모든 산출물은 서명된 SBOM과 출처를 지니며, 기록 보존법을 충족하도록 여러 해 동안 불변으로 저장됩니다. 제3자 의존성은 어떤 빌드가 쓰기 전에 스캔하고 승인하는 내부 저장소를 통해 프록시되어, 검증되지 않은 코드를 네트워크 밖에 둡니다. 기관은 서명되고 승격되고 다이제스트로 식별된 산출물만 배포하므로, 시민의 신고를 처리하는 소프트웨어가 리뷰되고 승인된 바로 그것임을 규제 기관과 대중에게 증명할 수 있습니다.

비즈니스 사례: 동기, ROI, TCO

빌드와 산출물 규율의 수익은 먼저 되찾은 엔지니어링 시간으로 나타납니다. 느린 빌드는 모든 변경에 모든 엔지니어에게 세금을 매기며, 큰 조직 규모에서는 비용이 복리로 쌓입니다. 하루 수천 번 돌아가는 빌드에서 몇 분을 줄이면 해마다 몇 사람-년을 되찾고, 정량화하기는 더 어렵지만 똑같이 실제로 엔지니어를 맥락 전환 대신 몰입 상태로 유지합니다. 원격 캐시와 좋은 증분성은 몇 주 안에 스스로 값을 하는 경우가 많습니다. 재현 가능하고 한 번 승격하는 산출물은 “스테이징에서는 됐는데” 사고의 한 부류 전체를 줄여, 리더가 이미 지켜보는 전달 지표인 변경 실패율과 평균 복구 시간을 낮춥니다.

더 크고 덜 보이는 수익은 위험 감소입니다. 서명된 산출물, SBOM, 출처는 공급망 사고를 몇 주짜리 비상 사태에서 범위가 정해진 몇 시간짜리 대응으로 바꾸고, 감사를 비상 훈련에서 질의로 바꿉니다. 규제 및 정부 맥락에서 그 추적 가능성은 운영 자체의 전제 조건이므로 투자는 선택이 아니라 구조적입니다. 이를 소홀히 하면 총소유비용은 반대로 갑니다. 가변 산출물과 재현할 수 없는 빌드는 아무도 완전히 설명할 수 없는 영역으로 쌓이고, 보존 정책 없이 저장소는 한도 없이 자라며, 모든 감사와 모든 사고가 있어야 할 것보다 더 많은 비용이 듭니다. 리더십을 설득하려면 빌드 속도를 엔지니어링 처리량에, 산출물 무결성을 감사 비용과 침해 노출에 연결하십시오. 둘 다 이미 자금을 대는 것입니다.

안티패턴과 함정

  • 환경마다 재빌드: 스테이징과 프로덕션에 새 바이트를 만들어, 테스트한 산출물이 배포된 산출물이라는 보장을 버리는 것.
  • 떠다니는 태그로 배포: 불변 다이제스트 대신 “latest”나 가변 태그를 실행해, 실행되는 것이 예측할 수 없고 추적할 수 없는 것.
  • 잠금 파일 없음: 떠다니는 버전 범위가 빌드가 시간에 따라 다르거나 악의적인 의존성을 조용히 끌어오게 하는 것.
  • 불완전한 캐시 키: 캐시 키에서 실제 입력을 빠뜨려 디버깅에 며칠을 낭비하는 낡은 결과를 만드는 것.
  • 보호되지 않은 공유 캐시: 신뢰할 수 없는 작성자가 원격 캐시를 오염시켜 소비자가 침해된 출력을 가져와 실행하게 하는 것.
  • 비결정적 빌드: 출력이 달라지고 검증을 무력화하는 내장 타임스탬프, 절대 경로, 고정되지 않은 도구.
  • 무거운 도구의 성급한 채택: 정당화할 만큼 빌드 그래프가 커지기 전에 Bazel급 복잡성을 떠안는 것.
  • 사후 고려로서의 SBOM과 출처: 빌드에서 생성하는 대신, 비싸고 믿을 수 없을 때 빌드 후에 공급망 메타데이터를 재구성하는 것.
  • 무한 보존: 저장 비용과 느린 조회가 허둥지둥 정리를 강제할 때까지 오래된 산출물을 만료시키지 않는 것.

성숙도 모델

  • 1단계, 시작: 빌드는 아무도 소유하지 않는 즉흥적 스크립트이며 흔히 개발자 기계에서 실행됩니다. 출력은 비결정적이고, 의존성은 잠금 파일 없이 떠다니며, 산출물은 환경마다 다시 빌드되고 가변 태그로 배포되며, 공유 캐시도 서명도 자재 명세서도 없습니다.
  • 2단계, 발전: 일부 팀이 빌드를 체크인된 정의에서 CI로 옮기고 잠금 파일을 채택했지만 실천은 조직 전반에서 일관되지 않습니다. 산출물은 기본적인 버전 관리가 있는 관리형 저장소에 놓일 수 있고, 로컬이나 단순한 원격 캐시가 흔한 빌드를 빠르게 하지만, 환경마다 재빌드가 여전히 일어나고 출처는 들쭉날쭉합니다.
  • 3단계, 표준화: 재현 가능하고 대체로 밀폐된 빌드가 문서화되어 조직 전체에서 시행되며, 고정된 툴체인과 모든 실제 입력을 해시하는 키가 있는 공유 원격 캐시를 씁니다. 산출물은 불변이고, 콘텐츠 주소 지정되고, 시맨틱 버전 관리되고, 한 번 빌드되어 어디서나 승격되고, 서명되며, SBOM과 함께 출하됩니다. 캐시 접근은 통제되고 보존 정책은 팀 전반에 일관되게 적용됩니다.
  • 4단계, 관리: 빌드 영역이 기준선에 대해 측정되고 통제됩니다. 빌드 시간, 캐시 적중률, 개발자 피드백 시간, 저장 비용이 명시적 목표와 함께 추적되고, 회귀가 행동을 촉발하며, 서명과 출처 검증이 배포 시점에 시행되어 실패한 검사가 릴리스를 막습니다. 빌드 시점 공급망 무결성이 SLSA 같은 등급 틀에 대해 평가되고, 숫자가 다음에 어디에 투자할지 이끕니다.
  • 5단계, 오케스트레이션: 빌드, 캐시, 산출물, 공급망 실천이 조직 전체에서 지속적으로 개선되고 통합됩니다. 도구, 보존, 보안 태세가 사고와 감사에서 배움에 따라 적응하고, 원격 실행과 캐싱은 코드베이스가 진화함에 따라 튜닝되며, 빌드 시점 무결성이 사후에 덧붙여지는 대신 더 넓은 보안 개발 수명주기에 엮여 있습니다.

논의를 위한 아이디어

  1. 현재 로컬 빌드 시간의 중앙값과 최악의 경우는 얼마이며, 원격 캐시가 각각에 무엇을 하겠습니까?
  2. 오늘 어떤 산출물을 가변 태그로 배포하며, 모두를 다이제스트로 배포하려면 무엇이 필요합니까?
  3. 빌드 그래프는 어디서 더 무거운 도구를 정당화하고, 어디서 그 도구가 아끼는 것보다 비용이 더 듭니까?
  4. 공유 빌드 캐시에는 누가 쓸 수 있으며, 오염된 항목이 프로덕션에 닿는 것을 무엇이 막습니까?
  5. 마지막으로 출하한 것의 서명된 SBOM을 만들 수 있습니까? 없다면 그쪽으로 가는 가장 작은 걸음은 무엇입니까?
  6. 빌드 산출물의 보존 정책은 무엇이며, 저장소가 오늘 얼마를 치르고 얼마를 치러야 합니까?

핵심 요점

  • 빌드는 전달의 첫 단계입니다. 하류의 모든 것이 그 결함을 물려받으므로 속도와 정확성에 자금을 대십시오.
  • 감사한 산출물이 출하하는 산출물임을 증명할 수 있도록, 고정된 툴체인과 잠금 파일로 빌드를 재현 가능하게 하고 가능한 곳에서는 밀폐되게 하십시오.
  • 로컬과 원격에서 캐시하고 증분 빌드하되, 모든 실제 입력을 해시하고 캐시를 보호하십시오. 공유 캐시는 공유된 신뢰 경계입니다.
  • 각 산출물을 한 번 빌드하고, 불변이고 콘텐츠 주소 지정되게 만들고, 의미 있게 버전을 관리하고, 정확히 그 산출물을 환경 전반에 승격하십시오.
  • 빌드 시점에 출처, 서명, SBOM을 생성하고 보존과 접근 통제와 함께 산출물을 저장해, 공급망 무결성과 감사 준비를 일반 작업의 부산물로 바꾸십시오.

참고 문헌과 더 읽을거리

  • Jez Humble and David Farley, Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation
  • Nicole Forsgren, Jez Humble, and Gene Kim, Accelerate: The Science of Lean Software and DevOps
  • Titus Winters, Tom Manshreck, and Hyrum Wright (eds.), Software Engineering at Google: Lessons Learned from Programming Over Time
  • Betsy Beyer, Chris Jones, Jennifer Petoff, and Niall Richard Murphy (eds.), Site Reliability Engineering: How Google Runs Production Systems
  • Peter Smith, Software Build Systems: Principles and Experience
  • The Open Source Security Foundation, SLSA: Supply-chain Levels for Software Artifacts (specification)
  • National Institute of Standards and Technology, Secure Software Development Framework (SSDF), SP 800-218
  • Tom Preston-Werner, Semantic Versioning Specification (SemVer)