Ansible 使用介绍

环境信息

  • Centos 7
  • ansible-core 2.16
  • Docker image python:3.12.3

安装

ansible-core 版本及 Python 版本支持对应关系

ansible-core Version Control Node Python Target Python / PowerShell
2.16 Python 3.10 - 3.12 Python 2.7
Python 3.6 - 3.12
Powershell 3 - 5.1

为了环境部署方便灵活,可以选择使用 python:3.12.3 的 Docker 镜像,以其为基础环境安装 ansible-core 2.16 或者直接使用 ansible 镜像启动。

# docker run --rm -it python:3.12.3 bash

# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

# python --version
Python 3.12.3

# pip install ansible

# pip list
Package Version
------------ -------
ansible 9.5.1
ansible-core 2.16.6
cffi 1.16.0
cryptography 42.0.7
Jinja2 3.1.4
MarkupSafe 2.1.5
packaging 24.0
pip 24.0
pycparser 2.22
PyYAML 6.0.1
resolvelib 1.0.1
setuptools 69.5.1
wheel 0.43.0

# ansible --version
ansible [core 2.16.6]
config file = None
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.12/site-packages/ansible
ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible
python version = 3.12.3 (main, May 14 2024, 07:23:41) [GCC 12.2.0] (/usr/local/bin/python)
jinja version = 3.1.4
libyaml = True

配置

Ansible 主配置文件为 /etc/ansible/ansible.cfg其中的配置都可以被 ansible-playbook 或者命令行参数覆盖

ansible 默认会读取环境变量 ANSIBLE_CONFIG 指定的配置文件,当前路径下的 ansible.cfg,以及用户家目录下的 .ansible.cfg,以及 /etc/ansible/ansible.cfg 作为配置文件,已第一个找到的为准

常用配置说明

配置项 说明 示例
inventory 指定 inventory (主机列表)文件的路径,默认为 /etc/ansible/hosts
remote_user (未指定用户时)连接远程主机时使用的用户
remote_port 连接远程主机时使用的(默认)端口
host_key_checking 默认启用。检查主机密钥可以防止服务器欺骗和中间人攻击。
如果主机重新安装并且在 know_hosts 中拥有不同的密钥,ansible 会提示确认密钥。
如果要禁用此行为,可以配置为 False
ask_pass 默认为 False。当设置为 True 时,ansible 要求输入远端服务器的密码,即使配置了免密登录
log_path 日志文件,默认 /var/log/ansible.log
pattern 当没有给出 pattern 时的默认 pattern,默认值是 * 即所有主机

inventory 配置文件

默认的 inventory 配置文件路径为 /etc/ansible/hosts,主要用来配置 Managed Hosts 列表

主机列表中的主机可以单独出现,也可以位于某个或者多个组中([] 开头的行)

/etc/ansible/hosts
ansible-demo1.local
ansible-demo2.local

[webserver]
webserver1.local
webserver2.local

[nginxserver]
# 匹配多个主机:nginx1.local, nginx2.local, nginx3.local, nginx4.local
nginx[1:4].local variable1=value1 variable2=value2
nginx-bak.local ansible_ssh_host=10.10.0.1 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=PASSWORD
127.0.0.1 http_port=80 maxRequestPerChild=808

常用配置说明

配置项 说明 示例
ansible_ssh_host 远程主机地址
ansible_ssh_port 远程主机端口
ansible_ssh_user 连接远程主机的 ssh 用户
ansible_ssh_pass 连接远程主机的 ssh 用户密码,建议使用 key 连接
ansible_ssh_private_key_file 连接远程主机的 ssh 私钥文件路径
ansible_sudo_pass sudo 密码(这种方式并不安全,强烈建议使用 --ask-sudo-pass)
ansible_connection 与主机的连接类型.比如:local, ssh 或者 paramiko
Ansible 1.2 以前默认使用 paramiko。1.2 以后默认使用 smart,smart 方式会根据是否支持 ControlPersist, 来判断 ssh 方式是否可行.
ansible_shell_type 目标系统的 shell 类型.默认情况下,命令的执行使用 sh 语法,可设置为 cshfish.
ansible_python_interpreter 目标主机的 python 路径
系统中有多个 Python, 或者命令路径不是 /usr/bin/python

