Przejdź do treści
Automatyzacja i DevOps W redakcji · ~16 min czytania

Ansible w venv z własnym Pythonem: budowa krok po kroku i dwie wersje obok siebie

Budujemy Ansible, które nie zależy od Pythona z systemu: z własnym interpreterem, przypiętymi wersjami i kolekcjami. Obok stawiamy drugą wersję dla starszych hostów i przełączamy terminal jednym aliasem. Polecenia sprawdziliśmy na Debianie 12 i 13 oraz na Ubuntu 22.04, 24.04 i 26.04.

Autor: Zespół LinuxLab

W redakcji. Artykuł jest wersją roboczą, nie jest jeszcze wersją finalną i może ulec zmianie.

Stan na 7 listopada 2026 r.: ansible-core 2.21.6 i 2.19.15, CPython 3.13.18 z wydania 20261026 projektu python-build-standalone. Budowę uruchomiliśmy w izolowanych maszynach labu, bez sudo. Nazwa old-01 jest przykładowa, trzeba ją zastąpić nazwą własnego hosta.

W tym artykule budujemy na kontrolerze dwa środowiska Ansible: main dla aktualnych hostów i legacy dla hostów z Pythonem 3.8. Każde ma własny interpreter, ansible-core i kolekcje, a budowa nie wymaga roota. Tekst jest dla administratorów, którzy znają podstawy Ansible i pracują z hostami w różnym wieku. Po przejściu instrukcji środowisko można odtworzyć z trzech małych plików, wersję przełączać osobno w każdym terminalu, a pakiet ansible usunąć z systemu. Trzeba jednak pamiętać, że interpreter spoza repozytoriów dystrybucji nie dostaje poprawek przez apt, więc aktualizacje są po naszej stronie.

Dlaczego Ansible z systemu przestaje wystarczać?

Wersję ansible-core z pakietu wyznacza wydanie systemu, a nie potrzeby hostów, którymi zarządzamy. Na pięciu maszynach naszego labu apt proponuje pięć różnych wersji i żadna nie należy do linii 2.21 ani nowszej:

SystemPython systemowyansible-core z apt
Debian 123.11.22.14.18
Debian 133.13.52.19.11
Ubuntu 22.043.10.122.12.0
Ubuntu 24.043.12.32.16.3
Ubuntu 26.043.14.42.20.1

Na Ubuntu 22.04 nie pomoże też pip ani pipx. Systemowy Python 3.10 jest za stary dla ansible-core 2.19 i nowszych wersji, bo wymagają one na kontrolerze co najmniej Pythona 3.11.

Drugi problem leży po stronie hostów. Każda linia ansible-core ma dwa zakresy Pythona: jeden dla kontrolera, czyli maszyny, z której uruchamiamy playbooki, i drugi dla hosta zarządzanego, na którym wykonują się moduły. Zakresy podajemy za dokumentacją Ansible:

ansible-corePython kontroleraPython hostaKoniec wsparcia
2.193.11-3.133.8-3.13listopad 2026
2.203.12-3.143.9-3.14maj 2027
2.213.12-3.143.9-3.14listopad 2027
2.223.13-3.153.9-3.15maj 2028

Od linii 2.20 host musi mieć co najmniej Pythona 3.9, a serwer z Ubuntu 20.04 ma 3.8.10. Aktualny rdzeń kończy tam pracę już na module ping:

old-01 | FAILED! => {
    "changed": false,
    "msg": "Ansible requires Python 3.9 or newer on the target. Current version: 3.8.10 (default, Aug 20 2026, 10:41:41) [GCC 9.4.0]"
}

Z tabeli wynika plan. Python 3.13 mieści się w zakresie kontrolera wszystkich czterech linii, więc jeden interpreter obsłuży oba środowiska. Środowisko main dostaje ansible-core 2.21.6, a legacy wersję 2.19.15 z ostatniej linii, która obsługuje hosty z Pythonem 3.8. Bierzemy najnowsze wydania poprawkowe, bo wersje z 2 listopada usuwają luki CVE-2026-89091 i CVE-2026-105929 w ansible-galaxy oraz CVE-2026-84503 w module git.

