26M25d

Young-Kyoo Kim·2026년 3월 26일

Airgapped(폐쇄망) 환경에서 AWX와 Ansible을 운영하는 것은 일반적인 환경보다 훨씬 까다롭지만, 이미 Proxy Nexus를 통해 Docker 이미지와 Python 패키지(PyPI)를 가져올 수 있다면 큰 고비는 넘기신 셈입니다.

다만, 실제 운영 시 '연쇄적인 의존성' 때문에 발생하는 문제들이 많습니다. 고려해야 할 핵심 요소 5가지를 정리해 드립니다.


1. Execution Environment (EE) 빌드 전략 (가장 중요)

폐쇄망에서는 ansible-builder가 인터넷에서 직접 베이스 이미지를 받거나 pip install을 할 수 없습니다.

  • Nexus 활용: execution-environment.yml에서 모든 소스를 Nexus 주소로 명시해야 합니다.
    • Base Image: quay.io 대신 내부-nexus-주소/ansible-runner:latest 사용.
    • Python: pip.conf 설정을 통해 내부 Nexus 서버를 바라보게 하거나, 빌드 시 --build-arg로 index-url을 넘겨야 합니다.
  • Binary 파일: mc, kubectl, cilium 등을 curl로 받는 로직은 실패합니다. 이 파일들을 미리 다운로드하여 Bitbucket 레포지토리에 포함하거나, 내부 파일 서버(Nexus Raw Repository 등)에 올려두고 그 주소를 호출해야 합니다.

2. Ansible Collection 관리

앤서블 모듈인 'Collection'은 보통 ansible-galaxy를 통해 인터넷(galaxy.ansible.com)에서 받습니다. 폐쇄망에서는 이것이 불가능합니다.

  • 해결책:
    1. 개발망(인터넷 가능)에서 ansible-galaxy collection download 명령으로 타르볼(.tar.gz) 파일을 받습니다.
    2. 이 파일을 Bitbucket의 특정 폴더(예: collections/ansible_collections/)에 넣거나, EE 빌드 시 _build/requirements.yml에 포함시켜 함께 굽습니다.

3. AWX Project Sync (Bitbucket 연동)

AWX 서버 자체가 Bitbucket과 통신이 가능해야 합니다.

  • SSL 인증서: 만약 내부 Bitbucket이나 Nexus가 사설 CA 인증서를 사용한다면, AWX(정확히는 프로젝트 동기화를 담당하는 컨테이너)에 해당 Root CA 인증서를 심어줘야 SSL certificate problem 에러가 나지 않습니다.

4. 대상 노드(RHEL 10)의 패키지 매니저 (dnf/yum)

플레이북에서 dnf install fio 같은 명령을 내릴 때, 각 노드들이 내부 레포지토리(Nexus 또는 Satellite)를 바라보고 있어야 합니다.

  • 사전 확인: 모든 노드에 baseos, appstream 외에도 필요한 도구가 포함된 레포지토리가 설정되어 있는지 확인하세요. 만약 안 되어 있다면, 플레이북의 Pre-check 단계에서 yum_repository 모듈을 사용해 내부 레포 설정을 먼저 밀어넣는 작업을 추가해야 합니다.

5. 시간 동기화 (NTP)

Cloud Native 환경(K8s, MinIO)에서 노드 간 시간 차이는 치명적입니다.

  • 요소: 외부 NTP 서버에 접속할 수 없으므로, 내부망의 표준 시간 서버(L3 스위치나 별도 NTP 서버)를 반드시 지정하고 모든 노드가 동기화되어 있는지 점검하는 로직을 플레이북 초기 단계에 넣으시는 것을 강력히 추천합니다.

💡 요약된 EE 빌드 파일 예시 (폐쇄망 버전)

version: 3
images:
  base_image:
    name: <내부-nexus-주소>/ansible/ansible-runner:latest

dependencies:
  python:
    - kubernetes
    # 빌드 시 pip index-url 설정을 내부 nexus로 지정해야 함
  ansible_network:
    collections:
      - /path/to/internal/collections/kubernetes-core-3.0.0.tar.gz

additional_build_steps:
  append_base:
    # 인터넷 curl 대신 내부 파일서버 이용
    - RUN curl -k https://<내부-nexus-raw>/mc -o /usr/local/bin/mc && chmod +x /usr/local/bin/mc
    - RUN curl -k https://<내부-nexus-raw>/kubectl -o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl

