Ansible安装配置最佳实践:从零基础到精通,避开新手常见坑,高效管理服务器集群

什么是Ansible及其核心优势
Ansible是一个现代化的IT自动化工具,用于配置管理、应用部署和任务自动化。与传统的自动化工具相比,Ansible具有以下显著优势:
无代理架构
:Ansible通过SSH协议工作,无需在目标服务器上安装任何客户端软件
简单易学
:使用人类可读的YAML语言编写Playbook
幂等性
:重复执行相同操作不会改变系统状态
模块化设计
:拥有超过1000个内置模块,覆盖几乎所有运维场景
跨平台支持
:支持Linux、Windows、网络设备等多种平台
安装Ansible
系统要求
控制节点:Linux/Unix系统(推荐CentOS 7+、Ubuntu 18.04+)
Python版本:2.7或3.5+
目标节点:任何支持SSH的系统
安装方法
1. 使用pip安装(推荐)
# 安装pip(如果未安装)
sudo yum install -y python-pip # CentOS/RHEL
sudo apt-get install -y python-pip # Ubuntu/Debian

# 安装Ansible
sudo pip install ansible
2. 使用系统包管理器
CentOS/RHEL:
sudo yum install -y epel-release
sudo yum install -y ansible
Ubuntu/Debian:
sudo apt-get update
sudo apt-get install -y software-properties-common
sudo add-apt-repository –yes –update ppa:ansible/ansible
sudo apt-get install -y ansible
3. 验证安装
ansible –version
预期输出:
ansible 2.9.6
config file = /etc/ansible/ansible.cfg
configured module search path = [‘/root/.ansible/plugins/modules’, ‘/usr/share/ansible/plugins/modules’]
ansible python module location = /usr/lib/python2.7/site-packages/ansible
executable location = /usr/bin/ansible
python version = 2.7.5 (default, Apr 9 2019, 14:30:38) [GCC 4.8.5 20150623 (Red Hat 4.8.5-3623)]
基础配置
1. 创建Ansible配置文件
Ansible默认从
/etc/ansible/ansible.cfg
读取配置,但建议在项目目录中创建本地配置:
mkdir ansible-project
cd ansible-project
touch ansible.cfg
编辑
ansible.cfg
:
[defaults]
# 主机库存文件路径
inventory = ./hosts

# 禁用SSH主机密钥检查(仅测试环境使用)
host_key_checking = False

# 日志文件路径
log_path = ./ansible.log

# 默认用户
remote_user = root

# 默认私钥路径
private_key_file = ~/.ssh/id_rsa

# 指定Python解释器(避免Python版本问题)
interpreter_python = /usr/bin/python3

[ssh_connection]
# SSH超时时间(秒)
timeout = 30

# 使用ControlPersist加速SSH连接
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
2. 创建主机库存文件
创建
hosts
文件:
[web_servers]
192.168.1.101
192.168.1.102
192.168.1.103

[db_servers]
192.168.1.201
192.168.1.202

[monitoring]
192.168.1.50

[production:children]
web_servers
db_servers
monitoring

[staging]
192.168.2.101
192.168.2.102
3. 配置SSH免密登录
在控制节点生成SSH密钥对:
ssh-keygen -t rsa -b 4096 -C “ansible@control-node”
将公钥分发到所有目标服务器:
ssh-copy-id root@192.168.1.101
ssh-copy-id root@192.168.1.102
# … 为所有服务器执行
基础使用:Ad-hoc命令
1. 测试连接
# 测试所有主机连通性
ansible all -m ping

# 测试特定主机组
ansible web_servers -m ping
2. 执行命令
# 查看所有主机的磁盘使用情况
ansible all -m command -a “df -h”

# 查看web_servers组的内存信息
ansible web_servers -m shell -a “free -m”

# 使用sudo权限执行命令
ansible db_servers -m shell -a “apt-get update” -b
3. 文件管理
# 创建目录
ansible web_servers -m file -a “path=/var/www/html state=directory mode=0755”

# 创建文件
ansible web_servers -m file -a “path=/var/www/html/index.html state=touch mode=0644”

# 复制文件
ansible web_servers -m copy -a “src=/local/path/file.conf dest=/etc/nginx/conf.d/file.conf owner=nginx group=nginx mode=0644”
Playbook编写最佳实践
1. Playbook基础结构
创建第一个Playbook
nginx.yml
:
—
– name: 部署和配置Nginx
hosts: web_servers
become: yes # 使用sudo权限
vars:
nginx_version: “1.18.0”
nginx_worker_processes: 4
nginx_listen_port: 80

