개요

  • awx-ee는 AWX에서 Job을 실행할 때 ansible-playbook이 동작하는 실행 환경(Execution Environment, EE) 컨테이너 이미지다.
  • 컨테이너가 생성되는 방식은 AWX 설치 환경에 따라 다르다. awx-operator(쿠버네티스)로 설치했다면 Job마다 Pod가 생성되고, 도커 기반 환경이라면 AWX 내부에서 podman 컨테이너로 실행된다 (여기에서는 AWX 도커 환경에서 운영 중인 경우를 기반으로 설명한다)
  • 기본 awx-ee 이미지에는 ansible-core와 자주 쓰는 컬렉션 일부만 들어 있다. 필요한 컬렉션이 없거나 다른 버전이 필요하면 직접 추가해야 한다.
  • 컬렉션은 AWX 프로젝트의 collections/requirements.yml로 실행 시점에 설치할 수도 있다. 하지만 아래 경우에는 EE 이미지를 직접 만드는 편이 낫다.
    • 모듈이 요구하는 Python 라이브러리 또는 시스템 패키지가 필요할 때 (Job 실행 시점에서는 설치 불가능)
    • 컬렉션 및 라이브러리 버전을 고정해서 실행 결과를 재현할 수 있도록 유지하고 싶을 때
  • 이 글에서는 커스텀 EE 이미지를 정의하고 GitHub Actions로 빌드해 GHCR에 올린 뒤 AWX에서 사용하는 과정까지 정리한다.


전체 흐름과 프로젝트 구조

전체 흐름

  1. execution-environment.yml에 이미지에 넣을 의존성을 정의한다.
  2. 로컬에서 tox -e docker로 빌드해 정상 동작을 확인한다.
  3. develop 브랜치를 master로 PR 병합한 뒤 GitHub Release를 실행한다.
  4. GitHub Actions가 이미지를 빌드해 GHCR(GitHub Container Registry)에 push한다.
  5. AWX에 GHCR 인증 정보와 실행 환경을 등록하고, 템플릿에서 해당 실행 환경을 선택한다.

프로젝트 구조

jslim-awx-ee/
├── .github/workflows/image-build.yml   # 이미지 빌드, push 워크플로
├── execution-environment.yml           # EE 정의 파일 (이미지 설계도)
├── requirements.txt                    # 빌드 도구(ansible-builder) 의존성
├── tox.ini                             # 빌드 명령 정의
└── docs/                               # 프로젝트 문서


ansible-builder

  • podman 또는 docker로 Ansible 실행 환경 컨테이너 이미지를 만들어 주는 도구다.
  • 설치: pip install ansible-builder (이 프로젝트는 3.1.1로 고정. 2026-10-02 기준 최신 버전)
  • 실행 예: ansible-builder build -t jslim-awx-ee:local -f execution-environment.yml -v 3
    • -t를 생략하면 ansible-execution-env:latest 태그가 붙는다.
    • 빌드 과정에서 현재 디렉터리에 context/ 빌드 컨텍스트(Containerfile 등)가 생성된다. 커밋 대상이 아니므로 .gitignore에 등록한다.
  • 이미지는 아래 4단계를 거쳐 만들어진다.
    1. base: base 이미지 위에 Python 인터프리터, ansible-core, ansible-runner를 설치한다.
    2. galaxy: ansible-galaxy로 컬렉션을 설치한다.
    3. builder: Python 패키지를 설치/빌드한다. 컴파일용 시스템 패키지(compile)는 여기서만 사용한다.
    4. final: 실행에 필요한 시스템 패키지, Python 패키지, 컬렉션만 모아 최종 이미지를 만든다.


execution-environment.yml

예제 파일

---
version: 3
images:
  base_image:
    name: quay.io/centos/centos:stream9
