本文详细介绍了 Ansible 自动化运维工具的基本概念与使用方法。作为一种基于 Python 的自动化工具,Ansible 可通过单个控制节点管理多个被管理节点,实现批量文件配置、部署和命令执行。文章涵盖了 Ansible 的安装步骤、主机环境准备、inventory 文件配置以及执行 ad-hoc 命令的实用示例,是运维人员和开发者快速掌握 Ansible 的入门指南。
安装#
Ansible 是一种自动化运维工具,可以在单个主机 (控制节点 control node) 上管理多个主机 (被管理节点 managed node),比如批量文件配置,批量部署,批量运行命令等,在运维或者部署服务集群时很有用。
由于 Ansible 是用 Python 实现的,所以安装也很简单:
pip install ansible当然也有其它很多种安装方式,比如通过源码安装,或者通过系统包管理器安装,最简单的就是使用 Pip。
主机的环境准备#
基于 Red Hat Enterprise Linux 5.14 x86_64 系统
ansible 通过 ssh 实现主机间的通信,所以首先要保证主机间能相互”看得见”,假设有两个主机:host1, host2
-
设置 hostname 在部署服务集群的时候,为了方便可以将各个机器进行命名。 修改
/etc/hostname文件,将相应的主机名改为如:node1,node2。 -
修改
/etc/hosts文件,比如当有两个主机时,在每个主机的/etc/hosts文件中增加如下内容:
172.31.89.117 node1172.31.67.18 node2每个 IP 与相应的主机名对应
- 配置 ssh 公私钥
在每个主机上执行
ssh-keygen命令,产生 rsa 类型密钥,然后执行ssh-copy-id将每个主机上的公钥传给另一个主机,
也可以手动在 ~/.ssh/authorized_keys 中添加。这样每个主机上就能不输入密码,通过 ssh 命令进入另一个主机。
Ansible 基本概念#
安装完成后,系统新增一系列命令,我们主要使用这三个:ansible, ansible-inventory, ansible-playbook
1. inventory 文件#
主机变量配置文件,可以理解为主机清单,主机库存文件。主要是在一个主节点上统一集中配置各个主机(节点)系统信息,网络位置等信息。
ansible 的默认主机配置文件在 /etc/ansible/hosts,如果在该默认路径下没有,则需要手动指定。
这个配置文件可以是 INI,或者 YML 格式。
如果是 INI 文件,配置如下:
inventory.ini
[myhosts]172.31.89.117172.31.67.18这里 myhosts 是组名 (group name),这里即 host 组,作为 key 值需要是唯一的,且大小写敏感。
同样如果是 YML 文件,配置如下:
inventory.yml
myhosts: hosts: node1: ansible_host: 172.31.89.117 node2: ansible_host: 172.31.67.18这里的 ansible_host 是 ansible 的默认主机变量,相应的还有 http_port 用来指定端口。
如果它们有共同的变量,可以这么写:
myhosts: hosts: node1: ansible_host: 172.31.89.117 http_port: 8080 node2: ansible_host: 172.31.67.18 http_port: 8081 vars: ansible_user: "name"其中 vars 与 hosts 同级,表示 vars 下面定义的变量是 host 间共用的。
如果是 ini 文件,则是:
[myhosts]172.31.89.117172.31.67.18
[myhosts:vars]ansible_user="name"可以嵌套其它组的组叫做元组 (metagroup),例如以下示例文件:
inventory-demo.yml
leafs: hosts: leaf01: ansible_host: 192.0.2.100 leaf02: ansible_host: 192.0.2.110
spines: hosts: spine01: ansible_host: 192.0.2.120 spine02: ansible_host: 192.0.2.130
network: children: leafs: spines:
webservers: hosts: webserver01: ansible_host: 192.0.2.140 webserver02: ansible_host: 192.0.2.150
datacenter: children: network: webservers:其中 network 是一个 metagroup,它的子组包含另外两个组:leafs, spines。
而 datacenter 也是一个 metagroup,它包含 network, webservers 这两个组。
所以这个配置文件是一个 data center 的主机配置文件。
可以用 ansible-inventory 命令来校验一下配置文件。(特别对于 .yml 文件,可以看是否有语法错误)
[root@node1]# ansible-inventory -i inventory.yml --list{ "_meta": { "hostvars": { "node1": { "ansible_host": "172.31.89.117" }, "node2": { "ansible_host": "172.31.67.18" } } }, "all": { "children": [ "ungrouped", "myhosts" ] }, "myhosts": { "hosts": [ "node1", "node2" ] }}同样可以使用 ansible 命令来 ping 一下 配置文件中的 host 组:
[root@node1]# ansible myhosts -m ping -i inventory.ini172.31.67.18 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "ping": "pong"}172.31.89.117 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "ping": "pong"}这里的 -m 指定了 ansible 内置的 ping 命令模块来执行通过 -i 指定的主机清单文件。
到这里为止,在介绍 playbook 之前,我们可以做一些好玩的事情:
使用 ansible 的方式除了使用写 playbook “剧本”(or 脚本) 来编排大量复杂的任务之外,还可以在命令行直接执行,在 ansible 中这叫做 ad-hoc 命令。
刚刚执行的 ansible myhosts -m ping -i inventory.ini 就是这种方式。
为了方便,我们将主机的 inventory 文件放到 /etc/ansible/hosts,默认会去这里找配置文件,因此不再需要使用 -i 手动指定了。
1. 批量管理主机#
举个例子 🙋♀️🌰,假如手上有 20 台主机,想批量 reboot 它们,配置好主机清单之后:
ansible myhosts -a "/sbin/reboot" -f 10选项 -f 10 f 表示 forks,表示并行任务数,这里是每次重启 10 个。
tips: ansible 可以并发执行任务,默认的并发数是 5
2. 批量处理文件#
比如将本机的配置文件批量复制到所有主机,使用 copy 命令模块:
[root@node1]$ ansible myhosts -m copy -a "src=/etc/ansible/hosts dest=/tmp/hosts"node2 | CHANGED => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": true, "checksum": "c03a95124b63b489f76cc2a72ac72571f98290a5", "dest": "/tmp/hosts", "gid": 0, "group": "root", "md5sum": "7402f43011c6b536aaf58fe8963fa843", "mode": "0644", "owner": "root", "secontext": "unconfined_u:object_r:admin_home_t:s0", "size": 107, "src": "/root/.ansible/tmp/ansible-tmp-1726336410.0496075-9635-157233006681802/source", "state": "file", "uid": 0}node1 | CHANGED => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": true, "checksum": "c03a95124b63b489f76cc2a72ac72571f98290a5", "dest": "/tmp/hosts", "gid": 0, "group": "root", "md5sum": "7402f43011c6b536aaf58fe8963fa843", "mode": "0644", "owner": "root", "secontext": "unconfined_u:object_r:admin_home_t:s0", "size": 107, "src": "/root/.ansible/tmp/ansible-tmp-1726336410.0473666-9634-77523772033960/source", "state": "file", "uid": 0}如果再重复执行一遍,并不会再次传输文件,除非之后文件的内容改变了,这个时候其 checksum 也变了。
重复执行的时候,不再是 CHANGED 状态,而是 SUCCESS, "changed": false。
tips: ansible 执行任务是幂等的,同一个操作可以重复执行,不影响结果。
可以查看一下,使用 shell 命令模块:
[root@node1 ]$ ansible myhosts -m shell -a 'cat /tmp/hosts'node2 | CHANGED | rc=0 >>myhosts: hosts: node1: ansible_host: 172.31.89.117 node2: ansible_host: 172.31.67.18node1 | CHANGED | rc=0 >>myhosts: hosts: node1: ansible_host: 172.31.89.117 node2: ansible_host: 172.31.67.18也可以删除掉:
[root@node1 /home ]$ ansible myhosts -m shell -a 'rm /tmp/hosts'node2 | CHANGED | rc=0 >>
node1 | CHANGED | rc=0 >>rc = 0 表示 shell 命令执行结果返回值为 0,执行成功
3. 批量包管理#
ansible 有专门的包管理模块,支持 apt, yum, dnf, dpkg,以及其它通用的包管理器。
当我要确定每个主机上已经安装了某个软件包,同时不想去升级它们:
ansible myhosts -m yum -a "name=python state=present"预期状态 state 是 present,所以当安装了这个包,就是符合预期,返回成功 SUCCESS。
如果要确认一个包还没安装:
[root@node1 ]$ ansible myhosts -m yum -a "name=acme state=absent"node2 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "msg": "Nothing to do", "rc": 0, "results": []}node1 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "msg": "Nothing to do", "rc": 0, "results": []}因为没有安装,与预期状态 state=absent 相符合,所以也返回 SUCCESS。
4. 批量管理服务#
在 RHEL 系统里,这里的服务包含 systemd 服务,init 服务等。
比如修改了网络配置文件后,需要确认网络服务已经重新 reload,加载新的配置文件了,则执行:
[root@node1 ]$ ansible myhosts -m service -a "name=NetworkManager state=reloaded" node2 | CHANGED => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": true, "name": "NetworkManager", "state": "started", # 返回信息太多了,其它的就不显示了这相当于在每个主机上执行了这个命令:systemctl reload NetworkManager.service。
这里的 state 可以是 reloaded, restarted, started, stopped,对应着 systemd 服务的 reload, restart, start, stop 操作。
5. 批量配置 cron 定时任务#
[root@node1]$ ansible all -m cron -a 'name="ntp update every 5 min" minute=*/5 job="/sbin/ntpdate 172.17.0.1 &> /dev/null"'node2 | CHANGED => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": true, "envs": [], "jobs": [ "ntp update every 5 min" ]}node1 | CHANGED => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": true, "envs": [], "jobs": [ "ntp update every 5 min" ]}这里 -a 指定的参数有 name, minute, job,分别是任务名称,任务定时器,任务脚本。
照例查看一下设置的任务:
[root@node1]$ ansible myhosts -m shell -a "crontab -l"node2 | CHANGED | rc=0 >>#Ansible: ntp update every 5 min*/5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/nullnode1 | CHANGED | rc=0 >>#Ansible: ntp update every 5 min*/5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null以上都用到了 ansible 的内置命令模块,我们可以使用 ansible-doc 命令查看这些命令模块:
ansible-doc -l # 获取全部模块的信息 (包括内置的和第三方的)ansible-doc -s MOD_NAME # 获取指定模块的使用帮助
# e.g.[root@node1]$ ansible-doc -s debug- name: Print statements during execution debug: msg: # The customized message that is printed. If omitted, prints a generic message. var: # A variable name to debug. Mutually exclusive with the `msg' option. Be aware that this # option already runs in Jinja2 context and has an implicit # `{{ }}' wrapping, so you should not be using Jinja2 # delimiters unless you are looking for double interpolation. verbosity: # A number that controls when the debug is run, if you set to 3 it will only run debug when # -vvv or above.2. playbook 文件#
Playbook#
inventory 文件指定了主机的各种配置信息,确定了每个节点的角色。而 playbook 文件就是指定要在主机上执行什么批量任务,比如安装集群,更新集群,卸载集群等等,或者只是批量执行某个命令。
简单讲,playbook 文件就是任务编排文件,可以类比理解为 Docker 中的 Dockerfile 文件。
在实际项目中,要执行的任务包含一系列的复杂的步骤,它们组成一系列任务 (play)。
Play#
playbook 文件中的,由一系列按顺序排列的任务 (task) 组成的有序任务列表。
Task#
某个任务中,某项单独的操作,一般通过使用 ansible 定义的某个的命令模块 (module) 来执行操作。
Module#
模块就是一些命令或者可执行文件的抽象,在 Task 中通过调用某个模块来执行具体的命令。
以下是一个 playbook 文件的示例:
- name: My first play hosts: myhosts tasks: - name: Ping my hosts ansible.builtin.ping:
- name: Print message ansible.builtin.debug: msg: Hello world这个一个 playbook 文件,里面很简单只有一个 play,因为只有一个 tasks,而这个 tasks 包含两个 task:
第一个 task 调用了 ansible 内置的 ping 命令模块,执行 ping 命令。
另一个 task 调用了 ansible 内置的 debug 命令模块,打印 debug 信息,将模拟 debug 信息传递给 msg 变量。
最后我们用 ansible-playbook 命令执行一下这个 playbook。
[root@node1]# ansible-playbook -i inventory.ini playbook.yml
PLAY [My first play] ***********************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************ok: [172.31.67.18]ok: [172.31.89.117]
TASK [Ping my hosts] ***********************************************************************************************************************************ok: [172.31.67.18]ok: [172.31.89.117]
TASK [Print message] ***********************************************************************************************************************************ok: [172.31.67.18] => { "msg": "Hello world"}ok: [172.31.89.117] => { "msg": "Hello world"}
PLAY RECAP *********************************************************************************************************************************************172.31.67.18 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0172.31.89.117 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0其中 Gathering Facts 任务是默认执行的,在实际任务开始前用来收集主机的信息,在这里是读取 inventory.ini 文件。
最后 PLAY RECAP 总结了本次执行任务的总体情况,它们都是 ok 状态的。
以上就完成了 ansible playbook 的 hello world 过程。
常用的 module#
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/index.html
子任务类#
ansible.builtin.import_playbook
ansible.builtin.include_role
ansible.builtin.include_tasks
ansible.builtin.import_tasks
命令类#
ansible.builtin.shell
ansible.builtin.command
ansible.builtin.raw
ansible.builtin.script
ansible.builtin.win_shell
文件管理类#
ansible.builtin.file
ansible.builtin.tempfile
ansible.builtin.lineinfile
ansible.builtin.copy
ansible.builtin.stat
ansible.builtin.find
ansible.builtin.git
包管理类#
ansible.builtin.pip
ansible.builtin.yum
ansible.builtin.apt
服务管理类#
ansible.builtin.service
ansible.builtin.service_facts
ansible.builtin.systemd_service
ansible.builtin.sysvinit
具体使用可以看官方文档,或者使用 ansible-doc -s [module] 查阅。
以上。