30. Image Builder

sangyeob·2026년 2월 28일

6부 Advanced Patterns 중 하나로, 그 동안 다른 카테고리에서 다르지 않는 복잡한 주제를 다룬다.

Image Builder 패턴은, 쿠버네티스 클러스터 외부에서 (로컬 PC나 CI 서버에서) 컨테이너 이미지를 빌드하던 것을, 클러스터 내부에서 직접 빌드하도록 한다.

Intro

쿠버네티스는 범용적인 목적의 오케스트레이션 엔진 이며, 단순히 애플리케이션을 실행하는 것 뿐 아니라 컨테이너 이미지를 빌드하기에도 적합하다. Image Builder 패턴은 클러스터 내부에서 컨테이너 이미지를 빌드하는 것이 왜 적합한지와 관련 테크닉에 대해 다룬다.

Problem

이제까지 다루었던 패턴은 쿠버네티스에서 애플리케이션 운영에 관한 것이이였다. (cloud native한 application을 위해 어떻게 개발하고 준비해야할지) 그러나, 이번 장에서 다룰 문제는 애플리케이션 빌드에 관한 것이다.

클러스터 내부 이미지 빌드의 장점

전통적인 애플리케이션 빌드 방식은 다음과 같다.

  1. 클러스터 외부에서 컨테이너 이미지를 빌드
  2. 레지스트리에 푸쉬
  3. 쿠버네티스 deployment에서 이미지 참조이다.

컨테이너 이미지 빌드를 클러스터 내부에서 하는 것은 다음과 같은 장점이 있다.

  • CI (Continuous Integration) 시스템은, 이미지 빌드 작업에 사용 가능한 컴퓨팅 자원을 효율적으로 찾아야하는 스케쥴링 문제에 직면한다. 쿠버네티스에서 CI 시스템 구축한다면, 이에 적합한 스케쥴러를 통해 문제를 해결할 수 있다.
  • CD (Continuous Delivery): 클러스터 내부에서 이미지 빌드가 된다면, CI/CD 모두 같은 인프라스트럭쳐를 공유할 수 있으며, "image 빌드 -> 컨테이너 실행" 과정이 더 간단해질 수 있다. (base image를 공유해 쉽게 재배포가 가능)

Image Builder pattern을 사용하면, 클러스터는 "이미지 빌드 + 배포 (deployment)" 모두 잘 파악할 수 있고, base image가 바뀌는 경우의 재배포도 자동화할 수 있다.

Solution

essential image-building techniques within a Kubernetes cluster (2023)

위 도구들은 크게 두 가지 카테고리로 분류할 수 있다.

  • Container image builder
    • 클러스터 내부에서 컨테이너 이미지를 생성하는 도구들이며, privileged access 없이 실행될 수 있다.
    • 애플리케이션 재배포 (redeployment)와는 관련 없다.
  • Build orchestration
    • Container image builder 도구들보다 상위 레벨이며, 이미지 생성을 위해 위 tool 들을 트리거 한다.
    • 빌드 관련 task를 지원하기도 한다. (ex. 이미지 빌드 후에, deployment descriptor를 업데이트하는 등)

Container Image Builder

핵심은 노드 host에 대한 privileged access 없이, 클러스터 내부에서 이미지를 빌드하는 것.

Rootless Builds

클러스터는 빌드 프로세스 전체를 완전히 제어해야하며, 여러 취약점으로부터 보호를 위한 높은 보안 기준이 필요하다. 이를 위한 대표적인 방법이, 루트 권한 없이 빌드를 실행하는 것 (rootless build)이다.

Docker 데몬은 컨테이너 실행을 위해 루트 권한이 필요하며, 컨테이너 이미지 빌드 시에도 마찬가지이다.

  • Docker는 백그라운드 데몬이 REST API를 통해 클라이언트의 명령 (컨테이너에 대한)을 수행하는데, 이 데몬의 작업 수행을 위해서는 루트 권한이 필요하다. (ex. 네트워크 or volume 관리 등의 작업 수행 시)