dependencies:
  python_interpreter:
    package_system: python3.13
    python_path: /usr/bin/python3.13
  ansible_core:
    package_pip: ansible-core==2.20.1
  ansible_runner:
    package_pip: ansible-runner==2.4.2
  galaxy: |
    ---
    collections:
      - name: ansible.posix
        version: "2.1.0"
      - name: ansible.utils
        version: "6.0.0"
      - name: community.general
        version: "12.2.0"
      - name: ansible.windows
        version: "3.3.0"
      - name: ansible.netcommon
        version: "8.2.0"
      - name: fortinet.fortios
        version: "2.4.2"
      - name: community.crypto
        version: "3.1.0"
      - name: awx.awx
        version: "24.6.1"
      - name: community.proxmox
        version: "1.5.0"
      - name: amazon.aws
        version: "10.1.2"
      - name: community.aws
        version: "10.0.0"
      - name: google.cloud
        version: "1.10.2"
      - name: community.docker
        version: "5.0.4"
      - name: containers.podman
        version: "1.18.0"
      - name: kubernetes.core
        version: "6.2.0"
      - name: community.mysql
        version: "4.0.1"
      - name: community.postgresql
        version: "4.2.0"
  system: |
    git-core [platform:rpm]
    python3.13-devel [platform:rpm compile]
    libcurl-devel [platform:rpm compile]
    git-lfs [platform:rpm]
    sshpass [platform:rpm]
    rsync [platform:rpm]
    unzip [platform:rpm]
    podman-remote [platform:rpm]
    cmake [platform:rpm compile]
    gcc [platform:rpm compile]
    gcc-c++ [platform:rpm compile]
    make [platform:rpm compile]
    openssl-devel [platform:rpm compile]
    libpq-devel [platform:rpm]
  python: |
    ansible-sign==0.1.4
    boto3>=1.34.0
    botocore>=1.34.0
    cryptography>=42.0.0
    docker>=7.1.0
    google-auth
    jsonpatch
    jsonschema
    kubernetes>=28.1.0
    ncclient
    netaddr
    paramiko
    proxmoxer==2.2.0
    psycopg[binary]>=3.2.3
    PyMySQL>=1.1.1
    python-daemon
    pywinrm>=0.4.3
    PyYAML
    pyyaml
    receptorctl
    requests
    six
    toml
    xmltodict
additional_build_steps:
  # [추가] 베이스 이미지 시작 직후, Python 설치 전에 EPEL 저장소를 먼저 활성화합니다.
  prepend_base:
    - RUN /usr/bin/dnf install -y epel-release
    - RUN /usr/bin/crb enable
  append_base:
    - RUN $PYCMD -m pip install -U pip
  append_final:
    - COPY --from=quay.io/ansible/receptor:v1.4.1 /usr/bin/receptor /usr/bin/receptor
    - RUN mkdir -p /var/run/receptor
    - RUN git lfs install --system
    # SymLink `python` -> `python3.13`
    - RUN alternatives --install /usr/bin/python python /usr/bin/python3.13 313

version

  • EE 정의 파일의 스키마 버전이다 (ansible-builder 버전 아님)
  • 3을 쓰려면 ansible-builder 3.x가 필요하다.

images

  • base_image: 실행 환경의 부모 이미지를 지정한다.
  • upstream awx-ee를 참고해 CentOS Stream 9를 사용한다.

dependencies

  • 최종 이미지에 설치할 의존성을 정의한다.

python_interpreter

  • package_system: dnf로 설치할 Python 시스템 패키지 이름
  • python_path: 이미지에서 사용할 Python 인터프리터 경로
  • ansible-core 2.20은 컨트롤 노드 Python 3.12 ~ 3.14를 지원하므로, 호환성을 맞추기 위해 Python 3.13을 사용한다.

ansible_core, ansible_runner

  • package_pip: pip에 그대로 전달할 값이다.
    • ansible-core: 최신 버전 설치 (버전 지정이 없다면)
    • ansible-core==2.20.1: 특정 버전 설치 (권장)
      • ansible community 13.x 기준으로 셋팅
  • 이전에는 ansible-core 2.14를 사용했지만 2.20으로 올렸다. 올리기 전에 관리 대상 노드의 Python 버전을 반드시 확인해야 한다.
ansible-core 컨트롤 노드 Python 대상 노드 Python
2.14 3.9 ~ 3.11 2.7, 3.5 ~ 3.11
2.20 3.12 ~ 3.14 3.9 ~ 3.14

⚠️ CentOS 7(Python 2.7/3.6)이나 기본 Python이 3.6인 RHEL 8 호스트는 2.20 이미지로 관리할 수 없다. 이런 호스트에는 Python 3.9 이상을 설치하거나, 구버전 ansible-core로 만든 EE를 따로 두고 템플릿별로 골라 쓴다.

galaxy

  • 설치할 Ansible 컬렉션 목록이며, 표준 requirements.yml 형식을 그대로 사용한다.
  • 버전은 따옴표로 감싼 문자열로 적는다. version: 2.10처럼 쓰면 YAML이 숫자 2.1로 해석할 수 있다.
