Ansible 执行环境镜像构建
Ansible 的执行环境构建其实就是容器镜像构建,只不过 Ansible 提供了一个命令 ansible-builder
来实现镜像的标准化构建。
ansible-builder
默认会使用当前目录下的 execution-environment.yaml
或 execution-environment.yml
来当作执行环境定义文件(就是 Containerfile
/Dockerfile
)。
执行环境定义文件配置
Ansible Builder 3.x 中 Execution Environment Definition File(执行环境定义文件)支持的 7 个顶级配置段(Top-level sections):
顶级字段名 | 说明 |
---|---|
additional_build_files |
指定构建过程中需要添加到 _build/ 上下文目录中的额外文件 |
additional_build_steps |
指定在不同构建阶段(base, builder, final)插入自定义命令,例如 RUN , COPY 等 |
build_arg_defaults |
设置传递给容器引擎构建命令(如 docker build )的默认参数,等效于 --build-arg |
dependencies |
指定所需依赖项,如 Python、系统包、Ansible 集合等 |
images |
定义基础镜像(base image)和构建镜像(builder image) |
options |
设置构建行为的额外选项,如是否保留缓存、中间构建目录等 |
version |
定义文件格式版本,当前为 3 |
additional_build_files 作用
该字段用于将自定义文件或目录添加到构建上下文目录中,供后续构建步骤(如 RUN
, COPY
)引用或复制。
additional_build_files:
- src: <源路径>
dest: <目标子路径>
src
:源路径
- 支持:
- 相对路径(相对于
execution-environment.yml
文件) - 绝对路径(不支持通配符)
- 通配符 glob(仅用于相对路径)
- 目录(整个目录会被复制)
- 相对路径(相对于
- 示例:
files/ansible.cfg
或/home/user/.ansible.cfg
dest
:目标子路径(相对于构建上下文的 _build/
)
- 不可为绝对路径
- 不可包含
..
- 若目录不存在会自动创建
- 示例:
files/configs
、custom/ansible
等
常配合
additional_build_steps
使用。
additional_build_steps 作用
- 在构建执行环境镜像的各阶段 插入定制命令
- 允许灵活控制构建过程,例如安装额外依赖、复制文件、设置环境变量等
关键词 | 插入位置 | 典型用途 |
---|---|---|
prepend_base |
基础镜像构建开始前 | 安装依赖、添加源配置 |
append_base |
基础镜像构建完成后 | 清理缓存、安装自定义软件包 |
prepend_galaxy |
安装集合前 | 拷贝 requirements.yml |
append_galaxy |
安装集合后 | 重新设置权限、清理缓存 |
prepend_builder |
builder 阶段开始前 | 安装内部工具包 |
append_builder |
builder 阶段结束后 | 自定义 Python 路径、虚拟环境 |
prepend_final |
final 阶段开始前 | 拷贝额外文件、环境变量 |
append_final |
final 阶段结束后 | 设置 ENTRYPOINT、LABEL、清理缓存等 |
additional_build_steps:
prepend_base:
- RUN echo "Installing base deps"
- RUN dnf install -y epel-release
append_base:
- RUN dnf clean all
prepend_final: |
COPY _build/scripts/welcome.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/welcome.sh
append_final:
- RUN echo "Build complete"
这个只是示例,像缓存清理
ansible-builder
默认就会去做。构建镜像时禁止使用
USER
指令,若需设置默认用户,应使用options.user
。
build_arg_defaults 作用
用于指定构建镜像时的默认 --build-arg
变量。这些参数会被硬编码写入 Containerfile
或 Dockerfile
中,适用于如安装 pre-release 集合、控制依赖行为、保留包管理缓存等用途。
构建参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
ANSIBLE_GALAXY_CLI_COLLECTION_OPTS |
str |
null | 传递给 ansible-galaxy collection install 的参数(如 --pre ) |
ANSIBLE_GALAXY_CLI_ROLE_OPTS |
str |
null | 传递给 ansible-galaxy role install 的参数(如 --no-deps ) |
PKGMGR_PRESERVE_CACHE |
str |
null | 控制包管理器缓存是否保留。可选值:always (不清理),其他值表示在系统依赖安装后清理;设置其他任意值会在最终阶段清理缓存 如果是空值的话会频繁清理缓存 |
dependencies 作用
dependencies
是 Ansible Builder 执行环境定义文件中的核心字段,用于定义要安装进最终镜像的所有依赖,包括:
ansible-core
与ansible-runner
- Python 包
- 系统包(如用 bindep 格式)
- Ansible Collections
- 可选的依赖排除规则(
exclude
)
字段名 | 类型 | 说明 |
---|---|---|
ansible_core |
dict |
使用 package_pip 指定 ansible-core 的安装方式,可为版本号、URL 等 pip 支持的格式 |
ansible_runner |
dict |
使用 package_pip 指定 ansible-runner 的安装方式,同样支持 pip 格式 |
galaxy |
dict / str / list |
指定要安装的 Ansible Collections,支持文件路径或嵌套字典结构 |
python |
str / list |
Python 依赖包,支持文件路径或直接列出 |
python_interpreter |
dict |
设置系统包管理器安装的 Python 版本(对应字段 package_system )和 Python 执行路径(对应字段 python_path ) |
system |
str / list |
系统包依赖,使用 bindep 格式,支持文件路径或直接列出 |
exclude |
dict |
指定要从 Collections 的依赖中排除的 Python 或系统依赖项(支持名称或正则)需要 Ansible Builder ≥ 3.1(格式看下边示例吧) |
ansible_core
或 ansible_runner
支持的格式(以 ansible_core
为例):
ansible_core:
package_pip: ansible-core
ansible_core:
package_pip: ansible-core==2.14.3
ansible_core:
package_pip: https://github.com/example_user/ansible/archive/refs/heads/ansible.tar.gz
官网的示例:
# 引用依赖文件
dependencies:
python: requirements.txt
system: bindep.txt
galaxy: requirements.yml
ansible_core:
package_pip: ansible-core==2.14.2
ansible_runner:
package_pip: ansible-runner==2.3.1
python_interpreter:
package_system: "python310"
python_path: "/usr/bin/python3.10"
# 直接内联定义依赖
dependencies:
python:
- pywinrm
system:
- iputils [platform:rpm]
galaxy:
collections:
- name: community.windows
- name: ansible.utils
version: 2.10.1
ansible_core:
package_pip: ansible-core==2.14.2
ansible_runner:
package_pip: ansible-runner==2.3.1
python_interpreter:
package_system: "python310"
python_path: "/usr/bin/python3.10"
# 排除指定依赖(需要 v3.1+)
dependencies:
exclude:
python:
- docker
system:
- python3-Cython
all_from_collections:
- ~community\..+ # 使用正则排除所有 community.* 相关集合的依赖
images 作用
用于指定构建执行环境的基础镜像(base image)。这个基础镜像决定了最终执行环境的操作系统以及预装的一些基础包。
字段名 | 类型 | 说明 |
---|---|---|
base_image |
dict |
定义基础镜像信息,必须包含 name |
子字段:
子字段名 | 类型 | 说明 |
---|---|---|
name |
str |
镜像名称,建议使用 registry/namespace/image:tag 格式,例如:quay.io/ansible/ansible-runner:latest |
signature_original_name |
str (可选) |
当使用镜像签名校验并采用镜像仓库镜像(镜像复制)时,指定原始签名名称 |
images:
base_image:
name: myregistry.local/ansible-runner:latest
signature_original_name: quay.io/ansible/ansible-runner:latest
镜像签名验证(Image Signature Verification)
如果使用的是 Podman 容器运行时,可以配置签名验证策略来确保镜像安全。
可选策略
策略值 | 说明 |
---|---|
ignore_all |
生成一个 policy.json 文件,不执行签名验证(默认跳过验证) |
system |
使用系统默认的签名策略文件(位于 /etc/containers/policy.json 等)进行验证 |
signature_required |
强制签名验证,必须在 base_image 中配置镜像签名来源,否则构建失败 |
通过 CLI 参数
--container-policy
传递,例如:ansible-builder build --container-policy signature_required
。
options 作用
options
是一个高级可选项集合,用于控制构建期间和容器最终行为的细节配置,如容器初始化方式、用户设置、包管理路径等。适合有自定义镜像构建需求的用户。
字段名 | 类型 | 默认值 | 说明 |
---|---|---|---|
container_init |
dict |
详见子字段 | 容器 ENTRYPOINT、CMD 和辅助工具(如 dumb-init)的配置 |
package_manager_path |
str |
/usr/bin/dnf |
指定包管理器路径,如 dnf 或 microdnf ,设置了这个就会用这个包管理器去安装 Python 解释器 |
skip_ansible_check |
bool |
false |
是否跳过 ansible/runner 安装校验 |
skip_pip_install |
bool |
false |
是否跳过 pip 安装(由用户手动处理) |
relax_passwd_permissions |
bool |
true |
是否允许 root 对 /etc/passwd 写权限(支持动态用户)(就是授予镜像执行用户 GID 0 的 root 组,以允许修改 /etc/passwd 添加用户,构建的镜像自带一个添加用户的脚本) |
workdir |
str |
/runner |
容器中进程的默认工作目录(有时会作为 entrypoint 创建的动态用户的家目录),若目录不存在将自动创建并递归赋予 root 组的 rwx 权限 |
user |
str /int |
1000 |
容器中默认用户(用户名或 UID) |
tags |
list (str ) |
["ansible-execution-env:latest"] |
构建成功后镜像使用的标签 |
container_init
子字段:
子字段名 | 类型 | 默认值 | 说明 |
---|---|---|---|
cmd |
list (str ) |
["bash"] |
指定容器中执行的默认命令(CMD 指令) |
entrypoint |
list (str ) |
["/opt/builder/bin/entrypoint", "dumb-init"] |
容器的 ENTRYPOINT,含信号转发和用户环境初始化; 这个 ENTRYPOINT 会保证容器启动时的用户拥有合适的环境和家目录(没有这个用户会尝试创建),否则会发出告警 |
package_pip |
str |
dumb-init==1.2.5 |
安装于最终镜像,用于支持 ENTRYPOINT 的 pip 包 |
options:
container_init:
package_pip: dumb-init>=1.2.5
entrypoint: ["dumb-init"]
cmd: ["csh"]
package_manager_path: /usr/bin/microdnf
skip_ansible_check: true
skip_pip_install: false
relax_passwd_permissions: false
workdir: /myworkdir
user: bob
tags:
- ee_custom:latest
version 作用
用于标识 execution-environment.yml
文件所使用的配置格式版本,确保 ansible-builder
工具正确解析该文件。
字段名 | 类型 | 默认值 | 说明 |
---|---|---|---|
version |
int | 1 |
schema 版本号;使用 ansible-builder 3.x 时,必须设置为 3 |
执行环境构建流程
-
准备好相关执行环境定义文件和依赖文件,通过
ansible-builder
或ansible-navigator builder
启动构建,比方说在构建目录执行ansible-builder build -vvv
-
ansible-builder
根据已有的配置文件,在构建镜像的工作目录生成context
目录,目录里包含Containerfile
(或Dockerfile
)、依赖文件和一些构建镜像用的脚本()[root@ansible-controller build]# tree context/ context/ ├── _build │ ├── bindep.txt │ ├── configs │ │ └── ansible.cfg │ ├── requirements.txt │ ├── requirements.yml │ └── scripts │ ├── assemble │ ├── check_ansible │ ├── check_galaxy │ ├── entrypoint │ ├── install-from-bindep │ ├── introspect.py │ └── pip_install └── Containerfile 3 directories, 12 files
-
整个镜像构建分三个阶段:
- 第一个阶段:构建一个 BASE 镜像,基础镜像里会把脚本和
entrypoint
拷贝进去并安装 Python 和 pip - 第二个阶段:以 BASE 镜像为基础,安装 Ansible Roles 和 Ansible Collections
- 第三个阶段:以 BASE 镜像为基础,生成最终要安装的 Python 包和系统包的清单
- 第四个阶段:以 BASE 镜像为基础,将之前安装的 Ansible Roles 和 Ansible Collections 复制进来,安装系统包和 Python 包(包括默认安装和手动设置的),创建需要的目录,并将必要的文件和目录设置好权限,设置容器标签、启动用户和启动命令。
- 第一个阶段:构建一个 BASE 镜像,基础镜像里会把脚本和
-
镜像构建完成后,根据命令参数(
--tag
)或配置文件的设置(tags:
)为镜像打tag
这个是默认的构建流程,除了默认的流程,还可以通过
additional_build_steps
添加自定义的构建指令。
构建一个执行环境镜像
[root@ansible-controller build]# tree .
.
├── ansible.cfg
├── bindep.txt
├── execution-environment.yml
├── requirements.txt
└── requirements.yml
0 directories, 5 files
[root@ansible-controller build]# cat execution-environment.yml
version: 3
build_arg_defaults:
ANSIBLE_GALAXY_CLI_COLLECTION_OPTS: "--pre"
images:
base_image:
name: registry.access.redhat.com/ubi8/ubi-minimal:8.10-1255
dependencies:
python_interpreter:
package_system: python3.12
python_path: /usr/bin/python3.12
ansible_core:
package_pip: ansible-core==2.16.14
ansible_runner:
package_pip: ansible-runner
python: requirements.txt
system: bindep.txt
galaxy: requirements.yml
additional_build_files:
- src: ./ansible.cfg
dest: configs
additional_build_steps:
append_final:
- COPY _build/configs/ansible.cfg /etc/ansible/ansible.cfg
- RUN echo "ADD ansible.cfg to /etc/ansible/!"
options:
container_init:
cmd: '["bash"]'
entrypoint: '["/opt/builder/bin/entrypoint", "dumb-init"]'
package_pip: dumb-init==1.2.5
package_manager_path: /usr/bin/microdnf
relax_passwd_permissions: true
workdir: /runner
user: '1000'
tags:
- ubi8-ee-py3.12-ansible-core2.16.14:v1
[root@ansible-controller build]# cat bindep.txt
sshpass [platform:rpm]
iputils [platform:rpm]
[root@ansible-controller build]# cat requirements.txt
ansible-lint
requests
six
psutil
[root@ansible-controller build]# cat requirements.yml
collections:
- name: ansible.posix
version: '<=1.6.2'
- name: ansible.utils
version: '<=5.1.0'
- name: community.crypto
- name: community.dns
- name: community.docker
- name: community.general
- name: containers.podman
- name: cloud.common
roles:
- name: geerlingguy.repo-epel
version: '3.1.1'
[root@ansible-controller build]# ansible-builder build -vvv \
--tag ubi8-ee-py3.12-ansible-core2.16.14:v2
这里有两个地方设置了
tag
,一个是execution-environment.yml
里的options
设置的,另一个是命令的--tag
选项设置的,命令优先级最高,上边的构建完成后只会有ubi8-ee-py3.12-ansible-core2.16.14:v2
这一个,没有ubi8-ee-py3.12-ansible-core2.16.14:v1
。