变量

在 inventory 的配置中,可以添加变量,以在 playbook 中使用变量,实现对不同主机的个性化配置。变量分好几个层级:

  • 主机变量: 跟在主机后面直接设置,变量的作用域仅限于主机。由于对变量名没有限制,所以前面的那些 ansible 的配置如果打错字了也不会报错,ansible 会认为这是一个主机变量
    /etc/ansible/hosts
    127.0.0.1 http_port=80 maxRequestPerChild=808
  • 组变量: 组变量需要新开一个 section, 配置在名为 [组名:vars] 的 section 中
    /etc/ansible/hosts
    [localloops]
    127.0.0.[1:5] ansible_connection=paramiko ansible_ssh_user=root

    [localloops:vars]
    http_port=8080

ansible 使用

在配置好 inventory 后,要使 ansible 可以连接到 Managed Host,需要使用密码或者 key 的方式进行认证,建议使用 Private Key 的方式进行认证。参考文档配置 ssh 公私钥免密码认证过程

配置完 hosts 之后,在命令行中可以调用一些命令来使用 ansible,如 ansible all -m ping。命令行的 ansible 工具大体格式如下:

ansible <pattern> -m <module> -a <arguments>

pattern 是一个标识,指定出了要操作的目标主机,可以使用以下方式匹配主机:

  • all 代表所有主机
  • section 名 代表组内的所有主机
  • 主机名 代表一个主机。如果主机名和组名冲突,则以先出现的为准。
  • ip/域名 或者 192.168.1.* 代表一个 IP 或者一组 IP
  • a:b a 和 b 都是组,取得两个组的并集。 a 和 b 都是 host,表示 的关系
  • a:!b a 和 b 都是组,取得属于 a 但是不属于 b 组中的主机
  • a:&b a组和b组的交集
  • a[0] 取得 a 组中的第一个主机,依次类推。
  • a[0:25] 代表 a 组中的第 1 个到第 25 个主机,也可以使用 a[0-25]

ansible 命令常用选项

选项 说明 示例
-m, --module-name 指定模块
默认为 command
ansible all -m ping
--become-user 'BECOME_USER' 切换到指定的用户执行操作,默认为 root
需要配合 -b, --become 选项使用
ansible all -m command -a "ls /root/" -b --become-user root
-b, --become 提升权限到 root 权限,需要用户有 sudo 权限
--list-hosts 列出 pattern 匹配的主机列表,不执行其他任何操作 ansible --list-hosts
-C, --check 不执行任何实际操作,而是对要执行的操作进行验证
-k, --ask-pass 询问连接密码
-o, --one-line 输出到一行里面
-f ,--forks 指定并发执行的数量,默认为 5

ansible 常用模块

command

command 是 ansible 默认的模块。command 模块不支持 shell 变量,也不支持管道等 shell 相关的东西.如果你想使用 shell 相关的这些东西, 请使用 shell 模块. [2]

shell

shell 模块启动一个 shell 然后执行命令

$ ansible raleigh -m shell -a 'echo $TERM'

使用 Ansible ad hoc 命令行接口时(与使用 Playbooks 的情况相反),尤其要注意 shell 引号的规则。比如在上面的例子中,如果使用双引号 "echo $TERM",会求出 TERM 变量在当前系统的值,而我们实际希望的是把这个命令传递 到其它机器执行。

ping

用来测试到目标服务器的连通性

# ansible all -m ping
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
k8s-master1 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).",
"unreachable": true
}
[WARNING]: Platform linux on host k8s-uat-master2 is using the discovered Python interpreter at /usr/bin/python, but future installation of
another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more
information.
k8s-master2 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}

setup

使用 setup 模块,ansible 可以收集各个 Managed Hosts 的 facts,其中包含了 Managed Hosts 的很多元数据。

# ansible k8s-master-nodes[0] -m setup

