跳转到内容

Ansible 自动化

Ansible 是一款无代理(Agentless)的 IT 自动化工具,通过 SSH 连接远程主机并执行配置管理、应用部署和任务编排。它使用 YAML 格式编写 Playbook,学习曲线平缓,是 Enterprise Linux 环境中最流行的自动化方案之一。

Ansible 只需要安装在控制节点(你执行命令的机器)上,被管理的远程主机不需要安装任何额外软件,只需要有 SSH 和 Python。

Terminal window
# 方法一:通过 DNF 安装(推荐)
sudo dnf install -y epel-release
sudo dnf install -y ansible-core
# 方法二:通过 pip 安装(获取最新版本)
sudo dnf install -y python3 python3-pip
pip3 install --user ansible
# 验证安装
ansible --version

Ansible 的模块以集合(Collection)形式分发:

Terminal window
# 安装常用集合
ansible-galaxy collection install ansible.posix
ansible-galaxy collection install community.general
ansible-galaxy collection install community.mysql
# 查看已安装集合
ansible-galaxy collection list

Inventory 文件定义了 Ansible 要管理的主机和分组信息。

创建 /etc/ansible/hosts 或项目目录下的 inventory.ini

# 单独的主机
10.0.0.100
# 定义组
[webservers]
web01.example.com
web02.example.com
web03.example.com
[dbservers]
db01.example.com ansible_port=2222
db02.example.com
[appservers]
app[01:05].example.com # app01 到 app05
# 组变量
[webservers:vars]
ansible_user=ops
ansible_become=true
http_port=80
[dbservers:vars]
ansible_user=dba
ansible_become=true
# 父组(包含多个子组)
[production:children]
webservers
dbservers
appservers
inventory.yml
all:
children:
webservers:
hosts:
web01.example.com:
web02.example.com:
vars:
ansible_user: ops
http_port: 80
dbservers:
hosts:
db01.example.com:
ansible_port: 2222
db02.example.com:
vars:
ansible_user: dba
production:
children:
webservers:
dbservers:
Terminal window
# 查看所有主机
ansible-inventory -i inventory.yml --list
# 以图形方式展示
ansible-inventory -i inventory.yml --graph
# 查看特定主机的变量
ansible-inventory -i inventory.yml --host web01.example.com

Ad-Hoc 命令适合执行一次性的快速操作。

Terminal window
# 测试所有主机连通性
ansible all -i inventory.yml -m ping
# 查看远程主机信息
ansible webservers -i inventory.yml -m setup
# 执行 Shell 命令
ansible all -i inventory.yml -m shell -a "uptime"
ansible all -i inventory.yml -m shell -a "free -h"
ansible all -i inventory.yml -m shell -a "df -h /"
# 管理软件包
ansible webservers -i inventory.yml -m dnf -a "name=nginx state=present" --become
ansible webservers -i inventory.yml -m dnf -a "name=* state=latest" --become
# 管理服务
ansible webservers -i inventory.yml -m service -a "name=nginx state=started enabled=yes" --become
# 复制文件
ansible webservers -i inventory.yml -m copy -a "src=/tmp/app.conf dest=/etc/app.conf owner=root mode=0644" --become
# 管理用户
ansible all -i inventory.yml -m user -a "name=deploy state=present groups=wheel" --become
# 重启主机
ansible dbservers -i inventory.yml -m reboot --become
# 限制执行范围
ansible webservers -i inventory.yml -m shell -a "hostname" --limit web01.example.com
# 并行控制(默认 5 个并行)
ansible all -i inventory.yml -m ping -f 20

Playbook 是 Ansible 的核心配置文件,使用 YAML 格式定义一系列有序的任务。

