CI VM에서 빌드하고, 쿠버네티스에서 실행하는 구조는 익숙하지만 운영 관점에서는 불일치가 누적됩니다.

예시
python:3.12-slim 보안 패치가 배포되었는데, 수동 트리거에 의존해서 1~2주 뒤에야 재빌드되는 경우가 실제로 자주 발생합니다.전통적인 Docker daemon 기반 빌드는 privileged 권한이나 소켓 마운트를 요구하는 경우가 많아 공격면이 커집니다.
| 방식 | 위험 요소 |
|---|---|
docker.sock 마운트 | 소켓에 접근한 컨테이너가 호스트 권한 상승 가능 |
privileged: true | 컨테이너 탈출 시 노드 전체 장악 가능 |
| DinD(Docker-in-Docker) | 중첩 daemon 운영으로 격리 경계 모호 |
반면 Kaniko/BuildKit(루트리스 구성)/Buildah 등은 daemon 의존을 줄이거나 제거하여 운영 보안성을 높일 수 있습니다.

| 항목 | Kaniko | BuildKit | CNB | S2I |
|---|---|---|---|---|
| 입력 | Dockerfile | Dockerfile/LLB | 소스코드(+빌드팩) | 소스코드(+builder 이미지) |
| 강점 | 단순 도입 | 성능/캐시/고급 기능 | Dockerfile 없이 표준화 | OpenShift 통합 |
| 운영 난이도 | 낮음 | 중간 | 중간 | OpenShift 기준 낮음 |
| 주의점 | 프로젝트 아카이브 상태 | 설정 복잡도 | 빌드팩 학습 필요 | OpenShift 종속 |
Kaniko는 Docker daemon 없이 Dockerfile 명령을 userspace에서 처리합니다. 각 명령 실행 후 파일시스템 변화를 스냅샷으로 계산하고, 변화분을 레이어 tar로 구성해 최종 이미지를 레지스트리에 푸시합니다.

주요 옵션 설명
| 옵션 | 설명 |
|---|---|
--snapshot-mode=redo | 변경 감지 정밀도를 높여 레이어 누락을 방지. 빌드 시간이 늘어날 수 있어 캐시와 함께 사용 |
--cache=true | 레이어 캐시 활성화 |
--cache-repo | 캐시를 저장할 레지스트리 경로 지정 |
운영 주의: Kaniko 저장소는 현재 GitHub에서 아카이브 상태입니다. 신규 플랫폼 설계 시에는 BuildKit/Buildah 또는 관리형 빌더를 병행 검토하시는 것이 안전합니다.
BuildKit은 Dockerfile을 내부 빌드 그래프(LLB, Low-Level Build)로 변환해 병렬 실행과 고급 캐시를 지원합니다. Kaniko 대비 대규모 빌드 팜에 적합합니다.
핵심 기능
-mount=type=cache : 의존성 캐시(npm/pip/maven) 컨테이너 레이어 밖에서 재사용-mount=type=secret : 빌드 시 비밀을 주입하되 이미지 레이어에는 포함되지 않음예시 — Maven 캐시 마운트
# syntax=docker/dockerfile:1.7
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /workspace
COPY pom.xml .
RUN --mount=type=cache,target=/root/.m2 mvn -q -DskipTests dependency:go-offline
COPY . .
RUN --mount=type=cache,target=/root/.m2 mvn -q -DskipTests package
/root/.m2 캐시가 빌드 간에 유지되어 의존성 다운로드 없이 재빌드가 가능합니다.
CNB는 Dockerfile 없이 소스코드 타입을 탐지하여 빌드합니다. 라이프사이클은 5단계로 구성됩니다.

| 단계 | 역할 |
|---|---|
| Analyze | 기존 이미지/캐시 메타데이터 확인 |
| Detect | 소스 분석 후 적용할 buildpack 조합 결정 |
| Restore | 이전 빌드 캐시 복원 |
| Build | buildpack 실행으로 아티팩트 생성 |
| Export | 최종 OCI 이미지 조합 및 레지스트리 푸시 |
CNB는 builder image(빌드 도구 포함)와 run image(최소 런타임)를 분리하여, 최종 이미지를 가볍고 안전하게 유지합니다.
예시
pack build 한 줄로 빌드하면, Paketo Buildpack이 자동으로 런타임 버전을 탐지하고 최적화된 이미지를 생성합니다.S2I는 OpenShift에서 오래 검증된 방식으로, builder 이미지 안의 스크립트로 소스를 이미지로 변환합니다.
assemble 스크립트: 소스와 의존성을 빌드run 스크립트: 런타임 시작 커맨드 정의BuildConfig + ImageStream + Trigger를 조합하면 Git Push 하나로 빌드~배포까지 자동화할 수 있습니다.