컬렉션 용도
ansible.posix ACL, mount, sysctl, firewalld 등 POSIX 시스템 관리
ansible.utils 데이터 검증 및 변환 필터 (IP 주소 필터, XML 변환, 스키마 검증 등)
community.general 특정 컬렉션에 속하지 않는 다양한 범용 모듈
ansible.windows Windows 기본 관리
ansible.netcommon 네트워크 장비용 공통 연결 플러그인 (network_cli, netconf, httpapi)
fortinet.fortios FortiGate(FortiOS) 방화벽 관리
community.crypto 인증서, 키 관리 (OpenSSL, ACME)
awx.awx 플레이북에서 AWX API 제어
community.proxmox Proxmox VE 관리
amazon.aws AWS 리소스 관리 (핵심 모듈)
community.aws AWS 리소스 관리 (확장 모듈)
google.cloud GCP 리소스 관리
community.docker Docker 관리
containers.podman Podman 관리
kubernetes.core 쿠버네티스, OpenShift 리소스 관리
community.mysql MySQL, MariaDB 관리
community.postgresql PostgreSQL 관리

컬렉션 버전을 고를 때는 아래를 확인한다.

  • 지원하는 ansible-core 버전: Galaxy 페이지의 “Requires Ansible” 항목, 또는 컬렉션의 meta/runtime.yml에 있는 requires_ansible 값을 확인한다.
  • 빌드한 뒤에는 ansible-galaxy collection list로 실제 설치된 버전을 확인한다 (아래 ‘빌드 결과 검증’ 참고).

python

  • 컬렉션 모듈이 내부에서 사용하는 Python 라이브러리 목록이며, requirements.txt 형식을 그대로 사용한다.
  • 모듈 공식 문서의 “Requirements” 항목에 필요한 라이브러리가 적혀 있다. 컬렉션만 넣고 라이브러리를 빠뜨리면 실행 시점에 ModuleNotFoundError가 발생한다.
패키지 사용처
boto3, botocore amazon.aws, community.aws
google-auth, requests google.cloud 등 HTTP API를 쓰는 컬렉션
kubernetes, jsonpatch kubernetes.core
docker community.docker
proxmoxer community.proxmox
PyMySQL community.mysql
psycopg[binary] community.postgresql
pywinrm ansible.windows (WinRM 연결)
ncclient ansible.netcommon (NETCONF)
paramiko SSH 연결 (paramiko 연결 플러그인, 네트워크 장비)
cryptography community.crypto
netaddr ansible.utils의 IP 주소 필터
jsonschema ansible.utils의 스키마 검증
xmltodict ansible.utils의 XML 변환 필터
ansible-sign AWX 프로젝트 콘텐츠 서명 검증
receptorctl receptor 제어 도구
python-daemon, six, toml, PyYAML upstream awx-ee 구성을 따른 공통 라이브러리

system

  • 설치할 시스템 패키지 목록이며 bindep 형식을 따른다.
  • [platform:rpm]: RHEL·CentOS 계열(rpm)에서만 설치한다.
  • compile: Python 패키지를 빌드할 때만 필요한 패키지다. builder 단계에서만 쓰이고 최종 이미지에는 들어가지 않는다. gcc, make 같은 컴파일 도구에 붙여 두면 이미지 크기를 줄일 수 있다.
구분 패키지 용도
실행용 git-core, git-lfs Git 저장소에 있는 프로젝트·컬렉션 사용
실행용 sshpass SSH 비밀번호 인증
실행용 rsync, unzip synchronize, unarchive 모듈
실행용 podman-remote 원격 Podman 서비스에 접속하는 클라이언트
실행용 libpq-devel PostgreSQL 클라이언트 라이브러리(libpq)
빌드용 (compile) python3.13-devel, libcurl-devel, openssl-devel, gcc, gcc-c++, make, cmake Python 패키지의 C 확장 컴파일

additional_build_steps

빌드 단계 앞뒤에 Containerfile 명령을 직접 넣을 때 쓴다. 단계마다 prepend_*(시작 직후)와 append_*(끝나기 직전)가 있다.

“ansible-builder” 단락에서 이미지 생성 과정을 참고하면 아래의 표를 이해하기 쉽다.