# site.yml - 基础 Web 服务器配置
---
- name: 配置 Web 服务器
hosts: webservers
become: true
vars:
http_port: 80
doc_root: /var/www/myapp
tasks:
- name: 安装必要软件包
ansible.builtin.dnf:
name:
- nginx
- firewalld
- vim-enhanced
state: present
- name: 创建网站目录
ansible.builtin.file:
path: "{{ doc_root }}"
state: directory
owner: nginx
group: nginx
mode: '0755'
- name: 部署首页
ansible.builtin.copy:
content: |
<!DOCTYPE html>
<html>
<head><title>Welcome</title></head>
<body><h1>Server: {{ ansible_hostname }}</h1></body>
</html>
dest: "{{ doc_root }}/index.html"
owner: nginx
group: nginx
mode: '0644'
- name: 启动并启用 Nginx
ansible.builtin.service:
name: nginx
state: started
enabled: true
- name: 启动并启用 Firewalld
ansible.builtin.service:
name: firewalld
state: started
enabled: true
- name: 开放 HTTP 端口
ansible.posix.firewalld:
service: http
permanent: true
state: enabled
immediate: true
Terminal window
# 执行 Playbook
ansible-playbook -i inventory.yml site.yml
# 模拟运行(不实际执行变更)
ansible-playbook -i inventory.yml site.yml --check
# 显示详细输出
ansible-playbook -i inventory.yml site.yml -v
ansible-playbook -i inventory.yml site.yml -vvv
# 限制在特定主机上运行
ansible-playbook -i inventory.yml site.yml --limit web01.example.com
# 从指定任务开始执行
ansible-playbook -i inventory.yml site.yml --start-at-task="启动并启用 Nginx"
# 逐步确认执行
ansible-playbook -i inventory.yml site.yml --step
# 指定额外变量
ansible-playbook -i inventory.yml site.yml -e "http_port=8080"
tasks:
# 安装单个软件包
- name: 安装 Nginx
ansible.builtin.dnf:
name: nginx
state: present
# 安装多个软件包
- name: 安装开发工具
ansible.builtin.dnf:
name:
- gcc
- make
- python3-devel
state: present
# 安装指定版本
- name: 安装指定版本
ansible.builtin.dnf:
name: nginx-1.20.1
state: present
# 更新所有软件包
- name: 系统更新
ansible.builtin.dnf:
name: '*'
state: latest
# 安装软件包组
- name: 安装开发工具组
ansible.builtin.dnf:
name: '@Development Tools'
state: present
# 卸载软件包
- name: 移除旧软件
ansible.builtin.dnf:
name: httpd
state: absent
tasks:
- name: 启动并启用服务
ansible.builtin.service:
name: nginx
state: started
enabled: true
- name: 重启服务
ansible.builtin.service:
name: nginx
state: restarted
- name: 重新加载服务配置
ansible.builtin.service:
name: nginx
state: reloaded
- name: 停止并禁用服务
ansible.builtin.service:
name: postfix
state: stopped
enabled: false
tasks:
# 从控制节点复制文件
- name: 复制配置文件
ansible.builtin.copy:
src: files/nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
backup: true
notify: 重载 Nginx
# 直接写入内容
- name: 写入配置
ansible.builtin.copy:
content: |
server {
listen 80;
server_name {{ ansible_fqdn }};
root /var/www/html;
}
dest: /etc/nginx/conf.d/default.conf
owner: root
group: root
mode: '0644'

Jinja2 模板是 Ansible 最强大的功能之一,可以根据变量动态生成配置文件。

创建模板文件 templates/nginx.conf.j2

# Ansible managed - Do not edit manually
worker_processes {{ ansible_processor_vcpus }};
events {
worker_connections {{ nginx_worker_connections | default(1024) }};
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
{% for vhost in virtual_hosts %}
server {
listen {{ vhost.port | default(80) }};
server_name {{ vhost.server_name }};
root {{ vhost.doc_root }};
{% if vhost.ssl | default(false) %}
listen 443 ssl;
ssl_certificate {{ vhost.ssl_cert }};
ssl_certificate_key {{ vhost.ssl_key }};
{% endif %}
}
{% endfor %}
}

在 Playbook 中使用模板:

- name: 配置 Web 服务器
hosts: webservers
become: true
vars:
nginx_worker_connections: 2048
virtual_hosts:
- server_name: app.example.com
doc_root: /var/www/app
port: 80
- server_name: api.example.com
doc_root: /var/www/api
port: 8080
tasks:
- name: 部署 Nginx 配置
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
validate: nginx -t -c %s
notify: 重载 Nginx
handlers:
- name: 重载 Nginx
ansible.builtin.service:
name: nginx
state: reloaded
tasks:
# 文件和目录管理
- name: 创建目录
ansible.builtin.file:
path: /data/app
state: directory
owner: app
group: app
mode: '0755'
- name: 创建符号链接
ansible.builtin.file:
src: /data/app/current
dest: /var/www/app
state: link
# 管理计划任务
- name: 添加定时备份任务
ansible.builtin.cron:
name: "数据库备份"
minute: "0"
hour: "2"
job: "/usr/local/bin/backup.sh >> /var/log/backup.log 2>&1"
user: root
# 管理 SELinux 布尔值
- name: 允许 Nginx 连接网络
ansible.posix.seboolean:
name: httpd_can_network_connect
state: true
persistent: true
# 管理 sysctl 参数
- name: 设置内核参数
ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: '1'
sysctl_set: true
reload: true
# 从 URL 下载文件
- name: 下载应用包
ansible.builtin.get_url:
url: https://example.com/releases/app-1.0.tar.gz
dest: /tmp/app-1.0.tar.gz
checksum: sha256:abc123...
# 解压文件
- name: 解压应用
ansible.builtin.unarchive:
src: /tmp/app-1.0.tar.gz
dest: /opt/
remote_src: true
tasks:
- name: 仅在 AlmaLinux 上执行
ansible.builtin.dnf:
name: almalinux-release
state: latest
when: ansible_distribution == "AlmaLinux"
- name: 仅在 EL9 上执行
ansible.builtin.debug:
msg: "Running on EL9"
when: ansible_distribution_major_version == "9"
- name: 当服务存在时重启
ansible.builtin.service:
name: nginx
state: restarted
when: "'nginx' in ansible_facts.packages"
tasks:
- name: 创建多个用户
ansible.builtin.user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
loop:
- { name: 'alice', groups: 'wheel' }
- { name: 'bob', groups: 'developers' }
- { name: 'charlie', groups: 'developers' }
- name: 启动多个服务
ansible.builtin.service:
name: "{{ item }}"
state: started
enabled: true
loop:
- nginx
- firewalld
- fail2ban

