安装、SSH 与第一次连通
本章目标:在控制节点装好 Ansible,建立到托管节点的 SSH 信任,跑通第一次 ping,并搭好后续章节共用的项目骨架。
完成后你应具备:
ansible --version显示 ansible-core 2.15+ansible web -m ping对练习主机全部SUCCESS- 固定的
~/ansible-for-ops目录与ansible.cfg
1. 控制节点装什么
Ansible 采用 控制节点推送、托管节点被动执行(默认 SSH)。你需要:
| 角色 | 需要什么 |
|---|---|
| 控制节点 | ansible-core、SSH 客户端、到目标的网络 |
| 托管节点 | SSH 服务、Python 3(绝大多数现代 Linux 已有)、可 sudo 的运维账号 |
优先安装 ansible-core(执行引擎),而不是一上来装带几百个 collection 的「大礼包」。缺模块时再按集合补装。
macOS
brew install ansible
ansible --version
Linux(pipx / uv,推荐隔离)
# pipx
pipx install ansible-core
# 或 uv
uv tool install ansible-core
ansible --version
python3 -c "import ansible; print(ansible.__version__)"
验证输出里该看什么
ansible [core 2.16.x] # 核心版本
config file = ... # 后面会被项目内 ansible.cfg 覆盖
executable location = ...
python version = 3.x ...
jinja version = ...
历史上 ansible 包 = core + 一堆 community collection。学习与生产锁定依赖时,明确 ansible-core 版本更重要;集合用 ansible-galaxy collection install 按需加。
2. 初始化练习工程
mkdir -p ~/ansible-for-ops/{inventory,group_vars,host_vars,playbooks,roles,files,templates}
cd ~/ansible-for-ops
git init # 可选,但强烈建议从第一天就进 Git
创建项目级 ansible.cfg(放在仓库根,执行时会自动优先生效):
[defaults]
inventory = ./inventory/hosts.ini
roles_path = ./roles
remote_user = ops
private_key_file = ~/.ssh/ansible_ed25519
host_key_checking = True
retry_files_enabled = False
interpreter_python = auto_silent
gathering = smart
fact_caching = memory
timeout = 30
stdout_callback = default
# 需要更易读的汇总时可换: ansible.posix.profile_tasks(需集合)
[privilege_escalation]
become = False
become_method = sudo
become_user = root
become_ask_pass = False
[ssh_connection]
pipelining = True
# 控制并发,弱跳板机可调低
; forks = 10
字段说明:
| 配置 | 作用 |
|---|---|
inventory | 默认清单,少打 -i |
private_key_file | 统一 Ansible 使用的私钥 |
host_key_checking | 保持开启;首次接入前核验并写入主机指纹,避免 MITM |
become | 默认不提权;只在需要改系统状态的 play/task 显式写 become: true |
pipelining | 减少 SSH 往返,加速;需 sudoers 允许 |
3. 准备运维账号与 SSH 免密
3.1 控制节点生成专用密钥
不要直接复用个人 Git 密钥;给 Ansible 单独一把,便于轮换与审计:
ssh-keygen -t ed25519 -C "ansible-control@$(hostname)" \
-f ~/.ssh/ansible_ed25519 -N ""
chmod 600 ~/.ssh/ansible_ed25519
chmod 644 ~/.ssh/ansible_ed25519.pub
3.2 托管节点侧(示例)
假设目标 IP 10.0.1.11,发行版为 Ubuntu。用已有 root/云厂商账号先登录一次,创建 ops:
# 在托管节点上执行
sudo useradd -m -s /bin/bash ops
sudo mkdir -p /home/ops/.ssh
sudo chmod 700 /home/ops/.ssh
# 把控制节点公钥写入(也可 ssh-copy-id)
echo 'ssh-ed25519 AAAA... ansible-control' | sudo tee /home/ops/.ssh/authorized_keys
sudo chmod 600 /home/ops/.ssh/authorized_keys
sudo chown -R ops:ops /home/ops/.ssh
# 仅限练习环境:后续示例需要安装软件、改配置和管理服务
echo 'ops ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/ops
sudo chmod 440 /etc/sudoers.d/ops
NOPASSWD:ALL 让 ops 能以 root 执行任何命令,只适合隔离的学习主机。生产应由平台管理员用 visudo -f /etc/sudoers.d/ops 配置经过评审的命令白名单,并让每个变更 play 显式声明 become: true。例如,可将安装、校验、受控 reload 等命令组成 Cmnd_Alias;不要因为图省事开放 ALL。
3.3 核验并保存主机指纹
保持 host_key_checking=True 时,首次连接前必须确认目标确实是那台主机。先从云控制台、虚拟机控制台或主机管理员处取得 ED25519 指纹,再比对扫描结果;ssh-keyscan 本身不能证明主机身份。
# 先比对输出指纹与可信渠道提供的指纹,确认一致后才执行下一行
ssh-keyscan -t ed25519 10.0.1.11 2>/dev/null | ssh-keygen -lf -
ssh-keyscan -H -t ed25519 10.0.1.11 >> ~/.ssh/known_hosts
chmod 600 ~/.ssh/known_hosts
控制节点验证:
ssh -i ~/.ssh/ansible_ed25519 ops@10.0.1.11 'whoami && sudo -n true && echo sudo_ok'
3.4 ssh-copy-id 快捷方式
若目标已允许密码登录一次:
ssh-copy-id -i ~/.ssh/ansible_ed25519.pub ops@10.0.1.11
4. 最小 Inventory
inventory/hosts.ini:
# 逻辑主机名(inventory_hostname)与真实地址分离
[web]
web1 ansible_host=10.0.1.11
# web2 ansible_host=10.0.1.12
[web:vars]
ansible_user=ops
# 若端口非 22:
# ansible_port=2222
也可用 YAML(等价):
# inventory/hosts.yml
all:
children:
web:
hosts:
web1:
ansible_host: 10.0.1.11
vars:
ansible_user: ops
本系列默认 ini,简单够用。
5. 第一次连通:ping
cd ~/ansible-for-ops
ansible web -m ping -vv
成功示例:
web1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ping 模块不是 ICMP,而是:SSH 登录 → 找 Python → 跑一小段 Ansible 模块代码 → 返回 pong。能 ping 通,说明后续绝大多数模块的通道是通的。
失败对照表
| 报错关键词 | 常见原因 | 处理 |
|---|---|---|
UNREACHABLE / timed out | 网络、安全组、SSH 端口、主机关机 | nc -vz IP 22、查云防火墙 |
Permission denied (publickey) | 用户错、公钥未装、私钥路径错 | 核对 ansible_user、private_key_file、authorized_keys |
Host key verification failed | known_hosts 冲突且开启了检查 | 更新 known_hosts,或临时关检查(仅实验) |
Missing sudo password | 无 NOPASSWD 且未 -K | 配 sudoers,或 ansible ... -K |
/usr/bin/python 找不到 | 极老系统或精简容器 | 设 ansible_python_interpreter=/usr/bin/python3 |
Failed to create temporary directory | home 权限、磁盘满、强制不可写 tmp | 查 ops 家目录权限与磁盘 |
指定解释器示例(写在 inventory 或 host_vars):
web1 ansible_host=10.0.1.11 ansible_python_interpreter=/usr/bin/python3
6. 用 ad-hoc 确认「能查、能提权」
# 发行版 facts(子集)
ansible web -m setup -a 'filter=ansible_distribution*'
# 当前用户(未提权)
ansible web -m command -a 'id' --become=false
# 提权后
ansible web -m command -a 'id'
# 磁盘概况(只读;声明不成变更)
ansible web -m command -a 'df -h /'
在模块参数里更规范的写法是 playbook;ad-hoc 适合排障与临时查询,不适合作为长期变更入口。
7. 建议的本地联调拓扑
没有两台云主机时,可用:
- 本地 Linux 虚拟机(UTM / VirtualBox / VMware)
- 云厂商一台抢占式/按量小规格
- 容器当演示(有局限:systemd/服务类任务容易失真)
service/systemd、内核参数、真实网络路径在容器里和宿主机差异很大。学模块语法可以,验证生产 playbook 请用真正的虚拟机或云主机。
8. 练习与验收
练习 A
- 再准备第二台主机
web2,写入 inventory ansible web -m ping两台均 SUCCESS
练习 B
- 故意写错
private_key_file,观察报错 - 改回后,用
-vvv看 SSH 实际命令行(排障时很有用)
验收清单
- 项目目录与
ansible.cfg就绪 - 专用 SSH 密钥可登录
ops -
ops可无密码 sudo(或你已知如何-K) -
ansible web -m ping全绿
下一章:Inventory 与变量 —— 把主机编组,并把配置从「写死在命令行」升级为「分层变量」。