Skip to content

필사 모드: 폐쇄망에서 모듈과 버전 고정 — RHEL 8·9·10이 서로 다른 이야기를 합니다

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

들어가며 — 저장소에 있는데 없다고 나옵니다

폐쇄망 반입 작업 중 가장 당황스러운 순간은 이겁니다.

# 분명히 rpm 파일이 저장소 디렉터리에 있다
ls /srv/repo/rhel9-appstream/ | grep nodejs
# nodejs-18.20.4-1.module+el9.4.0+21212+d9e3c1f2.x86_64.rpm

# 그런데 dnf는 없다고 한다
dnf list available nodejs
# Error: No matching Packages to list

파일은 있습니다. 메타데이터도 만들었습니다. 그런데 없다고 합니다.

이건 저장소가 깨진 게 아니라 모듈 필터링이 동작한 결과입니다. 그리고 이 현상은 RHEL 버전에 따라 나타나기도 하고 안 나타나기도 합니다. 이번 편은 그 구조와 해법입니다.

먼저 버전을 확인하세요

이 시리즈에서 버전 차이가 가장 큰 편이 이번 편입니다. 공식 문서 기준으로 정리하면 이렇습니다.

항목RHEL 8RHEL 9RHEL 10
모듈 제공AppStream의 핵심 방식9.1부터 수명주기가 짧은 추가 버전으로공식 DNF 문서에 모듈 장이 없음
기본 스트림있음. 문서: 기본 스트림은 메이저 릴리스 내내 바뀌지 않음문서: "no default module streams are predefined"해당 없음
스트림 지정 없이 설치기본 스트림이 자동 활성화됨스트림을 지정해야 함해당 없음
스트림 전환distro-syncmodule resetmodule enabledistro-syncdnf module switch-to 한 줄해당 없음
AppStream 제공 형식RPM, 모듈, Software CollectionsRPM, 모듈, Software Collections문서 기준 RPM과 Software Collections

RHEL 10 공식 "Managing software with the DNF tool" 문서에는 모듈 관련 장이 존재하지 않고, 부록의 명령 목록에도 dnf module 계열이 없습니다. RHEL 10만 쓰는 환경이라면 이번 편의 전반부는 건너뛰고 "버전을 고정합니다"부터 읽으시면 됩니다.

왜 모듈러 패키지가 사라지는가

RHEL 9 문서는 모듈 의존성을 "an additional layer on top of regular RPM dependencies"이며 "behave similarly to hypothetical dependencies between repositories"라고 설명합니다. 이 추가 계층이 동작하려면 모듈 메타데이터가 있어야 합니다.

그리고 dnf modulesync 문서가 결정적인 문장을 담고 있습니다. DNF는 모듈러 패키지를 설치할 때 모듈러 메타데이터를 요구한다는 것입니다.

이제 앞의 현상이 설명됩니다.

  1. dnf reposync로 AppStream을 받았는데 --download-metadata를 붙이지 않았습니다
  2. 패키지 파일은 다 왔지만 모듈 메타데이터는 오지 않았습니다
  3. createrepo_c는 rpm 파일에서 일반 메타데이터만 만듭니다. 모듈 데이터는 rpm 안에 없으므로 만들 수 없습니다
  4. 안쪽 dnf는 모듈러 RPM을 보고도 어느 스트림 소속인지 모릅니다
  5. 필터링 결과 그 패키지들이 조회되지 않습니다

해법은 세 갈래이고, 위쪽이 더 정석입니다.

방법 1 — 받을 때 모듈 메타데이터를 함께 가져온다

# --download-metadata를 반드시 붙인다
sudo dnf reposync \
  --repoid=rhel-9-for-x86_64-appstream-rpms \
  --download-path=/var/tmp/airgap-bundle/repos \
  --download-metadata \
  --gpgcheck \
  --arch=x86_64 --arch=noarch

--download-metadata는 저장소 메타데이터를 그대로 받아 즉시 저장소로 쓸 수 있게 합니다. 이 경우 반입 후 createrepo_c를 다시 돌릴 필요가 없고, 돌리면 오히려 모듈 데이터를 잃을 수 있으니 돌리지 마세요.

방법 2 — dnf modulesync로 모듈 포함 저장소를 만든다

특정 모듈만 필요할 때 가장 깔끔합니다. 문서의 설명은 이 명령이 "downloads packages from modules according to provided arguments and creates a repository with modular data in working directory"라는 것입니다. 다운로드와 저장소 생성을 한 번에 합니다.