키 실행 시점 이 프로젝트에서 하는 일
prepend_base base 단계 시작 직후, Python 설치 전 EPEL 설치와 CRB 저장소 활성화
append_base base 단계 마지막 pip 업그레이드
append_final final 단계 마지막 receptor 복사, git-lfs 설정, python 심볼릭 링크
  • $PYCMD: python_path로 지정한 인터프리터(/usr/bin/python3.13)를 가리킨다.
  • EPEL과 CRB
    • crb 명령은 EL9용 epel-release 패키지에 들어 있으므로 반드시 epel-release를 먼저 설치한다.
    • system 목록이 아니라 prepend_base에 둔 이유는 아래 ‘트러블슈팅’에서 설명한다.
  • receptor
    • awx-ee 기본 버전과 마찬가지로 receptor 바이너리를 이미지에 복사한다.
    • awx-operator 환경에서는 awx-ee 이미지가 컨트롤 플레인에서 receptor를 실행하는 용도로도 쓰여 기본 이미지에 포함되어 있다. 작업 실행 전용 EE라면 필수는 아니지만, 원본과 구성을 맞추기 위해 유지한다.
    • devel 태그는 변경이 잦기 때문에 빌드 결과를 재현할 수 없으므로 v1.4.1로 고정했다.
  • git lfs install --system: 시스템 전역에 Git LFS를 설정한다.
  • alternatives ...: python 명령이 python3.13을 가리키도록 한다.


로컬 빌드와 검증

GitHub Actions에 올리기 전에 로컬에서 먼저 빌드해 본다. CI에서도 같은 tox 명령을 쓰므로 로컬에서 성공하면 CI에서도 같은 결과를 기대할 수 있다.

requirements.txt

ansible-builder==3.1.1
  • 이미지 안에 설치되는 패키지가 아니라, 이미지를 만드는 도구의 의존성이다.
  • 이미지 안에 들어갈 Python 패키지는 execution-environment.yml의 python에 적는다.

tox.ini

[tox]
minversion = 1.6
skipsdist = True

[testenv]
basepython = python3
install_command = pip install {opts} {packages}
deps = -r{toxinidir}/requirements.txt

[testenv:docker]
passenv = HOME,DOCKER_BUILDKIT
allowlist_externals =
  /bin/bash
  docker
commands =
  ansible-builder build -v3 {posargs} --container-runtime=docker
  • tox는 가상 환경을 만들고 그 안에서 정해진 명령을 실행한다. 여기서는 빌드 명령을 로컬과 CI에서 똑같이 맞추는 용도로 쓴다.
  • skipsdist = True: 이 프로젝트는 Python 패키지가 아니므로 패키징 단계를 건너뛴다.
  • deps: requirements.txt의 ansible-builder를 가상 환경에 설치한다.
  • [testenv:docker]: tox -e docker로 실행할 환경이다.
    • passenv: 가상 환경 안으로 전달할 환경 변수 (BuildKit 사용 여부 등)
    • allowlist_externals: 가상 환경 밖의 명령(docker) 실행 허용
    • {posargs}: tox -e docker -- <인자>에서 -- 뒤의 인자가 이 자리에 들어간다.
    • --container-runtime=docker: ansible-builder 기본값은 podman이므로 docker를 명시한다.

빌드

pip install tox
tox -e docker -- --tag=jslim-awx-ee:local

빌드 결과 검증

# ansible-core, Python 버전 확인
docker run --rm jslim-awx-ee:local ansible --version

# 설치된 컬렉션과 버전 확인
docker run --rm jslim-awx-ee:local ansible-galaxy collection list

# 설치된 Python 패키지 확인
docker run --rm jslim-awx-ee:local python -m pip list


GHCR 토큰 준비

워크플로가 GHCR에 이미지를 push하려면 토큰이 필요하다. 워크플로를 처음 실행하기 전에 준비한다.

토큰 생성

  • GitHub 프로필 → Settings → Developer settings → Personal access tokens → Tokens (classic)
  • GitHub Packages는 classic 토큰만 지원한다. fine-grained 토큰으로는 인증할 수 없다.
  • 권한(scope)은 write:packages를 선택한다(이미지 pull 권한 포함).
  • 만료일을 지정했다면 만료 전에 갱신해야 한다. 만료되면 워크플로의 로그인 단계가 실패한다.

