从0到1掌握Ansible:让自动化运维不再是梦想
最近在公司推进自动化运维的时候,发现很多同事对Ansible还是一知半解,要么就是简单用用,要么就是直接放弃。其实Ansible真的没那么复杂,我用了这么多年,今天就把我的实战经验分享给大家。
说实话,刚开始接触Ansible的时候我也是懵的。那时候公司有几十台服务器需要管理,每次部署应用或者更新配置都要一台一台去操作,累死累活不说,还容易出错。后来接触到Ansible,真的是解放了双手。
Ansible到底是个什么东西?
简单来说,Ansible就是一个自动化工具。你可以把它理解成一个"遥控器",能够同时控制多台服务器干活。比如你要在100台服务器上安装nginx,传统方式得一台台登录去装,用Ansible的话,写个脚本,一键搞定。
我记得第一次用Ansible批量部署应用的时候,看着终端里刷刷刷地执行命令,那种感觉就像开了挂一样。原本需要一整天的工作,10分钟就搞定了。
控制节点和受控节点的关系
这个概念其实很好理解。控制节点就是你安装Ansible的那台机器,受控节点就是你要管理的那些服务器。
我一般会把Ansible装在跳板机上,这样管理起来比较方便。控制节点需要能SSH到所有受控节点,这是最基本的要求。
# 在控制节点安装Ansible
yum install ansible -y
# 或者用pip安装
pip install ansible受控节点其实什么都不用装,只要能SSH连接就行。这也是Ansible的一个优势,不像其他工具还要在目标机器上装agent。
清单文件(Inventory):管理服务器的花名册
Inventory文件就像是你的服务器通讯录,记录着所有需要管理的机器信息。
最简单的inventory文件长这样:
[webservers]
192.168.1.10
192.168.1.11
192.168.1.12
[databases]
192.168.1.20
192.168.1.21但实际生产环境中,我会写得更详细一些:
[webservers]
web01 ansible_host=192.168.1.10 ansible_user=root ansible_ssh_private_key_file=/root/.ssh/id_rsa
web02 ansible_host=192.168.1.11 ansible_user=root ansible_ssh_private_key_file=/root/.ssh/id_rsa
[databases]
db01 ansible_host=192.168.1.20 ansible_user=mysql ansible_become=yes
db02 ansible_host=192.168.1.21 ansible_user=mysql ansible_become=yes
[production:children]
webservers
databases这样写的好处是可以给不同的机器设置不同的连接参数。比如有些机器用密钥登录,有些用密码,有些需要sudo权限等等。
模块(Modules):Ansible的工具箱
Ansible有几千个模块,每个模块负责不同的功能。刚开始的时候不用全部掌握,先学会常用的几个就够了。
文件操作模块
copy模块用来复制文件:
- name: 复制配置文件
copy:
src: /local/path/nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'file模块用来管理文件和目录:
- name: 创建目录
file:
path: /opt/myapp
state: directory
owner: www
group: www
mode: '0755'软件包管理
yum模块在CentOS/RHEL上安装软件:
- name: 安装nginx
yum:
name: nginx
state: presentapt模块在Ubuntu/Debian上用:
- name: 安装nginx
apt:
name: nginx
state: present
update_cache: yes服务管理
systemd模块管理系统服务:
- name: 启动nginx服务
systemd:
name: nginx
state: started
enabled: yes我在实际使用中发现,掌握这几个模块就能解决80%的日常运维工作了。
任务(Tasks)和剧本(Playbooks):自动化的剧本
Task就是一个具体的操作,比如安装软件、复制文件等。Playbook就是把多个Task组织起来,形成一个完整的自动化流程。
我来分享一个实际的例子,部署一个简单的web应用:
---
- name: 部署web应用
hosts: webservers
become: yes
vars:
app_name: myapp
app_version: 1.2.3
tasks:
- name: 安装必要的软件包
yum:
name:
- nginx
- python3
- git
state: present
- name: 创建应用目录
file:
path: "/opt/{{ app_name }}"
state: directory
owner: nginx
group: nginx
mode: '0755'
- name: 下载应用代码
git:
repo: https://github.com/company/myapp.git
dest: "/opt/{{ app_name }}"
version: "{{ app_version }}"
notify: restart nginx
- name: 复制nginx配置
template:
src: nginx.conf.j2
dest: /etc/nginx/conf.d/myapp.conf
notify: restart nginx
- name: 启动nginx服务
systemd:
name: nginx
state: started
enabled: yes
handlers:
- name: restart nginx
systemd:
name: nginx
state: restarted这个playbook做了几件事:安装软件、创建目录、下载代码、配置nginx、启动服务。如果配置文件有变化,还会自动重启nginx。
变量和模板:让配置更灵活
在实际项目中,不同环境的配置肯定是不一样的。这时候就需要用到变量和模板了。
我通常会创建不同环境的变量文件:
group_vars/production.yml:
db_host: prod-db.company.com
db_port: 3306
app_env: production
log_level: warngroup_vars/staging.yml:
db_host: staging-db.company.com
db_port: 3306
app_env: staging
log_level: debug然后在模板文件中使用这些变量:
templates/app.conf.j2:
[database]
host = {{ db_host }}
port = {{ db_port }}
[app]
environment = {{ app_env }}
log_level = {{ log_level }}这样同一个playbook就能适用于不同的环境了。
角色(Roles):模块化管理的艺术
当项目变得复杂的时候,把所有东西都写在一个playbook里就不合适了。这时候就需要用到Role。
Role的目录结构是这样的:
roles/
nginx/
tasks/main.yml
handlers/main.yml
templates/
files/
vars/main.yml
defaults/main.yml我来展示一个nginx role的例子:
roles/nginx/tasks/main.yml:
---
- name: 安装nginx
yum:
name: nginx
state: present
- name: 复制主配置文件
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: 复制站点配置
template:
src: "{{ item }}"
dest: "/etc/nginx/conf.d/{{ item | basename | regex_replace('.j2$', '') }}"
with_fileglob:
- "../templates/sites/*.j2"
notify: restart nginx
- name: 启动nginx服务
systemd:
name: nginx
state: started
enabled: yesroles/nginx/handlers/main.yml:
---
- name: restart nginx
systemd:
name: nginx
state: restarted使用role的playbook就很简洁了:
---
- name: 配置web服务器
hosts: webservers
roles:
- nginx
- php
- mysql处理器(Handlers):响应式的自动化
Handler是Ansible里一个很有用的特性。它只有在被notify的时候才会执行,而且每次playbook运行时,同一个handler最多只会执行一次。
比如说,你修改了nginx配置文件,肯定要重启nginx服务。但如果你在playbook里修改了多个配置文件,你不希望每修改一个就重启一次,而是希望所有修改完成后再重启一次。这时候handler就派上用场了。
tasks:
- name: 修改nginx主配置
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: 修改站点配置
template:
src: site.conf.j2
dest: /etc/nginx/conf.d/site.conf
notify: restart nginx
- name: 修改ssl配置
template:
src: ssl.conf.j2
dest: /etc/nginx/conf.d/ssl.conf
notify: restart nginx
handlers:
- name: restart nginx
systemd:
name: nginx
state: restarted即使三个task都触发了notify,nginx也只会重启一次。
实战技巧和踩坑经验
SSH密钥管理
刚开始用Ansible的时候,我总是为SSH连接的问题头疼。后来发现最好的方式是用密钥认证,而且要做好密钥的分发。
# 生成密钥对
ssh-keygen -t rsa -b 4096 -f ~/.ssh/ansible_rsa
# 分发公钥到所有受控节点
ssh-copy-id -i ~/.ssh/ansible_rsa.pub user@target_host在inventory文件中指定私钥:
[servers]
server1 ansible_host=192.168.1.10 ansible_ssh_private_key_file=~/.ssh/ansible_rsa幂等性的重要性
Ansible的一个重要特性就是幂等性,也就是说多次执行同一个操作,结果应该是一样的。
比如这样写就不是幂等的:
- name: 添加用户到sudoers
shell: echo "myuser ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers每次执行都会添加一行,这显然不对。应该这样写:
- name: 添加用户到sudoers
lineinfile:
path: /etc/sudoers
line: "myuser ALL=(ALL) NOPASSWD:ALL"
state: present错误处理和调试
在实际使用中,playbook不可能一次就写对。我总结了几个调试技巧:
- 使用-v参数增加输出详细程度:
ansible-playbook -vvv playbook.yml- 使用debug模块输出变量值:
- name: 显示变量值
debug:
var: ansible_facts['os_family']- 使用ignore_errors忽略某些错误:
- name: 可能会失败的任务
command: some_command_that_might_fail
ignore_errors: yes- 使用failed_when自定义失败条件:
- name: 检查服务状态
command: systemctl is-active nginx
register: nginx_status
failed_when: nginx_status.rc != 0 and nginx_status.rc != 3性能优化
当管理的机器多了之后,性能就成了问题。我用过的几个优化技巧:
- 调整并发数:
# ansible.cfg
[defaults]
forks = 50- 开启SSH pipelining:
[ssh_connection]
pipelining = True- 使用strategy插件:
- name: 快速执行的playbook
hosts: all
strategy: free
tasks:
- name: 简单任务
ping:配置文件管理
Ansible的配置文件ansible.cfg可以放在几个地方,优先级从高到低是:
- ANSIBLE_CONFIG环境变量指定的文件
- 当前目录下的ansible.cfg
- 家目录下的.ansible.cfg
- /etc/ansible/ansible.cfg
我一般会在项目目录下放一个ansible.cfg:
[defaults]
inventory = inventory/hosts
host_key_checking = False
timeout = 30
forks = 20
remote_user = root
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
pipelining = True实际应用场景
我在工作中用Ansible解决了很多实际问题,分享几个典型场景:
批量系统初始化
新机器上架后需要做一堆初始化工作:创建用户、配置SSH、安装基础软件、设置时区等等。用Ansible写个playbook,新机器几分钟就能初始化完成。
应用部署
以前部署应用要登录每台服务器,停服务、备份、更新代码、重启服务。现在一个playbook搞定,还能做到零停机部署。
配置管理
系统配置变更是运维的家常便饭,用Ansible可以确保所有机器的配置一致性,还能追踪变更历史。
定期维护
比如日志清理、证书更新、安全补丁等定期任务,写成playbook配合cron执行,省心省力。
说到这里,我想起前段时间公司要给所有服务器更新SSL证书。如果手动操作,几百台机器得忙好几天。用Ansible写了个playbook,半小时就全部搞定了,而且零出错。
当然,Ansible也不是万能的。对于一些复杂的编排场景,可能还需要结合其他工具。但对于大部分运维工作来说,Ansible已经足够强大了。
学习Ansible最好的方式就是多动手实践。从简单的任务开始,比如批量执行命令、复制文件等,然后慢慢学习更复杂的特性。记住,任何工具都是为了解决实际问题的,不要为了用而用。
希望这篇文章能帮助大家更好地理解和使用Ansible。自动化运维的路还很长,但有了Ansible这个利器,我们的工作会轻松很多。如果你觉得这篇文章有用,记得点赞转发,让更多的同行受益!
关注@运维躬行录,一起在自动化运维的道路上越走越远!
公众号:耕云躬行录
个人博客:躬行笔记