Handlers 是一种特殊任务,仅在被 notify 触发时才执行,且在所有任务执行完毕后才运行:

- name: 配置服务器
hosts: webservers
become: true
tasks:
- name: 更新 Nginx 配置
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify:
- 验证 Nginx 配置
- 重载 Nginx
- name: 更新 sysctl 配置
ansible.builtin.copy:
src: sysctl.conf
dest: /etc/sysctl.d/99-custom.conf
notify: 应用 sysctl
handlers:
- name: 验证 Nginx 配置
ansible.builtin.command: nginx -t
- name: 重载 Nginx
ansible.builtin.service:
name: nginx
state: reloaded
- name: 应用 sysctl
ansible.builtin.command: sysctl --system

Role 是组织 Playbook 的最佳实践,将相关的任务、变量、模板、文件等按功能归类到标准化目录结构中。

Terminal window
# 使用 ansible-galaxy 创建 role 骨架
ansible-galaxy role init roles/webserver
# 生成的目录结构
tree roles/webserver/
# roles/webserver/
# ├── defaults/
# │ └── main.yml # 默认变量(优先级最低)
# ├── files/ # 静态文件
# ├── handlers/
# │ └── main.yml # Handlers
# ├── meta/
# │ └── main.yml # Role 元数据和依赖
# ├── tasks/
# │ └── main.yml # 主要任务
# ├── templates/ # Jinja2 模板
# └── vars/
# └── main.yml # 角色变量(优先级较高)
roles/webserver/defaults/main.yml
---
nginx_port: 80
nginx_worker_connections: 1024
doc_root: /var/www/html
roles/webserver/tasks/main.yml
---
- name: 安装 Nginx
ansible.builtin.dnf:
name: nginx
state: present
- name: 部署 Nginx 配置
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: 重载 Nginx
- name: 创建网站目录
ansible.builtin.file:
path: "{{ doc_root }}"
state: directory
owner: nginx
group: nginx
mode: '0755'
- name: 启动 Nginx
ansible.builtin.service:
name: nginx
state: started
enabled: true
roles/webserver/handlers/main.yml
---
- name: 重载 Nginx
ansible.builtin.service:
name: nginx
state: reloaded
site.yml
---
- name: 配置 Web 服务器
hosts: webservers
become: true
roles:
- webserver
- name: 配置数据库服务器
hosts: dbservers
become: true
roles:
- role: database
vars:
db_port: 3306
Terminal window
# 搜索 role
ansible-galaxy search nginx --platforms EL
# 安装 role
ansible-galaxy role install geerlingguy.nginx
# 从 requirements 文件批量安装
cat > requirements.yml << 'EOF'
---
roles:
- name: geerlingguy.nginx
- name: geerlingguy.mysql
collections:
- name: community.general
- name: ansible.posix
EOF
ansible-galaxy install -r requirements.yml
ansible-project/
├── ansible.cfg # Ansible 配置
├── inventory/
│ ├── production.yml # 生产环境主机
│ └── staging.yml # 测试环境主机
├── group_vars/
│ ├── all.yml # 所有主机的变量
│ ├── webservers.yml # Web 服务器组变量
│ └── dbservers.yml # 数据库组变量
├── host_vars/
│ └── web01.example.com.yml
├── roles/
│ ├── common/
│ ├── webserver/
│ └── database/
├── site.yml # 主 Playbook
├── webservers.yml
└── dbservers.yml
# ansible.cfg
[defaults]
inventory = inventory/production.yml
remote_user = ops
roles_path = roles
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
[privilege_escalation]
become = True
become_method = sudo
become_ask_pass = False
Terminal window
# 加密文件
ansible-vault encrypt group_vars/dbservers.yml
# 编辑加密文件
ansible-vault edit group_vars/dbservers.yml
# 解密文件
ansible-vault decrypt group_vars/dbservers.yml
# 运行包含加密数据的 Playbook
ansible-playbook site.yml --ask-vault-pass
# 使用密码文件
ansible-playbook site.yml --vault-password-file ~/.vault_pass

Ansible 是 Enterprise Linux 运维自动化的利器,从简单的 Ad-Hoc 命令到复杂的多角色 Playbook,都能高效地管理大规模服务器集群。结合 Cloud-Init 进行初始配置、再用 Ansible 完成后续部署是非常经典的工作流。