이와달리 아래에서 설명하는 다양한 클러스터 내 빌드 도구들은 nonpriviledged mode에서 컨테이너 이미지를 빌드하는 모드를 지원한다.

Dockerfile-Based builders

Dockerfile 형식을 기반으로 하는 builder 이다. 백그라운드 데몬과 전혀 통신하지 않고, nonpriviledged mode로 실행되는 빌드 프로세스 + REST API를 통해 원격으로 통신한다.

도구데몬 필요 여부특징제약
Buildah/Podman불필요완전한 daemonless, OCI 호환 이미지 빌드 도구-
Kaniko불필요K8s 빌드 컨테이너 특화, Google Cloud Build 핵심컨테이너 내 UID 0 필요 → OpenShift 와 같이 컨테이너 내부에서 루트 사용자로 실행을 허용하지 않는 클러스터는 사용 불가
BuildKit필요 (Buildkit 데몬되어 빌드 작 대기)Docker로부터 분리된 자체 빌드 엔진 프로젝트
LLB (Low-Level Build) 형식으로 복잡한 빌드 그래프 지원
분산 rootless 빌드 가능
-

Multilanguage builders

Spring Boot 애플리케이션이나 일반적인 Python 빌드와 같은 기존 프로젝트를 감지하고, 그에 맞는 이미지 빌드 흐름을 자동으로 선택하는 builder.

CNB(Cloud Native Buildpacks)

  • 소스 코드를 자동 감지해 OCI 이미지로 변환하는 lifecycle 제공하는 도구
  • 2012년 Heroku → Cloud Foundry 포크(cf push) → 2018년 CNCF 통합
  • lifecycle은 대략 3단계로 구성된다.
    • detect: 소스 코드를 분석해 적합한 buildpack 선택 (e.g. pom.xml → Java buildpack)
    • build: 선택된 buildpack들이 의존성 설치 및 빌드 수행 (e.g. npm install)
    • export: 최종 OCI 이미지 생성 후 레지스트리에 푸시
  • 대상 페르소나
    1. 개발자 (쿠버네티스 플랫폼에 코드를 배포하고자함, Dockerfile 작성없이 소스코드만 작성)
    2. Buildpack 작성자 (개별 buildpack을 생성하며, Builder로 분류)
  • 쿠버네티스 클러스터에서 CNI를 사용하기 위한 도구는 아래와 같다.
도구실행 위치특징
pack로컬CLI 명령어, Docker/Podman 런타임 필요
Tekton build task나 GitHub Actions 같은 CI 단계CI 파이프라인Builder 이미지에서 라이프사이클 직접 호출
kpackK8s 클러스터 내Operator 방식, CNB 개념 (Builder, Builder-packs)을 CRD로 표현

Specialized builders

특정 상황에서 + 독자적인 방식으로 이미지를 생성하는 builders.

  • 적용 범위는 좁지만, 유연성은 높이고 빌드 시간을 줄이는 최적화된 빌드 흐름을 제공
  • rootless 빌드를 수행
  • 애플리케이션 아티팩트로 이미지 레이어를 로컬에서 직접 생성하고, 컨테이너 이미지 레지스트리에 직접 푸시 (Dockerfile RUN 같은 명령을 실행하는 방식이 아님)
도구대상 언어/환경특징
JibJava (Maven/Gradle 같은 빌드 도구와 통합)아티팩트·의존성·리소스를 별도 레이어로 분리 → 재빌드 시간 최적화
koGolang원격 Git에서 직접 빌드, Pod 명세 자동 업데이트
ApkoAlpine 기반 이미지Apk 패키지를 빌딩 블록으로 사용 → 유사 이미지 재사용 용이

Build Orchestrators