Secrets 등록

  • 레포지토리 → Settings → Security and quality → Secrets and variables → Actions → New repository secret
  • 이름: GHCR_TOKEN, 값: 앞에서 만든 토큰

(선택) GITHUB_TOKEN으로 대신하기 - secrets 등록 대체

같은 레포지토리에 연결된 패키지라면 개인 토큰 없이 워크플로가 자동으로 발급하는 GITHUB_TOKEN으로도 push할 수 있다. 토큰 만료를 관리할 필요가 없어진다.

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3.3.0
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}


GitHub Actions 워크플로

image-build.yml

name: Docker Image CI with GHCR

on:
  # 1. 정식 배포: GitHub UI에서 Release를 생성(Publish)했을 때 실행
  release:
    types:
      - published

  workflow_dispatch:
    inputs:
      test_tag:
        description: 'Test Tag (e.g., test-v1)'
        required: false
        default: 'devel'

env:
  DOCKER_IMAGE: ghcr.io/${{ github.repository }}

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Install Python
        uses: actions/setup-python@v2
        with:
          python-version: "3.9"

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install tox

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3.3.0
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GHCR_TOKEN }}

      - name: Set Tag Name
        id: tag
        run: |
          if [[ "${{ github.event_name }}" == "release" ]]; then
            # Release 이벤트인 경우 태그 이름 사용 (예: v1.0.0)
            echo "TAG_NAME=${{ github.ref_name }}" >> $GITHUB_ENV
            echo "IS_RELEASE=true" >> $GITHUB_ENV
          else
            # 수동 실행인 경우 입력받은 태그 사용
            echo "TAG_NAME=${{ inputs.test_tag }}" >> $GITHUB_ENV
            echo "IS_RELEASE=false" >> $GITHUB_ENV
          fi

      - name: Build and Push image
        env:
          DOCKER_BUILDKIT: 1
        run: |
          FULL_IMAGE_NAME="${{ env.DOCKER_IMAGE }}:${{ env.TAG_NAME }}"

          # 1. 해당 태그(버전)로 빌드
          tox -e docker -- --tag=$FULL_IMAGE_NAME

          # 2. 해당 태그(버전) 푸시
          docker push $FULL_IMAGE_NAME

트리거 (on)

  • release: GitHub에서 Release를 발행했을 때 (정식 배포). 태그는 Release 태그 이름 (예: 1.0.0)을 사용
  • workflow_dispatch: Actions 탭에서 직접 실행했을 때 (테스트). 태그는 입력한 test_tag (기본값 devel)을 사용
  • 브랜치 push로는 빌드하지 않는다. 커밋할 때마다 이미지를 만들지 않고, 정식 이미지 태그는 Release로만 만들도록 통제하기 위해서다.

env

  • DOCKER_IMAGE: 태그를 뺀 이미지 이름이다. 태그는 빌드 단계에서 붙인다.
  • github.repository는 소유자/레포지토리 형식이므로 이미지 이름은 ghcr.io/susoterran/jslim-awx-ee가 된다.

⚠️ 이미지 이름에는 대문자를 쓸 수 없다. 사용자명이나 레포지토리 이름에 대문자가 있으면 소문자로 바꿔야 한다.

runs-on

steps

1. Checkout repository

  • 워크플로를 실행시킨 ref의 소스를 가져온다. 기본 브랜치를 가져오는 것이 아니다.
    • release 이벤트: Release 태그가 가리키는 커밋
    • workflow_dispatch: 실행할 때 고른 브랜치

2. Install Python

  • 러너에 Python 3.9를 설치하고 PATH에 추가한다.
  • 이 Python은 tox와 ansible-builder를 실행하는 용도일 뿐이다. 이미지 안의 Python(3.13)과는 관계없다.

3. Install dependencies

  • pip를 최신화하고 tox를 설치한다. ansible-builder는 tox가 가상 환경에 설치한다.

4. Login to GitHub Container Registry

  • GHCR에 로그인한다.
  • github.actor: 워크플로를 실행한 사용자 (github 컨텍스트)
  • secrets.GHCR_TOKEN: 앞에서 등록한 토큰

5. Set Tag Name

  • 이벤트 종류에 따라 이미지 태그를 정해 GITHUB_ENV에 TAG_NAME으로 저장한다. 이후 단계에서 env.TAG_NAME으로 읽는다.
  • release 이벤트의 github.ref_name은 Release 태그 이름이다.

