一、概述
1.1 背景介绍
在大规模服务器运维场景中,配置管理工具是基础设施自动化的核心组件。经过多年生产环境实践,Ansible和SaltStack作为两款主流的配置管理工具,各自占据了相当的市场份额。本文基于笔者在多个企业级项目中的实际运维经验,深入对比这两款工具的架构设计、性能表现、易用性和适用场景,为技术选型提供参考依据。
2015年至今,笔者先后在电商、金融、游戏等多个行业的生产环境中部署和使用过这两款工具。管理的服务器规模从几十台到上万台不等,积累了大量一手经验。Ansible以其无代理架构和低学习曲线著称,而SaltStack则以高性能和事件驱动架构见长。两者各有优劣,选择哪一款取决于具体的业务场景和团队技术栈。
1.2 技术特点
Ansible核心特点:
无代理架构(Agentless):通过SSH协议与目标主机通信,无需在被管理节点安装额外软件
声明式配置:使用YAML格式编写Playbook,描述期望达到的状态
幂等性设计:相同的任务多次执行结果一致,避免配置漂移
模块化架构:内置超过3000个模块,覆盖主流云平台和中间件
Push模式:从控制节点主动推送配置到被管理节点
SaltStack核心特点:
代理架构(Agent-based):在被管理节点部署minion进程,与master保持长连接
事件驱动:基于ZeroMQ消息队列实现异步通信,支持实时事件响应
高性能并发:得益于长连接设计,可同时管理数万台服务器
双模式支持:同时支持Push和Pull两种配置下发模式
远程执行引擎:除配置管理外,提供强大的远程命令执行能力
1.3 适用场景
Ansible适用场景:
| 场景类型 | 具体应用 | 推荐理由 |
|---|---|---|
| 中小规模集群 | 100-500台服务器 | 无代理架构降低运维复杂度 |
| 混合环境管理 | 物理机+虚拟机+容器 | SSH普适性强,兼容性好 |
| 网络设备配置 | 交换机、路由器、防火墙 | 丰富的网络模块支持 |
| CI/CD流水线 | Jenkins、GitLab集成 | 命令行友好,易于集成 |
| 临时任务执行 | Ad-hoc命令、应急操作 | 无需预装代理,即开即用 |
SaltStack适用场景:
| 场景类型 | 具体应用 | 推荐理由 |
|---|---|---|
| 大规模集群 | 1000+台服务器 | 长连接+ZeroMQ高性能通信 |
| 实时配置管理 | 频繁配置变更场景 | 事件驱动,响应速度快 |
| 复杂编排需求 | 多层级依赖关系 | 强大的状态依赖管理 |
| 云原生环境 | Kubernetes、容器编排 | Salt-SSH和Salt-Cloud模块 |
| 安全合规场景 | 实时监控配置状态 | Beacon和Reactor机制 |
1.4 环境要求
| 组件 | Ansible | SaltStack | 说明 |
|---|---|---|---|
| 控制节点操作系统 | CentOS Stream 9/Rocky 9/Ubuntu 24.04 | CentOS Stream 9/Rocky 9/Ubuntu 24.04 | 推荐使用LTS版本 |
| Python版本 | 3.11+ | 3.11+ | 控制节点和被管理节点 |
| 工具版本 | 2.16+ | 3006+ | 当前稳定版本 |
| 内存要求(控制节点) | 4GB+ | 8GB+ | Salt Master需要更多内存 |
| 网络要求 | SSH 22端口 | 4505/4506端口 | 防火墙放行 |
| 被管理节点 | 仅需SSH和Python | 需安装salt-minion | 代理vs无代理 |
二、详细步骤
2.1 准备工作
2.1.1 实验环境规划
本次对比测试环境采用以下架构:
控制节点: - 主机名:ops-master - IP地址:192.168.100.10 - 操作系统:Rocky Linux 9.3 - 配置:4核CPU / 16GB内存 / 100GB SSD 被管理节点(10台测试机): - 主机名:web-node-01 至 web-node-10 - IP地址:192.168.100.11 至 192.168.100.20 - 操作系统:Rocky Linux 9.3 - 配置:2核CPU / 4GB内存 / 50GB SSD
2.1.2 Ansible环境部署
步骤一:安装Ansible
# Rocky Linux 9 / CentOS Stream 9 sudo dnf install -y epel-release sudo dnf install -y ansible-core ansible-collection-ansible-posix ansible-collection-community-general # Ubuntu 24.04 sudo apt update sudo apt install -y software-properties-common sudo add-apt-repository --yes --update ppa:ansible/ansible sudo apt install -y ansible # 验证安装 ansible --version # ansible [core 2.16.3] # config file = /etc/ansible/ansible.cfg # configured module search path = ['/root/.ansible/plugins/modules'] # ansible python module location = /usr/lib/python3.11/site-packages/ansible # executable location = /usr/bin/ansible # python version = 3.11.7
步骤二:配置SSH免密登录
# 生成SSH密钥对
ssh-keygen -t ed25519 -C "ansible@ops-master" -f ~/.ssh/ansible_key -N ""
# 批量分发公钥到被管理节点
for i in {11..20}; do
ssh-copy-id -i ~/.ssh/ansible_key.pub root@192.168.100.$i
done
# 验证免密登录
ssh -i ~/.ssh/ansible_key root@192.168.100.11 "hostname"
步骤三:创建Ansible目录结构
# 创建项目目录
mkdir -p /opt/ansible/{inventory,playbooks,roles,group_vars,host_vars,files,templates}
# 目录结构说明
# /opt/ansible/
# ├── ansible.cfg # 主配置文件
# ├── inventory/ # 主机清单
# │ ├── hosts.ini # 静态清单
# │ └── dynamic.py # 动态清单脚本
# ├── playbooks/ # 剧本文件
# ├── roles/ # 角色目录
# ├── group_vars/ # 组变量
# ├── host_vars/ # 主机变量
# ├── files/ # 静态文件
# └── templates/ # Jinja2模板
步骤四:配置ansible.cfg
# /opt/ansible/ansible.cfg [defaults] # 主机清单路径 inventory = ./inventory/hosts.ini # 远程用户 remote_user = root # SSH私钥路径 private_key_file = ~/.ssh/ansible_key # 禁用主机密钥检查(生产环境建议开启) host_key_checking = False # 并发连接数 forks = 50 # 超时时间(秒) timeout = 30 # 日志路径 log_path = /var/log/ansible/ansible.log # 重试文件路径 retry_files_save_path = /tmp/ansible-retry # 角色搜索路径 roles_path = ./roles:/etc/ansible/roles # 回调插件,美化输出 stdout_callback = yaml # 显示任务执行时间 callback_whitelist = timer, profile_tasks [privilege_escalation] # 提权设置 become = True become_method = sudo become_user = root become_ask_pass = False [ssh_connection] # SSH参数优化 ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no # 管道模式,提升性能 pipelining = True # 控制路径 control_path = /tmp/ansible-ssh-%%h-%%p-%%r [persistent_connection] # 持久连接超时 connect_timeout = 30 command_timeout = 30
2.1.3 SaltStack环境部署
步骤一:配置Salt仓库
# Rocky Linux 9 / CentOS Stream 9 sudo rpm --import https://repo.saltproject.io/salt/py3/redhat/9/x86_64/SALT-PROJECT-GPG-PUBKEY-2023.pub sudo tee /etc/yum.repos.d/salt.repo << 'EOF' [salt-repo] name=Salt repo for RHEL/CentOS 9 baseurl=https://repo.saltproject.io/salt/py3/redhat/9/x86_64/3006 enabled=1 gpgcheck=1 gpgkey=https://repo.saltproject.io/salt/py3/redhat/9/x86_64/SALT-PROJECT-GPG-PUBKEY-2023.pub EOF # Ubuntu 24.04 curl -fsSL https://repo.saltproject.io/salt/py3/ubuntu/24.04/amd64/SALT-PROJECT-GPG-PUBKEY-2023.gpg | sudo gpg --dearmor -o /usr/share/keyrings/salt-archive-keyring.gpg echo"deb [signed-by=/usr/share/keyrings/salt-archive-keyring.gpg] https://repo.saltproject.io/salt/py3/ubuntu/24.04/amd64/3006 noble main" | sudo tee /etc/apt/sources.list.d/salt.list
步骤二:安装Salt Master
# Rocky Linux 9 sudo dnf install -y salt-master salt-minion salt-ssh salt-api # Ubuntu 24.04 sudo apt update sudo apt install -y salt-master salt-minion salt-ssh salt-api # 验证安装 salt --version # salt 3006.5
步骤三:配置Salt Master
# /etc/salt/master # 监听地址 interface:0.0.0.0 # 工作进程数 worker_threads:10 # 文件服务器后端 fileserver_backend: -roots # 文件根目录 file_roots: base: -/srv/salt/base dev: -/srv/salt/dev prod: -/srv/salt/prod # Pillar根目录 pillar_roots: base: -/srv/pillar/base prod: -/srv/pillar/prod # 自动接受minion密钥(生产环境不建议) auto_accept:False # 日志级别 log_level:info log_file:/var/log/salt/master # 作业缓存 job_cache:True keep_jobs:24 # 事件返回器 event_return:mysql # 超时设置 timeout:30 gather_job_timeout:30 # 启用Reactor reactor: -'salt/minion/*/start': -/srv/reactor/minion_start.sls # REST API配置 rest_cherrypy: port:8000 ssl_crt:/etc/pki/tls/certs/salt-api.crt ssl_key:/etc/pki/tls/private/salt-api.key
步骤四:创建Salt目录结构
# 创建目录结构
sudo mkdir -p /srv/{salt,pillar,reactor}/{base,dev,prod}
sudo mkdir -p /var/log/salt
# 目录结构说明
# /srv/
# ├── salt/ # 状态文件目录
# │ ├── base/ # 基础环境
# │ │ ├── top.sls # 顶层文件
# │ │ └── common/ # 通用配置
# │ ├── dev/ # 开发环境
# │ └── prod/ # 生产环境
# ├── pillar/ # Pillar数据目录
# │ ├── base/
# │ │ └── top.sls
# │ └── prod/
# └── reactor/ # Reactor配置
# └── base/
步骤五:启动Salt Master
# 启动服务 sudo systemctl enable --now salt-master sudo systemctl enable --now salt-api # 检查服务状态 sudo systemctl status salt-master sudo salt-master --version # 开放防火墙端口 sudo firewall-cmd --permanent --add-port=4505/tcp sudo firewall-cmd --permanent --add-port=4506/tcp sudo firewall-cmd --permanent --add-port=8000/tcp sudo firewall-cmd --reload
步骤六:部署Salt Minion到被管理节点
# 在被管理节点执行 # 安装salt-minion sudo dnf install -y salt-minion # 配置minion sudo tee /etc/salt/minion << 'EOF' # Master地址 master: 192.168.100.10 # Minion ID(默认使用主机名) id: web-node-01 # 日志级别 log_level: info # Grains配置 grains: roles: - webserver environment: production datacenter: dc1 EOF # 启动minion sudo systemctl enable --now salt-minion
步骤七:接受Minion密钥
# 在Master上查看待接受的密钥 sudo salt-key -L # Accepted Keys: # Denied Keys: # Unaccepted Keys: # web-node-01 # web-node-02 # ... # Rejected Keys: # 接受所有密钥 sudo salt-key -A -y # 验证连接 sudo salt '*' test.ping # web-node-01: # True # web-node-02: # True
2.2 核心配置
2.2.1 Ansible主机清单配置
静态清单示例:
# /opt/ansible/inventory/hosts.ini [webservers] web-node-01 ansible_host=192.168.100.11 web-node-02 ansible_host=192.168.100.12 web-node-03 ansible_host=192.168.100.13 web-node-04 ansible_host=192.168.100.14 web-node-05 ansible_host=192.168.100.15 [dbservers] db-node-01 ansible_host=192.168.100.16 db-node-02 ansible_host=192.168.100.17 [cacheservers] cache-node-01 ansible_host=192.168.100.18 cache-node-02 ansible_host=192.168.100.19 cache-node-03 ansible_host=192.168.100.20 [webservers:vars] nginx_port=80 app_env=production [all:vars] ansible_python_interpreter=/usr/bin/python3 ansible_ssh_private_key_file=~/.ssh/ansible_key
组变量配置:
# /opt/ansible/group_vars/webservers.yml --- # Nginx配置 nginx_version:"1.24" nginx_worker_processes:auto nginx_worker_connections:65535 nginx_keepalive_timeout:65 nginx_client_max_body_size:"100m" # 应用配置 app_name:"myapp" app_port:8080 app_log_level:"info" # 系统优化参数 sysctl_settings: net.core.somaxconn:65535 net.ipv4.tcp_max_syn_backlog:65535 net.ipv4.ip_local_port_range:"1024 65535" net.ipv4.tcp_tw_reuse:1 vm.swappiness:10
2.2.2 SaltStack状态配置
Top文件配置:
# /srv/salt/base/top.sls base: '*': -common.packages -common.users -common.sshd -common.ntp 'web-node-*': -nginx -php -app 'db-node-*': -mysql -backup 'cache-node-*': -redis -sentinel
通用包管理状态:
# /srv/salt/base/common/packages.sls # 基础软件包安装 base_packages: pkg.installed: -pkgs: -vim-enhanced -wget -curl -htop -iotop -net-tools -bind-utils -lsof -strace -tcpdump -sysstat -tree # 禁用不需要的服务 disable_postfix: service.dead: -name:postfix -enable:False
Pillar配置:
# /srv/pillar/base/top.sls base: '*': -common 'web-node-*': -nginx -app 'db-node-*': -mysql # /srv/pillar/base/common.sls timezone:Asia/Shanghai dns_servers: -223.5.5.5 -223.6.6.6 ntp_servers: -ntp.aliyun.com -ntp1.aliyun.com
2.3 启动和验证
2.3.1 Ansible验证测试
# 测试连通性 ansible all -m ping # 收集系统信息 ansible all -m setup -a "filter=ansible_distribution*" # 执行ad-hoc命令 ansible webservers -m shell -a "uptime" # 运行playbook ansible-playbook playbooks/site.yml --check --diff # 限制执行范围 ansible-playbook playbooks/site.yml --limit web-node-01 # 指定标签执行 ansible-playbook playbooks/site.yml --tags "nginx,config"
2.3.2 SaltStack验证测试
# 测试连通性 salt '*' test.ping # 收集Grains信息 salt '*' grains.items # 执行远程命令 salt 'web-node-*' cmd.run 'uptime' # 应用状态 salt '*' state.apply # 测试模式运行 salt '*' state.apply test=True # 高状态应用 salt '*' state.highstate # 刷新Pillar数据 salt '*' saltutil.refresh_pillar
三、示例代码和配置
3.1 完整配置示例
3.1.1 Ansible完整Playbook示例
# /opt/ansible/playbooks/deploy_webserver.yml
---
-name:部署Web服务器
hosts:webservers
become:yes
gather_facts:yes
vars:
nginx_version:"1.24"
php_version:"8.2"
app_name:"myapp"
deploy_user:"deploy"
deploy_group:"deploy"
app_root:"/var/www/{{ app_name }}"
pre_tasks:
-name:更新系统软件包缓存
ansible.builtin.dnf:
update_cache:yes
when:ansible_os_family=="RedHat"
-name:检查系统内存
ansible.builtin.fail:
msg:"系统内存不足,需要至少2GB"
when:ansible_memtotal_mb<2048
roles:
-role:common
tags:common
-role:nginx
tags:nginx
-role:php-fpm
tags:php
-role:app
tags:app
tasks:
-name:创建部署用户
ansible.builtin.user:
name:"{{ deploy_user }}"
group:"{{ deploy_group }}"
shell:/bin/bash
home:"/home/{{ deploy_user }}"
create_home:yes
state:present
-name:配置sudo权限
ansible.builtin.lineinfile:
path:/etc/sudoers.d/{{deploy_user}}
line:"{{ deploy_user }} ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx"
create:yes
mode:'0440'
validate:'visudo -cf %s'
-name:创建应用目录结构
ansible.builtin.file:
path:"{{ item }}"
state:directory
owner:"{{ deploy_user }}"
group:"{{ deploy_group }}"
mode:'0755'
loop:
-"{{ app_root }}"
-"{{ app_root }}/releases"
-"{{ app_root }}/shared"
-"{{ app_root }}/shared/logs"
-"{{ app_root }}/shared/config"
-name:部署Nginx虚拟主机配置
ansible.builtin.template:
src:templates/nginx/vhost.conf.j2
dest:/etc/nginx/conf.d/{{app_name}}.conf
owner:root
group:root
mode:'0644'
backup:yes
notify:重载Nginx配置
-name:配置PHP-FPM池
ansible.builtin.template:
src:templates/php/pool.conf.j2
dest:/etc/php-fpm.d/{{app_name}}.conf
owner:root
group:root
mode:'0644'
notify:重启PHP-FPM服务
-name:配置系统内核参数
ansible.posix.sysctl:
name:"{{ item.key }}"
value:"{{ item.value }}"
sysctl_file:/etc/sysctl.d/99-app-tuning.conf
reload:yes
state:present
loop:"{{ sysctl_settings | dict2items }}"
-name:配置文件描述符限制
ansible.builtin.template:
src:templates/system/limits.conf.j2
dest:/etc/security/limits.d/99-app.conf
owner:root
group:root
mode:'0644'
-name:部署日志切割配置
ansible.builtin.template:
src:templates/logrotate/app.j2
dest:/etc/logrotate.d/{{app_name}}
owner:root
group:root
mode:'0644'
handlers:
-name:重载Nginx配置
ansible.builtin.systemd:
name:nginx
state:reloaded
-name:重启PHP-FPM服务
ansible.builtin.systemd:
name:php-fpm
state:restarted
post_tasks:
-name:健康检查-验证Nginx运行状态
ansible.builtin.uri:
url:"http://localhost/health"
method:GET
status_code:200
timeout:10
register:health_check
retries:3
delay:5
until:health_check.status==200
-name:发送部署通知
ansible.builtin.uri:
url:"{{ notification_webhook }}"
method:POST
body_format:json
body:
text:"服务器 {{ inventory_hostname }} 部署完成"
status:"success"
when:notification_webhookisdefined
ignore_errors:yes
Nginx虚拟主机模板:
{# /opt/ansible/templates/nginx/vhost.conf.j2 #}
# {{ ansible_managed }}
# 应用名称: {{ app_name }}
# 生成时间: {{ ansible_date_time.iso8601 }}
upstream {{ app_name }}_backend {
least_conn;
{% for server in upstream_servers | default(['127.0.0.1:9000']) %}
server {{ server }} weight=1 max_fails=3 fail_timeout=30s;
{% endfor %}
keepalive 32;
}
server {
listen 80;
listen [::]:80;
server_name {{ server_name | default('_') }};
root {{ app_root }}/current/public;
index index.php index.html;
# 访问日志
access_log /var/log/nginx/{{ app_name }}_access.log main buffer=64k flush=5s;
error_log /var/log/nginx/{{ app_name }}_error.log warn;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# 静态文件缓存
location ~* .(jpg|jpeg|png|gif|ico|css|js|pdf|txt)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# 健康检查端点
location /health {
access_log off;
return 200 'OK';
add_header Content-Type text/plain;
}
# PHP处理
location ~ .php$ {
try_files $uri =404;
fastcgi_pass {{ app_name }}_backend;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# FastCGI优化
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;
fastcgi_buffer_size 64k;
fastcgi_buffers 16 64k;
}
# 默认路由
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# 禁止访问隐藏文件
location ~ /. {
deny all;
access_log off;
log_not_found off;
}
}
3.1.2 SaltStack完整状态示例
# /srv/salt/base/nginx/init.sls
{%setnginx=salt['pillar.get']('nginx',{})%}
{%setnginx_version=nginx.get('version','1.24')%}
{%setworker_processes=nginx.get('worker_processes','auto')%}
{%setworker_connections=nginx.get('worker_connections',65535)%}
# 安装Nginx
nginx_package:
pkg.installed:
-name:nginx
-version:"{{ nginx_version }}*"
# 创建必要目录
nginx_directories:
file.directory:
-names:
-/etc/nginx/conf.d
-/etc/nginx/ssl
-/var/log/nginx
-/var/cache/nginx
-user:root
-group:root
-mode:755
-makedirs:True
# 主配置文件
nginx_main_config:
file.managed:
-name:/etc/nginx/nginx.conf
-source:salt://nginx/files/nginx.conf.jinja
-template:jinja
-user:root
-group:root
-mode:644
-context:
worker_processes:{{worker_processes}}
worker_connections:{{worker_connections}}
-require:
-pkg:nginx_package
-watch_in:
-service:nginx_service
# 默认虚拟主机配置
nginx_default_vhost:
file.managed:
-name:/etc/nginx/conf.d/default.conf
-source:salt://nginx/files/default.conf.jinja
-template:jinja
-user:root
-group:root
-mode:644
-require:
-file:nginx_directories
-watch_in:
-service:nginx_service
# 删除默认欢迎页
nginx_remove_default_html:
file.absent:
-name:/usr/share/nginx/html/index.html
-require:
-pkg:nginx_package
# 系统内核优化
nginx_sysctl:
sysctl.present:
-names:
-net.core.somaxconn:
-value:65535
-net.ipv4.tcp_max_syn_backlog:
-value:65535
-net.ipv4.ip_local_port_range:
-value:"1024 65535"
-net.ipv4.tcp_tw_reuse:
-value:1
# 文件描述符限制
nginx_limits:
file.managed:
-name:/etc/security/limits.d/99-nginx.conf
-contents:|
nginx soft nofile 65535
nginx hard nofile 65535
nginx soft nproc 65535
nginx hard nproc 65535
-user:root
-group:root
-mode:644
# 日志切割配置
nginx_logrotate:
file.managed:
-name:/etc/logrotate.d/nginx
-source:salt://nginx/files/logrotate.conf
-user:root
-group:root
-mode:644
# 开放防火墙端口
nginx_firewall:
firewalld.present:
-name:public
-services:
-http
-https
-prune_services:False
# 启动Nginx服务
nginx_service:
service.running:
-name:nginx
-enable:True
-reload:True
-require:
-pkg:nginx_package
-file:nginx_main_config
Nginx主配置模板:
{# /srv/salt/base/nginx/files/nginx.conf.jinja #}
# Salt管理的配置文件 - 请勿手动修改
# 生成时间: {{ salt['status.time']('%Y-%m-%d %H:%M:%S') }}
# Minion ID: {{ grains['id'] }}
user nginx;
worker_processes {{ worker_processes }};
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
worker_rlimit_nofile 65535;
events {
use epoll;
worker_connections {{ worker_connections }};
multi_accept on;
}
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" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
log_format json escape=json '{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_response_time":"$upstream_response_time"'
'}';
access_log /var/log/nginx/access.log main buffer=64k flush=5s;
# 性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 4096;
# Gzip压缩
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml application/xml+rss text/javascript;
# 安全配置
server_tokens off;
# 请求体限制
client_max_body_size 100m;
client_body_buffer_size 128k;
# 代理缓冲配置
proxy_buffer_size 64k;
proxy_buffers 16 64k;
proxy_busy_buffers_size 128k;
# 包含虚拟主机配置
include /etc/nginx/conf.d/*.conf;
}
3.2 实际应用案例
3.2.1 案例一:多环境配置管理
场景描述: 管理开发、测试、生产三套环境,每套环境配置不同,需要实现配置隔离和统一管理。
Ansible实现方案:
# 目录结构 # /opt/ansible/ # ├── inventories/ # │ ├── dev/ # │ │ ├── hosts.ini # │ │ └── group_vars/ # │ │ └── all.yml # │ ├── staging/ # │ │ ├── hosts.ini # │ │ └── group_vars/ # │ │ └── all.yml # │ └── prod/ # │ ├── hosts.ini # │ └── group_vars/ # │ └── all.yml # /opt/ansible/inventories/dev/group_vars/all.yml --- env_name:development app_debug:true log_level:debug db_host:dev-db.internal redis_host:dev-redis.internal api_endpoint:https://api-dev.example.com # /opt/ansible/inventories/prod/group_vars/all.yml --- env_name:production app_debug:false log_level:warning db_host:prod-db.internal redis_host:prod-redis.internal api_endpoint:https://api.example.com # 执行命令 # 部署到开发环境 ansible-playbook-iinventories/devplaybooks/deploy.yml # 部署到生产环境 ansible-playbook-iinventories/prodplaybooks/deploy.yml
SaltStack实现方案:
# /srv/pillar/base/top.sls base: '*': -common dev: 'env:dev': -match:grain -dev/settings staging: 'env:staging': -match:grain -staging/settings prod: 'env:prod': -match:grain -prod/settings # /srv/pillar/dev/settings.sls env_name:development app_debug:true log_level:debug db_host:dev-db.internal redis_host:dev-redis.internal api_endpoint:https://api-dev.example.com # /srv/pillar/prod/settings.sls env_name:production app_debug:false log_level:warning db_host:prod-db.internal redis_host:prod-redis.internal api_endpoint:https://api.example.com # 执行命令 # 刷新Pillar salt'*'saltutil.refresh_pillar # 应用到开发环境 salt-G'env:dev'state.apply # 应用到生产环境 salt-G'env:prod'state.apply
3.2.2 案例二:滚动更新部署
场景描述: Web集群有20台服务器,需要实现滚动更新,每次更新5台,确保服务不中断。
Ansible实现方案:
# /opt/ansible/playbooks/rolling_update.yml
---
-name:滚动更新Web应用
hosts:webservers
become:yes
serial:5# 每批5台
max_fail_percentage:25# 最大失败比例25%
pre_tasks:
-name:从负载均衡器摘除当前节点
ansible.builtin.uri:
url:"http://{{ lb_api }}/api/v1/backends/{{ inventory_hostname }}/disable"
method:POST
headers:
Authorization:"Bearer {{ lb_api_token }}"
status_code:200
delegate_to:localhost
-name:等待当前连接处理完毕
ansible.builtin.wait_for:
timeout:30
-name:健康检查-确认节点已摘除
ansible.builtin.uri:
url:"http://{{ lb_api }}/api/v1/backends/{{ inventory_hostname }}/status"
method:GET
register:lb_status
until:lb_status.json.status=="disabled"
retries:6
delay:5
delegate_to:localhost
tasks:
-name:停止应用服务
ansible.builtin.systemd:
name:"{{ app_name }}"
state:stopped
-name:备份当前版本
ansible.builtin.shell:|
if [ -L {{ app_root }}/current ]; then
cp -a $(readlink -f {{ app_root }}/current) {{ app_root }}/rollback/$(date +%Y%m%d%H%M%S)
fi
args:
executable:/bin/bash
-name:清理旧版本(保留最近5个)
ansible.builtin.shell:|
cd {{ app_root }}/releases && ls -t | tail -n +6 | xargs -r rm -rf
args:
executable:/bin/bash
-name:下载新版本
ansible.builtin.unarchive:
src:"{{ artifact_url }}/{{ app_name }}-{{ app_version }}.tar.gz"
dest:"{{ app_root }}/releases/"
remote_src:yes
owner:"{{ deploy_user }}"
group:"{{ deploy_group }}"
-name:更新符号链接
ansible.builtin.file:
src:"{{ app_root }}/releases/{{ app_version }}"
dest:"{{ app_root }}/current"
state:link
owner:"{{ deploy_user }}"
group:"{{ deploy_group }}"
-name:运行数据库迁移
ansible.builtin.shell:|
cd {{ app_root }}/current && ./bin/migrate
args:
executable:/bin/bash
run_once:true
delegate_to:"{{ groups['webservers'][0] }}"
-name:启动应用服务
ansible.builtin.systemd:
name:"{{ app_name }}"
state:started
-name:等待应用启动
ansible.builtin.uri:
url:"http://localhost:{{ app_port }}/health"
status_code:200
register:health_result
until:health_result.status==200
retries:12
delay:5
post_tasks:
-name:将节点重新加入负载均衡器
ansible.builtin.uri:
url:"http://{{ lb_api }}/api/v1/backends/{{ inventory_hostname }}/enable"
method:POST
headers:
Authorization:"Bearer {{ lb_api_token }}"
status_code:200
delegate_to:localhost
-name:验证节点已恢复服务
ansible.builtin.uri:
url:"http://{{ lb_api }}/api/v1/backends/{{ inventory_hostname }}/status"
method:GET
register:lb_status
until:lb_status.json.status=="enabled"
retries:6
delay:5
delegate_to:localhost
SaltStack实现方案:
# /srv/salt/base/app/rolling_update.sls
{%setbatch_size=salt['pillar.get']('deploy:batch_size',5)%}
{%setapp_version=salt['pillar.get']('deploy:version')%}
{%setapp_root=salt['pillar.get']('app:root','/var/www/app')%}
# 从负载均衡器摘除
remove_from_lb:
http.query:
-name:http://{{pillar['lb_api']}}/api/v1/backends/{{grains['id']}}/disable
-method:POST
-header_dict:
Authorization:"Bearer {{ pillar['lb_api_token'] }}"
# 等待连接排空
wait_for_drain:
module.run:
-name:test.sleep
-length:30
-require:
-http:remove_from_lb
# 停止应用
stop_app:
service.dead:
-name:{{pillar['app_name']}}
-require:
-module:wait_for_drain
# 备份当前版本
backup_current:
cmd.run:
-name:|
if [ -L {{ app_root }}/current ]; then
cp -a $(readlink -f {{ app_root }}/current) {{ app_root }}/rollback/$(date +%Y%m%d%H%M%S)
fi
-require:
-service:stop_app
# 下载新版本
download_release:
archive.extracted:
-name:{{app_root}}/releases/
-source:{{pillar['artifact_url']}}/{{pillar['app_name']}}-{{app_version}}.tar.gz
-user:{{pillar['deploy_user']}}
-group:{{pillar['deploy_group']}}
-require:
-cmd:backup_current
# 更新符号链接
update_symlink:
file.symlink:
-name:{{app_root}}/current
-target:{{app_root}}/releases/{{app_version}}
-user:{{pillar['deploy_user']}}
-group:{{pillar['deploy_group']}}
-require:
-archive:download_release
# 启动应用
start_app:
service.running:
-name:{{pillar['app_name']}}
-require:
-file:update_symlink
# 健康检查
health_check:
http.wait_for_successful_query:
-name:http://localhost:{{pillar['app_port']}}/health
-status:200
-wait_for:60
-require:
-service:start_app
# 重新加入负载均衡器
add_to_lb:
http.query:
-name:http://{{pillar['lb_api']}}/api/v1/backends/{{grains['id']}}/enable
-method:POST
-header_dict:
Authorization:"Bearer {{ pillar['lb_api_token'] }}"
-require:
-http:health_check
# Salt滚动更新执行命令 salt 'web-node-*' state.apply app.rolling_update batch=5 batch_wait=30
3.2.3 案例三:配置合规性检查
场景描述: 定期检查所有服务器的安全配置是否符合基线要求,生成合规报告。
Ansible实现方案:
# /opt/ansible/playbooks/compliance_check.yml
---
-name:安全合规性检查
hosts:all
become:yes
gather_facts:yes
vars:
compliance_rules:
sshd:
PermitRootLogin:"no"
PasswordAuthentication:"no"
X11Forwarding:"no"
MaxAuthTries:"3"
Protocol:"2"
password_policy:
PASS_MAX_DAYS:"90"
PASS_MIN_DAYS:"7"
PASS_WARN_AGE:"14"
tasks:
-name:检查SSH配置合规性
ansible.builtin.shell:|
grep -E "^{{ item.key }}" /etc/ssh/sshd_config | awk '{print $2}'
register:sshd_check
loop:"{{ compliance_rules.sshd | dict2items }}"
changed_when:false
-name:记录SSH配置检查结果
ansible.builtin.set_fact:
sshd_compliance:"{{ sshd_compliance | default({}) | combine({item.item.key: {'expected': item.item.value, 'actual': item.stdout, 'compliant': item.stdout == item.item.value}}) }}"
loop:"{{ sshd_check.results }}"
-name:检查密码策略合规性
ansible.builtin.shell:|
grep -E "^{{ item.key }}" /etc/login.defs | awk '{print $2}'
register:password_check
loop:"{{ compliance_rules.password_policy | dict2items }}"
changed_when:false
-name:检查防火墙状态
ansible.builtin.systemd:
name:firewalld
register:firewall_status
-name:检查SELinux状态
ansible.builtin.command:getenforce
register:selinux_status
changed_when:false
-name:检查未授权SUID文件
ansible.builtin.shell:|
find /usr /bin /sbin -perm -4000 2>/dev/null | wc -l
register:suid_files
changed_when:false
-name:检查空密码账户
ansible.builtin.shell:|
awk -F: '($2 == "" ) { print $1 }' /etc/shadow
register:empty_password
changed_when:false
-name:生成合规性报告
ansible.builtin.set_fact:
compliance_report:
host:"{{ inventory_hostname }}"
timestamp:"{{ ansible_date_time.iso8601 }}"
ssh_config:"{{ sshd_compliance }}"
firewall_enabled:"{{ firewall_status.status.ActiveState == 'active' }}"
selinux_enforcing:"{{ selinux_status.stdout == 'Enforcing' }}"
suid_file_count:"{{ suid_files.stdout }}"
empty_password_users:"{{ empty_password.stdout_lines }}"
-name:保存合规性报告到本地
ansible.builtin.copy:
content:"{{ compliance_report | to_nice_json }}"
dest:"/tmp/compliance/{{ inventory_hostname }}_{{ ansible_date_time.date }}.json"
delegate_to:localhost
-name:汇总合规性报告
ansible.builtin.template:
src:templates/compliance_summary.html.j2
dest:"/tmp/compliance/summary_{{ ansible_date_time.date }}.html"
delegate_to:localhost
run_once:true
四、最佳实践和注意事项
4.1 最佳实践
4.1.1 性能优化
Ansible性能优化:
# /opt/ansible/ansible.cfg 性能优化配置 [defaults] # 增加并发数,根据控制节点性能调整 forks = 100 # 启用SSH持久连接 [ssh_connection] # ControlMaster复用SSH连接 ssh_args = -C -o ControlMaster=auto -o ControlPersist=600s -o PreferredAuthentications=publickey # 开启管道模式,减少SSH连接开销 pipelining = True # 使用更快的SCP传输 transfer_method = piped scp_if_ssh = smart
# Playbook层面优化 --- -name:优化后的Playbook hosts:all # 关闭自动收集facts,按需手动收集 gather_facts:no pre_tasks: # 仅收集必要的facts -name:收集最小化系统信息 ansible.builtin.setup: gather_subset: -min -network gather_timeout:10 tasks: # 使用free策略提升并发效率 -name:并行执行任务 # ... # 策略配置 # strategy: free # 自由策略,任务完成即继续 # strategy: linear # 线性策略(默认),等待所有主机完成
# 使用Mitogen加速Ansible执行(可选) pip install mitogen # ansible.cfg配置 # [defaults] # strategy_plugins = /path/to/mitogen/ansible_mitogen/plugins/strategy # strategy = mitogen_linear
SaltStack性能优化:
# /etc/salt/master 性能优化配置 # 增加工作线程数 worker_threads:20 # ZeroMQ高水位标记 zmq_hwm:10000 # 批量执行返回超时 gather_job_timeout:60 # 事件发布缓存 event_publisher_niceness:0 # 启用缓存 cache:localfs # 缓存过期时间(秒) cachedir:/var/cache/salt cache_job_timeout:3600 # Pillar缓存 pillar_cache:True pillar_cache_ttl:3600 # Git文件服务器缓存 gitfs_update_interval:60 gitfs_env_whitelist: -base -prod # 返回数据压缩 serial:msgpack
# 使用salt-ssh进行无代理连接测试 salt-ssh '*' test.ping --roster-file=/etc/salt/roster # 批量执行优化 salt '*' state.apply batch=100 batch_wait=5 # 异步执行长时间任务 salt '*' state.apply --async salt-run jobs.lookup_jid
4.1.2 安全加固
Ansible安全加固:
# 使用Ansible Vault加密敏感数据
# 创建加密文件
# ansible-vault create group_vars/all/vault.yml
# group_vars/all/vault.yml(加密后)
vault_db_password:"SuperSecretPassword123!"
vault_api_key:"sk-xxxxxxxxxxxxxxxxxxxx"
vault_ssl_private_key:|
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQE...
-----END PRIVATE KEY-----
# group_vars/all/vars.yml(引用加密变量)
db_password:"{{ vault_db_password }}"
api_key:"{{ vault_api_key }}"
# ansible.cfg 安全配置 [defaults] # 启用主机密钥检查 host_key_checking = True # 日志脱敏 no_log = True # 默认隐藏敏感输出 # 禁用cowsay nocows = True [privilege_escalation] # 使用sudo而非su become_method = sudo # 要求sudo密码 become_ask_pass = True
# Playbook安全实践
---
-name:安全配置示例
hosts:all
become:yes
tasks:
-name:创建数据库用户(隐藏密码输出)
community.mysql.mysql_user:
name:app_user
password:"{{ vault_db_password }}"
priv:'app_db.*:ALL'
state:present
no_log:true# 关键:隐藏任务输出
-name:部署包含敏感信息的配置文件
ansible.builtin.template:
src:config.yml.j2
dest:/etc/app/config.yml
owner:app
group:app
mode:'0600'# 仅所有者可读写
no_log:true
SaltStack安全加固:
# /etc/salt/master 安全配置 # 禁用自动接受密钥 auto_accept:False # 启用密钥认证 require_auth:True # 限制命令执行 publisher_acl: # 运维组可执行所有命令 ops_group: -'*' # 开发组只能执行特定命令 dev_group: -'web-*': -test.* -status.* -state.apply -'db-*': -test.ping # 启用审计日志 external_auth: pam: ops_group%: -'*' dev_group%: -'web-*': -test.ping -state.apply # SSL/TLS配置 ssl: keyfile:/etc/salt/pki/master/master.pem certfile:/etc/salt/pki/master/master.crt # 启用客户端证书验证 verify_master_pubkey_sign:True
# 使用SaltStack Pillar保护敏感数据 # /srv/pillar/base/secrets.sls #!jinja|gpg db_password: | -----BEGIN PGP MESSAGE----- hQEMA... -----END PGP MESSAGE----- api_token: | -----BEGIN PGP MESSAGE----- hQEMA... -----END PGP MESSAGE----- # 使用GPG加密Pillar数据 # gpg --encrypt --armor --recipient ops@example.com secret.txt
4.1.3 高可用架构
Ansible高可用架构:
┌─────────────────┐ │ GitLab/GitHub │ │ (Playbook仓库) │ └────────┬────────┘ │ ┌──────────────┼──────────────┐ │ │ │ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ Ansible │ │ Ansible │ │ Ansible │ │ Node 01 │ │ Node 02 │ │ Node 03 │ └──────┬─────┘ └──────┬─────┘ └──────┬─────┘ │ │ │ └───────────────┼───────────────┘ │ ┌───────┴───────┐ │ SSH Pool │ └───────┬───────┘ │ ┌───────────────────┼───────────────────┐ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Target │ │ Target │ │ Target │ │ Group A │ │ Group B │ │ Group C │ └─────────┘ └─────────┘ └─────────┘
# 使用AWX/Tower实现企业级高可用 # AWX Kubernetes部署配置 # awx-operator/awx.yml --- apiVersion:awx.ansible.com/v1beta1 kind:AWX metadata: name:awx namespace:awx spec: replicas:3 admin_user:admin admin_password_secret:awx-admin-password postgres_configuration_secret:awx-postgres-config postgres_storage_class:managed-premium postgres_storage_requirements: requests: storage:50Gi # 任务执行Pod配置 task_resource_requirements: requests: cpu:500m memory:1Gi limits: cpu:2000m memory:4Gi
SaltStack高可用架构:
┌─────────────────┐ │ Salt Syndic │ │ (Master of │ │ Masters) │ └────────┬────────┘ │ ┌──────────────┼──────────────┐ │ │ │ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ Master │ │ Master │ │ Master │ │ DC-A │ │ DC-B │ │ DC-C │ └──────┬─────┘ └──────┬─────┘ └──────┬─────┘ │ │ │ ▼ ▼ ▼ ┌──────────────────────────────────────────┐ │ Salt Minions │ │ (多Master故障转移配置) │ └──────────────────────────────────────────┘
# /etc/salt/minion 多Master配置 master: -salt-master-01.example.com -salt-master-02.example.com -salt-master-03.example.com # Master故障转移 master_type:failover master_shuffle:True master_alive_interval:30 # 随机选择Master random_master:True # 重试配置 retry_dns:30 retry_dns_count:3
# /etc/salt/master Syndic配置 # Salt Master同时作为Syndic syndic_master: salt-syndic.example.com syndic_master_port: 4506 syndic_log_file: /var/log/salt/syndic # Syndic认证 syndic_wait: 30
4.2 注意事项
4.2.1 配置注意
Ansible配置注意事项:
| 配置项 | 注意事项 | 建议值 |
|---|---|---|
| forks | 过高会导致控制节点资源耗尽 | CPU核心数 * 5 |
| timeout | 过短可能导致任务误判失败 | 30-60秒 |
| pipelining | 需要目标主机禁用requiretty | 生产环境建议开启 |
| host_key_checking | 生产环境必须开启 | True |
| gather_facts | 全量收集影响性能 | 按需收集 |
| become | 最小权限原则 | 仅必要时使用 |
SaltStack配置注意事项:
| 配置项 | 注意事项 | 建议值 |
|---|---|---|
| worker_threads | 根据CPU和连接数调整 | CPU核心数 * 2 |
| auto_accept | 生产环境严禁开启 | False |
| gather_job_timeout | 大规模执行需增加 | 60-120秒 |
| pillar_cache | 频繁更新场景需关闭 | 根据场景 |
| state_verbose | 生产环境关闭详细输出 | False |
| file_recv | 文件上传功能,有安全风险 | False |
4.2.2 常见错误
| 错误类型 | 错误描述 | 解决方案 |
|---|---|---|
| SSH连接超时 | UNREACHABLE! SSH timed out | 检查网络连通性,增加timeout值 |
| 权限拒绝 | Permission denied (publickey) | 检查SSH密钥配置和权限 |
| 模块未找到 | MODULE FAILURE | 安装对应的Ansible Collection |
| Python解释器 | /usr/bin/python: No such file | 指定ansible_python_interpreter |
| 幂等性失败 | changed状态不稳定 | 检查任务设计,使用when条件 |
| 变量未定义 | undefined variable | 使用default过滤器或检查变量定义 |
| Salt Minion离线 | Minion did not return | 检查minion服务状态和网络 |
| Salt密钥冲突 | Key mismatch | 删除旧密钥,重新认证 |
| Pillar渲染失败 | Pillar render error | 检查Jinja语法和变量引用 |
| 状态依赖错误 | Requisite failed | 检查require/watch依赖链 |
4.2.3 兼容性
操作系统兼容性矩阵:
| 操作系统 | Ansible 2.16+ | SaltStack 3006+ | 备注 |
|---|---|---|---|
| CentOS Stream 9 | 完全支持 | 完全支持 | 推荐 |
| Rocky Linux 9 | 完全支持 | 完全支持 | 推荐 |
| AlmaLinux 9 | 完全支持 | 完全支持 | 推荐 |
| Ubuntu 24.04 LTS | 完全支持 | 完全支持 | 推荐 |
| Debian 12 | 完全支持 | 完全支持 | 支持 |
| RHEL 9 | 完全支持 | 完全支持 | 企业版 |
| Windows Server 2022 | 部分支持 | 完全支持 | 需WinRM/SSH |
Python版本兼容性:
| Python版本 | Ansible 2.16+ | SaltStack 3006+ | 状态 |
|---|---|---|---|
| 3.9 | 支持 | 支持 | 维护中 |
| 3.10 | 支持 | 支持 | 推荐 |
| 3.11 | 完全支持 | 完全支持 | 推荐 |
| 3.12 | 支持 | 部分支持 | 测试中 |
五、故障排查和监控
5.1 故障排查
5.1.1 日志分析
Ansible日志配置和分析:
# 配置详细日志输出 export ANSIBLE_LOG_PATH=/var/log/ansible/ansible.log export ANSIBLE_DEBUG=True # 运行时启用调试 ansible-playbook playbook.yml -vvvv # 日志级别说明 # -v : 显示任务结果 # -vv : 显示任务配置和结果 # -vvv : 显示SSH连接信息 # -vvvv : 显示完整调试信息 # 分析日志常用命令 # 查看失败任务 grep -E "FAILED|UNREACHABLE" /var/log/ansible/ansible.log # 统计任务执行时间 grep "TASK|PLAY RECAP" /var/log/ansible/ansible.log # 提取特定主机日志 grep "web-node-01" /var/log/ansible/ansible.log
SaltStack日志配置和分析:
# Master日志位置 tail -f /var/log/salt/master # Minion日志位置 tail -f /var/log/salt/minion # 启用调试日志 salt-master -l debug salt-minion -l debug # 日志级别配置 # /etc/salt/master 或 /etc/salt/minion # log_level: debug # log_level_logfile: info # 分析日志 # 查看认证失败 grep -E "Authentication|denied" /var/log/salt/master # 查看执行错误 grep -E "Error|Exception|Traceback" /var/log/salt/master # 事件日志 salt-run state.event pretty=True
5.1.2 常见问题排查
问题一:Ansible SSH连接失败
# 诊断步骤 # 1. 检查SSH连通性 ssh -v -i ~/.ssh/ansible_key root@target_host # 2. 检查SSH配置 cat ~/.ssh/config cat /etc/ssh/ssh_config # 3. 测试Ansible连接 ansible target_host -m ping -vvvv # 4. 检查Python解释器 ansible target_host -m raw -a "which python3" # 常见解决方案 # 配置Python解释器 ansible_python_interpreter=/usr/bin/python3 # 禁用主机密钥检查(仅测试环境) export ANSIBLE_HOST_KEY_CHECKING=False
问题二:SaltStack Minion无响应
# 诊断步骤 # 1. 检查Minion服务状态 systemctl status salt-minion journalctl -u salt-minion -f # 2. 检查Master连接 salt-call --local test.ping salt-call test.ping -l debug # 3. 检查网络连通性 nc -zv salt-master 4505 nc -zv salt-master 4506 # 4. 重新生成Minion密钥 rm /etc/salt/pki/minion/minion_master.pub systemctl restart salt-minion # 5. 在Master上重新接受密钥 salt-key -d minion-id -y salt-key -a minion-id -y
问题三:状态应用失败
# Ansible诊断 # 检查任务详细输出 ansible-playbook playbook.yml --check --diff -v # 单步执行 ansible-playbook playbook.yml --step # 从特定任务开始 ansible-playbook playbook.yml --start-at-task="任务名称" # Salt诊断 # 测试模式运行 salt 'minion-id' state.apply test=True # 查看状态编译结果 salt 'minion-id' state.show_lowstate # 查看Pillar数据 salt 'minion-id' pillar.items
5.1.3 调试技巧
# Ansible调试模块使用
---
-name:调试示例
hosts:all
tasks:
-name:打印变量值
ansible.builtin.debug:
var:my_variable
-name:打印自定义消息
ansible.builtin.debug:
msg:"当前主机: {{ inventory_hostname }}, IP: {{ ansible_host }}"
-name:条件断点
ansible.builtin.pause:
prompt:"检查变量值是否正确,按Enter继续"
when:debug_mode|default(false)
-name:保存调试信息到文件
ansible.builtin.copy:
content:"{{ hostvars[inventory_hostname] | to_nice_json }}"
dest:"/tmp/debug_{{ inventory_hostname }}.json"
delegate_to:localhost
# Salt调试命令 # 查看模块文档 salt '*' sys.doc state.file # 查看可用模块 salt '*' sys.list_modules # 查看Grains salt '*' grains.items # 查看Pillar salt '*' pillar.items # 执行状态调试 salt-call state.apply nginx -l debug --local # 查看事件总线 salt-run state.event pretty=True tagmatch='salt/job/*'
5.2 性能监控
5.2.1 关键指标
Ansible执行性能指标:
| 指标名称 | 说明 | 健康阈值 | 告警阈值 |
|---|---|---|---|
| 任务执行时间 | 单个任务执行耗时 | < 30s | > 60s |
| Playbook总时长 | 整个Playbook执行时间 | < 10min | > 30min |
| 并发连接数 | 同时执行的SSH连接 | < forks值 | = forks值 |
| 失败任务比例 | 失败任务占比 | 0% | > 5% |
| 重试次数 | 任务重试次数 | 0 | > 3 |
SaltStack执行性能指标:
| 指标名称 | 说明 | 健康阈值 | 告警阈值 |
|---|---|---|---|
| 命令响应时间 | 从发送到返回 | < 5s | > 30s |
| Minion在线率 | 在线Minion比例 | > 99% | < 95% |
| Master CPU使用率 | 控制节点CPU | < 60% | > 80% |
| Master内存使用率 | 控制节点内存 | < 70% | > 85% |
| ZeroMQ队列长度 | 消息队列积压 | < 1000 | > 5000 |
| Job完成率 | 成功完成的任务 | > 99% | < 95% |
5.2.2 监控配置
Ansible监控脚本:
#!/usr/bin/env python3
# /opt/ansible/scripts/ansible_metrics.py
# Ansible执行指标收集脚本
import json
import time
import subprocess
from datetime import datetime
from pathlib import Path
class AnsibleMetrics:
def __init__(self, log_path="/var/log/ansible"):
self.log_path = Path(log_path)
self.metrics_file = self.log_path / "metrics.json"
def collect_execution_metrics(self, playbook_output):
"""解析Playbook执行结果,提取指标"""
metrics = {
"timestamp": datetime.now().isoformat(),
"ok": 0,
"changed": 0,
"unreachable": 0,
"failed": 0,
"skipped": 0,
"rescued": 0,
"ignored": 0
}
# 解析PLAY RECAP行
for line in playbook_output.split('
'):
if'ok='in line:
parts = line.split()
for part in parts:
if'='in part:
key, value = part.split('=')
if key in metrics:
metrics[key] = int(value)
return metrics
def export_prometheus_metrics(self, metrics):
"""导出Prometheus格式指标"""
output = []
output.append("# HELP ansible_task_total Ansible任务执行统计")
output.append("# TYPE ansible_task_total counter")
for key, value in metrics.items():
if key != "timestamp":
output.append(f'ansible_task_total{{status="{key}"}} {value}')
return'
'.join(output)
if __name__ == "__main__":
collector = AnsibleMetrics()
# 示例用法
result = subprocess.run(
["ansible-playbook", "playbook.yml"],
capture_output=True,
text=True
)
metrics = collector.collect_execution_metrics(result.stdout)
print(collector.export_prometheus_metrics(metrics))
SaltStack监控配置:
# /etc/salt/master.d/monitoring.conf # Salt Master监控配置 # 启用Salt API用于监控 rest_cherrypy: port:8000 ssl_crt:/etc/pki/tls/certs/salt-api.crt ssl_key:/etc/pki/tls/private/salt-api.key # 事件返回器配置(存储到MySQL) event_return:mysql mysql.host:'localhost' mysql.user:'salt' mysql.pass:'salt_password' mysql.db:'salt' # 返回器配置 return:mysql # Beacon监控配置示例 beacons: diskusage: -/:90% -/var:80% service: -services: nginx: onchangeonly:True salt-minion: onchangeonly:True load: -averages: 1m: -2.0 5m: -1.5 15m: -1.0
# /srv/salt/base/monitoring/salt_exporter.sls # 部署Salt Prometheus Exporter salt_exporter_download: file.managed: -name:/usr/local/bin/salt_exporter -source:https://github.com/example/salt-exporter/releases/latest/salt_exporter -mode:755 salt_exporter_service: file.managed: -name:/etc/systemd/system/salt-exporter.service -contents:| [Unit] Description=Salt Prometheus Exporter After=network.target [Service] Type=simple ExecStart=/usr/local/bin/salt_exporter--listen-address=:9175 Restart=always [Install] WantedBy=multi-user.target service.running: -name:salt-exporter -enable:True -require: -file:salt_exporter_download -file:salt_exporter_service
5.2.3 告警配置
# Prometheus告警规则
# /etc/prometheus/rules/ansible_alerts.yml
groups:
-name:ansible_alerts
rules:
-alert:AnsibleHighFailureRate
expr:|
sum(rate(ansible_task_total{status="failed"}[5m])) /
sum(rate(ansible_task_total[5m])) > 0.05
for:5m
labels:
severity:critical
annotations:
summary:"Ansible任务失败率过高"
description:"过去5分钟内Ansible任务失败率超过5%"
-alert:AnsiblePlaybookTimeout
expr:ansible_playbook_duration_seconds>1800
for:0m
labels:
severity:warning
annotations:
summary:"Ansible Playbook执行超时"
description:"Playbook执行时间超过30分钟"
-name:salt_alerts
rules:
-alert:SaltMinionOffline
expr:salt_minion_connected==0
for:5m
labels:
severity:critical
annotations:
summary:"Salt Minion离线"
description:"Minion {{ $labels.minion }} 已离线超过5分钟"
-alert:SaltMasterHighLoad
expr:salt_master_cpu_usage_percent>80
for:10m
labels:
severity:warning
annotations:
summary:"Salt Master负载过高"
description:"Salt Master CPU使用率超过80%持续10分钟"
5.3 备份与恢复
5.3.1 Ansible备份策略
#!/bin/bash
# /opt/ansible/scripts/backup.sh
# Ansible配置备份脚本
BACKUP_DIR="/backup/ansible"
DATE=$(date +%Y%m%d_%H%M%S)
ANSIBLE_HOME="/opt/ansible"
# 创建备份目录
mkdir -p "${BACKUP_DIR}/${DATE}"
# 备份配置文件
tar -czf "${BACKUP_DIR}/${DATE}/config.tar.gz"
"${ANSIBLE_HOME}/ansible.cfg"
"${ANSIBLE_HOME}/inventory/"
"${ANSIBLE_HOME}/group_vars/"
"${ANSIBLE_HOME}/host_vars/"
# 备份Playbooks和Roles
tar -czf "${BACKUP_DIR}/${DATE}/playbooks.tar.gz"
"${ANSIBLE_HOME}/playbooks/"
"${ANSIBLE_HOME}/roles/"
# 备份Vault密钥(加密存储)
if [ -f ~/.vault_pass ]; then
gpg --encrypt --recipient ops@example.com
-o "${BACKUP_DIR}/${DATE}/vault_pass.gpg"
~/.vault_pass
fi
# 保留最近30天备份
find "${BACKUP_DIR}" -type d -mtime +30 -exec rm -rf {} ;
echo"备份完成: ${BACKUP_DIR}/${DATE}"
5.3.2 SaltStack备份策略
#!/bin/bash
# /opt/salt/scripts/backup.sh
# SaltStack配置备份脚本
BACKUP_DIR="/backup/salt"
DATE=$(date +%Y%m%d_%H%M%S)
# 创建备份目录
mkdir -p "${BACKUP_DIR}/${DATE}"
# 备份Master配置
tar -czf "${BACKUP_DIR}/${DATE}/master_config.tar.gz"
/etc/salt/master
/etc/salt/master.d/
# 备份PKI密钥
tar -czf "${BACKUP_DIR}/${DATE}/pki.tar.gz"
/etc/salt/pki/
# 备份Salt状态和Pillar
tar -czf "${BACKUP_DIR}/${DATE}/srv_salt.tar.gz"
/srv/salt/
/srv/pillar/
/srv/reactor/
# 备份Minion密钥列表
salt-key -L > "${BACKUP_DIR}/${DATE}/minion_keys.txt"
# 备份Grains数据
salt '*' grains.items --out=json > "${BACKUP_DIR}/${DATE}/grains.json"
# 保留最近30天备份
find "${BACKUP_DIR}" -type d -mtime +30 -exec rm -rf {} ;
echo"备份完成: ${BACKUP_DIR}/${DATE}"
# 灾难恢复Playbook
# /opt/ansible/playbooks/salt_recovery.yml
---
-name:SaltStack灾难恢复
hosts:salt_master
become:yes
vars:
backup_path:/backup/salt/latest
tasks:
-name:停止SaltMaster服务
ansible.builtin.systemd:
name:salt-master
state:stopped
-name:恢复Master配置
ansible.builtin.unarchive:
src:"{{ backup_path }}/master_config.tar.gz"
dest:/
remote_src:yes
-name:恢复PKI密钥
ansible.builtin.unarchive:
src:"{{ backup_path }}/pki.tar.gz"
dest:/
remote_src:yes
-name:恢复Salt状态文件
ansible.builtin.unarchive:
src:"{{ backup_path }}/srv_salt.tar.gz"
dest:/
remote_src:yes
-name:启动SaltMaster服务
ansible.builtin.systemd:
name:salt-master
state:started
enabled:yes
-name:验证Minion连接
ansible.builtin.command:salt'*'test.ping
register:ping_result
-name:显示恢复结果
ansible.builtin.debug:
var:ping_result.stdout_lines
六、总结
6.1 技术要点回顾
通过本文的对比分析,从架构设计、部署方式、性能表现、易用性等多个维度对Ansible和SaltStack进行了全面评估:
架构对比:
| 对比维度 | Ansible | SaltStack |
|---|---|---|
| 通信方式 | SSH(无代理) | ZeroMQ(代理) |
| 配置语言 | YAML + Jinja2 | YAML + Jinja2 |
| 学习曲线 | 较低 | 中等 |
| 执行模式 | Push | Push + Pull |
| 扩展性 | Python模块 | Python模块 + Reactor |
| 实时性 | 按需执行 | 事件驱动 |
选型建议:
选择Ansible的场景:
中小规模集群(100-500台)
团队配置管理经验较少
混合环境(物理机+虚拟机+网络设备)
CI/CD集成需求强
不希望在目标机器安装代理
选择SaltStack的场景:
大规模集群(1000+台)
需要实时配置管理
复杂的事件驱动自动化
需要高频率配置变更
对执行性能有严格要求
6.2 进阶学习方向
Ansible进阶:
Ansible Tower/AWX企业级部署
自定义模块和插件开发
Ansible Galaxy角色开发和分享
Ansible与Kubernetes集成
Ansible Lint和测试框架
SaltStack进阶:
Salt Syndic分布式架构
Salt Cloud云资源管理
Salt Reactor高级应用
Salt Beacon监控集成
Salt Thorium智能自动化
6.3 参考资料
Ansible官方文档:https://docs.ansible.com/
SaltStack官方文档:https://docs.saltproject.io/
Ansible Galaxy:https://galaxy.ansible.com/
SaltStack Formulas:https://github.com/saltstack-formulas
Red Hat Ansible Automation Platform:https://www.redhat.com/en/technologies/management/ansible
附录
A. 命令速查表
Ansible常用命令:
# 连通性测试 ansible all -m ping # 执行Ad-hoc命令 ansible webservers -m shell -a "df -h" # 运行Playbook ansible-playbook playbook.yml # 检查模式(干运行) ansible-playbook playbook.yml --check --diff # 限制执行范围 ansible-playbook playbook.yml --limit web-node-01 # 指定标签 ansible-playbook playbook.yml --tags "nginx,config" # 跳过标签 ansible-playbook playbook.yml --skip-tags "backup" # 列出任务 ansible-playbook playbook.yml --list-tasks # 列出主机 ansible-playbook playbook.yml --list-hosts # 加密文件 ansible-vault encrypt secrets.yml # 解密文件 ansible-vault decrypt secrets.yml # 编辑加密文件 ansible-vault edit secrets.yml # 查看加密文件 ansible-vault view secrets.yml
SaltStack常用命令:
# 连通性测试 salt '*' test.ping # 执行远程命令 salt 'web-*' cmd.run 'uptime' # 应用状态 salt '*' state.apply # 高状态应用 salt '*' state.highstate # 测试模式 salt '*' state.apply test=True # 刷新Pillar salt '*' saltutil.refresh_pillar # 查看Grains salt '*' grains.items # 查看Pillar salt '*' pillar.items # 密钥管理 salt-key -L # 列出所有密钥 salt-key -a minion # 接受密钥 salt-key -d minion # 删除密钥 salt-key -A # 接受所有待定密钥 # 批量执行 salt '*' state.apply batch=5 # 异步执行 salt '*' state.apply --async salt-run jobs.lookup_jid
B. 配置参数详解
Ansible关键配置参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
| forks | 5 | 并发连接数 |
| timeout | 10 | SSH连接超时(秒) |
| remote_user | 当前用户 | 远程登录用户 |
| host_key_checking | True | 是否检查主机密钥 |
| pipelining | False | 是否启用管道模式 |
| gathering | implicit | Facts收集策略 |
| fact_caching | memory | Facts缓存后端 |
| retry_files_enabled | True | 是否生成重试文件 |
| stdout_callback | default | 输出回调插件 |
SaltStack关键配置参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
| worker_threads | 5 | 工作线程数 |
| timeout | 5 | 命令超时(秒) |
| gather_job_timeout | 10 | 任务收集超时(秒) |
| auto_accept | False | 自动接受密钥 |
| file_recv | False | 允许Minion上传文件 |
| pillar_cache | False | 启用Pillar缓存 |
| state_verbose | True | 详细状态输出 |
| log_level | warning | 日志级别 |
C. 术语表
| 术语 | Ansible | SaltStack |
|---|---|---|
| 控制节点 | Control Node | Master |
| 被管理节点 | Managed Node | Minion |
| 配置清单 | Inventory | Roster/Grains |
| 配置文件 | Playbook | State |
| 变量数据 | Variables | Pillar |
| 模块 | Module | Execution Module |
| 任务 | Task | State Function |
| 角色 | Role | Formula |
| 模板 | Template (Jinja2) | Template (Jinja2) |
| 事件 | Callback | Event/Reactor |
| 执行结果 | Return Value | Return |
全部0条评论
快来发表一下你的评论吧 !