- Published on
Kubernetes 이미지 풀 느림, eStargz로 가속하기
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스나 오토스케일 환경에서 배포 지연의 주범은 의외로 애플리케이션이 아니라 ImagePull 입니다. 특히 노드가 자주 교체되거나(Spot/Preemptible), 파드가 자주 재스케줄링되거나, 이미지가 큰데 레지스트리/네트워크가 그리 빠르지 않으면 ContainerCreating 구간이 길어집니다.
이 글에서는 Kubernetes에서 이미지 풀을 “완전히 다 받기 전에 실행”할 수 있게 해주는 eStargz(지연 풀, lazy pulling)로 체감 속도를 줄이는 방법을 설명합니다. 핵심은 containerd + stargz-snapshotter 조합이며, 이미지 빌드 단계에서 eStargz 포맷으로 변환해 레지스트리에 푸시하는 흐름입니다.
또한 원인 진단(어디서 시간이 새는지), 적용 시 주의점(캐시, 레이어 구성, 워밍), 그리고 운영에서 확인해야 할 지표를 함께 정리합니다.
Kubernetes 이미지 풀이 느려지는 대표 원인
이미지 풀이 느리다는 말은 사실 여러 구간을 섞어 부르는 경우가 많습니다.
- 다운로드 자체가 느림: 레지스트리 대역폭, NAT/프록시, VPC egress, TLS 핸드셰이크 반복, 레이어 수가 과도함
- 압축 해제/디스크 I/O가 느림: 대형 레이어 untar, 느린 루트 디스크, 노드의 I/O 경합
- 이미지 크기가 지나치게 큼: 불필요한 빌드 산출물, 패키지 캐시, 디버그 심볼 포함
- 노드 캐시가 자주 사라짐: 스케일 아웃/인, 노드 교체,
imagePullPolicy: Always - 동시 풀 경쟁: 한 노드에 여러 파드가 동시에 같은 이미지를 끌어오며 디스크/네트워크 병목
이 중 (2)와 (3) 영역은 “이미지를 모두 받아서 풀어야 실행 가능”이라는 전제 때문에 더 치명적입니다. eStargz는 이 전제를 깨서, 컨테이너가 실제로 필요한 파일부터 우선 가져오게 만들어 시작 시간을 줄입니다.
eStargz와 lazy pulling 개념 정리
Stargz란 무엇인가
stargz는 컨테이너 이미지 레이어(tar.gz)를 랜덤 액세스 가능하게 재구성한 포맷입니다. 일반적인 gzip tar는 특정 파일 하나를 읽으려면 앞부분을 순차적으로 풀어야 하는데, stargz는 인덱스를 포함해 필요한 범위만 HTTP Range 요청으로 가져올 수 있습니다.
eStargz(extended stargz)
eStargz는 stargz에 추가 메타데이터를 넣어 컨테이너 런타임이 더 효율적으로 원격 레이어를 마운트하고, 필요한 파일을 지연 로드할 수 있게 합니다.
효과가 큰 상황
- 앱 엔트리포인트가 읽는 파일이 레이어 전체 중 일부일 때
- 이미지가 크고(수백 MB~수 GB), 특히 untar 비용이 클 때
- 노드가 자주 바뀌어 캐시 히트율이 낮을 때
반대로, 시작 직후 레이어의 대부분을 읽는 워크로드(예: 거대한 모델 파일을 즉시 로드)는 체감이 제한적일 수 있습니다.
구성 요소: containerd + stargz-snapshotter
Kubernetes에서 eStargz를 쓰려면 보통 다음 조합을 사용합니다.
- CRI 런타임:
containerd - 스냅샷터:
stargz-snapshotter(원격 스냅샷/지연 풀 담당)
중요한 점은 “이미지가 eStargz여야 한다”와 “노드 런타임이 이를 해석해 lazy pulling을 수행해야 한다” 두 조건이 모두 필요하다는 것입니다.
적용 전: 병목 구간을 먼저 계측하기
무작정 eStargz를 적용하기 전에, 실제로 시간이 어디서 쓰이는지 확인해야 합니다.
1) 파드 이벤트로 ImagePull 시간 확인
kubectl describe pod -n your-ns your-pod
출력에서 Pulling / Pulled 사이 시간을 보거나, ContainerCreating이 긴지 확인합니다.
2) containerd 로그/메트릭 확인
노드에서 containerd 로그를 확인하거나, Prometheus로 containerd 관련 메트릭을 수집하는 구성이면 pull/untar 시간을 분리해 볼 수 있습니다.
3) CrashLoopBackOff와 혼동하지 않기
이미지 풀은 느린데, 컨테이너가 뜬 뒤 바로 죽어서 재시도하며 “계속 느려 보이는” 경우도 있습니다. 이 경우는 풀 최적화보다 앱/환경 문제를 먼저 해결해야 합니다.
eStargz 이미지 만들기: 빌드 파이프라인
가장 흔한 흐름은 다음 중 하나입니다.
buildkit로 이미지 빌드 후ctr-remote/stargzify로 변환nerdctl+ buildkit 환경에서 eStargz 변환/푸시
여기서는 개념적으로 “일반 OCI 이미지”를 “eStargz 이미지”로 변환해 레지스트리에 푸시하는 형태를 예시로 듭니다.
Dockerfile 최적화는 여전히 중요
eStargz가 만능은 아닙니다. 이미지 자체를 줄이면 네트워크/스토리지/캐시 모든 면에서 이득입니다.
# syntax=docker/dockerfile:1
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY /app/dist ./dist
COPY /app/node_modules ./node_modules
COPY package.json ./
CMD ["node", "dist/server.js"]
멀티 스테이지로 빌드 산출물만 남기고, 캐시/툴체인을 런타임 이미지에서 제거합니다.
eStargz 변환 예시(개념)
환경마다 도구 설치 방식이 다르지만, 보통 stargz-snapshotter 프로젝트에서 제공하는 ctr-remote를 사용합니다.
# 1) 일반 이미지 빌드
docker build -t registry.example.com/team/app:1.0.0 .
# 2) 레지스트리에 푸시(원본)
docker push registry.example.com/team/app:1.0.0
# 3) eStargz로 변환해 별도 태그로 푸시(예: :1.0.0-esgz)
# 아래 커맨드는 환경에 따라 다를 수 있으니, 핵심은 "convert" 후 "push" 흐름입니다.
ctr-remote images convert \
--estargz \
registry.example.com/team/app:1.0.0 \
registry.example.com/team/app:1.0.0-esgz
ctr-remote images push registry.example.com/team/app:1.0.0-esgz
운영에서는 보통 원본 태그를 덮기보다, -esgz 같은 별도 태그로 안전하게 검증한 뒤 점진 전환합니다.
노드 런타임 설정: stargz-snapshotter 활성화
1) stargz-snapshotter 설치
배포 방식은 크게 두 가지입니다.
- 노드에 패키지/바이너리로 설치 + systemd 서비스로 실행
- DaemonSet으로 배포(노드에 접근해 소켓/바이너리 구성)
어떤 방식을 쓰든 목표는 containerd가 스냅샷터로 stargz를 사용할 수 있게 만드는 것입니다.
2) containerd 설정에서 스냅샷터 지정
/etc/containerd/config.toml에서 CRI 플러그인 설정에 스냅샷터를 지정합니다. 예시는 다음과 같습니다.
[plugins."io.containerd.grpc.v1.cri".containerd]
snapshotter = "stargz"
설정 변경 후에는 보통 containerd 재시작이 필요합니다.
sudo systemctl restart containerd
주의: 클러스터 전체에 일괄 적용하기 전에, 별도 노드풀(카나리)에서 먼저 검증하는 것을 권장합니다.
Kubernetes에서 사용하는 방법(Deployment)
이미지를 eStargz 태그로 바꾸는 것만으로도 동작하는 구성이 많습니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 3
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: registry.example.com/team/app:1.0.0-esgz
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
imagePullPolicy는 캐시 전략과 직결됩니다. 노드 교체가 잦아 캐시가 의미 없으면 IfNotPresent가 큰 차이를 못 만들 수 있지만, 노드가 유지되는 환경에서는 pull 빈도를 줄여 효과가 큽니다.
성능 검증: “풀 시간”이 아니라 “시작 체감 시간”을 보자
eStargz의 목표는 레이어 다운로드 완료 시간을 0으로 만드는 것이 아니라, 컨테이너가 Ready 상태가 되기까지의 시간을 줄이는 것입니다.
추천 측정 지표:
PodScheduled부터Ready까지의 시간ContainerCreating구간의 길이- 노드의 네트워크 egress/ingress 변화(초기 스파이크가 줄고, 실행 중에 분산될 수 있음)
- 앱의 cold start SLO(예: 배포 후 95퍼센타일 Ready 시간)
간단한 비교는 다음처럼 할 수 있습니다.
# 파드 생성 시간 확인
kubectl get pod -n your-ns -w
# 특정 파드의 타임라인을 이벤트로 확인
kubectl describe pod -n your-ns your-pod | sed -n '/Events:/,$p'
운영에서 자주 겪는 함정과 해결 팁
1) 레지스트리가 Range 요청을 제대로 처리해야 함
eStargz는 HTTP Range 요청에 크게 의존합니다. 사내 프록시, WAF, 특정 레지스트리 설정이 Range를 비효율적으로 처리하면 효과가 줄거나 문제가 생길 수 있습니다.
점검 포인트:
- 레지스트리/프록시가 Range 요청을 허용하는지
- 캐시 계층이 Range를 조각 캐싱으로 망치지 않는지
2) “시작 후 느려짐”으로 보일 수 있음
lazy pulling은 필요한 파일을 그때그때 가져옵니다. 따라서 앱 시작은 빨라졌는데, 특정 API가 처음 호출될 때 파일 페이지인이 발생해 지연이 생길 수 있습니다.
대응 방법:
- readiness probe 전에 워밍업(필요 파일을 미리 읽기)
- 자주 쓰는 경로에 대해 초기화 루틴에서 파일 접근
예: 컨테이너 시작 시 중요한 파일을 한 번 읽어 캐시를 당겨오는 방식
# entrypoint.sh 예시
set -e
node dist/warmup.js || true
exec node dist/server.js
3) 이미지 레이어 구성에 따라 효과가 달라짐
앱 실행에 필요한 파일이 레이어 뒤쪽에 몰려 있으면, 시작 시점에 더 많은 Range fetch가 발생할 수 있습니다.
팁:
- 엔트리포인트에 필요한 바이너리/스크립트/설정 파일을 앞 레이어에 배치
- 불필요한 파일을 제거해 인덱스/탐색 비용 자체를 줄이기
4) 노드 리소스 압박과 OOM 이슈
이미지 풀 최적화와 별개로, 노드가 메모리 압박을 받으면 containerd/런타임/앱 모두가 영향을 받습니다. 특히 스케일 아웃 시점에 여러 파드가 동시에 뜨면서 메모리 압박이 커질 수 있습니다.
eStargz 도입 전략(실전)
- 카나리 노드풀을 만든다(예:
nodepool=stargz). - 해당 노드풀에만
stargz-snapshotter와containerd설정을 적용한다. - Deployment에
nodeSelector로 카나리 워크로드만 태운다.
spec:
template:
spec:
nodeSelector:
nodepool: stargz
- 같은 앱을
:원본과:esgz로 나눠 A/B 테스트한다. - Ready 시간, 오류율, p95 지연, 레지스트리 트래픽 패턴을 비교한다.
eStargz가 아닌 대안도 함께 검토하기
eStargz는 강력하지만, 환경에 따라 다른 최적화가 더 싸고 확실할 수 있습니다.
- 이미지 자체 슬림화(멀티 스테이지, 불필요 파일 제거)
- 레지스트리를 노드와 같은 리전에 두기, VPC 엔드포인트 사용
- 노드 로컬 캐시(이미지 프리풀 DaemonSet)로 워밍
- 큰 이미지의 구성 재설계(모델/정적 자산을 오브젝트 스토리지로 분리)
빌드 파이프라인이 느려 전체 릴리즈 속도가 문제라면, 이미지 풀과는 별개로 CI 캐시 전략도 점검해야 합니다.
정리
Kubernetes에서 이미지 풀이 느려 배포가 답답할 때, eStargz는 “다운로드가 끝나야 실행된다”는 병목을 깨서 파드가 Ready 되기까지의 체감 시간을 줄이는 실전적인 해법입니다.
성공의 핵심은 세 가지입니다.
- 이미지가 eStargz로 준비되어 있을 것
- 노드 런타임이
stargz-snapshotter를 통해 lazy pulling을 수행할 것 - 성능 측정 기준을 “풀 완료”가 아니라 “Ready까지의 시간”으로 잡고, 워밍/레이어 구성까지 함께 최적화할 것
이 세 가지를 갖추면, 특히 오토스케일과 잦은 노드 교체가 있는 환경에서 배포 속도와 안정성이 눈에 띄게 좋아집니다.