Build Orchestrator는 Tekton, Argo CD, Flux 같은 CI/CD 플랫폼을 말하며,빌드, 테스트, 릴리즈, 배포, 보안 스캔 등 애플리케이션의 전체 자동화 관리 라이프사이클을 다룬다. 범용 CI/CD 플랫폼 외에도, 컨테이너 이미지를 생성하기 위한 더 특화된 오케스트레이터를 사용할 수 있다.

Build Orchestrator특징
OpenShift BuildsK8s 클러스터 내 빌드 중 가장 오래되고 성숙한 방식
kbldKubernetes에서 빌드, 구성, 배포를 위한 툴셋인 Carvel의 일부
컨테이너 빌드 후 YAML의 image 필드를 새 이미지 경로로 자동 업데이트
Kubernetes Job표준 K8s Job으로 빌드 트리거
빌드 Pod는 source repository에서 코드를 가져와 클러스터 내 빌더로 이미지 생성

Build Pod

모든 Build Orchestrator의 대표적인 작업 흐름은 다음과 같다.

  1. 주어진 원격 Git 저장소에서 소스 코드를 체크아웃
  2. 컴파일 언어의 경우, 컨테이너 내부에서 로컬 빌드 수행
  3. Container Image Builder 절에서 설명한 기법 중 하나로 애플리케이션 이미지를 빌드
  4. 이미지를 원격 이미지 레지스트리에 푸시
  5. 선택적으로, 새 이미지 참조로 Deployment를 업데이트. 배포 전략에 따라 애플리케이션의 재배포를 트리거

Kaniko를 사용한 Pod 빌드

apiVersion: v1
kind: Pod
metadata:
  name: build
spec:
  # 빌드 단계는 initContainer로 순차 실행 보장
  initContainers:
  # [1단계] 원격 Git 저장소에서 소스 코드 체크아웃
  - name: git-sync
    image: k8s.gcr.io/git-sync/git-sync
    args: [
      "--one-time",          # 1회 실행 후 종료
      "--depth", "1",        # 최신 커밋만 가져옴 (shallow clone)
      "--root", "/workspace",
      "--repo", "https://github.com/k8spatterns/random-generator.git",
      "--dest", "main",
      "--branch", "main"
    ]
    volumeMounts:
    - name: source
      mountPath: /workspace  # 소스 코드를 공유 볼륨에 저장

  # [2단계] Kaniko로 이미지 빌드 & 레지스트리 푸시
  - name: build
    image: gcr.io/kaniko-project/executor
    args:
    - "--context=dir:///workspace/main/"                                        # 소스 코드 위치
    - "--destination=index.docker.io/k8spatterns/random-generator-kaniko"       # 푸시할 레지스트리 경로
    - "--image-name-with-digest-file=/workspace/image-name"                     # 빌드된 이미지 digest를 파일로 저장 (다음 단계에서 사용)
    securityContext:
      privileged: false      # rootless 빌드 (Pod 자체는 비특권)
    volumeMounts:
    - name: kaniko-secret
      mountPath: /kaniko/.docker  # Kaniko가 인증 정보를 찾는 고정 경로
    - name: source
      mountPath: /workspace       # 소스 코드 및 digest 파일 공유

  containers:
  # [3단계] 빌드된 이미지로 Deployment 업데이트
  - name: image-update
    image: k8spatterns/image-updater
    args:
    - "random"           # 업데이트할 Deployment 이름
    - "/opt/image-name"  # Kaniko가 저장한 이미지 digest 파일 경로
    volumeMounts:
    - name: source
      mountPath: /opt    # digest 파일 접근을 위해 공유 볼륨 마운트

  volumes:
  # Docker Hub 인증 정보 (registry-creds Secret에서 로드)
  - name: kaniko-secret
    secret:
      secretName: registry-creds
      items:
      - key: .dockerconfigjson
        path: config.json  # Kaniko가 기대하는 파일명

  # 컨테이너 간 데이터 공유용 임시 볼륨 (노드 로컬 파일시스템)
  - name: source
    emptyDir: {}

  # Deployment를 patch할 수 있는 권한을 가진 ServiceAccount
  serviceAccountName: build-pod

  # 빌드는 1회성 작업이므로 재시작 불필요
  restartPolicy: Never

