파이프라인 실행 절차를 Claude Code Skill 3종으로 등록
세 파이프라인의 실행법이 문서에만 있어 "그 문서를 읽어야만" 알 수 있었다. 관련 작업 시 자동 로드되도록 Skill 로 등록한다. - catalog-update-pipeline: agent/update_catalog 파이프라인. CATALOG_ROOT 가 개인 로컬 경로로 하드코딩돼 있어 오버라이드 필수라는 점, create_pr 단계가 주석 처리돼 PR 이 생성되지 않는다는 점을 명시했다. - sbom-cve-gate: SBOM·스캔·게이트. CoverageProbe(ok/none/n-a) 해석과 게이트가 현재 warn-only 라는 점을 명시했다. - self-build-image: 자체 빌드. 오케스트레이터는 하나뿐이라는 원칙과 전역 ARG 선언, 게이트 PASS 가 동작을 보장하지 않는다는 점을 명시했다. Skill 은 절차 본문을 복제하지 않고 권위 문서를 가리킨다 — 문서가 단일 출처이고 Skill 은 실행 계약과 함정·현재 상태만 담는다. 함께 고친 stale 문서(image-authoring.md): - "images/ 디렉토리 자체가 없다" → 실제로는 이미지 3종이 있고 push 까지 됐다 - "자동화된 배포 테스트 절차는 아직 없다" → deploy-test-procedure.md 와 전용 스크립트가 있다 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
---
|
||||
name: self-build-image
|
||||
description: 자체 빌드 하드닝 이미지를 추가하거나 기존 빌드 정의를 변경할 때 사용한다. "이미지 자체 빌드해줘", "하드닝 이미지 추가", "차단 CVE를 자체 빌드로 해소", "build-hardened-image.sh 실행", "images/ 아래 새 이미지" 같은 요청이 해당한다. 현재 cloudnative-pg, cnpg-postgresql, etcd 3종이 있다.
|
||||
---
|
||||
|
||||
# 자체 빌드 하드닝 이미지
|
||||
|
||||
CVE 게이트 대응 우선순위(상위 태그 교체 → 베이스 OS 교체 → **자체 빌드** → 예외 승인)에서
|
||||
앞의 두 단계로 해소가 안 될 때만 온다.
|
||||
|
||||
## 원칙 — 오케스트레이션은 항상 하나다
|
||||
|
||||
**`scripts/build/build-hardened-image.sh` 하나가 모든 자체 빌드 이미지를 빌드한다.**
|
||||
OS 패키지 재설치든 소스 컴파일이든 스크립트는 같고, 차이는 전부 `images/<image>/` 안에 있다.
|
||||
|
||||
**"이 이미지는 성격이 다르다"는 이유로 새 오케스트레이션 스크립트를 만들지 않는다** —
|
||||
절차(빌드 → 기능검증 → SBOM → 스캔 → 게이트 → push)는 이미지 종류와 무관하게 동일하다.
|
||||
SBOM·스캔·게이트도 다시 만들지 않는다 — `build-hardened-image.sh`가 이미
|
||||
`scan-sbom.sh`/`cve-gate.py`를 호출한다.
|
||||
|
||||
## 실행
|
||||
|
||||
```sh
|
||||
IMAGE=<image> BASE_OS=<variant> bash scripts/build/build-hardened-image.sh /tmp/out
|
||||
```
|
||||
|
||||
`images/<image>/<variant>.build.env`가 계약(`DOCKERFILE`, `TARGET`, `BUILD_ARGS` 등)을
|
||||
선언하면 스크립트는 이미지 종류를 몰라도 된다. 전체 계약표와 신규 이미지 추가 7단계 절차는
|
||||
[.claude/image-authoring.md](../../image-authoring.md)에 있다 — 여기 복제하지 않는다.
|
||||
|
||||
CI는 `build-image.yml`이 `image` 입력으로 이미 파라미터화돼 있다. `images/<image>/catalog.env`만
|
||||
추가하면 워크플로 수정 없이 태울 수 있다.
|
||||
|
||||
## 실측된 함정
|
||||
|
||||
- **`FROM`에 쓰는 `ARG`는 첫 `FROM` 이전(전역 스코프)에 선언한다.** 스테이지 내부에 선언하면
|
||||
그 스테이지 지역 변수가 되어 이후 `FROM`의 이미지명 해석에 쓰이지 않고 빈 이미지명 에러가 난다.
|
||||
- **게이트 PASS는 "동작한다"를 증명하지 않는다.** CVE 스캐너는 런타임 요구사항(오퍼레이터가
|
||||
자신의 파일 레이아웃에 의존하는 것 등)을 전혀 보지 못한다. 배포 검증을 반드시 한다 —
|
||||
절차는 [.claude/deploy-test-procedure.md](../../deploy-test-procedure.md).
|
||||
- **`CoverageProbe`가 `ok`인지 확인한다.** `none`이면 findings 0건이 진짜 0건이 아니라
|
||||
스캐너에 그 배포판 데이터가 없다는 뜻이다(`sbom-cve-gate` skill 참고).
|
||||
- **롤링 태그를 쓰지 않는다.** 같은 앱 버전이라도 베이스 업데이트 결과가 시점마다 달라
|
||||
태그에 빌드일을 포함한다(예: `1.30.0-security-hardened-20260804`).
|
||||
|
||||
## 마무리
|
||||
|
||||
카탈로그 values(`custom-values.yaml`/`dip-values.yaml`) 태그 갱신은
|
||||
`scripts/build/patch-catalog-tag.py`가 한다. 조사·결정 근거는 PR 설명과
|
||||
[MEMORY.md](../../../MEMORY.md)에 남긴다. `images/**`+`manifests/helm/**` 변경은 PR로만 반영한다.
|
||||
Reference in New Issue
Block a user