이처럼 "모든 외부 유입 경로를 내부 주소로 치환"하는 작업이 폐쇄망 설정의 핵심입니다. 특히 1번 과정에서 pipdnf가 내부 Nexus를 제대로 바라보지 못해 빌드가 실패하는 경우가 많으니, 이 부분의 Proxy 설정을 먼저 검증해 보시는 것이 좋습니다.

==

폐쇄망(Air-gapped) 환경에서 Nexus를 Binary Repository(Raw)Collection Proxy로 활용하여 자동화 환경을 구축하는 것은 매우 현명한 선택입니다. 인터넷이 가능한 환경에서 미리 파일을 준비하고, 이를 내부 Nexus에 업로드하여 AWX가 가져가게 하는 절차를 정리해 드립니다.


1. 전용 바이너리(mc, kubectl, cilium) 준비 및 업로드

인터넷이 되는 환경에서 파일을 다운로드한 뒤, 내부 Nexus의 Raw Repository에 올리는 과정입니다.

① 파일 다운로드 (인터넷 가능 환경)

# MinIO Client
curl -LO https://dl.min.io/client/mc/release/linux-amd64/mc

# kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"

# Cilium CLI
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
curl -L --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-amd64.tar.gz

② Nexus 업로드

Nexus UI에서 raw-hosted 레지스트리를 만든 후, 위 파일들을 업로드합니다. (또는 curl로 업로드 가능)

  • 경로 예시: https://<nexus-url>/repository/raw-hosted/bin/mc

③ EE 빌드 시 적용 (execution-environment.yml)

이제 curl 주소를 내부 Nexus 주소로 변경합니다. -k 옵션은 사설 인증서일 경우 SSL 검증을 스킵하기 위함입니다.

additional_build_steps:
  append_base:
    - RUN curl -kL https://<nexus-url>/repository/raw-hosted/bin/mc -o /usr/local/bin/mc && chmod +x /usr/local/bin/mc
    - RUN curl -kL https://<nexus-url>/repository/raw-hosted/bin/kubectl -o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl

2. Ansible Collection 미리 받기 (Offline Bundle)

Collection은 단순 파일이 아니라 의존성이 얽혀 있어 ansible-galaxy 명령어로 묶어서 받아야 합니다.

① requirements.yml 작성 (인터넷 가능 환경)

필요한 컬렉션 목록을 적습니다.

---
collections:
  - name: kubernetes.core
    version: 3.0.0
  - name: community.general
  - name: amazon.aws

② 컬렉션 다운로드

# 'my_collections' 폴더에 타르볼(.tar.gz) 형태로 저장됩니다.
ansible-galaxy collection download -r requirements.yml -p ./my_collections

③ 내부망으로 이동 및 활용

my_collections 폴더를 통째로 내부 Bitbucket 레포지토리에 포함시키거나, Nexus에 올려둡니다.

EE 빌드 시 포함하는 방법:
execution-environment.yml에서 다운로드한 파일 경로를 지정합니다.

dependencies:
  ansible_network:
    collections:
      - ./my_collections/kubernetes-core-3.0.0.tar.gz
      - ./my_collections/community-general-8.0.0.tar.gz

3. Python 패키지(PyPI) Nexus 프록시 설정

Python 패키지는 개별로 받기보다 Nexus의 PyPI Proxy 기능을 쓰는 것이 훨씬 편합니다.

  1. Nexus 설정: pypi (proxy) 레포지토리를 생성하고 Remote URL을 https://pypi.org/로 설정합니다.
  2. EE 빌드 시 설정: 빌드 과정에서 pip가 Nexus를 바라보게 합니다.
additional_build_steps:
  prepend_base:
    # pip가 내부 Nexus를 바라보도록 설정 파일 생성
    - RUN mkdir -p /etc/pip.conf && echo -e "[global]\nindex-url = https://<nexus-url>/repository/pypi-proxy/simple\ntrusted-host = <nexus-ip/domain>" > /etc/pip.conf

4. 최종 체크리스트 (폐쇄망 성공 요건)

  • SSL 인증서 문제: 내부 Nexus가 HTTPS를 사용하고 사설 인증서라면, 빌드 시 -k 옵션을 쓰거나 RUN update-ca-trust 명령으로 내부 Root CA를 이미지에 심어줘야 합니다.
  • Base Image: quay.io/ansible/ansible-runner 이미지도 미리 받아서 내부 Docker Hosted Registry에 업로드한 뒤, base_image: name: 부분에 내부 주소를 적어주세요.

