Docker –network none 로 오프라인 패키지 설치 검증하기

By | 2026년 9월 3일
Table of Contents

Docker –network none 로 오프라인 패키지 설치 검증하기

검증이전!!!!

배포용 패키지(.deb, 커스텀 설치 스크립트 등)를 만들 때 가장 자주 놓치는 실수는
"개발 PC에는 이미 깔려 있어서 문제가 안 보이는 의존성" 입니다.

개발자 로컬 환경에는 이런저런 라이브러리가 이것저것 깔려 있다 보니, 정작 오프라인
설치 번들에는 빠져 있는 의존성이 있어도 개발 PC에서는 설치가 잘 됩니다. 그런데
막상 폐쇄망(에어갭) 환경의 실제 대상 장비에 가져가면 apt가 인터넷에 접속하려다
실패하면서 설치가 중간에 멈추죠.

이 글은 이런 문제를 배포 전에 잡아내기 위해, Docker 컨테이너에 네트워크 인터페이스
자체를 제거한 상태
로 설치 번들 하나만 반입해서 정말로 자급자족이 되는지 검증하는
절차를 정리한 것입니다. 특정 패키지 이름 없이 어떤 프로젝트에도 적용할 수 있게
일반화했습니다.


왜 –network none 인가

  • --network none은 컨테이너에 네트워크 인터페이스 자체를 만들지 않습니다.
    방화벽 규칙으로 막는 것과 달리 "연결 시도 자체가 물리적으로 불가능한" 상태라
    검증 결과를 신뢰할 수 있습니다.
  • 이렇게 만든 컨테이너 안에서 설치 스크립트를 돌렸을 때 apt-get
    Could not connect to... 같은 메시지를 낸다면, 그건 오프라인 번들에
    빠진 의존성이 있다는 확실한 증거입니다.

왜 베이스 이미지를 그냥 ubuntu:XX.04로 하면 안 되는가

베이스 이미지를 순정 상태로 두면 검증 자체가 의미 없어집니다. 두 가지를 신경 써야 합니다.

  1. 베이스 이미지는 실제 배포 대상 호스트의 "최소 상태"를 흉내내야 한다.
    개발/빌드 전용 패키지(build-essential, cmake 등)는 베이스 이미지에
    넣지 마세요. 실제 배포 대상 호스트에는 없을 것들이라, 넣으면 대상 호스트보다
    관대한 환경이 되어 검증이 느슨해집니다.
  2. 설치 대상 패키지가 스스로 설치할 의존성은 베이스 이미지에 미리 깔지 말 것.
    미리 깔아두면 "번들이 정말 이걸 가져오는지"를 확인할 수 없어 테스트가 무의미해집니다.

즉 베이스 이미지에 넣을 목록은 아래아 같습니다.

설치
 - (실제 배포 대상 호스트에 이미 존재하는 패키지)

제외
  − (개발/빌드 전용 도구)
  − (설치 대상 패키지 자신의 직접 의존성)

이 목록을 확실히 하는 가장 좋은 방법은 실기기(또는 실기기와 동일한 이미지)에서
apt-get install --simulate <패키지명>을 돌려 어떤 패키지가 이미 만족되어 있는지
실측하는 것입니다. 감으로 목록을 짜지 마세요.


1. Dockerfile 템플릿

<베이스-런타임-패키지들> 자리를 위 방법으로 실측한 목록으로 채워 넣으세요.
서비스 데몬(systemd 유닛)까지 기동 검증하려면 systemd를 함께 넣습니다.

FROM ubuntu:26.04

ENV DEBIAN_FRONTEND=noninteractive