tasks:
– name: 安装Nginx
yum:
name: nginx
state: present
when: ansible_os_family == “RedHat”

– name: 安装Nginx (Debian)
apt:
name: nginx
state: present
update_cache: yes
when: ansible_os_family == “Debian”

– name: 创建Nginx配置目录
file:
path: /etc/nginx/conf.d
state: directory
mode: 0755

– name: 配置Nginx
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: 0644
notify: 重启Nginx

– name: 启动Nginx并设置开机自启
service:
name: nginx
state: started
enabled: yes

handlers:
– name: 重启Nginx
service:
name: nginx
state: restarted
2. 使用模板文件
创建模板目录和文件:
mkdir templates
touch templates/nginx.conf.j2
编辑
templates/nginx.conf.j2
:
user {{ nginx_user | default(‘nginx’) }};
worker_processes {{ nginx_worker_processes }};

error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;

events {
worker_connections {{ nginx_worker_connections | default(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;

# Gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

# 虚拟主机配置
server {
listen {{ nginx_listen_port }};
server_name {{ ansible_fqdn }};

root /var/www/html;
index index.html index.htm;

location / {
try_files $uri $uri/ =404;
}
}
}
3. 运行Playbook
# 语法检查
ansible-playbook nginx.yml –syntax-check

# 模拟运行(dry-run)
ansible-playbook nginx.yml –check

# 正式执行
ansible-playbook nginx.yml

# 指定特定主机组
ansible-playbook nginx.yml –limit web_servers

# 并行执行(提高速度)
ansible-playbook nginx.yml -f 10 # 10个并行任务
角色(Roles)组织
1. 创建角色结构
ansible-galaxy init roles/nginx
生成的目录结构:
roles/nginx/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
└── vars
└── main.yml
2. 完善角色内容
tasks/main.yml:
—
– name: 安装Nginx
package:
name: nginx
state: present

– name: 创建配置目录
file:
path: /etc/nginx/conf.d
state: directory
mode: 0755

– name: 配置Nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: 0644
notify: 重启Nginx

– name: 启动Nginx
service:
name: nginx
state: started
enabled: yes
handlers/main.yml:
—
– name: 重启Nginx
service:
name: nginx
state: restarted
defaults/main.yml:
—
nginx_worker_processes: 4
nginx_listen_port: 80
nginx_user: nginx
nginx_worker_connections: 1024
3. 使用角色
创建
site.yml
:
—
– name: 部署Web服务器
hosts: web_servers
become: yes
roles:
– nginx
– common
运行角色:
ansible-playbook site.yml
高级技巧与最佳实践
1. 变量管理
组变量:
创建
group_vars/
目录
mkdir group_vars
touch group_vars/web_servers.yml
编辑
group_vars/web_servers.yml
:
—
nginx_worker_processes: 8
nginx_listen_port: 8080
主机变量:
创建
host_vars/
目录
mkdir host_vars
touch host_vars/192.168.1.101.yml
编辑
host_vars/192.168.1.101.yml
:
—
nginx_listen_port: 8081
Vault加密敏感数据:
# 创建加密变量文件
ansible-vault create group_vars/production/secrets.yml

# 编辑加密文件
ansible-vault edit group_vars/production/secrets.yml

# 运行Playbook时提供密码
ansible-playbook site.yml –ask-vault-pass
2. 错误处理与重试
– name: 安装软件包(带重试)
yum:
name: “{{ package_name }}”
state: present
register: install_result
until: install_result is succeeded
retries: 3
delay: 10
ignore_errors: yes
3. 条件判断与循环
– name: 安装多个软件包
package:
name: “{{ item }}”
state: present
loop:
– vim
– curl
– wget
– git

– name: 根据系统版本配置
template:
src: “config-{{ ansible_distribution_version }}.j2”
dest: /etc/app/config.conf
when: ansible_distribution == “CentOS”
4. 性能优化
使用pipelining加速SSH:
在
ansible.cfg
中添加:
[ssh_connection]
pipelining = True
使用事实缓存:
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = ./facts_cache
新手常见坑及解决方案
1. SSH连接问题
问题:
Failed to connect to the host via ssh
解决方案:
# 1. 检查SSH密钥权限
chmod 600 ~/.ssh/id_rsa

# 2. 手动测试SSH连接
ssh -i ~/.ssh/id_rsa root@192.168.1.101

# 3. 在ansible.cfg中增加超时时间
[ssh_connection]
timeout = 60
2. Python版本问题
问题:
/usr/bin/python: not found
解决方案:
# 在ansible.cfg中指定Python解释器
[defaults]
interpreter_python = /usr/bin/python3
或在Playbook中指定:
– name: 安装软件包
yum:
name: nginx
state: present
vars:
ansible_python_interpreter: /usr/bin/python3
3. 权限问题
问题:
Permission denied
解决方案:
# 明确使用become
– name: 安装软件
yum:
name: nginx
state: present
become: yes
become_method: sudo
4. 模板渲染错误
问题:
变量未定义导致模板渲染失败
解决方案:
# 使用默认值
{{ nginx_port | default(80) }}

# 或在vars中定义默认值
vars:
nginx_port: 80
5. 幂等性问题
问题:
重复执行Playbook导致意外结果
解决方案:
# 使用notify和handlers
– name: 配置文件变更
template:
src: config.j2
dest: /etc/app/config.conf
notify: 重启服务

handlers:
– name: 重启服务
service:
name: app
state: restarted
实战案例:部署LAMP栈
创建
lamp.yml
:
—
– name: 部署LAMP栈
hosts: web_servers
become: yes
vars:
mysql_root_password: “secure_password_123”
db_name: “myapp”
db_user: “myapp_user”
db_password: “app_password_456”

tasks:
# 安装Apache
– name: 安装Apache
package:
name: httpd
state: present

– name: 启动Apache
service:
name: httpd
state: started
enabled: yes

# 安装PHP
– name: 安装PHP及扩展
package:
name: “{{ item }}”
state: present
loop:
– php
– php-mysql
– php-gd
– php-mbstring

# 安装MySQL
– name: 安装MySQL
package:
name: mariadb-server
state: present

– name: 启动MySQL
service:
name: mariadb
state: started
enabled: yes

# 配置MySQL
– name: 设置MySQL root密码
mysql_user:
name: root
password: “{{ mysql_root_password }}”
check_implicit_admin: yes
login_user: root
login_password: “”

– name: 创建数据库
mysql_db:
name: “{{ db_name }}”
state: present
login_user: root
login_password: “{{ mysql_root_password }}”

– name: 创建数据库用户
mysql_user:
name: “{{ db_user }}”
password: “{{ db_password }}”
priv: “{{ db_name }}.*:ALL”
state: present
login_user: root
login_password: “{{ mysql_root_password }}”

# 配置虚拟主机
– name: 创建应用目录
file:
path: /var/www/{{ db_name }}
state: directory
mode: 0755

– name: 配置Apache虚拟主机
template:
src: templates/vhost.conf.j2
dest: /etc/httpd/conf.d/{{ db_name }}.conf
notify: 重启Apache

handlers:
– name: 重启Apache
service:
name: httpd
state: restarted
创建模板
templates/vhost.conf.j2
:

ServerName {{ ansible_fqdn }}
DocumentRoot /var/www/{{ db_name }}


Options Indexes FollowSymLinks
AllowOverride All
Require all granted

ErrorLog /var/log/httpd/{{ db_name }}_error.log
CustomLog /var/log/httpd/{{ db_name }}_access.log combined

集群管理高级技巧
1. 滚动更新策略
– name: 滚动更新Web服务器
hosts: web_servers
become: yes
serial: 2 # 每次更新2台服务器

tasks:
– name: 从负载均衡器摘除
uri:
url: “http://lb.example.com/api/disable/{{ inventory_hostname }}”
method: POST

– name: 更新应用
yum:
name: myapp
state: latest

– name: 重启应用
service:
name: myapp
state: restarted

– name: 等待健康检查
uri:
url: “http://{{ inventory_hostname }}/health”
status_code: 200
retries: 10
delay: 5

– name: 重新加入负载均衡器
uri:
url: “http://lb.example.com/api/enable/{{ inventory_hostname }}”
method: POST
2. 动态库存
创建动态库存脚本
inventory.py
:
#!/usr/bin/env python3

import json
import subprocess

def get_ec2_instances():
“””获取AWS EC2实例”””
try:
result = subprocess.run([
‘aws’, ‘ec2’, ‘describe-instances’,
‘–filters’, ‘Name=tag:Environment,Values=production’,
‘–query’, ‘Reservations[].Instances[].{id:InstanceId, ip:PrivateIpAddress, tags:Tags}’,
‘–output’, ‘json’
], capture_output=True, text=True)

instances = json.loads(result.stdout)
inventory = {‘_meta’: {‘hostvars’: {}}}

for instance in instances:
host_id = instance[‘id’]
ip = instance[‘ip’]

# 提取标签
tags = {tag[‘Key’]: tag[‘Value’] for tag in instance[‘tags’]}
env = tags.get(‘Environment’, ‘unknown’)
role = tags.get(‘Role’, ‘unknown’)

# 添加到主机组
if env not in inventory:
inventory[env] = {‘hosts’: []}
if role not in inventory:
inventory[role] = {‘hosts’: []}

inventory[env][‘hosts’].append(ip)
inventory[role][‘hosts’].append(ip)

# 添加主机变量
inventory[‘_meta’][‘hostvars’][ip] = {
‘ansible_user’: ‘ec2-user’,
‘ansible_ssh_private_key_file’: ‘~/.ssh/aws-key.pem’,
‘ec2_id’: host_id,
‘ec2_tags’: tags
}

return inventory
except Exception as e:
return {‘error’: str(e)}

if __name__ == ‘__main__’:
print(json.dumps(get_ec2_instances()))
使用动态库存:
# 使脚本可执行
chmod +x inventory.py

# 使用动态库存
ansible -i inventory.py all -m ping
3. 集群监控与状态收集
– name: 收集集群状态
hosts: all
gather_facts: yes

tasks:
– name: 收集系统信息
setup:
filter: “{{ item }}”
loop:
– ansible_distribution
– ansible_distribution_version
– ansible_memtotal_mb
– ansible_processor_vcpus
– ansible_default_ipv4.address

– name: 检查磁盘空间
command: df -h /
register: disk_usage

– name: 检查服务状态
service_facts:

– name: 生成状态报告
copy:
content: |
Server: {{ inventory_hostname }}
OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
Memory: {{ ansible_memtotal_mb }} MB
CPU: {{ ansible_processor_vcpus }} cores
Disk Usage: {{ disk_usage.stdout }}
Services: {{ ansible_facts.services | dict2items | selectattr(‘value.state’, ‘equalto’, ‘running’) | map(attribute=’key’) | list }}
dest: “/tmp/status_{{ inventory_hostname }}.txt”
监控与日志
1. 启用详细日志
在
ansible.cfg
中:
[defaults]
# 日志级别
verbosity = 2

# Callback插件
stdout_callback = yaml
callback_whitelist = timer, profile_tasks, profile_roles
2. 使用Callback插件
创建自定义callback插件
plugins/callback/custom.py
:
from ansible.plugins.callback import CallbackBase
import json

class CallbackModule(CallbackBase):
def v2_runner_on_ok(self, result):
host = result._host
task = result._task
self._display.display(f”✅ {host.name}: {task.name}”)

def v2_runner_on_failed(self, result):
host = result._host
task = result._task
self._display.display(f”❌ {host.name}: {task.name} – {result._result.get(‘msg’, ”)}”)

def v2_runner_on_unreachable(self, result):
host = result._host
self._display.display(f”⚠️ {host.name}: Unreachable”)
3. 集成监控系统
– name: 推送指标到Prometheus
uri:
url: “http://prometheus-pushgateway:9091/metrics/job/ansible”
method: POST
body: |
ansible_tasks_total{host=”{{ inventory_hostname }}”,status=”success”} 1
ansible_execution_time{host=”{{ inventory_hostname }}”} {{ ansible_execution_time }}
headers:
Content-Type: text/plain
总结
通过本文的详细指导,您应该已经掌握了Ansible的安装、配置、基础使用、高级技巧以及常见问题的解决方案。关键要点:
始终遵循幂等性原则
:确保Playbook可以安全地重复执行
使用角色组织代码
:提高代码复用性和可维护性
合理管理变量
:使用group_vars、host_vars和Vault
错误处理
:使用try/catch机制和重试策略
性能优化
:使用pipelining、事实缓存和并行执行
测试验证
:始终先使用
–check
模式测试
通过这些最佳实践,您可以高效地管理大规模服务器集群,实现自动化运维的目标。