目录
- Ansible 概述与安装
- 主机清单 (Inventory)
- Ad-hoc 临时命令与常用模块
- Playbook 剧本基础
- 变量 (Variables)
- 逻辑控制 (When & Loop)
- Handlers 触发器
- Jinja2 模板
- Roles 角色编排
- ansible配置nginx例子(无roles角色编排)
- ansible配置nginx例子(roles角色编排)
- ansible当中选择连接远程服务器的用户的优先级
- Ansible 的
ping不是 ICMP ping(不是网络 ping) - 不同 ping 失败类型,对应 “能否执行其他操作” 的结论
1. Ansible 概述与安装
1.1 简介
Ansible 是一个开源的自动化运维工具,基于 Python 开发。
- 无 Agent (Agentless): 不需要被管理节点安装客户端,通过 SSH 协议通信。
- 幂等性 (Idempotency): 大多数模块具有幂等性,即多次执行相同的操作,结果是一致的,不会产生副作用。
- 声明式: 描述“想要达到什么状态”,而不是“如何做”。
1.2 安装
以 CentOS/RHEL 为例:
# 安装 EPEL 源
sudo yum install epel-release -y
# 安装 Ansible
sudo yum install ansible -y
# 验证安装
ansible --version
1.3 基础配置
配置文件通常位于 /etc/ansible/ansible.cfg。建议在项目目录下创建自定义 ansible.cfg:
[defaults]
inventory = ./hosts
remote_user = root
host_key_checking = False # 关闭 SSH 主机密钥检查
2. 主机清单 (Inventory)
Inventory 文件定义了 Ansible 管理的主机。默认位置 /etc/ansible/hosts,也可以通过 -i 指定。
2.1 基础格式
# 未分组的主机
192.168.1.10
# 分组 [webservers]
[webservers]
192.168.1.11 192.168.1.12 # 连续 IP 写法
[dbservers]
db-[1:3].example.com # 对应 db-1, db-2, db-3
2.2 主机变量与组变量
[webservers]
web1 ansible_host=192.168.1.11 ansible_port=2222
[webservers:vars]
http_port=80
2.3 组嵌套 (Children)
[apache]
web1
[nginx]
web2
[all_web:children]
apache nginx
3. Ad-hoc 临时命令与常用模块
Ad-hoc 适用于临时执行一次性任务。
语法: ansible <主机模式> -m <模块名> -a "<参数>"
3.1 常用模块详解
| 模块 | 描述 | 示例命令 |
|---|---|---|
| ping | 测试连通性 (非 ICMP ping) | ansible all -m ping |
| command | (默认模块) 执行 Shell 命令,不支持管道/重定向 | ansible web -m command -a "uptime" |
| shell | 执行 Shell 命令,支持管道/重定向 | ansible web -m shell -a "ps -ef | grep nginx" |
| copy | 复制文件到远程主机 | ansible web -m copy -a "src=./idx.html dest=/var/www/html/" |
| file | 管理文件/目录属性 (创建、删除、权限) | ansible web -m file -a "path=/tmp/dir state=directory mode=755" |
| yum/apt | 软件包管理 | ansible web -m yum -a "name=nginx state=present" |
| service | 服务管理 (启动、停止、重启) | ansible web -m service -a "name=nginx state=started enabled=yes" |
| user | 用户管理 | ansible web -m user -a "name=deploy uid=1001" |
| setup | 收集远程主机信息 (Facts) | ansible web -m setup |
4. Playbook 剧本基础
Playbook 是 Ansible 的核心,使用 YAML 格式定义一系列任务。
4.1 核心结构
- Hosts: 指定执行的主机
- Tasks: 任务列表
- Remote_user: 远程执行用户
4.2 示例:部署 Web 服务器
创建文件 web_deploy.yml:
---
- name: 部署 Nginx Web 服务器
hosts: webservers
remote_user: root
gather_facts: yes # 收集主机信息
tasks:
- name: 安装 Nginx
yum:
name: nginx
state: present
- name: 确保 Nginx 正在运行并开机自启
service:
name: nginx
state: started
enabled: yes
- name: 部署自定义首页
copy:
src: index.html
dest: /usr/share/nginx/html/index.html
执行 Playbook:
ansible-playbook web_deploy.yml
5. 变量 (Variables)
5.1 变量定义位置与优先级
优先级从低到高:
- Inventory 文件中的变量
- Playbook 中的
vars - 命令行传递
-e "var=value"
5.2 在 Playbook 中定义
- hosts: all
vars:
package_name: httpd
service_port: 8080
tasks:
- name: 安装包
yum:
name: "{{ package_name }}"
5.3 注册变量 (Register)
将任务的输出保存到变量中,常用于后续调试或判断。
tasks:
- name: 检查文件是否存在
shell: ls /tmp/flag
register: file_check
ignore_errors: yes
- name: 输出检查结果
debug:
msg: "文件检查结果: {{ file_check.rc }}"
6. 逻辑控制 (When & Loop)
6.1 条件判断 (When)
使用 when 关键字进行条件判断。支持 and, or, not, ==, != 等逻辑。
tasks:
- name: 仅在 CentOS 系统安装 httpd
yum:
name: httpd
state: present
when: ansible_distribution == "CentOS"
- name: 逻辑组合示例
command: /bin/something
when:
- ansible_distribution == "CentOS"
- ansible_memtotal_mb > 1024
# 列表形式默认为 AND 关系
# 或者: when: ansible_distribution == "CentOS" and ansible_memtotal_mb > 1024
6.2 循环语句 (Loop)
推荐使用 loop (旧版本为 with_items)。
tasks:
- name: 创建多个用户
user:
name: "{{ item }}"
state: present
loop:
- user1
- user2
- user3
- name: 循环字典列表
user:
name: "{{ item.name }}"
uid: "{{ item.id }}"
loop:
- { name: 'alice', id: 1010 }
- { name: 'bob', id: 1011 }
7. Handlers 触发器
Handlers 是特殊的任务,只有在被其他任务通过 notify 触发且该任务状态为 changed 时才会执行。常用于重启服务。
tasks:
- name: 修改配置文件
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart Nginx # 触发 Handler
handlers:
- name: Restart Nginx # 名称必须与 notify 一致
service:
name: nginx
state: restarted
8. Jinja2 模板
使用 template 模块可以动态生成配置文件。Ansible 使用 Python 的 Jinja2 模板引擎。
8.1 模板语法
{{ variable }}: 变量输出{% control %}: 逻辑控制 (if, for){# comment #}: 注释
8.2 示例
Playbook 变量:
vars:
worker_processes: 4
vhosts:
- port: 8080
name: site1
- port: 8081
name: site2
模板文件 nginx.conf.j2:
worker_processes {{ worker_processes }};
http {
{% for vhost in vhosts %}
server {
listen {{ vhost.port }};
server_name {{ vhost.name }}.example.com;
}
{% endfor %}
}
9. Roles 角色编排
Roles 是 Ansible 组织 Playbook 的最佳实践方式,它将变量、任务、文件、模板等按目录结构分离,实现代码复用。
9.1 标准目录结构
roles/
└── common/
├── tasks/
│ └── main.yml # 主任务入口
├── handlers/
│ └── main.yml # Handlers
├── templates/ # 存放 .j2 模板
├── files/ # 存放静态文件 (copy 模块用)
├── vars/
│ └── main.yml # 角色内部变量
├── defaults/
│ └── main.yml # 默认变量 (优先级最低)
└── meta/
└── main.yml # 依赖关系
9.2 创建 Role
可以使用 ansible-galaxy init <role_name> 快速创建目录结构。
9.3 使用 Roles
在主 Playbook (site.yml) 中调用:
---
- hosts: webservers
roles:
- common
- nginx
- { role: php, php_version: 7.4 } # 传递参数给 Role
9.4 任务重用与 Tags
# roles/common/tasks/main.yml
- name: 安装基础工具
yum:
name: ["wget", "curl", "vim"]
state: present
总结: 本笔记构建了 Ansible 的核心知识体系。建议按照 安装 -> Ad-hoc -> Playbook -> Roles 的路径进行实践,逐步掌握自动化运维的能力。
10.例子1:nginx_deploy
本示例将演示如何编写一个完整的 Ansible Playbook,用于在远程主机上自动化部署 Nginx Web 服务器。整个流程包含三个核心步骤:安装 (Install) -> 配置 (Configure) -> 启动 (Start)。
1. 项目目录结构
建议的目录结构如下,保持清晰和规范:
nginx-deploy/
├── hosts # 主机清单文件
├── deploy_nginx.yml # 主 Playbook 剧本
└── templates/
└── nginx.conf.j2 # Nginx 配置文件模板
2. 编写主机清单 (hosts)
定义目标主机及相关变量。
文件内容: hosts
[webservers]
192.168.1.101
192.168.1.102
[webservers:vars]
http_port=80 server_name=example.com
3. 编写配置文件模板 (templates/nginx.conf.j2)
使用 Jinja2 模板语法,将变量嵌入配置文件中。当变量改变时,Ansible 会自动更新远程主机的配置。
文件内容: templates/nginx.conf.j2
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 使用变量定义日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
# 使用 Inventory 中定义的变量
listen {{ http_port }};
server_name {{ server_name }};
root /usr/share/nginx/html;
location / {
}
}
}
4. 编写 Playbook (deploy_nginx.yml)
这是核心执行文件,串联了安装、配置、启动的逻辑,并使用 Handlers 处理服务重启。
文件内容: deploy_nginx.yml
---
- name: 自动化部署 Nginx 服务
hosts: webservers
remote_user: root
gather_facts: yes # 收集系统信息,用于判断系统类型等
vars:
nginx_package_name: nginx
nginx_service_name: nginx
nginx_config_path: /etc/nginx/nginx.conf
tasks:
# -------------------------------------------------------
# 1. 安装阶段 (Install)
# -------------------------------------------------------
- name: 安装 EPEL 源 (CentOS/RHEL 需要)
yum:
name: epel-release
state: present
when: ansible_os_family == "RedHat"
- name: 安装 Nginx 软件包
yum:
name: "{{ nginx_package_name }}"
state: present
# -------------------------------------------------------
# 2. 配置阶段 (Configure)
# -------------------------------------------------------
- name: 分发 Nginx 配置文件
template:
src: templates/nginx.conf.j2
dest: "{{ nginx_config_path }}"
owner: root
group: root
mode: '0644'
validate: 'nginx -t -c %s' # 分发前先验证配置文件的语法正确性
notify: Reload Nginx # 如果配置文件发生变化,通知 Handler
# -------------------------------------------------------
# 3. 启动阶段 (Start)
# -------------------------------------------------------
- name: 确保 Nginx 服务已启动并设置为开机自启
service:
name: "{{ nginx_service_name }}"
state: started
enabled: yes
# -------------------------------------------------------
# Handlers: 仅在被 notify 且状态改变时触发
# -------------------------------------------------------
handlers:
- name: Reload Nginx
service:
name: "{{ nginx_service_name }}"
state: reloaded
5. 执行 Playbook
在终端中运行以下命令来执行部署:
5.1 语法检查
在执行前,建议先检查语法是否有误:
ansible-playbook -i hosts deploy_nginx.yml --syntax-check
5.2 模拟执行 (Dry Run)
查看将会发生什么改变,但不实际执行:
ansible-playbook -i hosts deploy_nginx.yml --check
5.3 正式执行
ansible-playbook -i hosts deploy_nginx.yml
6. 关键点解析
- 幂等性 (Idempotency):
yum模块:如果 Nginx 已经安装,不会重复安装。template模块:如果远程文件与模板生成的内容一致,不会覆盖,也不会触发 Handler。service模块:如果服务已经启动,不会重复启动。
- Handlers (触发器):
- 我们在配置文件的任务中使用了
notify: Reload Nginx。 - 这意味着,只有当
nginx.conf的内容真正发生改变时,Ansible 才会执行Reload Nginx这个 Handler。 - 这避免了每次运行 Playbook 都重启服务,保证了服务的稳定性。
- 我们在配置文件的任务中使用了
- Validate (配置验证):
validate: 'nginx -t -c %s'是一个非常实用的技巧。- 它会在新配置文件覆盖远程文件之前,先用 Nginx 的语法检查工具测试新生成的临时文件。
- 如果语法检查失败,Ansible 会终止任务,不会覆盖旧的配置文件,从而避免因配置错误导致服务挂掉。
11.例子2:nginx_deploy(roles)
本示例演示如何将之前的 Nginx 单文件 Playbook 重构为 Ansible Roles 结构。Roles 能够实现任务、变量、文件和处理器的分离,极大提高了代码的可读性与复用性。
1. 目录结构设计
标准的 Roles 项目结构如下:
ansible-project/
├── hosts # 主机清单
├── site.yml # 总入口 Playbook
└── roles/
└── nginx/ # Nginx 角色目录
├── tasks/
│ └── main.yml # 核心任务逻辑
├── handlers/
│ └── main.yml # 触发器逻辑
├── templates/
│ └── nginx.conf.j2 # 配置文件模板
├── vars/
│ └── main.yml # 角色专用变量 (优先级较高)
└── defaults/
└── main.yml # 默认变量 (优先级最低,方便用户覆盖)
2. 编写角色文件
2.1 默认变量 (roles/nginx/defaults/main.yml)
定义角色的默认值,使用者可以在 Playbook 中轻松覆盖这些值。
---
nginx_port: 80
nginx_server_name: localhost
nginx_user: nginx
nginx_config_path: /etc/nginx/nginx.conf
2.2 任务文件 (roles/nginx/tasks/main.yml)
只包含具体的执行步骤,不再包含 hosts 等定义。
---
- name: 安装 EPEL 源 (CentOS/RHEL)
yum:
name: epel-release
state: present
when: ansible_os_family == "RedHat"
- name: 安装 Nginx
yum:
name: nginx
state: present
- name: 配置 Nginx
template:
src: nginx.conf.j2
dest: "{{ nginx_config_path }}"
owner: root
group: root
mode: '0644'
validate: 'nginx -t -c %s'
notify: Reload Nginx # 触发 Handler
- name: 启动 Nginx 并设置开机自启
service:
name: nginx
state: started
enabled: yes
2.3 处理器文件 (roles/nginx/handlers/main.yml)
定义被 notify 触发的操作。
---
- name: Reload Nginx
service:
name: nginx
state: reloaded
2.4 模板文件 (roles/nginx/templates/nginx.conf.j2)
使用变量占位。注意这里使用了 defaults/main.yml 中定义的变量名。
user {{ nginx_user }};
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen {{ nginx_port }};
server_name {{ nginx_server_name }};
root /usr/share/nginx/html;
location / {
index index.html index.htm;
}
}
}
3. 编写入口文件
3.1 主机清单 (hosts)
[webservers]
192.168.1.101
192.168.1.102
3.2 总 Playbook (site.yml)
这是执行的入口,负责将主机和角色关联起来。我们可以在这里覆盖默认变量。
---
- name: 部署 Web 服务集群
hosts: webservers
remote_user: root
gather_facts: yes
roles:
- role: nginx
# 在调用角色时覆盖默认变量
vars:
nginx_port: 8080
nginx_server_name: www.example.com
4. 执行部署
在项目根目录下执行:
# 检查目录结构是否正确
tree .
# 执行 Playbook
ansible-playbook -i hosts site.yml
5. 为什么使用 Roles?
对比之前的单文件 Playbook,Roles 的优势在于:
- 解耦: 任务、变量、配置分离,修改配置文件不需要去翻找任务代码。
- 复用: 这个
nginx角色可以被多个项目共用。例如,你可以同时部署一个 web 集群和一个 api 集群,只需在site.yml中调用两次该角色并传入不同的端口变量即可。 - 清晰:
site.yml非常简洁,只描述了“谁做什么”,而“怎么做”的细节被封装在 Roles 内部。
12.ansible当中选择连接远程服务器的用户的优先级
当同时配置时,Ansible 会按以下优先级选择用户(从高到低):
- 任务级别(task)的
remote_user - Play 级别(play)的
remote_user - 命令行指定的
ansible_user - 主机清单 / 变量文件里的
ansible_user - Ansible 配置文件(
ansible.cfg)里的remote_user - 执行 ansible 命令的本地系统用户
举个例子:
- 清单里配置
ansible_user=admin - Playbook 里配置
remote_user=root→ 最终会用root连接(Play 级别 remote_user 优先级更高)。
13. Ansible 的ping不是 ICMP ping(不是网络 ping)
Ansible 的ansible zabbix -m ping和你之前用的ping 172.16.1.81完全是两回事:
- 系统
ping:只验证网络层可达(ICMP 协议); - Ansible
ping:验证「网络可达 + SSH 登录成功 + Python 环境兼容 + Ansible 模块能执行」的全链路,是 Ansible 操作的 “通行证”。
14. 不同 ping 失败类型,对应 “能否执行其他操作” 的结论
| Ansible ping 失败类型 | 其他操作能否执行? | 核心原因 |
|---|---|---|
Connection timed out(端口 22 超时) | 完全不能 | 网络 / 防火墙拦截,连 SSH 都连不上,任何操作都无法传输到目标主机 |
Permission denied(SSH 认证失败) | 完全不能 | 密钥 / 密码不对,SSH 登录失败,模块无法传输到目标主机 |
ModuleNotFoundError(Python 依赖) | 完全不能 | 模块能传输到目标主机,但执行时 Python 依赖缺失,所有模块都会报类似错误 |
SUCCESS但ping返回pong(成功) | 可以正常执行 | 全链路兼容,copy、apt、service 等模块都能正常运行 |
简单说:Ansible ping 失败 = 目标主机不在 Ansible 的 “可管理列表” 里,所有依赖 Ansible 的操作(配置管理、软件安装、命令执行等)都无法在这台 zabbix 服务器上完成。
Comments NOTHING