# 대상 호스트 베이스 + systemd(서비스 기동 검증용)
RUN apt-get update && apt-get install -y --no-install-recommends \
      systemd systemd-sysv \
      <베이스-런타임-패키지들> \
    && apt-get clean && rm -rf /var/lib/apt/lists/*

# 설치 스크립트가 기존 계정 존재를 전제로 한다면 여기서 미리 만들어 둡니다.
# (패키지가 직접 만드는 계정이라면 이 줄은 생략)
# RUN groupadd -g 1000 <계정명> && useradd -m -u 1000 -g 1000 -s /bin/bash <계정명>

STOPSIGNAL SIGRTMIN+3
CMD ["/sbin/init"]

빌드는 온라인 상태에서 1회만 하면 됩니다.

sudo docker build -t pkg-verify-base -f Dockerfile.verify .

2. Phase A — 파일 배치 + 오프라인 의존성 해결 검증

설치 스크립트가 systemd 유무와 무관하게 실행 가능하다면(즉, systemctl 실패를
무시하도록 작성돼 있다면), 서비스 기동까지 볼 필요 없는 이 단계는 일반 컨테이너
가볍게 먼저 확인할 수 있습니다.

# 네트워크 인터페이스 자체가 없어 아웃바운드가 물리적으로 불가능
sudo docker run -d --name verify-a --network none pkg-verify-base sleep infinity

# 오프라인 반입을 흉내: 빌드 트리나 apt 캐시는 옮기지 않고 설치 파일만 옮김
sudo docker cp ./my-package-offline-install.run verify-a:/root/

# 네트워크 접속을 시도하는지 지켜보며 실행
sudo docker exec verify-a /root/my-package-offline-install.run

설치 후 확인 항목 예시:

sudo docker exec verify-a test -x /usr/bin/my-binary && echo "OK: 바이너리"
sudo docker exec verify-a test -f /usr/lib/systemd/system/my-service.service && echo "OK: 유닛"
sudo docker exec verify-a test -f /etc/my-app/config.env && echo "OK: 설정 파일"

3. Phase B — 서비스 기동 검증 (systemd 컨테이너)

파일 배치까지는 문제없었어도, 실제로 서비스가 active (running) 상태까지
올라가는지는 systemd가 PID 1로 떠 있는 컨테이너에서 별도로 확인해야 합니다.

sudo docker run -d --name verify-b --network none --privileged \
  --tmpfs /run --tmpfs /run/lock \
  -v /sys/fs/cgroup:/sys/fs/cgroup:rw \
  pkg-verify-base

# systemd가 정상 기동했는지 확인 (몇 초 걸릴 수 있음)
sudo docker exec verify-b systemctl is-system-running --wait || true

호스트의 cgroup 구성에 따라 systemd가 뜨지 않으면(docker logs verify-b
cgroup 관련 에러) --cgroupns=host 플래그를 추가해 재시도하세요.

sudo docker cp ./my-package-offline-install.run verify-b:/root/
sudo docker exec verify-b /root/my-package-offline-install.run

sudo docker exec verify-b systemctl enable --now my-service.service
sudo docker exec verify-b systemctl status my-service.service --no-pager

active (running)이 아니라면 로그부터 봅니다.

sudo docker exec verify-b journalctl -u my-service.service --no-pager | tail -30

이 단계에서 자주 나오는 실패 원인은 대부분 다음 둘 중 하나입니다.

  • 유닛 파일의 User=가 실제로 존재하지 않는 계정을 가리키는 경우
    (Failed to determine user credentials: No such process)
  • 실행 파일이 오프라인 번들에 없는 라이브러리를 동적 링크하고 있는 경우
    (error while loading shared libraries)

참고 — 애플리케이션 자체의 네트워크 통신까지 검증하려면:
--network none 컨테이너는 호스트와도 통신할 수 없으므로, 서비스가 외부
API나 서버와 실제로 데이터를 주고받는지까지 확인하려면 이 단계만 별도로
네트워크를 붙인 컨테이너로 분리해서 진행해야 합니다. 이 문서의 범위는
"설치 자체가 네트워크 없이 완결되는가"이지 "런타임 통신이 되는가"가 아닙니다.

4. 제거(remove/purge) 검증

제거는 네트워크가 필요 없는 로컬 동작이므로 --network none 상태 그대로
진행합니다.

sudo docker exec verify-b apt remove -y my-package
sudo docker exec verify-b systemctl is-active my-service.service || echo "정지됨(기대값)"
sudo docker exec verify-b test -f /etc/my-app/config.env && echo "OK: 설정 보존됨(remove는 삭제 안 함)"

sudo docker exec verify-b apt purge -y my-package
sudo docker exec verify-b test -d /etc/my-app && echo "FAIL: 삭제 안 됨" || echo "OK: purge로 삭제됨"

패키지가 만들지 않은 기존 계정이 있었다면, purge 후에도 그 계정은 남아있는 게
정상입니다(패키지 책임 범위 밖이므로).

5. 정리

sudo docker rm -f verify-a verify-b

체크리스트

확인 항목
검증 환경이 실제 대상 호스트를 흉내내는지 (개발용/설치대상 자체 의존성 제외) 베이스 이미지 구성
설치 파일만 반입해 네트워크 접속 시도 없이 설치되는지 Phase A
설치 후 파일 배치가 정상인지 Phase A
systemd 기준 서비스가 실제로 기동되는지 Phase B
remove/purge 후 상태가 기대대로인지 제거 검증

한 줄 요약

오프라인 설치 번들을 "완전하다"고 주장하려면, 개발 PC가 아니라 대상 호스트를
흉내낸 깨끗한 환경 + 물리적으로 네트워크가 없는 컨테이너
에서 설치 파일 하나만으로
끝까지 돌아가는지 눈으로 확인하는 게 가장 확실합니다. 감으로 의존성 목록을
추정하지 말고, 실기기에서 apt-get install --simulate로 실측한 값을 근거로
삼으세요.

답글 남기기