Ansible 教程:自动化配置管理工具详解
1. 什么是 Ansible?
Ansible 是一个开源的自动化配置管理和应用部署工具,由 Red Hat 开发和维护。它使用简单的声明式语言(YAML)来描述配置和任务,通过 SSH 协议在远程主机上执行操作,不需要在目标主机上安装额外的代理软件。
1.1 核心概念
- 控制节点(Control Node):运行 Ansible 命令的主机。
- 被控节点(Managed Nodes):被 Ansible 管理的远程主机。
- 清单(Inventory):被控节点的列表,定义了主机和主机组。
- 模块(Module):执行特定任务的代码单元,如文件操作、包管理等。
- 任务(Task):使用模块执行的单个操作。
- 剧本(Playbook):定义一系列任务的 YAML 文件,用于自动化复杂的工作流程。
- 角色(Role):可重用的任务和配置集合。
- 变量(Variable):用于参数化配置和任务。
- 事实(Facts):Ansible 自动收集的被控节点信息。
1.2 核心特性
- 无代理架构:不需要在被控节点上安装代理软件,通过 SSH 执行操作。
- 简单易用:使用 YAML 编写配置和任务,语法简洁明了。
- 幂等性:重复执行相同的操作不会产生副作用。
- 模块化:提供丰富的内置模块,支持各种任务。
- 可扩展性:支持自定义模块和插件。
- 强大的剧本系统:通过剧本定义复杂的工作流程。
- 多平台支持:支持 Linux、Windows、macOS 等多种操作系统。
1.3 适用场景
- 配置管理:统一管理和配置多台服务器。
- 应用部署:自动化应用的部署和更新。
- 任务编排:协调多个服务器上的任务执行。
- 持续集成/持续部署(CI/CD):与 CI/CD 系统集成,自动化构建和部署流程。
- 基础设施即代码(IaC):将基础设施配置代码化。
- 云资源管理:管理云服务提供商的资源。
2. 安装和配置
2.1 安装 Ansible
2.1.1 Linux 安装
在 Ubuntu/Debian 上安装:
# 更新包索引
sudo apt update
# 安装 Ansible
sudo apt install ansible在 CentOS/RHEL 上安装:
# 安装 EPEL 仓库
sudo yum install epel-release
# 安装 Ansible
sudo yum install ansible使用 pip 安装:
# 安装 pip
sudo apt install python3-pip # Ubuntu/Debian
sudo yum install python3-pip # CentOS/RHEL
# 安装 Ansible
pip3 install ansible2.1.2 macOS 安装
使用 Homebrew 安装:
# 安装 Homebrew(如果尚未安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 安装 Ansible
brew install ansible2.1.3 Windows 安装
Windows 上可以通过 Windows Subsystem for Linux (WSL) 安装 Ansible:
启用 WSL:
- 打开 PowerShell 作为管理员
- 运行:
wsl --install - 重启计算机
安装 Ubuntu WSL 分发版:
- 打开 Microsoft Store
- 搜索并安装 "Ubuntu"
- 启动 Ubuntu 并完成初始化
在 Ubuntu WSL 中安装 Ansible:
sudo apt update
sudo apt install ansible2.2 验证安装
ansible --version2.3 配置 SSH 密钥
Ansible 使用 SSH 与被控节点通信,建议配置 SSH 密钥认证以避免密码提示:
# 生成 SSH 密钥对
ssh-keygen -t rsa -b 2048
# 将公钥复制到被控节点
ssh-copy-id username@remote_host2.4 配置清单文件
创建一个清单文件 inventory.ini:
# 简单清单
[webservers]
web1.example.com
web2.example.com
[dbservers]
db1.example.com
# 使用别名和端口
[webservers]
web1 ansible_host=web1.example.com ansible_port=2222 ansible_user=ubuntu
web2 ansible_host=web2.example.com ansible_user=ubuntu
[dbservers]
db1 ansible_host=db1.example.com ansible_user=ubuntu
# 分组
[all:children]
webservers
dbservers3. 基本使用
3.1 测试连接
# 测试与所有主机的连接
ansible all -i inventory.ini -m ping
# 测试与特定组的连接
ansible webservers -i inventory.ini -m ping
# 测试与特定主机的连接
ansible web1 -i inventory.ini -m ping3.2 执行临时命令
# 在所有主机上执行命令
ansible all -i inventory.ini -a "ls -la"
# 在特定组上执行命令
ansible webservers -i inventory.ini -a "df -h"
# 使用 sudo 执行命令
ansible all -i inventory.ini -b -a "apt update"
# 执行命令并指定用户
ansible all -i inventory.ini -u ubuntu -a "whoami"3.3 使用模块
# 使用 file 模块创建目录
ansible webservers -i inventory.ini -m file -a "path=/tmp/test state=directory"
# 使用 copy 模块复制文件
ansible webservers -i inventory.ini -m copy -a "src=./test.txt dest=/tmp/test.txt"
# 使用 apt 模块安装包
ansible webservers -i inventory.ini -b -m apt -a "name=nginx state=present"
# 使用 service 模块启动服务
ansible webservers -i inventory.ini -b -m service -a "name=nginx state=started enabled=yes"3.4 编写和运行剧本
创建一个简单的剧本文件 playbook.yml:
---
- name: Configure web servers
hosts: webservers
become: yes
tasks:
- name: Update apt cache
apt:
update_cache: yes
- name: Install nginx
apt:
name: nginx
state: present
- name: Start and enable nginx
service:
name: nginx
state: started
enabled: yes
- name: Copy index.html
copy:
src: ./index.html
dest: /var/www/html/index.html
mode: '0644'运行剧本:
ansible-playbook -i inventory.ini playbook.yml3.5 使用变量
创建变量文件 vars.yml:
---
nginx_package: nginx
nginx_service: nginx
web_root: /var/www/html
index_file: index.html更新剧本文件:
---
- name: Configure web servers
hosts: webservers
become: yes
vars_files:
- vars.yml
tasks:
- name: Update apt cache
apt:
update_cache: yes
- name: Install nginx
apt:
name: "{{ nginx_package }}"
state: present
- name: Start and enable nginx
service:
name: "{{ nginx_service }}"
state: started
enabled: yes
- name: Copy index.html
copy:
src: "./{{ index_file }}"
dest: "{{ web_root }}/{{ index_file }}"
mode: '0644'4. 高级功能
4.1 使用角色
创建角色目录结构:
mkdir -p roles/nginx/{tasks,handlers,templates,files,vars,defaults,meta}创建角色文件:
roles/nginx/tasks/main.yml:
---
- name: Update apt cache
apt:
update_cache: yes
- name: Install nginx
apt:
name: nginx
state: present
- name: Copy nginx configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: Copy index.html
copy:
src: index.html
dest: /var/www/html/index.html
mode: '0644'
- name: Start and enable nginx
service:
name: nginx
state: started
enabled: yesroles/nginx/handlers/main.yml:
---
- name: restart nginx
service:
name: nginx
state: restartedroles/nginx/templates/nginx.conf.j2:
user www-data;
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name {{ inventory_hostname }};
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
include /etc/nginx/conf.d/*.conf;
}roles/nginx/files/index.html:
<!DOCTYPE html>
<html>
<head>
<title>Ansible Test</title>
</head>
<body>
<h1>Hello from {{ inventory_hostname }}!</h1>
</body>
</html>使用角色的剧本:
---
- name: Configure web servers with nginx role
hosts: webservers
become: yes
roles:
- nginx4.2 使用条件语句
---
- name: Configure servers based on OS
hosts: all
become: yes
tasks:
- name: Install nginx on Debian/Ubuntu
apt:
name: nginx
state: present
when: ansible_os_family == "Debian"
- name: Install nginx on CentOS/RHEL
yum:
name: nginx
state: present
when: ansible_os_family == "RedHat"
- name: Start and enable nginx
service:
name: nginx
state: started
enabled: yes4.3 使用循环
---
- name: Install multiple packages
hosts: webservers
become: yes
tasks:
- name: Install packages
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- curl
- wget
- git
- name: Create multiple directories
file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- /tmp/test1
- /tmp/test2
- /tmp/test34.4 使用事实和注册变量
---
- name: Use facts and registered variables
hosts: webservers
tasks:
- name: Get system information
shell: uname -a
register: uname_output
- name: Display system information
debug:
msg: "System information: {{ uname_output.stdout }}"
- name: Show ansible facts
debug:
var: ansible_facts['distribution']
- name: Show all facts
debug:
var: ansible_facts4.5 使用模板
创建模板文件 templates/index.html.j2:
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>{{ header }}</h1>
<p>{{ description }}</p>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<p>Server: {{ inventory_hostname }}</p>
<p>IP Address: {{ ansible_facts['default_ipv4']['address'] }}</p>
</body>
</html>使用模板的剧本:
---
- name: Use templates
hosts: webservers
become: yes
vars:
title: "Ansible Template Example"
header: "Welcome to My Website"
description: "This page was generated using Ansible templates."
items:
- Item 1
- Item 2
- Item 3
tasks:
- name: Copy template
template:
src: templates/index.html.j2
dest: /var/www/html/index.html
mode: '0644'5. 实用案例
5.1 部署 WordPress
场景:使用 Ansible 部署 WordPress 网站。
步骤:
- 创建项目目录:
mkdir ansible-wordpress && cd ansible-wordpress- 创建清单文件:
inventory.ini:
[wordpress]
server1 ansible_host=your-server-ip ansible_user=ubuntu- 创建变量文件:
vars/main.yml:
---
# WordPress 配置
wordpress_db_name: wordpress
wordpress_db_user: wordpressuser
wordpress_db_password: your-strong-password
wordpress_db_host: localhost
wordpress_version: 6.2.2
wordpress_download_url: https://wordpress.org/wordpress-{{ wordpress_version }}.tar.gz
wordpress_install_dir: /var/www/html
wordpress_site_title: "My WordPress Site"
wordpress_admin_user: admin
wordpress_admin_password: your-admin-password
wordpress_admin_email: admin@example.com
# 系统配置
php_packages:
- php
- php-mysql
- php-curl
- php-gd
- php-mbstring
- php-xml
- php-xmlrpc
- php-soap
- php-intl
- php-zip- 创建角色:
roles/common/tasks/main.yml:
---
- name: Update apt cache
apt:
update_cache: yes
- name: Install basic packages
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg-agent
- software-properties-common
- unzip
state: presentroles/mysql/tasks/main.yml:
---
- name: Install MySQL server
apt:
name: mysql-server
state: present
- name: Start and enable MySQL
service:
name: mysql
state: started
enabled: yes
- name: Create WordPress database
mysql_db:
name: "{{ wordpress_db_name }}"
state: present
- name: Create WordPress database user
mysql_user:
name: "{{ wordpress_db_user }}"
password: "{{ wordpress_db_password }}"
priv: "{{ wordpress_db_name }}.*:ALL"
state: presentroles/php/tasks/main.yml:
---
- name: Install PHP and extensions
apt:
name: "{{ php_packages }}"
state: present
- name: Restart Apache
service:
name: apache2
state: restartedroles/wordpress/tasks/main.yml:
---
- name: Download WordPress
get_url:
url: "{{ wordpress_download_url }}"
dest: "/tmp/wordpress-{{ wordpress_version }}.tar.gz"
- name: Extract WordPress
unarchive:
src: "/tmp/wordpress-{{ wordpress_version }}.tar.gz"
dest: /tmp
remote_src: yes
- name: Copy WordPress files
copy:
src: /tmp/wordpress/
dest: "{{ wordpress_install_dir }}"
remote_src: yes
owner: www-data
group: www-data
- name: Create WordPress config file
template:
src: templates/wp-config.php.j2
dest: "{{ wordpress_install_dir }}/wp-config.php"
owner: www-data
group: www-data
- name: Set permissions
file:
path: "{{ wordpress_install_dir }}"
state: directory
recurse: yes
owner: www-data
group: www-data
- name: Install WordPress
shell:
cmd: >
php {{ wordpress_install_dir }}/wp-cli.phar core install
--url=http://{{ ansible_default_ipv4.address }}
--title="{{ wordpress_site_title }}"
--admin_user={{ wordpress_admin_user }}
--admin_password={{ wordpress_admin_password }}
--admin_email={{ wordpress_admin_email }}
--path={{ wordpress_install_dir }}
creates: "{{ wordpress_install_dir }}/wp-content"roles/wordpress/templates/wp-config.php.j2:
<?php
/**
* The base configuration for WordPress
*/
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', '{{ wordpress_db_name }}' );
/** MySQL database username */
define( 'DB_USER', '{{ wordpress_db_user }}' );
/** MySQL database password */
define( 'DB_PASSWORD', '{{ wordpress_db_password }}' );
/** MySQL hostname */
define( 'DB_HOST', '{{ wordpress_db_host }}' );
/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );
/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
/**#@+*/
define( 'AUTH_KEY', 'put your unique phrase here' );
define( 'SECURE_AUTH_KEY', 'put your unique phrase here' );
define( 'LOGGED_IN_KEY', 'put your unique phrase here' );
define( 'NONCE_KEY', 'put your unique phrase here' );
define( 'AUTH_SALT', 'put your unique phrase here' );
define( 'SECURE_AUTH_SALT', 'put your unique phrase here' );
define( 'LOGGED_IN_SALT', 'put your unique phrase here' );
define( 'NONCE_SALT', 'put your unique phrase here' );
/**#@-*/
$table_prefix = 'wp_';
define( 'WP_DEBUG', false );
if ( !defined( 'ABSPATH' ) )
define( 'ABSPATH', dirname( __FILE__ ) . '/' );
require_once( ABSPATH . 'wp-settings.php' );- 创建主剧本:
playbook.yml:
---
- name: Deploy WordPress
hosts: wordpress
become: yes
vars_files:
- vars/main.yml
roles:
- common
- mysql
- php
- wordpress- 运行剧本:
ansible-playbook -i inventory.ini playbook.yml- 测试访问:
打开浏览器访问服务器的 IP 地址,应该看到 WordPress 网站。
5.2 配置负载均衡器
场景:使用 Ansible 配置 Nginx 作为负载均衡器,前端两台 Web 服务器。
步骤:
- 创建项目目录:
mkdir ansible-loadbalancer && cd ansible-loadbalancer- 创建清单文件:
inventory.ini:
[loadbalancer]
lb1 ansible_host=your-lb-ip ansible_user=ubuntu
[webservers]
web1 ansible_host=your-web1-ip ansible_user=ubuntu
web2 ansible_host=your-web2-ip ansible_user=ubuntu
[all:children]
loadbalancer
webservers- 创建变量文件:
vars/main.yml:
---
# 负载均衡器配置
load_balancer_ip: your-lb-ip
web_servers:
- web1 ansible_host=your-web1-ip
- web2 ansible_host=your-web2-ip
# Web 服务器配置
web_root: /var/www/html- 创建角色:
roles/webserver/tasks/main.yml:
---
- name: Update apt cache
apt:
update_cache: yes
- name: Install nginx
apt:
name: nginx
state: present
- name: Create index.html
template:
src: templates/index.html.j2
dest: "{{ web_root }}/index.html"
mode: '0644'
- name: Start and enable nginx
service:
name: nginx
state: started
enabled: yesroles/webserver/templates/index.html.j2:
<!DOCTYPE html>
<html>
<head>
<title>Web Server {{ inventory_hostname }}</title>
</head>
<body>
<h1>Hello from {{ inventory_hostname }}!</h1>
<p>This is a test page for the load balancer setup.</p>
</body>
</html>roles/loadbalancer/tasks/main.yml:
---
- name: Update apt cache
apt:
update_cache: yes
- name: Install nginx
apt:
name: nginx
state: present
- name: Configure nginx as load balancer
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: Start and enable nginx
service:
name: nginx
state: started
enabled: yesroles/loadbalancer/templates/nginx.conf.j2:
user www-data;
worker_processes auto;
events {
worker_connections 1024;
}
http {
upstream web_servers {
{% for server in groups['webservers'] %}
server {{ hostvars[server]['ansible_host'] }}:80;
{% endfor %}
}
server {
listen 80;
server_name {{ load_balancer_ip }};
location / {
proxy_pass http://web_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}roles/loadbalancer/handlers/main.yml:
---
- name: restart nginx
service:
name: nginx
state: restarted- 创建主剧本:
playbook.yml:
---
- name: Configure web servers
hosts: webservers
become: yes
vars_files:
- vars/main.yml
roles:
- webserver
- name: Configure load balancer
hosts: loadbalancer
become: yes
vars_files:
- vars/main.yml
roles:
- loadbalancer- 运行剧本:
ansible-playbook -i inventory.ini playbook.yml- 测试访问:
打开浏览器访问负载均衡器的 IP 地址,刷新页面应该会看到不同的 Web 服务器响应。
6. 代码示例
6.1 基本剧本示例
playbook.yml:
---
- name: Basic server setup
hosts: all
become: yes
tasks:
- name: Update apt cache
apt:
update_cache: yes
- name: Install essential packages
apt:
name:
- curl
- wget
- git
- unzip
- htop
state: present
- name: Create a welcome message
copy:
content: "Welcome to {{ inventory_hostname }}!\n"
dest: /etc/motd
mode: '0644'
- name: Check system status
debug:
msg:
- "Hostname: {{ ansible_hostname }}"
- "IP Address: {{ ansible_default_ipv4.address }}"
- "OS: {{ ansible_distribution }} {{ ansible_distribution_version }}"
- "Memory: {{ ansible_memory_mb.real.total }} MB"运行命令:
ansible-playbook -i inventory.ini playbook.yml6.2 部署 Node.js 应用
playbook.yml:
---
- name: Deploy Node.js application
hosts: webservers
become: yes
vars:
app_name: my-node-app
app_dir: /opt/{{ app_name }}
app_repo: https://github.com/yourusername/{{ app_name }}.git
node_version: 18.x
tasks:
- name: Update apt cache
apt:
update_cache: yes
- name: Install dependencies
apt:
name:
- git
- curl
- build-essential
state: present
- name: Install Node.js
shell:
cmd: |
curl -fsSL https://deb.nodesource.com/setup_{{ node_version }} | bash -
apt-get install -y nodejs
args:
executable: /bin/bash
- name: Create application directory
file:
path: "{{ app_dir }}"
state: directory
owner: ubuntu
group: ubuntu
- name: Clone application repository
git:
repo: "{{ app_repo }}"
dest: "{{ app_dir }}"
version: main
become: no
- name: Install npm dependencies
npm:
path: "{{ app_dir }}"
become: no
- name: Create systemd service
template:
src: templates/app.service.j2
dest: /etc/systemd/system/{{ app_name }}.service
- name: Start and enable application
systemd:
name: "{{ app_name }}"
state: started
enabled: yes
daemon_reload: yes
- name: Install nginx
apt:
name: nginx
state: present
- name: Configure nginx reverse proxy
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/{{ app_name }}
- name: Enable nginx site
file:
src: /etc/nginx/sites-available/{{ app_name }}
dest: /etc/nginx/sites-enabled/{{ app_name }}
state: link
- name: Restart nginx
service:
name: nginx
state: restartedtemplates/app.service.j2:
[Unit]
Description={{ app_name }}
After=network.target
[Service]
User=ubuntu
WorkingDirectory={{ app_dir }}
ExecStart=/usr/bin/node {{ app_dir }}/app.js
Restart=always
RestartSec=10
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.targettemplates/nginx.conf.j2:
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}运行命令:
ansible-playbook -i inventory.ini playbook.yml7. 常见问题和解决方案
7.1 SSH 连接问题
问题:Ansible 无法连接到被控节点。
解决方案:
- 检查网络连接是否正常
- 确保 SSH 服务在被控节点上运行
- 检查 SSH 密钥是否正确配置
- 检查防火墙设置是否允许 SSH 连接
- 使用
-vvv参数查看详细的连接日志
7.2 权限问题
问题:Ansible 执行命令时提示权限不足。
解决方案:
- 使用
-b或--become参数以 root 用户执行命令 - 使用
-u参数指定具有足够权限的用户 - 确保 sudo 配置正确,允许无密码 sudo
7.3 模块执行失败
问题:Ansible 模块执行失败,提示错误信息。
解决方案:
- 检查模块参数是否正确
- 检查被控节点的环境是否满足模块要求
- 使用
-vvv参数查看详细的执行日志 - 尝试在被控节点上手动执行相同的操作,查看是否有错误
7.4 剧本执行缓慢
问题:Ansible 剧本执行速度很慢。
解决方案:
- 减少任务数量,合并相似的任务
- 使用
async和poll参数异步执行长时间运行的任务 - 启用 SSH 连接复用
- 减少事实收集的范围
- 使用
strategy: free策略并行执行任务
7.5 变量未定义
问题:Ansible 提示变量未定义。
解决方案:
- 检查变量名称是否正确
- 确保变量在正确的作用域中定义
- 使用
default过滤器为变量提供默认值 - 检查变量文件是否正确加载
8. 总结
Ansible 是一个强大的自动化配置管理和应用部署工具,它通过简单的 YAML 语法和无代理架构,使得服务器管理变得更加高效和可靠。Ansible 不仅可以用于配置管理,还可以用于应用部署、任务编排、持续集成/持续部署等多种场景。
8.1 优点
- 无代理架构:不需要在被控节点上安装额外的软件。
- 简单易用:使用 YAML 编写配置和任务,语法简洁明了。
- 幂等性:重复执行相同的操作不会产生副作用。
- 模块化:提供丰富的内置模块,支持各种任务。
- 可扩展性:支持自定义模块和插件。
- 强大的剧本系统:通过剧本定义复杂的工作流程。
- 多平台支持:支持 Linux、Windows、macOS 等多种操作系统。
- 活跃的社区:拥有庞大的用户社区和丰富的文档。
8.2 局限性
- 性能:对于大规模基础设施,执行速度可能较慢。
- Windows 支持:虽然支持 Windows,但某些模块可能功能有限。
- 复杂逻辑:处理复杂的条件逻辑和循环可能不如编程语言灵活。
- 状态管理:默认情况下,Ansible 不跟踪系统状态,每次执行都重新评估。
8.3 适用场景
- 配置管理:统一管理和配置多台服务器。
- 应用部署:自动化应用的部署和更新。
- 任务编排:协调多个服务器上的任务执行。
- 持续集成/持续部署(CI/CD):与 CI/CD 系统集成,自动化构建和部署流程。
- 基础设施即代码(IaC):将基础设施配置代码化。
- 云资源管理:管理云服务提供商的资源。
- 灾难恢复:快速重建基础设施。
- 合规性检查:确保服务器配置符合合规要求。
通过本教程,你应该已经掌握了 Ansible 的基本概念、安装配置、基本使用和常见场景的应用。在实际工作中,Ansible 可以大大简化服务器管理和应用部署的工作,提高工作效率和系统可靠性。