1.3 第一次批量执行:从 ssh 循环到 ad-hoc


1.3 第一次批量执行:从 ssh 循环到 ad-hoc

本节摘要:一节完整的动手课。从一段所有人都写过的 shell 循环出发,逐步改造成 Ansible 临时命令(ad-hoc),途中建立清单(Inventory)、模块(Module)、返回状态三个第一印象,并在结尾留一道把临时命令升级为剧本的练习。环境要求:一台 Linux 控制机,两台可 SSH 登录的目标机。

先把手工方式的问题摆上桌

假设你管着三台 web 机,hosts 文件里存着地址。现在的任务:确认三台机器的 Nginx 是否都在运行。手工时代的标准写法:

# 手工循环:许多人用了多年的写法 for h in web1 web2 web3; do echo "=== $h ===" ssh "$h" "systemctl is-active nginx" done

这段脚本第一次写的时候很顺手,但它的缺陷会随规模暴露:web2 加进清单时你要改脚本;某天机器换成非标准端口,你要在循环里加端口参数;输出格式靠 echo 拼接,没有结构化的成败汇总;最要命的是它只做"查看",一旦要"修改",ssh "$h" "systemctl restart nginx" 的执行结果你只能靠退出码猜。问题不在 shell 写得不好,而在每一层能力都要自己造:清单、并发、结果汇总、失败处理。

Ansible 的对应写法只要一行:

# 前提:ansible.cfg 或环境变量指明清单文件 ansible web -m ansible.builtin.service -a "name=nginx state=started" -i inventory.ini

执行后你会看到类似输出:

web1 | SUCCESS => { "ansible_facts": {"name": "nginx", "state": "started", "status": {"ActiveState": "active"}}, "changed": false } web2 | CHANGED => { "changed": true, "state": {"name": "nginx", "state": "started"} } web3 | SUCCESS => {...} PLAY RECAP ******************************************************************** web1 : ok=1 changed=0 unreachable=0 failed=0 web2 : ok=1 changed=1 unreachable=0 failed=0 web3 : ok=1 changed=0 unreachable=0 failed=0

逐行读这段输出,三个概念就位。第一,web 是清单里的组名——机器的集合先有名字,命令才能按名字下达,而不是按 IP 列表。第二,-m service 指定模块,模块是 Ansible 的原子能力单元,它知道"started"在 systemd 机器上意味着执行 systemctl,在 SysV 机器上又意味着另一套动作——你写的意图被翻译成了平台正确的命令。第三,每个结果带一个 changed 布尔值:web1 本来就在跑,什么都没做,标记 false;web2 原来是停着的,被拉起,标记 true。这个区分在第 1.1 节讲幂等性时出现过,现在你亲眼见到它了。

环境搭建的五步清单

还没有环境的读者按下面顺序操作,全程约一刻钟。控制机以 Ubuntu 为例,其他发行版只差包管理器。

# 第一步:控制机安装 Ansible(官方 PPA 保证版本较新) sudo apt update && sudo apt install -y ansible # 第二步:准备目标机的 SSH 免密(把控制机公钥分发过去) ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519 for h in web1 web2 web3; do ssh-copy-id "$h"; done # 第三步:写一份最小清单(INI 格式,最直观) cat > inventory.ini <<'EOF' [web] web1 ansible_host=10.0.0.11 web2 ansible_host=10.0.0.12 web3 ansible_host=10.0.0.13 [web:vars] ansible_user=deploy EOF # 第四步:验证连通性(ping 模块实质是跑一个 Python 探针,不是 ICMP) ansible all -m ansible.builtin.ping -i inventory.ini # 第五步:查看某台机器的关键事实(Facts) ansible web1 -m ansible.builtin.setup -i inventory.ini | head -20

第三步的清单值得停一下。ansible_host 覆盖默认的按名字解析;[web:vars] 给整组定义变量,ansible_user 指定登录用户——如果三台机器的登录用户不同,把变量挪到主机行即可,主机变量的优先级高于组变量。这份清单是第 3.1 节的主角,现在能读懂组、主机、变量三层的含义就够了。

第五步的 setup 模块会返回一台机器的完整事实:操作系统版本、CPU 核数、内存、网卡地址、磁盘挂载。后续剧本里写的 ansible_memtotal_mbansible_default_ipv4.address 都来自这里。先看一眼建立印象,变量系统如何使用这些事实,第 3.4 节展开。

拓扑与执行路径

动手环节的机器关系如下图:控制机只依赖 SSH 与 Python 这两个既有设施,把模块代码临时送达目标机执行。

图 1-3:最小实验环境的执行拓扑

图 1-3:最小实验环境的执行拓扑

常见第一坑与自查方法

坑一,ping 模块报 "Failed to connect via ssh"。九成是免密没配好或 known_hosts 首次确认卡住。先用 ssh web1 手工登录一次确认链路,再回来跑;批量环境下可以临时设置 ANSIBLE_HOST_KEY_CHECKING=False(仅限实验环境,生产禁用)。

坑二,报错 "module ... needs Python"。目标机没装 Python 或版本过低,装一个即可——这是无代理架构"唯一前提"的兑现时刻。

坑三,权限不足。模块默认用登录用户执行,涉及系统目录的操作要么以 sudo 用户登录,要么加 -b(become)参数提权。ansible web -m package -a "name=htop state=present" -b 是常见组合。提权方式与密码策略的正式讨论在第 4.3 节。

本节的练习:把 1.1 节那段"确保 nginx 已安装且在运行"的两个任务保存成 nginx.yml,用 ansible-playbook nginx.yml -i inventory.ini 跑两遍,对照第二遍输出里 changed 归零的现象,在终端里亲眼确认幂等。剧本的逐行语法下一章才正式讲,现在照抄能跑就行——先让手熟悉节奏,脑子的事交给第 2、3 章。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U