본문으로 건너뛰기

인터넷 없는 환경에 컨테이너 배포하기 (에어갭)

· 약 5분

인터넷이 막힌 에어갭(air-gap) 환경에 컨테이너 이미지를 반입하고 설치하는 방법과, "받아올 수 있다"는 가정 때문에 데이는 지점을 정리합니다.

결론부터 말하면, 필요한 모든 이미지를 미리 묶어 반입하고, 내부 사설 레지스트리에서만 받게 합니다. 그리고 코드와 스크립트에 숨은 "필요할 때 인터넷에서 받아온다"는 가정을 전부 걷어내야 합니다.

에어갭(air-gap)이란

에어갭은 보안을 위해 외부 인터넷과 물리적으로 분리된 환경을 말합니다. 고객사 데이터센터처럼 데이터가 밖으로 나가면 안 되는 곳에서 씁니다.

마트 없는 산장에 살림을 차리는 일에 비유할 수 있습니다. 인터넷이 되는 곳에서는 재료가 떨어지면 사 오면 되지만, 에어갭에서는 필요한 것을 하나도 빠짐없이 미리 싸 들고 들어가야 합니다.

왜 어려운가

에어갭이 어려운 이유는, 평소 설치가 인터넷에서 무언가를 받아오는 것을 전제로 하기 때문입니다. 컨테이너 이미지는 보통 외부 레지스트리(Docker Hub 등)에서 받아오는데, 에어갭에서는 그 경로가 막혀 있습니다.

게다가 받아야 할 것은 우리 서비스 이미지만이 아닙니다. 베이스 이미지, 사이드카, 데이터베이스, 모니터링 도구까지 전부 필요합니다. 하나라도 빠지면 설치가 중간에 멈춥니다.

앱 이미지뿐 아니라 베이스·사이드카·DB·OS 패키지·헬름 차트까지 모두 반입해야 한다

배포 방법 (네 단계)

에어갭 배포는 크게 네 단계입니다. ① 필요한 이미지를 빠짐없이 모으고, ② 묶어서 반입하고, ③ 내부 사설 레지스트리에 올리고, ④ 매니페스트가 그 레지스트리를 가리키게 합니다.

인터넷 있는 곳에서 이미지를 묶어 반입하고 내부 사설 레지스트리로 배포하는 에어갭 흐름

1. 필요한 이미지 목록 만들기

매니페스트(쿠버네티스 YAML, compose 파일 등)에서 쓰는 이미지를 전부 추출합니다. 베이스·사이드카까지 누락 없이 모읍니다.

# k8s 매니페스트에서 image: 줄만 추출
grep -rhoE 'image:\s*\S+' manifests/ | awk '{print $2}' | sort -u

2. 이미지를 묶어서 반입

인터넷이 되는 곳에서 이미지를 받아 하나의 파일로 묶습니다.

# 여러 이미지를 한 tar 로 저장
docker save -o bundle.tar app:1.0 postgres:16 nats:2.10 ...
# (또는 레지스트리 간 직접 복사: skopeo copy / crane)

bundle.tar를 USB·승인된 매체로 에어갭 안으로 반입합니다.

3. 내부 사설 레지스트리에 올리기

에어갭 안에 사설 레지스트리를 띄우고, 반입한 이미지를 풀어 올립니다.

docker load -i bundle.tar
docker tag app:1.0 registry.internal:5000/app:1.0
docker push registry.internal:5000/app:1.0

4. 매니페스트가 내부 레지스트리를 가리키게

이미지 주소를 외부 레지스트리에서 내부 레지스트리로 바꿉니다. 설치는 이제 인터넷이 아니라 내부에서만 이미지를 받습니다.

# before: image: docker.io/library/app:1.0
image: registry.internal:5000/app:1.0

흔한 함정

  • 의존 이미지 누락: 우리 앱만 챙기고 베이스·사이드카·DB를 빠뜨리기 쉽습니다. 매니페스트에서 기계적으로 전부 추출해 모읍니다.
  • 사설 레지스트리 pull이 느림: 작은 이미지는 괜찮지만 큰 이미지는 레지스트리를 거치면 오래 걸립니다. 용량이 큰 이미지는 레지스트리를 거치지 않고 컨테이너 런타임으로 직접 반입하는 방법을 씁니다(ctr image import 등).
  • 설치 스크립트에 숨은 인터넷 가정: 패키지 설치, 외부 URL 다운로드 같은 코드가 곳곳에 숨어 있습니다. 막힌 환경에서 줄줄이 멈추므로, "받아온다"는 가정을 하나씩 찾아 제거합니다.

Q&A

  • 사설 레지스트리 없이 그냥 이미지만 넣으면 안 되나요?
    • 노드마다 docker load로 직접 넣을 수도 있지만, 노드가 늘면 관리가 힘듭니다. 사설 레지스트리를 한 번 띄워 두는 편이 깔끔합니다.
  • 어떤 이미지를 빠뜨렸는지 어떻게 확인하나요?
    • 설치를 한 번 돌려 보고, 이미지 pull 실패 로그를 모읍니다. 그 목록을 다음 번들에 추가합니다. 처음부터 완벽히 모으긴 어렵습니다.
  • 이미지 외에 또 챙길 것이 있나요?
    • 있습니다. OS 패키지, 언어 런타임 의존성, 헬름 차트, 설치 스크립트가 받는 모든 외부 파일이 대상입니다.

참고자료

  • docker save / docker load, skopeo copy, crane
  • containerd ctr image import