6. Build and Push image

  • DOCKER_BUILDKIT=1: BuildKit으로 빌드한다(tox의 passenv로 전달된다).
  • tox -e docker -- --tag=...: 로컬 빌드와 같은 명령에 이미지 태그만 넘긴다.
  • 빌드한 이미지를 GHCR에 push한다.

이미지 배포

정식 배포 (Release)

  1. develop 브랜치를 master로 PR 병합한다.
  2. 레포지토리 메인 화면의 Releases → Draft a new release
  3. Choose a tag에 새 버전(예: 1.0.0)을 입력하고, Target은 master를 선택한다.
  4. 제목과 변경 내용을 작성한다.
  5. Publish release를 누르면 Actions 탭에서 워크플로가 실행된다.
  6. 빌드가 성공하면 레포지토리 메인 화면 오른쪽 Packages에 해당 태그의 이미지가 나타난다.

⚠️ Release 태그 이름이 그대로 이미지 태그가 된다. v1.0.0으로 만들면 이미지 태그도 v1.0.0이다.

테스트 빌드 (수동 실행)

  1. Actions 탭 → Docker Image CI with GHCR 선택
  2. Run workflow → 빌드할 브랜치 선택 (devel)
  3. Run workflow를 눌러 실행한다.


AWX에서 실행 환경 사용하기

인증 정보 생성

GHCR에 처음 올린 패키지는 기본적으로 private이므로, AWX가 이미지를 받으려면 인증 정보가 필요하다.

  • 리소스 → 인증 정보 → 추가
  • 인증 정보 유형: 컨테이너 레지스트리
  • 인증 URL(Authentication URL): ghcr.io
  • 사용자 이름(Username): GitHub 사용자명
  • 비밀번호 또는 토큰(Password or Token): read:packages 권한의 classic 토큰
    • 빌드용 토큰(write:packages)과 따로 만들어 최소 권한으로 쓰는 것을 권장한다.
  • SSL 확인 체크


실행 환경 생성

  • 관리 → 실행 환경 → 추가
  • 이미지: 이미지 주소와 태그를 입력한다. 예: ghcr.io/susoterran/jslim-awx-ee:1.0.0
  • 레지스트리 인증 정보: 앞에서 만든 인증 정보를 선택한다.
  • Pull: 이미지를 언제 받아올지 정한다.
    • devel처럼 같은 이름으로 계속 덮어쓰는 태그는 “항상”을, 1.0.0처럼 고정된 버전 태그는 “존재하지 않는 경우에만”을 고르면 불필요한 pull을 줄일 수 있다.


  • 이미지 주소는 GitHub 레포지토리의 Packages 화면에서 확인할 수 있다.


템플릿에 실행 환경 지정

  • 리소스 → 템플릿 → 추가(또는 편집) → 실행 환경에서 앞서 만든 실행 환경을 선택한다.
  • 실행 환경은 여러 개 등록할 수 있다. 앞에서 말한 구형 호스트용 EE처럼 템플릿마다 다른 EE를 고를 수 있다.


트러블슈팅

EPEL은 prepend_base에서 활성화한다

  • 처음에는 system 목록에 epel-release를 넣었고, 목록 맨 위로 옮겨 보기도 했지만 해결되지 않았다.
  • system 목록은 순서가 의미가 없다. bindep 목록 전체가 한 번의 dnf 명령으로 설치되므로, 같은 명령 안에서 저장소를 먼저 켜고 그 저장소의 패키지를 설치할 수 없다.
  • 또 python_interpreter의 Python 패키지는 base 단계에서 설치되는데, system 패키지는 그보다 뒤 단계에서 설치된다.
  • 그래서 base 단계가 시작되자마자 실행되는 prepend_base에서 EPEL을 설치하고 CRB를 활성화했다.
additional_build_steps:
  prepend_base:
    - RUN /usr/bin/dnf install -y epel-release
    - RUN /usr/bin/crb enable

Python 버전을 올리면 binary 패키지 버전도 점검한다

  • psycopg[binary]처럼 미리 빌드된 바이너리(wheel)로 설치하는 패키지는 이미지의 Python 버전용 wheel이 있어야 설치된다.
  • 처음 고정했던 psycopg[binary]==3.1.8은 Python 3.13보다 먼저 나온 버전이라 3.13용 wheel이 없다. 그래서 >=3.2.3으로 조건을 바꿨다.


참고자료