Openshift Build

Red Hat OpenShift는 Kubernetes의 엔터프라이즈 배포판으로 Kubernetes 기능 + 통합 컨테이너 이미지 레지스트리 + 싱글 사인온(SSO) 지원 + 새로운 UI 등 엔터프라이즈 관련 기능을 추가하고, Kubernetes에 네이티브 이미지 빌드 기능도 추가한다.

OpenShift Build는 Kubernetes가 관리하는 클러스터 통합 방식으로 이미지를 직접 빌드하는 최초의 방법이며, 이미지 빌드를 위한 여러 전략을 지원한다:

  • Source-to-Image (S2I): 애플리케이션 소스 코드를 가져와 언어별 S2I 빌더 이미지의 도움으로 실행 가능한 아티팩트를 생성한 후 통합 레지스트리에 이미지를 푸시한다.
  • Docker build: Dockerfile과 컨텍스트 디렉토리를 사용하여 Docker 데몬이 하는 것처럼 이미지를 생성
  • Pipeline build: 사용자가 Tekton 파이프라인을 구성할 수 있도록 허용하여 내부적으로 관리되는 Tekton의 빌드 작업과 매핑
  • Custom 빌드: 이미지를 생성하는 방법에 대한 완전한 제어권을 제공하며, 커스텀 빌드 내에서 빌드 컨테이너 안에 직접 이미지를 생성하고 레지스트리에 푸시한다.

Build input source의 종류는 다음과 같다. Input을 어떻게 설정하고 사용할지는 빌드 위 전략에 따라 달라진다.

  • Git: 소스를 가져올 원격 URL로 지정된 저장소
  • Dockerfile: 빌드 구성 리소스의 일부로 직접 저장된 Dockerfile
  • Image: 현재 빌드를 위해 파일을 추출하는 다른 컨테이너 이미지
  • Secret: 빌드를 위한 기밀 정보를 제공하는 라소스
  • Binary: 외부에서 모든 입력을 제공하는 소스

Openshift Build의 핵심 리소스/개념은 다음과 같다.

개념설명
BuildConfig모든 빌드 정보를 담는 중앙 리소스. oc CLI로 정의 및 트리거
ImageStream컨테이너 이미지 참조 목록 (Docker 저장소와 유사)
ImageStreamTagImageStream 내 태그된 개별 이미지 참조
trigger이벤트에 대한 일종의 리스너 (imageChange 트리거는 ImageStreamTag 변경에 반응)
이미지 재빌드, Pod 재배포를 트리거하는데 사용

Discussion

클러스터 내부에서 컨테이너 이미지를 빌드하는 두 가지 방식을 살펴봤다.

1. plain build Pod

모든 build system에서 실행해야하는 핵심적인 작업은 다음과 같다.

소스 코드 가져오기
-> 소스 코드에서 실행 가능한 아티팩트 생성
-> 애플리케이션 아티팩트를 포함하는 컨테이너 이미지 생성
-> 이미지 레지스트리에 푸시
-> 새로 생성된 이미지를 레지스트리에서 가져오도록 Deployment 업데이트

build orchestrator가 다뤄야할 manual 작업이 많기 때문에, 바로 production 레벨에서 사용하기는 쉽지 않다.

2. Openshift build system

ImageStream 트리거를 통해 여러 빌드를 연결
-> 빌드가 애플리케이션 컨테이너 이미지를 업데이트 
-> 애플리케이션자동 재배포

빌드와 배포를 잘 통합했기 때문에 CD의 궁극적인 목표에 한 걸음 다가갔다고 할 수 있지만, Kubernetes Openshift 배포판을 사용할 때만 사용이 가능하다.

0개의 댓글