예시
fabric8/s2i-java builder를 공통 표준으로 쓰면, 팀별 Dockerfile 편차와 유지보수 부담을 줄일 수 있습니다.직접 이미지 URL은 시간이 지나면 깨질 수 있어, 아래 공식 페이지의 최신 다이어그램을 함께 참고하시기 바랍니다.
단순한 빌드 Pod 예시입니다. initContainer에서 소스를 클론하고, Kaniko가 Dockerfile을 읽어 이미지를 레지스트리에 푸시합니다.
빌드 Pod는 전용 ServiceAccount로 실행해 권한을 최소화하는 것이 원칙입니다.
apiVersion: v1
kind: Namespace
metadata:
name: image-builder
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: image-builder-sa
namespace: image-builder
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: image-builder-role
namespace: image-builder
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: image-builder-rb
namespace: image-builder
subjects:
- kind: ServiceAccount
name: image-builder-sa
namespace: image-builder
roleRef:
kind: Role
name: image-builder-role
apiGroup: rbac.authorization.k8s.io
kubectl create ns image-builder
kubectl -n image-builder create secret docker-registry kaniko-secret \\
--docker-server=index.docker.io \\
--docker-username=<DOCKERHUB_USER> \\
--docker-password=<DOCKERHUB_TOKEN>
Kaniko는 root로 실행되므로 Pod 레벨 runAsNonRoot는 적용하지 않고, 대신 전용 네임스페이스와 RBAC으로 보완합니다.
apiVersion: v1
kind: Pod
metadata:
name: random-generator-build
namespace: image-builder
spec:
serviceAccountName: image-builder-sa
restartPolicy: Never
volumes:
- name: workspace
emptyDir: {}
- name: docker-config
secret:
secretName: kaniko-secret
items:
- key: .dockerconfigjson
path: config.json
initContainers:
- name: fetch-source
image: alpine/git:2.45.2
command: ["sh", "-c"]
args:
- git clone <https://github.com/k8spatterns/random-generator> /workspace
resources:
requests:
cpu: 100m
memory: 128Mi
ephemeral-storage: 256Mi
limits:
cpu: 500m
memory: 512Mi
ephemeral-storage: 1Gi
volumeMounts:
- name: workspace
mountPath: /workspace
containers:
- name: build-with-kaniko
image: gcr.io/kaniko-project/executor:v1.23.2
args:
- --dockerfile=/workspace/Dockerfile
- --context=/workspace
- --destination=index.docker.io/<DOCKERHUB_USER>/random-generator:kaniko
- --snapshot-mode=redo
- --cache=true
- --cache-repo=index.docker.io/<DOCKERHUB_USER>/kaniko-cache
resources:
requests:
cpu: 500m
memory: 1Gi
ephemeral-storage: 2Gi
limits:
cpu: "2"
memory: 4Gi
ephemeral-storage: 8Gi
volumeMounts:
- name: workspace
mountPath: /workspace
- name: docker-config
mountPath: /kaniko/.docker
# 적용 및 상태 확인
kubectl -n image-builder apply -f build-pod.yaml
kubectl -n image-builder get pod random-generator-build -w
# 빌드 로그 확인
kubectl -n image-builder logs -f pod/random-generator-build -c build-with-kaniko
# 이벤트/리소스 현황 확인
kubectl -n image-builder describe pod random-generator-build
트러블슈팅 가이드
| 증상 | 원인 | 조치 |
|---|---|---|
로그에 Pushed index.docker.io/... | 성공 | 없음 |
OOMKilled 이벤트 | memory limit 초과 | memory limit 상향 |
| CPU throttling 이벤트 | cpu limit 낮음 | cpu limit 상향 |
Evicted 이벤트 | 디스크 부족 | ephemeral-storage 상향 또는 캐시 전략 재설계 |
"빌드 성공"에서 멈추지 않고, 취약점 스캔 통과 + 이미지 서명 완료까지 파이프라인에 포함하는 운영형 예시입니다.