Czym ten venv różni się od zwykłego?

venv (ang. virtual environment) to katalog z osobnymi bibliotekami Pythona. Polecenie python3 -m venv nie kopiuje jednak interpretera: bin/python3 jest linkiem do /usr/bin/python3. Wersję Pythona nadal wybiera więc dystrybucja, a po aktualizacji systemu do wydania z nowszym Pythonem venv traci swoje biblioteki.

Dlatego interpreter bierzemy z projektu python-build-standalone. To gotowe, samodzielne kompilacje CPythona, z których korzysta też narzędzie uv. Paczka rozpakowana do dowolnego katalogu działa bez instalacji, a venv tworzymy właśnie z tego interpretera. Nie potrzebujemy nawet pakietu python3-venv, którego nie było na żadnej maszynie labu.

Po budowie układ katalogów wygląda tak:

~/.local/share/ansible-envs/     zbudowane środowiska, poza repozytorium
├── .cache/                      pobrana paczka z Pythonem
├── main/
│   ├── python/                  interpreter z paczki
│   ├── bin/                     python, pip i polecenia ansible*
│   ├── lib/python3.13/          ansible-core i kolekcje w site-packages
│   └── shims/                   linki wyłącznie do poleceń ansible*
└── legacy/                      to samo z ansible-core 2.19.15

~/ansible-envs/                  definicje, te pliki trzymamy w repozytorium
├── main/
│   ├── requirements.txt         dokładne wersje pakietów Pythona
│   ├── requirements.yml         dokładne wersje kolekcji
│   └── env.conf                 wersja i suma interpretera
└── legacy/

Interpreter i venv leżą w jednym katalogu, więc całe środowisko usuwa się jednym rm -rf, a system nic o nim nie wie. Definicje są osobno, bo to je trzymamy w repozytorium, a zbudowany katalog da się z nich odtworzyć.

Prześledźmy jedno polecenie. Administrator wpisuje ansible-playbook site.yml w terminalu ze środowiskiem legacy. Powłoka znajduje plik w legacy/shims, bo ten katalog stoi na początku PATH. To link do legacy/bin/ansible-playbook, a pierwsza linia tego skryptu wskazuje bezwzględną ścieżką legacy/bin/python. Python czyta plik pyvenv.cfg, używa interpretera z legacy/python i ładuje ansible-core 2.19.15 z legacy/lib/python3.13/site-packages. Ten rdzeń wysyła na host z Pythonem 3.8 moduły, które taki Python potrafi wykonać. Jeśli na początku PATH stanie main/shims, to samo polecenie pójdzie przez 2.21.6 i zakończy się błędem z poprzedniej sekcji.

Jeśli systemowy Python mieści się w tabeli i wystarcza jedna wersja Ansible, ten układ jest niepotrzebny. Wtedy lepszy jest pipx install ansible-core, opisany w artykule Ansible od podstaw. Własny interpreter ma sens, gdy Python systemu jest za stary, gdy potrzebujemy dwóch linii naraz albo gdy wersje muszą się zgadzać na kilku stacjach.

Co przygotować przed budową?

Polecenia uruchamiamy na kontrolerze jako zwykły użytkownik, bez sudo. Potrzebny jest Linux x86-64 z glibc (GNU C Library), narzędzia curl, tar i sha256sum oraz dostęp HTTPS do github.com, pypi.org i galaxy.ansible.com. Każde środowisko zajmuje około 200 MB, a pobrana paczka z Pythonem 34 MB.

Bloki poleceń aż do sekcji o prompcie wykonujemy w jednym terminalu, bo korzystają z tych samych zmiennych. Zaczynamy od ich ustawienia:

NAME=main
CORE=2.21.6
ENV_PYTHON_VERSION=3.13.18
ENV_PYTHON_BUILD=20261026
ENV_PYTHON_SHA256=df15fe60d3ff55bf9dcf25824b5572756bb8816ff93865a273d3301d7066a8b7
FILE=cpython-$ENV_PYTHON_VERSION+$ENV_PYTHON_BUILD-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz
ROOT=~/.local/share/ansible-envs
DEFS=~/ansible-envs

