BuildKit vs Kaniko: Kubernetes 환경 컨테이너 빌드 전환 검토

진웅·2026년 2월 2일

CI/CD

목록 보기
7/7

개요

Kaniko가 2025년 6월 공식 archived 되면서 BuildKit으로의 전환이 필요한 시점이다. BuildKit은 병렬 빌드, 향상된 캐싱, Multi-arch 지원 등 Kaniko 대비 우수한 성능과 기능을 제공한다.


Kaniko의 현재 상태

Google Container Tools의 Kaniko 프로젝트가 2025년 6월 3일 공식 archived 되었다.

  • 더 이상 버그 수정이나 보안 패치 제공 없음
  • CVE 취약점 발생 시 대응 불가
  • Chainguard에서 fork하여 유지보수 중이나 제한적

기존 Kaniko 파이프라인은 당장 문제없이 동작하지만, 장기적으로 BuildKit 전환을 권장한다.


Kaniko vs BuildKit 비교

핵심 특징 비교표

항목KanikoBuildKit
유지보수 상태Archived (2025.06)Active (Docker 공식)
빌드 방식Sequential (순차)DAG 기반 병렬 처리
캐싱기본적, 일관성 이슈Content-addressable, Registry 캐시
Multi-arch미지원네이티브 지원
Rootless 지원기본 rootlessrootless 옵션
Privileged 필요불필요rootless: 불필요 / privileged: 필요
빌드 속도기준약 2~3배 빠름
메모리 사용대용량 이미지에서 높음효율적
Secrets 마운트미지원RUN --mount=type=secret
SSH 마운트미지원RUN --mount=type=ssh

장단점 상세

Kaniko 장점 (과거형)

  • Kubernetes 네이티브 설계
  • 완전한 userspace 실행으로 보안 우수
  • Docker daemon 불필요
  • 100% Dockerfile 호환

Kaniko 단점

  • 프로젝트 중단으로 인한 보안 리스크
  • 순차 빌드로 느린 속도
  • Multi-arch 빌드 미지원
  • 복잡한 빌드에서 높은 메모리 사용
  • 캐시 일관성 문제

BuildKit 장점

  • Docker 팀 공식 유지보수 (Docker Engine 23.0+ 기본 빌더)
  • DAG 기반 병렬 실행으로 빠른 빌드
  • 고급 캐싱 전략 (Registry, Local, Inline)
  • Multi-platform 빌드 네이티브 지원
  • Secrets/SSH 마운트 지원
  • 활발한 개발 및 커뮤니티

BuildKit 단점

  • 초기 설정이 Kaniko보다 복잡
  • mTLS 설정 권장으로 인증서 관리 필요
  • privileged 모드 사용 시 보안 고려 필요 (rootless로 해결 가능)

BuildKit Kubernetes 배포 방식

BuildKit은 여러 배포 패턴을 지원한다.

1. Deployment + Service

  • Registry-side 캐시 활용 시 적합
  • 랜덤 로드밸런싱
  • 스케일 아웃 용이

2. StatefulSet

  • Client-side 로드밸런싱
  • Consistent Hash 모드 활용 가능
  • 캐시 히트율 향상

3. Job

  • 일회성 빌드에 적합
  • daemon pod 불필요

4. docker buildx create (Kubernetes Driver)

  • buildx CLI로 직접 Kubernetes 리소스 생성
  • 간편한 설정

BuildKit 구성 가이드 (Jenkins + ArgoCD 환경)

Step 1: BuildKit Namespace 및 배포

# buildkit-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: buildkit
---
# buildkit-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: buildkitd
  namespace: buildkit
  labels:
    app: buildkitd