각 단계는 runAfter로 순서가 보장되며, scan 단계에서 HIGH/CRITICAL 취약점이 발견되면 sign과 배포는 실행되지 않습니다.
Pipeline에서 재사용할 Kaniko 빌드 Task를 별도로 정의합니다. workspace에서 Dockerfile을 읽어 이미지를 빌드하고 레지스트리에 푸시합니다.
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: kaniko-build-task
namespace: image-builder
spec:
params:
- name: image
type: string
workspaces:
- name: source
steps:
- name: kaniko
image: gcr.io/kaniko-project/executor:v1.23.2
args:
- --dockerfile=$(workspaces.source.path)/Dockerfile
- --context=$(workspaces.source.path)
- --destination=$(params.image)
- --cache=true
- --cache-repo=index.docker.io/<DOCKERHUB_USER>/kaniko-cache
volumeMounts:
- name: docker-config
mountPath: /kaniko/.docker
volumes:
- name: docker-config
secret:
secretName: kaniko-secret
Tip: Tekton에서는
ServiceAccount에 registry secret을secrets필드로 연결하는 방식도 일반적입니다. 사내 표준에 따라 선택하시면 됩니다.
4개 Task를 순서대로 연결합니다. fetch-source는 Tekton Hub의 git-clone Task를 resolver로 참조합니다.
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: secure-image-pipeline
namespace: image-builder
spec:
params:
- name: image
type: string
- name: repo-url
type: string
workspaces:
- name: source
tasks:
# 1단계: 소스 클론
- name: fetch-source
taskRef:
resolver: hub
params:
- name: name
value: git-clone
- name: version
value: "0.9"
params:
- name: url
value: $(params.repo-url)
workspaces:
- name: output
workspace: source
# 2단계: Kaniko 빌드 & 레지스트리 푸시
- name: build
runAfter: [fetch-source]
taskRef:
name: kaniko-build-task
params:
- name: image
value: $(params.image)
workspaces:
- name: source
workspace: source
# 3단계: Trivy 취약점 스캔
- name: scan
runAfter: [build]
taskSpec:
params:
- name: image
type: string
steps:
- name: trivy
image: aquasec/trivy:0.58.0
script: |
trivy image --severity HIGH,CRITICAL --exit-code 1 $(params.image)
params:
- name: image
value: $(params.image)
# 4단계: Cosign 이미지 서명
- name: sign
runAfter: [scan]
taskSpec:
params:
- name: image
type: string
steps:
- name: cosign
image: cgr.dev/chainguard/cosign:v2.4.1
script: |
cosign sign --yes $(params.image)
params:
- name: image
value: $(params.image)
Tip (Trivy 레지스트리 인증): private 레지스트리 이미지를 스캔할 때는
TRIVY_USERNAME/TRIVY_PASSWORD환경변수로 인증 정보를 전달하셔야 합니다. Tekton에서는 kaniko-secret의 값을 env로 주입하는 방식이 일반적입니다.
Tip (Cosign keyless 동작 조건):
cosign sign --yes의 keyless 서명은 OIDC 토큰 공급자가 있어야 동작합니다. GKE Workload Identity, EKS IRSA, GitHub Actions OIDC 환경이 전제됩니다. 로컬/바닐라 클러스터에서는 KMS 키 방식(cosign sign --key gcpkms://...)으로 대체하셔야 합니다.
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: secure-image-pipelinerun
namespace: image-builder
spec:
pipelineRef:
name: secure-image-pipeline
params:
- name: image
value: index.docker.io/<DOCKERHUB_USER>/random-generator:secure
- name: repo-url
value: <https://github.com/k8spatterns/random-generator>
workspaces:
- name: source
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
# 파이프라인 전체 적용
kubectl -n image-builder apply -f secure-pipeline.yaml
# 실행 상태 확인
kubectl -n image-builder get pipelinerun
# 실시간 로그 확인
kubectl -n image-builder logs -l tekton.dev/pipelineRun=secure-image-pipelinerun -f
Tip: Tekton Hub의 공식 Task(
git-clone등)를 사용할 때는 버전을 고정하고 내부 보안 검토를 거친 후 사내 표준 Task로 관리하시는 것을 권장드립니다.
빌드 속도에 가장 큰 영향을 미치는 요소는 캐시입니다. 운영 규모와 환경에 따라 전략을 선택하셔야 합니다.
| 전략 | 장점 | 단점 | 추천 상황 |
|---|---|---|---|
| 로컬(emptyDir/PVC) 캐시 | 구성 단순, 속도 빠름 | 노드 종속, 주기적 정리 필요 | 단일 클러스터 소규모 반복 빌드 |
| Registry 캐시 | 멀티 노드 공유 가능 | 레지스트리 비용, 네트워크 I/O | 멀티 노드/멀티 파이프라인 환경 |
| 원격 오브젝트 스토리지(S3/GCS) | 대규모 운영에 유리, 비용 효율 | 설정 복잡도 높음 | 플랫폼팀 빌드 팜 표준 구성 |
예시
-cache-repo로 레지스트리 캐시를 지원합니다.-cache-to type=registry, -cache-from type=registry로 원격 캐시를 활성화하실 수 있습니다.ARM 기반 노드(AWS Graviton, Apple Silicon CI 환경 등)가 늘어나면서 단일 아키텍처 이미지만 제공하면 스케줄링 제약이 생깁니다.
예시
linux/amd64,linux/arm64를 동시에 빌드해 manifest list로 푸시할 수 있습니다.# buildx 멀티 아키텍처 빌드 예시
docker buildx build \\
--platform linux/amd64,linux/arm64 \\
--push \\
-t <REGISTRY>/myapp:latest .
Kaniko GitHub 저장소는 현재 아카이브 상태로, 신규 기능 개발과 보안 패치가 사실상 중단되었습니다. 장기 운영에서는 다음 전략이 필요합니다.
| 상황 | 권장 조치 |
|---|---|
| 신규 플랫폼 설계 | BuildKit 또는 관리형 빌드 서비스(Cloud Build 등)로 시작 |
| 기존 Kaniko 파이프라인 보유 | 현재 버전 고정 + 대체 경로 병행 검토 |
| 보안 패치 필요 시 | Buildah 또는 BuildKit으로 단계적 전환 |
운영 환경에서 Image Builder를 안전하게 유지하기 위해 아래 정책을 함께 설계하셔야 합니다.
baseline 적용 — Kaniko는 root로 실행되므로 restricted(runAsNonRoot 강제)와 호환 불가. RBAC으로 접근 범위를 보완latest) 대신 digest(@sha256:...) 기반 배포를 기본값으로 운영해 재현성 확보Kubernetes v1.35는 2025년 12월 17일 릴리스되었고, 현재(2026-02-28) 활성 브랜치입니다.
빌드 워크로드는 피크 시간대에 CPU/메모리 요구량이 급변합니다. v1.35부터 GA된 In-place Resize를 활용하면 Pod 재생성 없이 리소스를 조정할 수 있어 빌드 큐 적체 상황에서 유연하게 대응하실 수 있습니다.
운영 팁
podCertificate projected volume을 통해 mTLS 인증서를 Pod에 네이티브하게 주입/자동 회전할 수 있습니다. 사내 레지스트리와 mTLS를 강제하는 환경에서 cert-manager + sidecar 조합을 대체할 수 있는 방향입니다.
v1.35 [beta] (disabled by default)Tekton/Argo 등 CRD를 사용하는 빌드 도구의 API 버전 전환 시 저장 버전 마이그레이션이 자동화됩니다. v1.35 릴리스 블로그에서는 "beta, 기본 활성화"로 소개하지만, 문서 페이지 표기와 차이가 있을 수 있으므로 업그레이드 후 클러스터 설정을 직접 확인하시기 바랍니다.
v1.35는 containerd 1.x 지원의 마지막 릴리스입니다. v1.36 업그레이드 전에는 반드시 노드 런타임을 containerd 2.x로 전환하는 계획을 세우셔야 합니다.
# 노드별 런타임 버전 일괄 확인
kubectl get nodes \\
-o jsonpath='{range .items[*]}{.metadata.name}{"\\t"}{.status.nodeInfo.containerRuntimeVersion}{"\\n"}{end}'
운영 팁
kubelet_cri_losing_support 메트릭을 모니터링해 조기 경보를 설정하시면 업그레이드 타이밍을 놓치지 않으실 수 있습니다.30장 Image Builder 패턴의 핵심은 "빌드도 배포처럼 쿠버네티스 워크로드로 운영한다" 는 점입니다. 클러스터 안으로 빌드를 가져오면 정책/감사/리소스 관리가 일관되게 적용되고, 이벤트 기반 재빌드 체인으로 보안 패치 대응도 빨라집니다.
2026년 현재 실무에서 "빌드 성공" 하나만으로는 부족합니다. 아래 요소를 함께 설계해야 완성도 있는 플랫폼이 됩니다.
| 영역 | 핵심 고려 사항 |
|---|---|
| 보안 | daemon-less 빌더 선택, PSA baseline, digest 배포 |
| 공급망 | Trivy 스캔 게이트, Cosign 서명, Admission 검증 |
| 성능 | 캐시 전략(레지스트리/오브젝트 스토리지), 멀티아키 빌드 |
| 지속성 | Kaniko 아카이브 대응, containerd 2.x 전환 계획 |
| 운영 유연성 | In-place Resize, Pod Certificates 점진 도입 |
도구 선택보다 중요한 것은 빌드 파이프라인 전체를 하나의 운영 대상으로 바라보는 관점입니다. 이 장의 패턴이 그 출발점이 됩니다.