跳到主要内容

安装、SSH 与第一次连通

本章目标:在控制节点装好 Ansible,建立到托管节点的 SSH 信任,跑通第一次 ping,并搭好后续章节共用的项目骨架。

完成后你应具备:

  1. ansible --version 显示 ansible-core 2.15+
  2. ansible web -m ping 对练习主机全部 SUCCESS
  3. 固定的 ~/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 vs ansible-core

历史上 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
不要把练习 sudoers 原样带入生产

NOPASSWD:ALLops 能以 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_userprivate_key_fileauthorized_keys
Host key verification failedknown_hosts 冲突且开启了检查更新 known_hosts,或临时关检查(仅实验)
Missing sudo password无 NOPASSWD 且未 -K配 sudoers,或 ansible ... -K
/usr/bin/python 找不到极老系统或精简容器ansible_python_interpreter=/usr/bin/python3
Failed to create temporary directoryhome 权限、磁盘满、强制不可写 tmpops 家目录权限与磁盘

指定解释器示例(写在 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. 建议的本地联调拓扑

没有两台云主机时,可用:

  1. 本地 Linux 虚拟机(UTM / VirtualBox / VMware)
  2. 云厂商一台抢占式/按量小规格
  3. 容器当演示(有局限:systemd/服务类任务容易失真)
不要用 Docker 容器假装生产主机

service/systemd、内核参数、真实网络路径在容器里和宿主机差异很大。学模块语法可以,验证生产 playbook 请用真正的虚拟机或云主机。


8. 练习与验收

练习 A

  1. 再准备第二台主机 web2,写入 inventory
  2. ansible web -m ping 两台均 SUCCESS

练习 B

  1. 故意写错 private_key_file,观察报错
  2. 改回后,用 -vvv 看 SSH 实际命令行(排障时很有用)

验收清单

  • 项目目录与 ansible.cfg 就绪
  • 专用 SSH 密钥可登录 ops
  • ops 可无密码 sudo(或你已知如何 -K
  • ansible web -m ping 全绿

下一章:Inventory 与变量 —— 把主机编组,并把配置从「写死在命令行」升级为「分层变量」。