spec:
  serviceName: buildkitd
  podManagementPolicy: Parallel
  replicas: 2
  selector:
    matchLabels:
      app: buildkitd
  template:
    metadata:
      labels:
        app: buildkitd
    spec:
      containers:
        - name: buildkitd
          image: moby/buildkit:v0.18.1-rootless
          args:
            - --addr
            - unix:///run/buildkit/buildkitd.sock
            - --addr
            - tcp://0.0.0.0:1234
            - --oci-worker-no-process-sandbox
          ports:
            - containerPort: 1234
              protocol: TCP
          securityContext:
            runAsUser: 1000
            runAsGroup: 1000
          readinessProbe:
            exec:
              command:
                - buildctl
                - debug
                - workers
            initialDelaySeconds: 5
            periodSeconds: 30
          livenessProbe:
            exec:
              command:
                - buildctl
                - debug
                - workers
            initialDelaySeconds: 5
            periodSeconds: 30
          volumeMounts:
            - name: buildkitd-data
              mountPath: /home/user/.local/share/buildkit
  volumeClaimTemplates:
    - metadata:
        name: buildkitd-data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 50Gi
---
# buildkit-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: buildkitd
  namespace: buildkit
spec:
  selector:
    app: buildkitd
  ports:
    - port: 1234
      targetPort: 1234
      protocol: TCP

Step 2: Jenkins RBAC 설정

# jenkins-buildkit-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: buildkit-user
  namespace: buildkit
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list"]
  - apiGroups: [""]
    resources: ["pods/exec"]
    verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: jenkins-buildkit-binding
  namespace: buildkit
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: buildkit-user
subjects:
  - kind: ServiceAccount
    name: jenkins
    namespace: jenkins

Step 3: Jenkins Agent Pod Template

# jenkins-agent-pod.yaml (podTemplate에서 사용)
apiVersion: v1
kind: Pod
metadata:
  labels:
    jenkins-agent: buildkit
spec:
  serviceAccountName: jenkins
  containers:
    - name: docker
      image: docker:27-cli
      command: ["sleep"]
      args: ["infinity"]
      env:
        - name: DOCKER_HOST
          value: ""
      volumeMounts:
        - name: docker-config
          mountPath: /root/.docker
          readOnly: true
  volumes:
    - name: docker-config
      secret:
        secretName: docker-registry-credentials

Step 4: Jenkinsfile 예시 (BuildKit 사용)

pipeline {
    agent {
        kubernetes {
            yaml '''
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: docker
    image: docker:27-cli
    command: ["sleep"]
    args: ["infinity"]
    volumeMounts:
    - name: docker-config
      mountPath: /root/.docker
      readOnly: true
  volumes:
  - name: docker-config
    secret:
      secretName: docker-registry-credentials
'''
        }
    }
    
    environment {
        REGISTRY = 'your-registry.com'
        IMAGE_NAME = 'your-app'
        BUILDKIT_HOST = 'tcp://buildkitd.buildkit.svc.cluster.local:1234'
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Setup BuildKit') {
            steps {
                container('docker') {
                    sh '''
                        # BuildKit remote driver 생성
                        docker buildx create \
                            --name kube-builder \
                            --driver remote \
                            ${BUILDKIT_HOST} \
                            --use
                        
                        # Builder 상태 확인
                        docker buildx inspect --bootstrap
                    '''
                }
            }
        }
        
        stage('Build & Push') {
            steps {
                container('docker') {
                    sh '''
                        docker buildx build \
                            --push \
                            --platform linux/amd64,linux/arm64 \
                            --cache-from type=registry,ref=${REGISTRY}/${IMAGE_NAME}:cache \
                            --cache-to type=registry,ref=${REGISTRY}/${IMAGE_NAME}:cache,mode=max \
                            -t ${REGISTRY}/${IMAGE_NAME}:${BUILD_NUMBER} \
                            -t ${REGISTRY}/${IMAGE_NAME}:latest \
                            .
                    '''
                }
            }
        }
        
        stage('Update Helm Values') {
            steps {
                script {
                    // Bitbucket Helm Chart repo 업데이트
                    withCredentials([usernamePassword(
                        credentialsId: 'bitbucket-credentials',
                        usernameVariable: 'GIT_USER',
                        passwordVariable: 'GIT_PASS'
                    )]) {
                        sh '''
                            git clone https://${GIT_USER}:${GIT_PASS}@bitbucket.org/your-org/helm-charts.git
                            cd helm-charts/your-app
                            
                            # values.yaml 이미지 태그 업데이트
                            sed -i "s/tag:.*/tag: ${BUILD_NUMBER}/" values.yaml
                            
                            git config user.email "jenkins@your-domain.com"
                            git config user.name "Jenkins CI"
                            git add values.yaml
                            git commit -m "Update image tag to ${BUILD_NUMBER}"
                            git push origin main
                        '''
                    }
                }
            }
        }
    }
    
    post {
        always {
            container('docker') {
                sh 'docker buildx rm kube-builder || true'
            }
        }
    }
}

