在 Rocky Linux 10、RHEL 和 Ubuntu 上安装与升级 Ansible

安装 Ansible 后得到什么版本,更多取决于控制节点使用的 Python,而不是输入的安装命令。在默认配置的 Rocky Linux 10 或 Ubuntu 24.04 上,pipx install ansible-core 能无报错完成,却安装了前一条发布分支。文中当前的 ansible-core 不支持在 Python 3.12 上运行,因此 pip 会自动选择仍兼容该解释器的最新版本;如果不检查版本字符串,很容易忽略这一点。

本指南先比较各安装方法实际得到的版本,再介绍 Rocky Linux 10 的 pipx 安装流程:AlmaLinux 10 相同,RHEL 10 则提供不依赖 EPEL 的变体。随后介绍 AppStream 软件包,以及 Ubuntu 上的 apt、PPA 和 uv 选项,并配置 SSH 密钥、ansible.cfg、主机清单、临时命令和包含 Vault 密文的首个剧本。文中的实验于 2026 年 11 月在家庭实验室的三台虚拟机上完成,Rocky Linux 10 控制节点使用 ansible-core 2.22.0;下面的输出均来自该实验环境。

不同安装方法得到的 ansible-core 版本

表中只有两种方法安装了文中最新的 ansible-core,而且都要求控制节点使用 Python 3.13 或更高版本。其他方法得到的版本落后一条或多条发布分支,只要这是有意选择,就没有问题。标注“本次实验”的各行均在 Rocky Linux 10.2 和 Ubuntu 24.04.4 上测试;来自其他指南的两行则单独标明出处。

方法 控制节点 Python 实际安装结果 依据
pipx install --python python3.14 ansible-core(Rocky 10) 3.14.8(AppStream) ansible-core 2.22.0 本次实验
uv tool install --python 3.14 ansible-core(Ubuntu 24.04) 3.14.10(由 uv 下载) ansible-core 2.22.0 本次实验
pipx install ansible-core(Rocky 10 或 Ubuntu 24.04) 3.12(系统解释器) ansible-core 2.21.6 本次实验
pipx install --python python3.14 --include-deps ansible(Rocky 10) 3.14.8 ansible 14.5.0,包含 ansible-core 2.21.6 本次实验
dnf install ansible-core(Rocky 10 AppStream) 3.12.15 ansible-core 2.16.16,不包含集合 本次实验
dnf install ansible(Rocky 10,已启用 EPEL) 不适用 No match for argument(找不到匹配软件包) 本次实验
apt install ansible(Ubuntu 24.04) 3.12.3 ansible 9.2.0,包含 ansible-core 2.16.3 本次实验
ppa:ansible/ansible(Ubuntu 24.04) 3.12.3 ansible 14.5.0,包含 ansible-core 2.21.5 本次实验检查的候选软件包版本
apt install ansible(Ubuntu 26.04) 系统解释器 ansible-core 2.20.1 Ubuntu 26.04 指南
apt install ansible(Debian 13) 系统解释器 ansible 12.0.0,包含 ansible-core 2.19.11 Debian 13 指南,2026 年 10 月

有两行结果可能出乎意料。完整的 ansible 社区软件包仍锁定前一条 ansible-core 分支,因为实验时该套件的下一主版本在 PyPI 上仍处于 alpha 阶段。因此,此时“安装完整套件”和“安装最新引擎”是两种选择。Rocky Linux 10 的 AppStream 确实提供可用的 ansible-core,只是落后六条发布分支,也不包含集合。如果需要最新引擎和少量集合,可以通过 pipx 安装 ansible-core,再用 ansible-galaxy 添加集合,第 2 步采用的就是这种方式。

实验使用 Proxmox 上的三台虚拟机。下面的名称和地址会出现在后续命令与输出中:

  • homelab-ctl:Rocky Linux 10.2 控制节点,10.0.1.10。
  • web01:Rocky Linux 10.2 被管理节点,SELinux 为 enforcing 模式,10.0.1.21。
  • nas01:Ubuntu 24.04.4 LTS,被管理节点,同时用于 Python 3.12 控制节点测试,10.0.1.22。
  • 每台主机都有非 root 用户 kibet,并配置免密码 sudo。