# nodejs 모듈 전체를 받아 모듈 데이터를 포함한 저장소를 만든다
dnf modulesync nodejs

# 특정 스트림과 프로필, 의존성까지 지정한 위치에
dnf --destdir=/var/tmp/airgap-bundle/nodejs modulesync nodejs:18/minimal --resolve

# 최신 모듈만
dnf modulesync --newest-only nodejs

--resolve는 "Resolve and download needed dependencies", -n, --newest-only는 "Download only packages from the newest modules"로 문서화되어 있습니다.

문서가 소개하는 두 단계 방식도 폐쇄망에 잘 맞습니다. 연결망 장비에서 dnf module install로 실제 필요한 것을 확정한 뒤 dnf modulesync --destdir=...를 실행하면, 그 시스템이 실제로 요구한 패키지만 담긴 저장소가 나옵니다.

방법 3 — module_hotfixes로 필터링을 끈다

마지막 수단입니다. dnf 설정 문서는 module_hotfixes를 "Set this to True to disable module RPM filtering and make all RPMs from the repository available. The default is False"로 정의합니다.

# /etc/yum.repos.d/airgap-appstream.repo
[airgap-appstream]
name=RHEL 9 AppStream (airgap, no modular metadata)
baseurl=file:///srv/repo/rhel9-appstream
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
module_hotfixes=1
metadata_expire=-1

패키지는 즉시 보입니다. 대신 모듈이 제공하던 보장이 사라집니다. 서로 다른 스트림의 패키지가 섞여 설치될 수 있고, 그 조합은 Red Hat이 시험한 조합이 아닙니다. 방법 1이나 2가 가능하다면 그쪽을 쓰세요.

빈 installroot에서 모듈이 통째로 빠지는 함정

2편에서 본 --installroot 기법에는 모듈과 관련된 함정이 하나 더 있습니다. dnf 공식 문서의 경고를 그대로 옮깁니다.

On a modular system you may also want to use the
--setopt=module_platform_id=<module_platform_name:stream> command-line option
when creating the installroot, otherwise the module_platform_id value will be
taken from the /etc/os-release file within the installroot (and thus it will be
empty at the time of creation, the modular dependency could be unsatisfied and
modules content could be excluded).

빈 루트에는 릴리스 정보 파일이 없으니 플랫폼 ID가 비고, 그 결과 모듈 콘텐츠가 제외된 채로 다운로드가 성공합니다. 에러 없이 조용히 빠지기 때문에 가장 발견하기 어려운 종류의 사고입니다.

# RHEL 9 기준 — 플랫폼 ID까지 명시한다
sudo dnf download \
  --installroot=/var/tmp/airgap-root \
  --releasever=9.4 \
  --setopt=module_platform_id=platform:el9 \
  --setopt=reposdir=/etc/yum.repos.d \
  --resolve --alldeps \
  --destdir=/var/tmp/airgap-bundle/rpms \
  nodejs

플랫폼 ID 값은 시스템에서 확인할 수 있습니다.

# 현재 시스템의 플랫폼 ID를 그대로 읽는다
grep PLATFORM_ID /etc/os-release
# PLATFORM_ID="platform:el9"

버전을 고정합니다

여기부터는 모듈이 없는 RHEL 10에도 그대로 해당됩니다.

폐쇄망에서 재현이 깨지는 가장 흔한 원인은 마이너 버전이 움직이는 것입니다. 릴리스 버전 변수는 기본적으로 rpmdb에서 유도되므로, 그냥 두면 시스템이 올라갈 때 따라 올라갑니다. 고정하는 방법은 세 가지이고 적용 범위가 다릅니다.

# 1. 한 번의 명령에만 적용 — 반입용 다운로드에 쓴다
dnf download --releasever=9.4 --resolve --alldeps httpd

# 2. 시스템 전체에 고정 (서브스크립션 사용 시) — Red Hat 문서 표기
sudo subscription-manager release --set 9.4

# 현재 고정값 확인
subscription-manager release

# 3. dnf 변수 파일로 고정 — 서브스크립션을 쓰지 않는 환경
echo "9.4" | sudo tee /etc/dnf/vars/releasever

3번은 Red Hat의 업그레이드 문서가 RHUI 환경에서 릴리스 버전을 수동 지정하는 방법으로 안내하는 방식입니다. 폐쇄망 사내 미러처럼 서브스크립션 도구가 개입하지 않는 구성에서도 같은 원리로 동작합니다. CentOS Stream이나 Rocky, Alma를 쓴다면 대개 이 방식을 씁니다.