CORE i ENV_PYTHON_VERSION to wersje wybrane w pierwszej sekcji. ENV_PYTHON_SHA256 to suma SHA-256 paczki z pliku SHA256SUMS w wydaniu 20261026. ROOT to katalog zbudowanych środowisk, a DEFS katalog z definicjami, który warto od razu zastąpić ścieżką w swoim repozytorium. Na procesorach ARM64 nazwa pliku zawiera aarch64 zamiast x86_64, a sumę bierzemy z tego samego pliku SHA256SUMS. Tego wariantu nie testowaliśmy.

Jak zbudować pierwsze środowisko krok po kroku?

Te same kroki wykonuje skrypt ansible-env, opisany w osobnej sekcji. Ręczna budowa pokazuje, co dzieje się po kolei, i nie wymaga naszego repozytorium.

Krok 1: pobieramy interpreter i sprawdzamy sumę

mkdir -p "$ROOT/.cache"
curl -fL -o "$ROOT/.cache/$FILE" \
  "https://github.com/astral-sh/python-build-standalone/releases/download/$ENV_PYTHON_BUILD/${FILE//+/%2B}"
echo "$ENV_PYTHON_SHA256  $ROOT/.cache/$FILE" | sha256sum --check -

Znak + w nazwie pliku trzeba w adresie zapisać jako %2B, stąd zapis ${FILE//+/%2B}. Powodzenie oznacza linię zakończoną słowem OK, a w polskiej lokalizacji DOBRZE. Przy FAILED usuwamy plik i nie idziemy dalej. Suma potwierdza, że mamy ten sam plik, który sprawdziliśmy w labie. Nie zastępuje decyzji, czy ufamy autorom paczki.

Krok 2: rozpakowujemy Pythona i tworzymy venv

mkdir "$ROOT/$NAME"
tar -xzf "$ROOT/.cache/$FILE" -C "$ROOT/$NAME"
"$ROOT/$NAME/python/bin/python3" -m venv --prompt "ansible-$NAME" "$ROOT/$NAME"
"$ROOT/$NAME/bin/python" --version
readlink -f "$ROOT/$NAME/bin/python"

Oczekujemy wersji Python 3.13.18 oraz ścieżki zakończonej /main/python/bin/python3.13. Jeśli druga linia wskazuje /usr/bin, venv powstał na Pythonie systemowym. Wtedy usuwamy katalog przez rm -rf "$ROOT/$NAME" i powtarzamy ten krok.

Krok 3: instalujemy ansible-core

"$ROOT/$NAME/bin/python" -m pip install "ansible-core==$CORE"
"$ROOT/$NAME/bin/python" -m pip check
"$ROOT/$NAME/bin/ansible" --version | grep -E 'core|python version'

pip check sprawdza zgodność zainstalowanych zależności. Na końcu powinniśmy zobaczyć rdzeń i Pythona ze środowiska:

No broken requirements found.
ansible [core 2.21.6]
  python version = 3.13.18 (main, Oct 26 2026, 14:08:20) [Clang 23.1.3 ] (/home/admin/.local/share/ansible-envs/main/bin/python)

Krok 4: instalujemy kolekcje do środowiska

Domyślnie ansible-galaxy instaluje kolekcje do ~/.ansible/collections, czyli do katalogu wspólnego dla wszystkich wersji. Starszy rdzeń dostałby wtedy kolekcje dobrane dla nowszego. Dlatego instalujemy je do site-packages środowiska, które Ansible przeszukuje automatycznie, a wersje zapisujemy w pliku requirements.yml. Listę kolekcji zastępujemy tym, czego używają nasze playbooki.

mkdir -p "$DEFS/$NAME"
cat >"$DEFS/$NAME/requirements.yml" <<'EOF'
---
collections:
  - name: ansible.posix
    version: 2.2.2
  - name: community.general
    version: 13.3.0
  - name: community.library_inventory_filtering_v1
    version: 1.1.5
EOF
SITE=$("$ROOT/$NAME/bin/python" -c 'import sysconfig; print(sysconfig.get_path("purelib"))')
ANSIBLE_COLLECTIONS_PATH=$SITE "$ROOT/$NAME/bin/ansible-galaxy" \
  collection install -r "$DEFS/$NAME/requirements.yml"
ANSIBLE_COLLECTIONS_PATH=$SITE "$ROOT/$NAME/bin/ansible-galaxy" collection list

Na końcu listy powinny być te trzy kolekcje:

ansible.posix                            2.2.2
community.general                        13.3.0
community.library_inventory_filtering_v1 1.1.5

Trzecia z nich to zależność community.general. Wpisujemy ją jawnie, bo inaczej przebudowa wzięłaby jej najnowszą wersję. Ostrzeżenie o kolekcji ansible._protomatter można zignorować. To wewnętrzna kolekcja ansible-core, która nie ma pliku z numerem wersji.

Krok 5: tworzymy katalog shims

mkdir "$ROOT/$NAME/shims"
for tool in "$ROOT/$NAME"/bin/ansible*; do ln -s "../bin/${tool##*/}" "$ROOT/$NAME/shims/"; done
ls "$ROOT/$NAME/shims"

Katalog shims zawiera tylko linki do poleceń ansible*. Dzięki niemu można dodać Ansible do PATH bez podmieniania python3 i pip. ls powinno pokazać dziesięć pozycji, od ansible do ansible-vault.

Krok 6: zapisujemy dokładne wersje

"$ROOT/$NAME/bin/python" -m pip freeze >"$DEFS/$NAME/requirements.txt"
cat >"$DEFS/$NAME/env.conf" <<EOF
ENV_PYTHON_VERSION=$ENV_PYTHON_VERSION
ENV_PYTHON_BUILD=$ENV_PYTHON_BUILD
ENV_PYTHON_SHA256=$ENV_PYTHON_SHA256
EOF
cat "$DEFS/$NAME/requirements.txt" "$DEFS/$NAME/env.conf"

pip freeze zapisuje wszystkie pakiety, także zależności. W naszym przypadku plik ma dziewięć pozycji, od ansible-core==2.21.6 do resolvelib==1.2.1. Plik env.conf zapamiętuje wersję i sumę interpretera. Razem z requirements.yml to kompletna definicja środowiska i to te trzy małe pliki, a nie katalog z venv, trafiają do repozytorium. Katalog venv ma w skryptach bezwzględne ścieżki, więc nie da się go przenieść w inne miejsce ani skopiować na inną stację.

Cała budowa razem z pobieraniem zajęła w labie około minuty.

Jak dodać drugie środowisko dla starszych hostów?

Paczka z Pythonem jest już pobrana, więc na kontrolerze powtarzamy kroki od 2 do 6 z inną nazwą i wersją rdzenia. Kolekcje zostają te same:

NAME=legacy
CORE=2.19.15
mkdir -p "$ROOT/$NAME" "$DEFS/$NAME"
cp "$DEFS/main/requirements.yml" "$DEFS/main/env.conf" "$DEFS/$NAME/"
tar -xzf "$ROOT/.cache/$FILE" -C "$ROOT/$NAME"
"$ROOT/$NAME/python/bin/python3" -m venv --prompt "ansible-$NAME" "$ROOT/$NAME"
"$ROOT/$NAME/bin/python" -m pip install "ansible-core==$CORE"
"$ROOT/$NAME/bin/python" -m pip check
SITE=$("$ROOT/$NAME/bin/python" -c 'import sysconfig; print(sysconfig.get_path("purelib"))')
ANSIBLE_COLLECTIONS_PATH=$SITE "$ROOT/$NAME/bin/ansible-galaxy" \
  collection install -r "$DEFS/$NAME/requirements.yml"
mkdir "$ROOT/$NAME/shims"
for tool in "$ROOT/$NAME"/bin/ansible*; do ln -s "../bin/${tool##*/}" "$ROOT/$NAME/shims/"; done
"$ROOT/$NAME/bin/python" -m pip freeze >"$DEFS/$NAME/requirements.txt"
"$ROOT/$NAME/bin/ansible" --version | head -1

Ostatnia linia powinna pokazać ansible [core 2.19.15]. Teraz porównujemy oba rdzenie na starszym hoście. Nazwę old-01 zastępujemy własnym hostem z Pythonem 3.8, do którego działa logowanie przez SSH. Moduł ping niczego na nim nie zmienia.

"$ROOT/main/bin/ansible" -i old-01, all -m ping
"$ROOT/legacy/bin/ansible" -i old-01, all -m ping

Pierwsze polecenie kończy się komunikatem Ansible requires Python 3.9 or newer on the target, a drugie powinno zwrócić:

old-01 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Oba wyniki pochodzą z serwera z Ubuntu 20.04 i Pythonem 3.8.10, a nazwę hosta zmieniliśmy. W labie nie ma takiej maszyny, więc powtórzyliśmy test interpreterem 3.8.20 z tego samego projektu. legacy wykonał ping, setup i moduł community.general.ini_file, a main odmówił tym samym komunikatem.

Środowisko legacy jest rozwiązaniem przejściowym. Wsparcie linii 2.19 kończy się w listopadzie 2026, a nowszej linii dla Pythona 3.8 nie będzie, więc docelowo podnosimy system na starszym hoście i kasujemy to środowisko. Zakres 2.19 kończy się też na Pythonie 3.13, dlatego do hostów z Ubuntu 26.04 i Pythonem 3.14 używamy main.

Jak przełączać środowiska?

Mamy trzy sposoby, które różnią się zasięgiem:

SposóbZasięgCo się zmieniaKiedy go używamy
Pełna ścieżka do bin/ansible-playbookjedno polecenienic poza tym poleceniemcron, CI (Continuous Integration), skrypty
source .../bin/activatebieżący terminalansible*, python, pippraca z pakietami środowiska
Aliasy ans-* z promptubieżący terminal albo jedno polecenietylko ansible*codzienna praca

Dwa pierwsze sposoby działają od razu, na kontrolerze:

"$ROOT/legacy/bin/ansible" --version | head -1

source "$ROOT/legacy/bin/activate"
ansible --version | head -1
python --version
deactivate

W obu przypadkach powinniśmy zobaczyć ansible [core 2.19.15], a python --version w aktywowanym środowisku pokaże Python 3.13.18. deactivate przywraca poprzedni PATH.

Pełne activate podmienia też python3. Skrypty z #!/usr/bin/env python3, które korzystają z bibliotek systemowych, przestaną wtedy działać, bo venv ich nie widzi. Dlatego do codziennej pracy wygodniejszy jest katalog shims.

Stary katalog kolekcji może zasłonić środowisko. Bez zmiennej ANSIBLE_COLLECTIONS_PATH Ansible szuka najpierw w ~/.ansible/collections, a dopiero potem w site-packages. W teście niepełna kopia community.general w katalogu domowym sprawiła, że moduł community.general.ini_file kończył się błędem Cannot resolve, choć środowisko miało wersję 13.3.0. Przy pełnej ścieżce i activate trzeba więc ustawić tę zmienną albo przenieść stary katalog poleceniem mv ~/.ansible/collections ~/.ansible/collections.old. Aliasy ans-* ustawiają ją same.

Jak zainstalować prompt z aliasami?

Przy dwóch rdzeniach łatwo uruchomić playbook nie tą wersją, dlatego załadowane środowisko powinno być cały czas widoczne. Służy do tego prompt z katalogu devops-prompt w naszym publicznym repozytorium, na licencji Apache 2.0. Pokazuje branch gita, wersję Ansible, kod wyjścia i czas trwania polecenia, a czas uruchomienia każdego polecenia zostawia w konsoli. Wymaga basha 5.0 lub nowszego.

Instalację wykonujemy na kontrolerze. Zmienia ona tylko ~/.bashrc bieżącego użytkownika i zostawia jego kopię. Warto wcześniej przeczytać plik, który będzie ładowany do powłoki.

git clone https://github.com/sysgroup/linuxlab.git ~/linuxlab
mkdir -p ~/.config
cp ~/linuxlab/devops-prompt/devops-prompt.bash ~/.config/
cp ~/.bashrc ~/.bashrc.before-devops-prompt
printf '\n[ -r ~/.config/devops-prompt.bash ] && . ~/.config/devops-prompt.bash\n' >>~/.bashrc
exec bash

exec bash zastępuje bieżącą powłokę, więc zmienne z poprzednich sekcji znikają. Nowy terminal startuje z main, o ile takie środowisko istnieje:

╭─[admin@stacja]─[~/ansible]─[git:main *%=]─[ansible:main 2.21.6 py3.13]
╰─❯ ans
▸ 2026-11-06 21:16:07
  legacy   core 2.19.15  python 3.13.18
* main     core 2.21.6   python 3.13.18
Przełączanie: ans-legacy ans-main, jednorazowo: ans-run <środowisko> <polecenie>
╭─[admin@stacja]─[~/ansible]─[git:main *%=]─[ansible:main 2.21.6 py3.13]
╰─❯ ans-legacy
▸ 2026-11-06 21:16:09
╭─[admin@stacja]─[~/ansible]─[git:main *%=]─[ansible:legacy 2.19.15 py3.13]
╰─❯ ansible-playbook site.yml | tee run.log
▸ 2026-11-06 21:16:14
... wynik ...
✘ rc=2|0 · 3m12s · koniec 21:19:26

Linia z ▸ to chwila wciśnięcia Enter i zostaje w konsoli nad wynikiem polecenia. rc=2|0 to kody wyjścia wszystkich poleceń potoku: ansible-playbook zakończył się błędem, choć samo $? po tee wynosiłoby zero. Wersje prompt odczytuje z plików środowiska, bez uruchamiania Pythona, bo ansible --version trwa około pół sekundy.

ans-main i ans-legacy przełączają tylko bieżący terminal, więc w drugim oknie może działać inna wersja. ans-run legacy ansible-playbook site.yml uruchamia jedno polecenie bez przełączania. Alias powstaje sam dla każdego katalogu w ~/.local/share/ansible-envs, a ans pokazuje listę. Legenda znaków gita i ustawienia są w README.

Prompt wycofujemy, przywracając kopię. Następny terminal startuje już ze starym promptem:

cp ~/.bashrc.before-devops-prompt ~/.bashrc
rm ~/.config/devops-prompt.bash

Jak zbudować to samo jednym skryptem?

Skrypt ansible-env z katalogu devops-prompt wykonuje kroki z tego artykułu i przydaje się przy kolejnych stacjach. Definicje z katalogu envs mają te same wersje, a ich pliki mają ten sam format co pliki z ręcznej budowy. Polecenia uruchamiamy na kontrolerze jako zwykły użytkownik, w klonie repozytorium z poprzedniej sekcji:

cd ~/linuxlab/devops-prompt
./ansible-env build main
./ansible-env build legacy
./ansible-env check

build pobiera interpreter albo bierze go z .cache, za każdym razem sprawdza sumę i na końcu porównuje wynik z definicją. Bez żadnego środowiska na koncie cała budowa trwa około półtorej minuty. check powinien zakończyć się takim wynikiem:

legacy: Python 3.13.18, ansible [core 2.19.15]
  kolekcje zgodne z pinami: 3
main: Python 3.13.18, ansible [core 2.21.6]
  kolekcje zgodne z pinami: 3

Skrypt nie nadpisuje istniejącego środowiska, więc po ręcznej budowie trzeba użyć ./ansible-env build --force main. Stara wersja czeka wtedy obok i wraca sama, jeśli budowa się nie uda albo zostanie przerwana. Własne definicje wskazujemy zmienną ANSIBLE_ENVS_DEFS, a pliki z ręcznej budowy działają bez zmian:

ANSIBLE_ENVS_DEFS=~/ansible-envs ./ansible-env check

Dwa kolejne polecenia zastępują kroki ręczne. ./ansible-env install-shell instaluje prompt tak samo jak blok z poprzedniej sekcji. ./ansible-env link-bin tworzy linki w ~/.local/bin dla narzędzi, które nie czytają ~/.bashrc: do main bez sufiksu, a do pozostałych środowisk z sufiksem, na przykład ansible-playbook-legacy. Pliku spoza środowisk nie nadpisuje. Opis wszystkich poleceń jest w README.

Jak aktualizować i odtwarzać środowisko?

Nowe wydanie poprawkowe ansible-core

Na kontrolerze aktualizujemy pakiet i od razu zapisujemy nowe wersje. Zapis ~=2.21.0 trzyma aktualizację w linii 2.21:

ROOT=~/.local/share/ansible-envs
DEFS=~/ansible-envs
NAME=main
"$ROOT/$NAME/bin/python" -m pip install -U 'ansible-core~=2.21.0'
"$ROOT/$NAME/bin/python" -m pip check
"$ROOT/$NAME/bin/python" -m pip freeze >"$DEFS/$NAME/requirements.txt"

Potem uruchamiamy rzeczywisty playbook z --check na hoście testowym i dopiero po tym teście zapisujemy plik w repozytorium. Cofnięcie to przebudowa z poprzedniej wersji requirements.txt.

Odtworzenie środowiska z definicji

Te same polecenia odtwarzają środowisko po awarii i zmieniają wersję Pythona. Przy nowym interpreterze najpierw wpisujemy nowe wartości do env.conf. Potem na kontrolerze wczytujemy definicję:

ROOT=~/.local/share/ansible-envs
DEFS=~/ansible-envs
NAME=main
. "$DEFS/$NAME/env.conf"
FILE=cpython-$ENV_PYTHON_VERSION+$ENV_PYTHON_BUILD-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz

Jeśli paczki nie ma jeszcze w .cache, bo jest nowa albo pracujemy na nowej stacji, pobieramy ją blokiem z kroku 1. Stare środowisko przenosimy na bok, bo venv trzeba zbudować w docelowej ścieżce. Na nowej stacji, gdzie mamy tylko pliki z repozytorium, pomijamy polecenie mv.

mv "$ROOT/$NAME" "$ROOT/$NAME.previous"
mkdir "$ROOT/$NAME"
tar -xzf "$ROOT/.cache/$FILE" -C "$ROOT/$NAME"
"$ROOT/$NAME/python/bin/python3" -m venv --prompt "ansible-$NAME" "$ROOT/$NAME"
"$ROOT/$NAME/bin/python" -m pip install --no-deps -r "$DEFS/$NAME/requirements.txt"
"$ROOT/$NAME/bin/python" -m pip check
SITE=$("$ROOT/$NAME/bin/python" -c 'import sysconfig; print(sysconfig.get_path("purelib"))')
ANSIBLE_COLLECTIONS_PATH=$SITE "$ROOT/$NAME/bin/ansible-galaxy" \
  collection install -r "$DEFS/$NAME/requirements.yml"
mkdir "$ROOT/$NAME/shims"
for tool in "$ROOT/$NAME"/bin/ansible*; do ln -s "../bin/${tool##*/}" "$ROOT/$NAME/shims/"; done
"$ROOT/$NAME/bin/ansible" --version | head -1

--no-deps sprawia, że pip instaluje dokładnie to, co jest w pliku, bez szukania nowszych zależności. Ostatnia linia powinna pokazać tę samą wersję rdzenia co przed przebudową. Po udanym teście kasujemy kopię przez rm -rf "$ROOT/$NAME.previous". Gdy coś pójdzie nie tak, wracamy do poprzedniego stanu:

rm -rf "$ROOT/$NAME"
mv "$ROOT/$NAME.previous" "$ROOT/$NAME"
"$ROOT/$NAME/bin/ansible" --version | head -1

Przeniesione środowisko działa po powrocie, bo ścieżki w jego skryptach znów się zgadzają. Skrypt wykonuje tę samą przebudowę i ten sam powrót jednym poleceniem ./ansible-env build --force main.

Nowa linia ansible-core obok starej

ansible-core 2.22.0 ukazało się 2 listopada i wymaga na kontrolerze Pythona 3.13. Systemowy Python z Ubuntu 24.04 już go nie uruchomi, a interpreter ze środowiska tak. Pierwszego wydania nowej linii nie wprowadzamy od razu do main. Ustawiamy zmienne z sekcji przygotowania i powtarzamy blok z sekcji o legacy, zmieniając na początku wartości na NAME=next i CORE=2.22.0. W nowym terminalu pojawi się alias ans-next. Gdy testy przejdą, nowa wersja trafia do main, a next kasujemy.

Jak usunąć pakiet ansible z systemu?

Pakiet usuwamy dopiero wtedy, gdy środowisko obsłużyło już rzeczywisty playbook. Narzędzia, które nie czytają ~/.bashrc, takie jak cron, CI czy rozszerzenia edytora, nie znają aliasów, więc najpierw sprawdzamy, czy nie wołają ansible z PATH. Najprościej podać im pełną ścieżkę, na przykład w crontabie:

0 5 * * * /home/admin/.local/share/ansible-envs/main/bin/ansible-playbook /home/admin/ansible/site.yml

Polecenia poniżej dotyczą Debiana i Ubuntu. Na kontrolerze sprawdzamy, co jest zainstalowane:

apt list --installed 2>/dev/null | grep -i ansible
pipx list 2>/dev/null | grep -i ansible

Następnie usuwamy to, co znaleźliśmy. Pierwsze polecenie dotyczy pakietu z apt, drugie instalacji przez pipx:

sudo apt remove ansible ansible-core
pipx uninstall ansible-core

Narzędzia, którym nie da się podać pełnej ścieżki, dostają linki do main w ~/.local/bin. Ten katalog musi być w ich PATH.

mkdir -p ~/.local/bin
ln -s ~/.local/share/ansible-envs/main/shims/ansible* ~/.local/bin/
type -a ansible

Na liście z type -a nie powinno być /usr/bin/ansible. Cofnięcie to rm ~/.local/bin/ansible* i sudo apt install ansible. Środowiska w katalogu domowym od tego pakietu nie zależą.

Jakie ograniczenia ma ten układ?

  • Interpreter nie dostaje poprawek z apt. Poprawki bezpieczeństwa Pythona wnosimy sami przez przebudowę, dlatego trzeba obserwować wydania python-build-standalone.
  • Plik requirements.txt przypina wersje, ale nie sumy pakietów. Sumą sprawdzamy tylko paczkę z Pythonem.
  • Środowisko jest przywiązane do ścieżki i architektury. Przenosimy definicje, a nie katalog.
  • Testowaliśmy Debiana i Ubuntu na x86-64. Innych dystrybucji i ARM64 nie sprawdzaliśmy.
  • Terminal VS Code gubi kody potoku. Prompt pokazuje tam tylko kod ostatniego polecenia.

Co sprawdzić przed przejściem na nowe środowisko?

  1. pip check nie zgłasza błędów, a ansible --version pokazuje oczekiwany rdzeń i Pythona.
  2. Pliki requirements.txt, requirements.yml i env.conf są w repozytorium.
  3. Rzeczywisty playbook przeszedł z --check na hoście testowym.
  4. Starszy host odpowiada na ping ze środowiska legacy.
  5. Cron, CI i skrypty nie wołają ansible z PATH albo mają linki w ~/.local/bin.
  6. Katalog ~/.ansible/collections nie zasłania kolekcji ze środowiska.
  7. Wiadomo, kto i kiedy aktualizuje interpreter.

Źródła

Administracja serwerami Linux i DevOps

Usługi administracji serwerami i DevOps prowadzimy pod marką LinuxAdmin. LinuxLab pozostaje bazą wiedzy z poradnikami i wynikami testów.