k8s-uat-master1 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"10.244.0.1",
"172.31.30.123",
"10.244.0.0",
"172.17.0.1"
],
"ansible_all_ipv6_addresses": [
"fe80::84d:ccff:fe5f:56c5",
"fe80::3c4a:4bff:fe20:512",
"fe80::836:c2ff:fefb:e925",
"fe80::146d:22ff:fe20:cf86"
],
...

配合 setup 模块的 filter 参数,可以从输出中过滤出各种信息

# ansible fm-k8s-uat-master-nodes[0] -m setup -a "filter=ansible_default_ipv4"
k8s-master1 | SUCCESS => {
"ansible_facts": {
"ansible_default_ipv4": {
"address": "172.31.30.123",
"alias": "eth0",
"broadcast": "172.31.31.255",
"gateway": "172.31.16.1",
"interface": "eth0",
"macaddress": "0a:36:c2:fb:e9:25",
"mtu": 9001,
"netmask": "255.255.240.0",
"network": "172.31.16.0",
"type": "ether"
},
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false
}

常见错误

Permission denied

在以普通用户 ssh 登陆远程主机(Managed Host)的情况下,执行某些操作可能因为普通用户权限不足导致操作失败

# ansible my-hosts -m command -a "cat /root/.ssh/authorized_keys" 
k8s-master2 | FAILED | rc=1 >>
cat: /root/.ssh/authorized_keys: Permission deniednon-zero return code

k8s-master1 | FAILED | rc=1 >>
cat: /root/.ssh/authorized_keys: Permission deniednon-zero return code

k8s-master3 | FAILED | rc=1 >>
cat: /root/.ssh/authorized_keys: Permission deniednon-zero return code

此种情况,可以使用以下方式解决。***前提是远程登陆使用的用户具有 sudo 权限***。建议使用 -b 选项。

  • 使用 sudo 命令。如果用户没有 sudo 权限,ansible 会被阻塞,后台等待用户输入 sudo 密码,直到等待超时失败。具体报错参考以下示例中的 k8s-master2 输出
    # ansible my-hosts -m command -a "sudo cat /root/.ssh/authorized_keys" 
    k8s-master1 | CHANGED | rc=0 >>
    no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="echo 'Please login as the user \"centos\" rather than the user \"root\".';echo;sleep 10" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCGKLNMv82MQJUuQ9aZPsDofBj96aQlS1kNV2doSwyLatgMNBZ6rzgQuNOJ2DH87IzD1mZ0wL7iApvk6gkxSxcz5tmbU8dfYOYJdlBlhxGk2Nkg2V3P9FPC0hBY73szEV+1DUoqwl+COAsAXO9Uiebr0faQvWOkVT7pypunnjPrBBUaaXn2IcoPIdXZfXVLjXH2JbWSHL5J+yIGHewSMzZ/Xx7u6hwxUP0QLFHrnhD0WDukoBjoUZ2sshMP+DHgoyWjCg+uVpmjJAksp80f34WfNku5Grt90kYEj+N+x2JQ1Y4aQIXASIwDshicbJIsl+RMIMmwe+TElUJ6g9aa0qCr op2-east1-1031
    k8s-master3 | CHANGED | rc=0 >>
    no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="echo 'Please login as the user \"centos\" rather than the user \"root\".';echo;sleep 10" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCGKLNMv82MQJUuQ9aZPsDofBj96aQlS1kNV2doSwyLatgMNBZ6rzgQuNOJ2DH87IzD1mZ0wL7iApvk6gkxSxcz5tmbU8dfYOYJdlBlhxGk2Nkg2V3P9FPC0hBY73szEV+1DUoqwl+COAsAXO9Uiebr0faQvWOkVT7pypunnjPrBBUaaXn2IcoPIdXZfXVLjXH2JbWSHL5J+yIGHewSMzZ/Xx7u6hwxUP0QLFHrnhD0WDukoBjoUZ2sshMP+DHgoyWjCg+uVpmjJAksp80f34WfNku5Grt90kYEj+N+x2JQ1Y4aQIXASIwDshicbJIsl+RMIMmwe+TElUJ6g9aa0qCr op2-east1-1031

    k8s-master2 | FAILED! => {
    "ansible_facts": {
    "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "module_stderr": "Shared connection to 172.31.30.115 closed.\r\n",
    "module_stdout": "[sudo] password for centos: \r\n\r\n{\"changed\": true, \"end\": \"2023-09-01 09:24:40.247480\", \"stdout\": \"\", \"cmd\": [\"sudo\", \"cat\", \"/root/.ssh/authorized_keys\"], \"failed\": true, \"delta\": \"0:05:02.696092\", \"stderr\": \"\\nWe trust you have received the usual lecture from the local System\\nAdministrator. It usually boils down to these three things:\\n\\n #1) Respect the privacy of others.\\n #2) Think before you type.\\n #3) With great power comes great responsibility.\\n\\nsudo: timed out reading password\", \"rc\": 1, \"invocation\": {\"module_args\": {\"creates\": null, \"executable\": null, \"_uses_shell\": false, \"strip_empty_ends\": true, \"_raw_params\": \"sudo cat /root/.ssh/authorized_keys\", \"removes\": null, \"argv\": null, \"warn\": true, \"chdir\": null, \"stdin_add_newline\": true, \"stdin\": null}}, \"start\": \"2023-09-01 09:19:37.551388\", \"warnings\": [\"Consider using 'become', 'become_method', and 'become_user' rather than running sudo\"], \"msg\": \"non-zero return code\"}\r\n",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 1
    }
  • 使用 ansible -b 选项。如果用户没有 sudo 权限, ansible 会因为没有输入 sudo 密码而执行失败,具体报错(Missing sudo password)参考以下示例中的 k8s-master2 输出
    # ansible my-hosts -m command -a "cat /root/.ssh/authorized_keys" -b
    k8s-master2 | FAILED | rc=-1 >>
    Missing sudo password

    k8s-master1 | CHANGED | rc=0 >>
    no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="echo 'Please login as the user \"centos\" rather than the user \"root\".';echo;sleep 10" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCGKLNMv82MQJUuQ9aZPsDofBj96aQlS1kNV2doSwyLatgMNBZ6rzgQuNOJ2DH87IzD1mZ0wL7iApvk6gkxSxcz5tmbU8dfYOYJdlBlhxGk2Nkg2V3P9FPC0hBY73szEV+1DUoqwl+COAsAXO9Uiebr0faQvWOkVT7pypunnjPrBBUaaXn2IcoPIdXZfXVLjXH2JbWSHL5J+yIGHewSMzZ/Xx7u6hwxUP0QLFHrnhD0WDukoBjoUZ2sshMP+DHgoyWjCg+uVpmjJAksp80f34WfNku5Grt90kYEj+N+x2JQ1Y4aQIXASIwDshicbJIsl+RMIMmwe+TElUJ6g9aa0qCr op2-east1-1031
    [WARNING]: Platform linux on host k8s-uat-master3 is using the discovered Python interpreter at /usr/bin/python, but future installation of
    another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more
    information.
    k8s-master3 | CHANGED | rc=0 >>
    no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="echo 'Please login as the user \"centos\" rather than the user \"root\".';echo;sleep 10" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCGKLNMv82MQJUuQ9aZPsDofBj96aQlS1kNV2doSwyLatgMNBZ6rzgQuNOJ2DH87IzD1mZ0wL7iApvk6gkxSxcz5tmbU8dfYOYJdlBlhxGk2Nkg2V3P9FPC0hBY73szEV+1DUoqwl+COAsAXO9Uiebr0faQvWOkVT7pypunnjPrBBUaaXn2IcoPIdXZfXVLjXH2JbWSHL5J+yIGHewSMzZ/Xx7u6hwxUP0QLFHrnhD0WDukoBjoUZ2sshMP+DHgoyWjCg+uVpmjJAksp80f34WfNku5Grt90kYEj+N+x2JQ1Y4aQIXASIwDshicbJIsl+RMIMmwe+TElUJ6g9aa0qCr op2-east1-1031

参考链接

ansible 官方文档
Ansible中文权威指南

脚注