기존 Kaniko 파이프라인 마이그레이션

Before: Kaniko

stage('Build with Kaniko') {
    steps {
        container('kaniko') {
            sh '''
                /kaniko/executor \
                    --dockerfile=Dockerfile \
                    --context=${WORKSPACE} \
                    --destination=${REGISTRY}/${IMAGE}:${TAG}
            '''
        }
    }
}

After: BuildKit

stage('Build with BuildKit') {
    steps {
        container('docker') {
            sh '''
                docker buildx create --name builder --driver remote tcp://buildkitd.buildkit:1234 --use
                docker buildx build --push \
                    -t ${REGISTRY}/${IMAGE}:${TAG} \
                    .
                docker buildx rm builder
            '''
        }
    }
}

캐싱 전략

Registry Cache (권장)

docker buildx build \
    --cache-from type=registry,ref=registry.example.com/myapp:cache \
    --cache-to type=registry,ref=registry.example.com/myapp:cache,mode=max \
    -t registry.example.com/myapp:v1 \
    --push .

Local Cache (StatefulSet 사용 시)

docker buildx build \
    --cache-from type=local,src=/cache \
    --cache-to type=local,dest=/cache,mode=max \
    -t registry.example.com/myapp:v1 \
    --push .

보안 고려사항

mTLS 설정 (Production 권장)

  1. 인증서 생성
./create-certs.sh buildkitd.buildkit.svc.cluster.local
kubectl create secret generic buildkit-daemon-certs \
    --from-file=ca.pem \
    --from-file=cert.pem \
    --from-file=key.pem \
    -n buildkit
  1. BuildKit에 TLS 설정 적용
args:
  - --addr
  - tcp://0.0.0.0:1234
  - --tlscacert=/certs/ca.pem
  - --tlscert=/certs/cert.pem
  - --tlskey=/certs/key.pem

Network Policy

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: buildkit-ingress
  namespace: buildkit
spec:
  podSelector:
    matchLabels:
      app: buildkitd
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: jenkins
      ports:
        - protocol: TCP
          port: 1234

전환 시 체크리스트

  • BuildKit StatefulSet/Deployment 배포
  • Jenkins ServiceAccount에 BuildKit RBAC 권한 부여
  • Registry 인증 Secret 생성
  • Jenkins Agent Pod Template 업데이트
  • Jenkinsfile buildx 명령어로 변경
  • 캐싱 전략 설정
  • (선택) mTLS 설정
  • (선택) Network Policy 적용
  • 기존 Kaniko 리소스 정리

트러블슈팅

자주 발생하는 문제

1. BuildKit 연결 실패

error: failed to dial gRPC: rpc error: connection refused

→ BuildKit Service 및 Pod 상태 확인, 포트 1234 연결 가능 여부 점검

2. Registry Push 실패

unauthorized: authentication required

→ Docker config secret 마운트 확인, imagePullSecrets 설정 점검

3. Rootless 모드 실패

failed to create container: permission denied

--oci-worker-no-process-sandbox 옵션 추가, securityContext 확인

4. 캐시 히트 안됨
→ StatefulSet 사용 시 consistent hash 적용 고려, Registry cache 설정 확인


참고 자료

profile
bytebliss

0개의 댓글