패키지 단위로 묶을 때는 versionlock을 씁니다.

sudo dnf install python3-dnf-plugin-versionlock

# 현재 설치된 버전으로 잠근다
sudo dnf versionlock add httpd

# 잠금 목록 확인
dnf versionlock list

# 글롭으로 직접 지정 (NEVRA 해석 없이 그대로 사용)
sudo dnf versionlock add --raw 'httpd-2.4.57-*'

# 특정 항목 해제
sudo dnf versionlock delete httpd

# 전부 해제
sudo dnf versionlock clear

문서 기준으로 add는 "Add a versionlock for all available packages matching the spec", --raw는 "Do not resolve <package-name-spec> to NEVRAs to find specific version to lock to"이고, 설정 파일은 /etc/dnf/plugins/versionlock.conf입니다.

주의할 점이 있습니다. Red Hat의 업그레이드 문서는 메이저 업그레이드 전에 dnf versionlock clear로 잠금을 풀라고 안내합니다. 잠금이 남아 있으면 의존성 해결이 실패하기 때문입니다. 폐쇄망에서도 대규모 반입 전에 잠금 상태를 확인하는 절차를 넣어 두는 편이 좋습니다.

상태를 기록하고 재현합니다

번들이 같아도 설치 순서가 다르면 결과가 달라질 수 있습니다. 첫 서버에서 확정한 상태를 기록해 두고 나머지 서버에 그대로 적용하는 편이 안전합니다.

# 1. 활성화된 모듈 스트림과 설치된 프로필 (RHEL 8 / 9)
dnf module list --installed > state-modules.txt

# 2. 설치된 패키지 전체를 NEVRA로 기록
rpm -qa --queryformat '%{NAME}\t%|EPOCH?{%{EPOCH}}:{0}|\t%{VERSION}\t%{RELEASE}\t%{ARCH}\n' \
  | sort > state-packages.tsv

# 3. 사용자가 명시적으로 설치한 것만 (의존성 제외)
dnf history userinstalled > state-userinstalled.txt

# 4. 트랜잭션 이력
dnf history list > state-history.txt

3번이 특히 유용합니다. dnf history userinstalled는 사용자가 직접 설치한 패키지만 보여 주므로, 그 목록만 다른 서버에서 설치하면 의존성은 알아서 따라옵니다. 전체 패키지 목록을 그대로 밀어 넣는 것보다 훨씬 안전합니다.

RHEL 8에서 스트림을 바꿔야 한다면 문서의 순서를 그대로 따르세요. 문서는 먼저 yum distro-sync가 "Nothing to do. Complete!"로 끝나는지 확인하고, 그 다음 yum module resetyum module enable을 거쳐 다시 yum distro-sync를 실행하라고 안내합니다. 중간에 의존성 충돌이 나면 --allowerasing이 필요하며, Perl 모듈은 기본 RHEL 8 설치의 일부 패키지가 Perl 5.26에 의존하기 때문에 항상 이 옵션이 필요하다고 명시합니다.

RHEL 9라면 한 줄입니다.

# RHEL 9 전용 — 스트림 전환을 한 명령으로
sudo dnf module switch-to nodejs:20

서브스크립션 없이 Red Hat 콘텐츠를 재배포하는 것은 계약 위반일 수 있으니 조직의 라이선스 조건을 먼저 확인하세요. 모듈 메타데이터 역시 Red Hat 콘텐츠의 일부입니다.

마치며 — 버전을 먼저 확인하고, 메타데이터를 잃지 마세요

이번 편의 요점은 셋입니다.

모듈러 RPM은 메타데이터 없이는 보이지 않습니다. reposync--download-metadata를 붙이거나 dnf modulesync를 쓰세요. module_hotfixes는 마지막 수단입니다.

빈 installroot에서는 플랫폼 ID를 명시하세요. 빠뜨리면 모듈 콘텐츠가 에러 없이 제외됩니다.

릴리스 버전을 고정하세요. 이것 하나로 재현 실패의 상당수가 사라지고, 모듈이 없는 RHEL 10에서도 똑같이 유효합니다.

명령과 옵션은 2026-08-15에 공식 문서에서 확인했습니다. RHEL 버전에 따라 다르므로 사용 중인 버전의 문서로 다시 확인하세요.

직접 해보기

이전 / 다음 편

참고 자료

현재 단락 (1/108)

폐쇄망 반입 작업 중 가장 당황스러운 순간은 이겁니다.

작성 글자: 0원문 글자: 7,377작성 단락: 0/108