被管理节点只需 SSH 和 Python 3.9 或更高版本,当前常见发行版已经满足要求,因此不必为了 Ansible 本身在 web01 或 nas01 上安装额外组件。控制节点只向外发起到 SSH 22 端口的连接,也不需要为它开放额外防火墙端口。

第 1 步:安装前检查控制节点的 Python

文中当前的 ansible-core 要求控制节点使用 Python 3.13 或更高版本。PyPI 标明 Requires-Python >=3.13,ansible-core 变更日志也明确写出:“不再支持控制节点上的 Python 3.12。”Rocky Linux 10、RHEL 10 和 Ubuntu 24.04 的默认 python3 都是 Python 3.12,因此这会影响常见控制节点发行版。被管理节点要求不同,模块仍可在 Python 3.9 或更高版本上运行。

首先检查控制节点的 python3 是什么版本。homelab-ctl 使用 Rocky 默认解释器:

python3 --version

结果为 Python 3.12.15,低于要求。为展示其影响,实验使用 pipx 对系统 Python 进行安装,pipx 安装方法见第 2 步。无需重复这个测试;如果执行了,请先用 pipx uninstall ansible-core 删除虚拟环境,再进入第 2 步:

pipx install ansible-core

应阅读输出最后一行,而非只看上面的“done!”。pip 自动解析到了仍然支持 Python 3.12 的最新版本:

creating virtual environment...
creating shared libraries...
upgrading shared libraries...
WARNING: Skipping setuptools as it is not installed.
installing ansible-core...
done! ✨ 🌟 ✨
  installed package ansible-core 2.21.6, installed using Python 3.12.15

显式要求新分支,可以把没有提示的旧版本安装变成明确错误,这是快速确认控制节点解释器问题的方法:

pipx install --force 'ansible-core>=2.22'

pip 列出了新分支中所有被跳过的构建版本及原因。

Fatal error from pip prevented installation. Full pip output in file:
    /home/kibet/.local/state/pipx/log/cmd_2026-11-03_04.23.19_pip_errors.log

Some possibly relevant errors from pip install:
    ERROR: Ignored the following yanked versions: 2.14.0rc1.post0
    ERROR: Ignored the following versions that require a different python version: 2.22.0 Requires-Python >=3.13; 2.22.0b1 Requires-Python >=3.13; 2.22.0b2 Requires-Python >=3.13; 2.22.0rc1 Requires-Python >=3.13; 2.22.0rc2 Requires-Python >=3.13
    ERROR: Could not find a version that satisfies the requirement ansible-core>=2.22 (from versions: 0.0.1a1, 2.11.0b1, ... , 2.21.6rc1, 2.21.6)
    ERROR: No matching distribution found for ansible-core>=2.22

Ubuntu 主机行为相同。其 apt 提供的 pipx 1.4.3 在 Python 3.12.3 上安装了同一旧版核心;显式固定新分支时,也报出相同的 Requires-Python >=3.13 错误。下面的终端截图来自 Rocky 测试:

Python 3.12 下 pipx 安装旧版核心,显式要求 2.22 时提示 Python 版本不兼容

如果剧本尚未适配第 9 步描述的变化,继续使用前一条分支是合理选择。如果需要当前版本,就要在控制节点上准备更新的解释器。下一步会保留 dnf 等系统工具依赖的系统 python3,为 Ansible 单独提供新版本。

第 2 步:通过 pipx 在 Rocky Linux 10 上安装 Ansible

Rocky Linux 10 的 AppStream 将 Python 3.14 作为独立的 python3.14 软件包提供,pipx 则来自 EPEL。安装这两个包后,/usr/bin/python3 仍为 3.12,dnf 正常使用系统解释器,Ansible 使用基于 3.14 的独立虚拟环境。还应考虑:AppStream 中额外 Python 版本的支持周期短于系统 3.12,因此在 RHEL 10 生命周期后期,可能需要基于更新版本重建虚拟环境。启用 EPEL 并安装两个软件包:

sudo dnf install -y epel-release
sudo dnf install -y python3.14 pipx

接着让 pipx 使用 3.14 解释器。不提供 --python 时,它会使用系统 3.12,重复第 1 步的结果。如果 pipx 已经安装旧版 ansible-core,会打印“already seems to be installed. Not modifying existing installation”,然后正常退出,旧版仍保持不变。因此应先执行 pipx uninstall ansible-core,或使用第 9 步的 pipx reinstall:

pipx install --python python3.14 ansible-core

这次,汇总行显示了目标版本。

creating virtual environment...
installing ansible-core...
done! ✨ 🌟 ✨
  installed package ansible-core 2.22.0, installed using Python 3.14.8

pipx 将可执行文件放入 ~/.local/bin。Rocky 默认的 ~/.bashrc 已将该目录加入 PATH,实验中的 pipx ensurepath 也确认了这一点,因此打开新 shell 即可。继续之前,检查是否真的安装了新核心。

ansible --version

重点核对两行:ansible [core ...] 是引擎版本;python version 应指向使用 3.14 的 pipx 虚拟环境,而非 /usr/bin/python3:

ansible [core 2.22.0]
  config file = None
  configured module search path = ['/home/kibet/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/kibet/.local/share/pipx/venvs/ansible-core/lib64/python3.14/site-packages/ansible
  ansible collection location = /home/kibet/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/kibet/.local/bin/ansible
  python version = 3.14.8 (main, Oct  1 2026, 00:00:00) [GCC 14.3.1 20251022 (Red Hat 14.3.1-4)] (/home/kibet/.local/share/pipx/venvs/ansible-core/bin/python)
  jinja version = 3.1.6
  pyyaml version = 6.0.3 (with libyaml v0.2.5)

下面是 homelab-ctl 上同一次操作的终端截图:

Rocky Linux 10 上通过 Python 3.14 与 pipx 安装 ansible-core 2.22.0

RHEL 10 的 Red Hat 仓库没有 epel-release 软件包,因此上面的第一条命令在那里会失败。可以按照 RHEL 的方式启用 EPEL,即 CodeReady Builder 加 EPEL release RPM,EPEL on RHEL 10 指南介绍了该方法;也可以跳过 pipx,使用只依赖 AppStream 的普通虚拟环境。下面这个变体是在 Rocky 主机上测试的,并未在有订阅的 RHEL 主机上测试;它同样在 Python 3.14.8 上安装了当前核心:

sudo dnf install -y python3.14
python3.14 -m venv ~/ansible-venv
~/ansible-venv/bin/pip install ansible-core

采用虚拟环境方式时,要么直接调用 ~/ansible-venv/bin/ansible,要么将 ~/ansible-venv/bin 加入 PATH,因为没有工具替你把可执行文件链接到 ~/.local/bin。

只添加实际使用的集合

ansible-core 仅包含 ansible.builtin 模块;community.general 或 ansible.posix 下的模块需要从 Galaxy 安装。实验更倾向这种方式而非完整套件:控制节点执行 pipx install --python python3.14 --include-deps ansible 耗时 25 秒,占用 476 MB,安装约 90 个集合,却仍使用前一条核心分支。可以只安装大多数服务器剧本需要的两个集合:

ansible-galaxy collection install community.general ansible.posix

Galaxy 自动解析依赖,因此之后的列表中会出现第三个集合。查看当前安装的集合:

ansible-galaxy collection list

集合安装到用户主目录,位于 pipx 虚拟环境之外,因此以后重装 ansible-core 时仍会保留:

# /home/kibet/.ansible/collections/ansible_collections
Collection                               Version
---------------------------------------- -------
ansible.posix                            2.2.2
community.general                        14.0.0
community.library_inventory_filtering_v1 1.1.5

如果更希望使用完整套件,并能接受旧版引擎,可以执行前面的 --include-deps ansible 命令。但不要同时用 pipx 安装这两个包,因为它们都提供同名的 ansible 可执行文件。

第 3 步:需要发行版构建时使用 AppStream 软件包

Rocky Linux 10 AppStream 的 ansible-core RPM 可以正常工作,只是版本较旧:使用 Python 3.12,落后六条发布分支,不包含集合。AppStream 和 EPEL 10 都没有完整 ansible 软件包,因此 dnf install ansible 会直接失败。实验在 web01 上测试了这两种安装:

sudo dnf install -y ansible-core

版本检查会显示 /usr/lib/python3.12 下的 RPM 路径,以及 /etc/ansible/ansible.cfg 系统配置文件:

Rocky AppStream 安装 ansible-core 2.16.16,完整 ansible 软件包不可用

当支持合同或合规基线要求使用发行版 RPM 时,这个软件包有意义,因为它与操作系统其他部分一起通过 dnf upgrade 更新。对家庭实验室,或依据当前文档编写新剧本的环境,第 2 步的 pipx 安装更适合作为默认选择,官方文档示例通常面向较新的核心。同一控制节点上不要混用两种方式:PATH 中排在前面的 ansible 会生效,而 Rocky 的 ~/.local/bin 排在 /usr/bin 之前。

第 4 步:在 Ubuntu 24.04 上安装 Ansible

Ubuntu 24.04 有三种来源,但它们各自都无法直接安装文中当前的核心。实验在 nas01 上逐项检查,具体版本见上表:

  • sudo apt install ansible:完整套件,核心与 Rocky AppStream 同属较旧分支。
  • ppa:ansible/ansible:完整套件,使用前一条核心分支。
  • pipx install ansible-core:使用 Python 3.12.3,得到前一条核心分支。

Ubuntu 24.04 官方软件源没有 python3.13 或 python3.14,apt-cache policy 都找不到。因此,获取当前核心的较简洁路径是 uv:它把独立 Python 构建下载到主目录,保留系统解释器。先通过 apt 安装 pipx,再用 pipx 安装 uv,最后由 uv 创建 Ansible 环境。如果之前的 pipx install ansible-core 已在 ~/.local/bin 留下 ansible,应先用 pipx uninstall ansible-core 删除,实验在 nas01 上也是如此,以免两个工具争用同一个文件:

sudo apt update
sudo apt install -y pipx
pipx install uv
~/.local/bin/uv tool install --python 3.14 ansible-core

uv 自动下载 Python 3.14.10,用 1.97 秒准备九个软件包,并安装当前 ansible-core,随后提醒 ~/.local/bin 尚未加入 PATH:

Ubuntu 24.04 上 uv 下载 Python 3.14 并安装新核心

修正一次路径后,打开新 shell,就能直接调用 ansible 和 uv,无需完整路径:

pipx ensurepath

如果希望 Ansible 与系统一起更新,并接受较旧分支,可以选择 apt,这与 Rocky AppStream RPM 的版本路线相似。注意,apt-cache policy 显示软件包来自 noble/universe;没有 Ubuntu Pro 时,该仓库不提供标准 Ubuntu 安全维护。PPA 是折中选项,提供基于前一分支的完整套件。若在 Ubuntu 26.04 上新建控制节点,其 apt 软件包版本较新,见上表;Ubuntu 26.04 Ansible 指南和 Debian 13 Ansible 指南分别介绍了对应发行版的完整流程。

第 5 步:在控制节点配置 SSH 密钥

Ansible 以普通用户通过 SSH 连接被管理节点,并使用 sudo 提权,因此控制节点需要一把各节点信任的密钥。在 homelab-ctl 上生成:

ssh-keygen -t ed25519 -C "kibet@homelab-ctl"

将公钥复制到每个节点。ssh-copy-id 使用现有登录方式,新服务器通常是密码登录,并把公钥追加到 ~/.ssh/authorized_keys:

ssh-copy-id kibet@10.0.1.21
ssh-copy-id kibet@10.0.1.22

每次操作只要求输入一次该用户的密码,并以 Number of key(s) added: 1 结束。接着使用禁止密码提示的 BatchMode 测试;如果打印主机名,Ansible 也能以相同方式连接:

ssh -o BatchMode=yes kibet@10.0.1.21 hostname
ssh -o BatchMode=yes kibet@10.0.1.22 hostname

两个节点分别返回 web01 和 nas01。首次登录也会把各节点的主机密钥写入 ~/.ssh/known_hosts;下一步保持主机密钥检查开启,因此这一点很重要。如果私钥设置了口令,每个会话中可通过 eval "$(ssh-agent -s)" 和 ssh-add 将其加载到代理,避免 Ansible 停下来询问口令。

第 6 步:编写 ansible.cfg 与主机清单

项目目录拥有自己的 ansible.cfg,可以让配置与使用它的剧本放在一起。Ansible 按以下顺序读取找到的第一份配置:ANSIBLE_CONFIG、./ansible.cfg、~/.ansible.cfg、/etc/ansible/ansible.cfg。pipx 安装没有系统配置文件,这也是之前 ansible --version 显示 config file = None 的原因。创建项目:

mkdir -p ~/homelab-ansible/group_vars/all
cd ~/homelab-ansible

用编辑器打开配置文件:

vim ~/homelab-ansible/ansible.cfg

下面就是实验第 6、7 步实际使用的配置,第 8 步会增加一行:

[defaults]
inventory = ./inventory.ini
remote_user = kibet
host_key_checking = True
retry_files_enabled = False
timeout = 30

[privilege_escalation]
become = True
become_method = sudo
become_ask_pass = False

[ssh_connection]
pipelining = True

保留 host_key_checking 开启,因为第 5 步已将密钥写入 known_hosts;关闭它只会掩盖主机密钥变化,而这种变化正应该被发现。pipelining 减少每个任务所需的 SSH 往返次数,本文所有剧本在两个发行版上都启用了该选项。接着编辑主机清单:

vim ~/homelab-ansible/inventory.ini

定义两个组,每组一台主机,并为所有节点固定解释器:

[webservers]
web01 ansible_host=10.0.1.21

[storage]
nas01 ansible_host=10.0.1.22

[all:vars]
ansible_python_interpreter=/usr/bin/python3

增加 [all:vars] 是因为首次 ping 没有它时出现了警告。Ansible 在两个节点上自动发现 /usr/bin/python3.12,并提醒“以后安装其他 Python 解释器可能导致发现不同的解释器”。固定为 /usr/bin/python3 可以消除这一警告,也能适应节点上的 Python 小版本升级。

确认 Ansible 读取了你的配置。ansible-config 现在默认使用 -t all,所以输出也包含 SSH 连接选项等插件配置:

ansible-config dump --only-changed

每一行都在括号中显示配置来源文件。新的 CONNECTION 区块中会出现 pipelining;顶部弃用提示来自 ansible-core 检查 oneline 和 tree 回调,不是你的配置造成的:

[WARNING]: Deprecation warnings can be disabled by setting `deprecation_warnings=False` in ansible.cfg.
[DEPRECATION WARNING]: oneline has been deprecated. Use another callback plugin, or vendor and/or move the oneline callback to a collection. This feature will be removed from ansible-core version 2.23.
Origin: <unknown>

oneline

[DEPRECATION WARNING]: tree has been deprecated. Use another callback plugin, or vendor and/or move the tree callback to a collection. This feature will be removed from ansible-core version 2.23.
Origin: <unknown>

tree

CONFIG_FILE() = /home/kibet/homelab-ansible/ansible.cfg
DEFAULT_BECOME(/home/kibet/homelab-ansible/ansible.cfg) = True
DEFAULT_BECOME_ASK_PASS(/home/kibet/homelab-ansible/ansible.cfg) = False
DEFAULT_BECOME_METHOD(/home/kibet/homelab-ansible/ansible.cfg) = sudo
DEFAULT_HOST_LIST(/home/kibet/homelab-ansible/ansible.cfg) = ['/home/kibet/homelab-ansible/inventory.ini']
DEFAULT_REMOTE_USER(/home/kibet/homelab-ansible/ansible.cfg) = kibet
DEFAULT_TIMEOUT(/home/kibet/homelab-ansible/ansible.cfg) = 30
HOST_KEY_CHECKING(/home/kibet/homelab-ansible/ansible.cfg) = True
RETRY_FILES_ENABLED(/home/kibet/homelab-ansible/ansible.cfg) = False

GALAXY_SERVERS:

CONNECTION:
==========

ssh:
___
host_key_checking(/home/kibet/homelab-ansible/ansible.cfg) = True
pipelining(/home/kibet/homelab-ansible/ansible.cfg) = True
remote_user(/home/kibet/homelab-ansible/ansible.cfg) = kibet
timeout(/home/kibet/homelab-ansible/ansible.cfg) = 30

增加 -t base 可以得到过去那种只包含核心配置的视图,没有插件区块和回调警告,适合解析输出的脚本:

ansible-config 的全部插件配置与仅核心配置输出比较

默认 -t all 也意味着,[ssh_connection] 等插件所属区块现在会由 ansible-config 验证,而不再被报告为未知。因此编辑文件后执行 ansible-config 操作,有助于发现这些区块中配置项的拼写错误。

第 7 步:运行第一批临时命令

ping 模块是真正的连通性测试:通过 SSH 登录,在节点上运行 Python,返回 pong,一次验证密钥、用户和解释器。应在项目目录内执行,以便读取 ansible.cfg:

cd ~/homelab-ansible
ansible all -m ansible.builtin.ping

两个节点都正常回应;解释器已固定,因此不再出现 discovered_interpreter_python 提示:

nas01 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
web01 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

临时命令(ad-hoc)默认使用 command 模块,所以执行只读检查时无需指定模块。可以先检查系统运行时间:

ansible all -a "uptime"

每台主机单独报告结果。CHANGED 只说明命令执行了,因为 Ansible 无法判断任意命令是否真正修改了系统:

nas01 | CHANGED | rc=0 >>
 04:28:13 up 8 min,  1 user,  load average: 0.03, 0.12, 0.06
web01 | CHANGED | rc=0 >>
 04:28:13 up 9 min,  1 user,  load average: 0.00, 0.22, 0.19

剧本通过 facts(系统事实)决定分支行为。对于混合发行版环境,可以先查看发行版事实:

ansible all -m ansible.builtin.setup -a "filter=ansible_distribution*"

应留意新的 ansible_distribution_cpe_name 事实。它读取 /etc/os-release 的 CPE_NAME;Rocky 返回其 CPE 字符串,而 Ubuntu 不提供该字段,返回 null:

Rocky 与 Ubuntu 节点的 Ansible ping 和发行版事实

环境中包含 Ubuntu 或 Debian 主机时,不要依据 cpe_name 构造条件,因为 null 可能使字符串比较失败。ansible_distribution 和 ansible_os_family 仍是较稳妥的键;os_family 与 distribution 值列表列明了各发行版的返回值。Ansible 临时命令指南还介绍了更多类似单行命令。

第 8 步:运行包含 Vault 密文的首个剧本

安装软件包并把机密写入配置文件的剧本,可以展示新安装环境中最先注意到的两点:任务的幂等性,以及现在显示为 $REDACTED$ 的机密。先创建只有当前用户能读取的 Vault 密码文件:

echo "S9rv-Lab-2026" > ~/.vault_pass
chmod 600 ~/.vault_pass

这样写入的密码会留在 shell 历史中,因此共享主机上应改用 vim ~/.vault_pass 手动输入。接着告诉 Ansible 文件位置。只有文件创建后才能增加配置,否则 ansible.cfg 指向不存在的文件时,每次运行 ansible 或 ansible-playbook 都会因“The vault password file … was not found”失败。实验执行了下面这条 sed 命令,将设置插入 [defaults] 中 timeout 的下一行:

sed -i "/^timeout = 30/a vault_password_file = ~/.vault_pass" ansible.cfg

将机密写入所有主机共享的变量文件,再原地加密。ansible-vault 从 ansible.cfg 读取密码文件,因此不会再次提示输入:

echo "db_pass: Tr0ub4dor-Homelab" > group_vars/all/vault.yml
ansible-vault encrypt group_vars/all/vault.yml
head -2 group_vars/all/vault.yml

文件现在以 Vault 标头开头,而不是 YAML,读取仓库的人无法直接看到密码:

$ANSIBLE_VAULT;1.1;AES256
39386639376631366132613730326636646464613363363938343437356330373363396335363830

接着创建剧本:

vim ~/homelab-ansible/site.yml

它使用跨发行版的 package 模块安装 chrony,通过 debug 输出密码,将密码写入 /etc/app.env,并显示注册结果:

---
- name: First playbook against the homelab
  hosts: all
  tasks:
    - name: Install chrony
      ansible.builtin.package:
        name: chrony
        state: present

    - name: Show which password the app will use
      ansible.builtin.debug:
        msg: "Connecting to the database with {{ db_pass }}"

    - name: Write the password into the app config
      ansible.builtin.copy:
        dest: /etc/app.env
        content: "DB_PASS={{ db_pass }}\n"
        mode: "0600"
      register: env_file

    - name: Show the registered copy result
      ansible.builtin.debug:
        var: env_file

先检查语法,再运行:

ansible-playbook site.yml --syntax-check
ansible-playbook site.yml

Rocky 镜像已安装 chrony,因此这项任务只有 nas01 报告变化。值得关注的是 debug 任务:ansible-core 将解密后的 Vault 值登记为机密,在显示输出时进行掩码处理:

Vault 剧本输出中的密码显示为 REDACTED

最后一个任务输出注册结果。除了查看有哪些字段,也要注意缺少什么:

ok: [web01] => {
    "env_file": {
        "ansible_parsed": true,
        "changed": true,
        "checksum": "9ec34752266f36693a6380801a6dfb0156803ae6",
        "dest": "/etc/app.env",
        "diff": [],
        "failed": false,
        "gid": 0,
        "group": "root",
        "md5sum": "f351c41165f3b9312fbc1423900095fe",
        "mode": "0600",
        "owner": "root",
        "secontext": "system_u:object_r:etc_t:s0",
        "size": 26,
        "src": "/home/kibet/.ansible/tmp/ansible-tmp-1793680111.4514003-52386-10982625494549/.source.env",
        "state": "file",
        "uid": 0
    }
}

结果没有 invocation 键,因为 Python 模块默认不再将其加入任务结果。这个变化在前一条发布分支就已引入,因此从至少两条分支之前升级才可能首次遇到。如果旧剧本读取 result.invocation.module_args,可设置 ANSIBLE_INJECT_INVOCATION=true,或在 [defaults] 下设置 inject_invocation = True,该键就会重新出现,实验第二次运行确认了这一点。secontext 也表明 web01 上的文件获得正常的 etc_t 标签,SELinux enforcing 模式无需额外处理。

掩码只影响显示,这一点适合自行验证。先让 Ansible 读回文件,再通过普通 SSH 读取:

ansible web01 -a "cat /etc/app.env"
ssh kibet@10.0.1.21 sudo cat /etc/app.env

Ansible 连命令输出中的值也会遮蔽,而磁盘上的文件仍保存真实密码:

web01 | CHANGED | rc=0 >>
DB_PASS=$REDACTED$
DB_PASS=Tr0ub4dor-Homelab

因此,template 和 copy 任务仍收到真实机密,只有日志、回调和终端显示 $REDACTED$。过去 Vault 值可能直接以明文显示,标记为 no_log 的模块参数则返回字面占位符 VALUE_SPECIFIED_IN_NO_LOG_PARAMETER。依赖该占位符的测试需要更新,因为模块结果现在携带真实值,掩码发生在显示阶段。Ansible Vault 教程进一步说明 Vault ID 和重新加密;首个 Ansible 剧本教程则介绍处理器与变量。

第 9 步:将现有安装升级到新核心

如果几个月前用系统 Python 和 pipx 安装了 Ansible,pipx upgrade 不会迁移到新发布分支。虚拟环境仍使用 Python 3.12,pip 找不到更高且兼容的版本,仍报告成功:

pipx upgrade ansible-core

这里的“Already at latest”指的是当前解释器可用的最新版本,并非 PyPI 上最新版本。

upgrading shared libraries...
upgrading ansible-core...
ansible-core is already at latest version 2.21.6 (location:
/home/kibet/.local/share/pipx/venvs/ansible-core)

应使用较新解释器重建虚拟环境。pipx reinstall 保留包名,替换底层 Python;先按第 2 步安装 python3.14:

pipx reinstall --python python3.14 ansible-core

终端截图展示了 homelab-ctl 的升级前后,最终使用新核心与 Python 3.14.8:

pipx upgrade 保持旧版,reinstall 切换 Python 3.14 后升级成功

Ubuntu 上通过 uv 并使用 --python 3.14 安装后,日常升级命令是 uv tool upgrade ansible-core。实验主机已经是最新版本,因此返回“Nothing to upgrade”。升级运行真实业务剧本的控制节点前,应核对实验中遇到的行为变化:

  • 注册结果不再包含 invocation 键;从至少两条分支之前升级时需要关注。
  • no_log 值和 Vault 机密显示为 $REDACTED$。
  • uri 的 follow_redirects: yes 现在失败;原来的 yes 改为 all,no 改为 none。
  • ansible-config 命令默认使用 -t all。

前两项主要影响检查任务结果的剧本,而 uri 的变化会直接让任务失败,因此应先在角色中搜索 follow_redirects。第 1 步提到的变更日志列出其他变化,第 6 步的 oneline 和 tree 警告也说明,这两个回调计划在下一版 ansible-core 中移除。

实验中遇到的故障

下面每个错误都在编写原文的实验过程中实际出现,随后给出当时采用的修复方法。

“No matching distribution found for ansible-core”

控制节点使用 Python 3.12,pip 输出上一行会说明原因:Requires-Python >=3.13。Rocky 或 RHEL 10 上从 AppStream 安装 python3.14,并向 pipx 传入 --python python3.14;Ubuntu 24.04 上则使用 uv tool install --python 3.14。如果不固定版本,可能完全没有错误,只安装较旧核心,因此始终检查“installed package”一行。

“Attempting to decrypt but no vault secrets found”

跳过或后来删除 vault_password_file 配置时,会遇到这个错误。实验最初尚未加入该行,一旦存在 group_vars/all/vault.yml,即使普通 ansible web01 -m ansible.builtin.ping 也会失败,因为 Ansible 无论执行临时命令还是剧本,都会加载所访问主机的组变量。每条命令传入 --vault-password-file ~/.vault_pass 可以解决,但第 8 步写入 ansible.cfg 的 vault_password_file 能对整个项目生效。密码文件权限应为 600,并排除在 Git 之外。

“value of follow_redirects must be one of: all, none, safe, urllib2, got: yes”

实验对 web01 执行了带 follow_redirects=yes 的临时 uri 请求,这是许多旧角色的写法,任务报出了上述错误。uri 模块已删除 yes 和 no 取值。旧的 yes 对应 all,旧的 no 对应 none,应一一替换;模块默认的 safe 只跟随 GET 与 HEAD 的重定向,对期望 30x 状态码的任务可能造成无提示的行为变化。同一次请求改用 follow_redirects=safe 后返回状态 200。

“Permission denied (publickey,gssapi-keyex,gssapi-with-mic)”

实验第一次运行 ssh-copy-id,打印“All keys were skipped because they already exist on the remote system”,但随后登录 web01 失败并出现此错误。ssh-copy-id 通过尝试登录判断密钥是否存在;当时测试连接通过另一把密钥成功,因此跳过了复制。如果目标主机不可能已有这把密钥却出现跳过提示,可运行 ssh-copy-id -f kibet@10.0.1.21 强制复制,再重复第 5 步的 BatchMode 测试。

“Ansible requires Python 3.9 or newer on the target”

这是被管理节点的 Python 要求,消息来自 ansible-core 模块代码。Rocky 10、Ubuntu 24.04 和 Debian 13 均高于最低要求,所以通常只会在仍使用平台 Python 3.6 的 RHEL 8 等旧主机上遇到。为该主机安装较新 Python,并在主机清单中设置其 ansible_python_interpreter。

回滚或移除 Ansible

如果新核心导致剧本失败,需要当天恢复旧版本,可在同一 pipx 虚拟环境中固定前一条分支。--force 复用已有环境,因此 Python 仍为 3.14:

pipx install --force "ansible-core<2.22"

实验控制节点这样恢复了前一核心;执行同样的命令但仅指定 ansible-core,则重新回到当前版本。要完全移除 Ansible,应使用与安装方式对应的命令:

  • pipx:pipx uninstall ansible-core。
  • uv:uv tool uninstall ansible-core。
  • Rocky 或 RHEL RPM:sudo dnf remove ansible-core。
  • Ubuntu apt:sudo apt purge ansible ansible-core。

这些卸载操作都不影响 ~/.ansible/collections 或项目目录,因此集合和剧本在重装后仍会保留。只有需要完全清理时才删除 ~/.ansible。

Avatar photo

关于 木子

Email: muzi@vip.rockylinux.cn 微信:VebinLee QQ: 2306867585
Founder of the Rocky Linux Chinese community, MVP、VMware vExpert、TVP, advocate for cloud native technologies, with over ten years of experience in site reliability engineering (SRE) and the DevOps field. Passionate about Cloud Computing、Microservices、CI&CD、DevOps、Kubernetes, currently dedicated to promoting and implementing Rocky Linux in Chinese-speaking regions.
用一杯咖啡支持我们,我们的每一篇[文档]都经过实际操作和精心打磨,而不是简单地从网上复制粘贴。期间投入了大量心血,只为能够真正帮助到您。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