이렇게 준비해두면, 인터넷이 끊긴 환경에서도 "우리만의 표준 샌드박스(EE)"를 언제든 다시 빌드하고 AWX에서 안정적으로 점검 플레이북을 돌릴 수 있습니다.

혹시 Nexus에 이미지를 올리는 Docker 명령어(Tag, Push)가 익숙하지 않으시다면 그 부분도 정리해 드릴까요? (폐쇄망에서는 이 단계에서 실수가 가장 많이 발생합니다.)

===

폐쇄망 환경에서는 docker 대신 RHEL의 표준 컨테이너 도구인 Podman을 사용하는 경우가 많습니다. Podman은 Docker와 명령어가 거의 동일하지만, 네임스페이스 관리인증서 처리 방식에서 약간의 차이가 있습니다.

인터넷망에서 받은 이미지를 내부 Nexus(Docker Hosted Registry)에 올리는 과정을 단계별로 정리해 드립니다.


1. 외부 이미지를 로컬로 가져오기 (인터넷 가능 환경)

먼저 필요한 베이스 이미지를 인터넷에서 pull 받습니다.

# Ansible Runner 베이스 이미지 다운로드
podman pull quay.io/ansible/ansible-runner:latest

2. 이미지에 내부 Nexus 태그(Tag) 달기

이미지 이름에 내부 Nexus의 도메인과 포트를 포함시켜야 Podman이 어디로 전송할지 인식합니다.

# 형식: podman tag [기존이미지] [Nexus주소]/[이미지이름]:[태그]
podman tag quay.io/ansible/ansible-runner:latest \
<nexus-url>:<port>/ansible/ansible-runner:latest
  • <nexus-url>:<port>: 예시) 192.168.1.50:5000 또는 nexus.internal:5000

3. Nexus 로그인 (Login)

이미지를 올리기 전, Nexus의 Docker Hosted 레포지토리에 권한 인증을 해야 합니다.

# Nexus 계정으로 로그인
podman login <nexus-url>:<port> -u <admin-id> -p <admin-password>
  • 주의: 만약 Nexus가 사설 SSL 인증서를 사용한다면 --tls-verify=false 옵션을 추가하거나, 시스템에 Root CA를 등록해야 로그인에 성공합니다.

4. 이미지 푸시 (Push)

태그를 단 이미지를 실제 내부 Nexus 저장소로 전송합니다.

# 형식: podman push [태그된이미지이름]
podman push <nexus-url>:<port>/ansible/ansible-runner:latest

5. (중요) 폐쇄망 환경의 특이점과 해결책

폐쇄망 운영 시 가장 자주 발생하는 SSL 인증서 오류를 해결하는 2가지 방법입니다.

방법 A: 비보안 레지스트리(Insecure Registry) 설정

인증서 검증을 무시하고 싶을 때 사용합니다. (테스트 환경 권장)

  • 파일 경로: /etc/containers/registries.conf
[[registry]]
location = "<nexus-url>:<port>"
insecure = true  # 이 설정을 true로 변경

방법 B: 사설 인증서(Self-signed) 신뢰 등록

실무 운영 환경에서 권장하는 방식입니다. Nexus의 인증서(.crt) 파일을 아래 경로에 넣으면 Podman이 자동으로 인식합니다.

# 1. 인증서 보관 폴더 생성 (레지스트리 주소와 동일한 이름)
mkdir -p /etc/containers/certs.d/<nexus-url>:<port>

# 2. Nexus 서버의 인증서 복사
cp nexus_ca.crt /etc/containers/certs.d/<nexus-url>:<port>/ca.crt

6. AWX에서 이 이미지 사용하기

이제 AWX UI에서 Execution Environments를 등록할 때, 아까 tag 명령에 썼던 주소(nexus.internal:5000/ansible/ansible-runner:latest)를 입력하면 됩니다.

다음으로 무엇을 도와드릴까요?

  • "Nexus에 올린 이미지를 AWX에서 가져올 때 ImagePullBackOff 에러가 나요."
  • "Podman으로 빌드한 이미지를 AWX의 EE로 등록하는 화면 설정을 보여주세요."

===

0개의 댓글