使用Ansible批量管理Linux服务器配置指南

描述

 

问题背景

运维工作中,经常需要对几十台甚至上百台服务器执行相同的操作:安装软件包、修改配置文件、重启服务、部署应用、收集日志。如果手动登录每台服务器操作,效率低、容易出错,且无法追溯变更历史。

Ansible 是一款基于 SSH 的自动化运维工具,无需在目标服务器安装 Agent,通过 YAML 编写剧本(Playbook),实现批量配置管理、应用部署、任务编排。

这篇文章面向初中级运维工程师,从环境搭建到实战案例,逐步讲解如何使用 Ansible 管理 Linux 服务器。


适用场景

  • 批量安装软件包
  • 批量修改配置文件
  • 批量重启服务
  • 批量执行脚本
  • 应用部署和回滚
  • 用户和权限管理
  • 定时任务配置
  • 日志收集和分析
  • 系统初始化和标准化
  • 灾难恢复和数据迁移

核心知识点

Ansible 架构

Ansible 由以下组件组成:

  1. 控制节点(Control Node):安装 Ansible 的机器,用于执行命令和剧本
  2. 受控节点(Managed Node):被管理的目标服务器
  3. Inventory(清单):定义受控节点的列表,支持静态文件和动态脚本
  4. Module(模块):执行具体任务的工具,如 yumcopyservice
  5. Playbook(剧本):YAML 格式的任务编排文件
  6. Role(角色):可复用的任务集合,包含任务、变量、模板、文件

Ansible 工作原理

  1. 控制节点读取 Inventory,确定目标主机
  2. 控制节点通过 SSH 连接目标主机
  3. 控制节点将模块和参数传输到目标主机
  4. 目标主机执行模块,返回结果
  5. 控制节点收集结果并输出

Ansible 特点

  • 无需 Agent,基于 SSH
  • 幂等性:多次执行结果一致,不会重复操作
  • 声明式:描述期望状态,Ansible 自动判断是否需要变更
  • 可读性强:YAML 格式,易于理解和维护
  • 模块丰富:内置数千个模块,覆盖常见运维场景

整体实施思路


			   安装 Ansible     ↓ 配置 SSH 免密登录     ↓ 编写 Inventory 清单     ↓ 测试连通性     ↓ 使用 Ad-Hoc 命令执行简单任务     ↓ 编写 Playbook 执行复杂任务     ↓ 使用 Role 组织可复用任务     ↓ 集成版本控制和 CI/CD     ↓ 监控和日志收集 

实战步骤

第一步:安装 Ansible

在控制节点(运维机器)上安装 Ansible。

CentOS / RHEL


			   bashyum install epel-release -y yum install ansible -y 

Ubuntu / Debian


			   bashapt update apt install ansible -y 

使用 pip 安装


			   bashpip3 install ansible 

验证安装:


			   bashansible --version 

输出示例:


			   ansible [core 2.14.2]   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/python3.9/site-packages/ansible   ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections   executable location = /usr/bin/ansible   python version = 3.9.7 (default, Sep 16 2021, 1358) [GCC 8.5.0 20210514 (Red Hat 8.5.0-3)]   jinja version = 3.1.2   libyaml = True 

第二步:配置 SSH 免密登录

Ansible 通过 SSH 连接目标服务器,建议配置免密登录。

生成 SSH 密钥对:


			   bashssh-keygen -t rsa -b 4096 -C "ansible@example.com" -f ~/.ssh/ansible_rsa -N "" 

将公钥复制到目标服务器:


			   bashssh-copy-id -i ~/.ssh/ansible_rsa.pub root@192.168.1.10 ssh-copy-id -i ~/.ssh/ansible_rsa.pub root@192.168.1.11 ssh-copy-id -i ~/.ssh/ansible_rsa.pub root@192.168.1.12 

测试免密登录:


			   bashssh -i ~/.ssh/ansible_rsa root@192.168.1.10 

如果无需输入密码即可登录,说明配置成功。

配置 SSH 客户端使用该密钥:

编辑 ~/.ssh/config


			   Host 192.168.1.*     IdentityFile ~/.ssh/ansible_rsa     User root     StrictHostKeyChecking no     UserKnownHostsFile=/dev/null 

第三步:编写 Inventory 清单

Inventory 定义受控节点的列表,支持 INI 和 YAML 格式。

创建 /etc/ansible/hosts(默认位置):


			   ini[webservers] web1 ansible_host=192.168.1.10 web2 ansible_host=192.168.1.11 [dbservers] db1 ansible_host=192.168.1.12 db2 ansible_host=192.168.1.13 [all:vars] ansible_user=root ansible_ssh_private_key_file=~/.ssh/ansible_rsa 

或使用 YAML 格式:


			   yamlall:   children:     webservers:       hosts:         web1:           ansible_host: 192.168.1.10         web2:           ansible_host: 192.168.1.11     dbservers:       hosts:         db1:           ansible_host: 192.168.1.12         db2:           ansible_host: 192.168.1.13   vars:     ansible_user: root     ansible_ssh_private_key_file: ~/.ssh/ansible_rsa 

也可以使用自定义 Inventory 文件:


			   bashansible -i /path/to/inventory all -m ping 

第四步:测试连通性

使用 ping 模块测试目标主机是否可达:


			   bashansible all -m ping 

输出示例:


			   web1 | SUCCESS => {     "changed": false,     "ping": "pong" } web2 | SUCCESS => {     "changed": false,     "ping": "pong" } db1 | SUCCESS => {     "changed": false,     "ping": "pong" } db2 | SUCCESS => {     "changed": false,     "ping": "pong" } 

如果显示 SUCCESS 和 pong,说明连接成功。

如果显示 UNREACHABLE,检查:

  1. 目标主机是否在线
  2. SSH 端口是否开放
  3. SSH 密钥是否配置正确
  4. 防火墙是否阻止连接

第五步:使用 Ad-Hoc 命令执行简单任务

Ad-Hoc 命令用于快速执行单个任务,不需要编写 Playbook。

查看主机信息


			   bashansible all -m setup 

输出包含目标主机的详细信息,如操作系统、内核版本、IP 地址、CPU、内存等。

过滤输出:


			   bashansible all -m setup -a "filter=ansible_distribution*" 

输出示例:


			   web1 | SUCCESS => {     "ansible_facts": {         "ansible_distribution": "CentOS",         "ansible_distribution_major_version": "7",         "ansible_distribution_release": "Core",         "ansible_distribution_version": "7.9"     },     "changed": false } 

执行 Shell 命令


			   bashansible all -m shell -a "uptime" 

输出示例:


			   web1 | CHANGED | rc=0 >>  1025 up 10 days,  2:15,  1 user,  load average: 0.08, 0.03, 0.05 web2 | CHANGED | rc=0 >>  1025 up 5 days,  8:45,  1 user,  load average: 0.12, 0.05, 0.02 

安装软件包


			   bashansible webservers -m yum -a "name=nginx state=present" 
  • name=nginx:软件包名称
  • state=present:确保已安装
  • state=absent:确保已卸载
  • state=latest:确保是最新版本

启动服务


			   bashansible webservers -m service -a "name=nginx state=started enabled=yes" 
  • state=started:启动服务
  • state=stopped:停止服务
  • state=restarted:重启服务
  • enabled=yes:设置开机自启

复制文件


			   bashansible webservers -m copy -a "src=/tmp/index.html dest=/usr/share/nginx/html/index.html owner=nginx group=nginx mode=0644" 
  • src:源文件路径(控制节点)
  • dest:目标路径(受控节点)
  • owner:文件所有者
  • group:文件所属组
  • mode:文件权限

创建用户


			   bashansible all -m user -a "name=deploy state=present shell=/bin/bash" 
  • state=present:确保用户存在
  • state=absent:删除用户
  • shell:默认 Shell
  • groups:附加组
  • append=yes:追加到附加组,不覆盖现有组

创建目录


			   bashansible all -m file -a "path=/data/backup state=directory owner=root group=root mode=0755" 
  • state=directory:确保目录存在
  • state=absent:删除目录或文件
  • state=touch:创建空文件

下载文件


			   bashansible all -m get_url -a "url=https://example.com/file.tar.gz dest=/tmp/file.tar.gz mode=0644" 

执行脚本


			   bashansible all -m script -a "/tmp/init.sh" 

脚本在控制节点,Ansible 会将其传输到目标主机并执行。


第六步:编写 Playbook 执行复杂任务

Playbook 是 YAML 格式的任务编排文件,支持变量、循环、条件判断、错误处理。

基础 Playbook 示例

创建 install_nginx.yml


			   yaml--- - name: Install and configure Nginx   hosts: webservers   become: yes   tasks:     - name: Install Nginx       yum:         name: nginx         state: present     - name: Start and enable Nginx       service:         name: nginx         state: started         enabled: yes     - name: Copy index.html       copy:         src: /tmp/index.html         dest: /usr/share/nginx/html/index.html         owner: nginx         group: nginx         mode: 0644     - name: Restart Nginx       service:         name: nginx         state: restarted 

执行 Playbook:


			   bashansible-playbook install_nginx.yml 

输出示例:


			   PLAY [Install and configure Nginx] ********************************************* TASK [Gathering Facts] ********************************************************* ok: [web1] ok: [web2] TASK [Install Nginx] *********************************************************** changed: [web1] changed: [web2] TASK [Start and enable Nginx] ************************************************** changed: [web1] changed: [web2] TASK [Copy index.html] ********************************************************* changed: [web1] changed: [web2] TASK [Restart Nginx] *********************************************************** changed: [web1] changed: [web2] PLAY RECAP ********************************************************************* web1                       : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 web2                       : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

使用变量

在 Playbook 中定义变量:


			   yaml--- - name: Install packages   hosts: all   become: yes   vars:     packages:       - vim       - git       - curl       - wget   tasks:     - name: Install packages       yum:         name: "{{ item }}"         state: present       loop: "{{ packages }}" 

或在 Inventory 中定义变量:


			   ini[webservers] web1 ansible_host=192.168.1.10 nginx_port=80 web2 ansible_host=192.168.1.11 nginx_port=8080 

在 Playbook 中使用:


			   yaml--- - name: Configure Nginx   hosts: webservers   become: yes   tasks:     - name: Update Nginx config       lineinfile:         path: /etc/nginx/nginx.conf         regexp: 'listens+d+;'         line: "        listen {{ nginx_port }};"         backrefs: yes 

使用条件判断


			   yaml--- - name: Install package based on OS   hosts: all   become: yes   tasks:     - name: Install package on CentOS       yum:         name: httpd         state: present       when: ansible_distribution == "CentOS"     - name: Install package on Ubuntu       apt:         name: apache2         state: present       when: ansible_distribution == "Ubuntu" 

使用循环


			   yaml--- - name: Create multiple users   hosts: all   become: yes   tasks:     - name: Create users       user:         name: "{{ item }}"         state: present       loop:         - user1         - user2         - user3 

使用模板

创建 Jinja2 模板 nginx.conf.j2


			   nginxserver {     listen {{ nginx_port }};     server_name {{ server_name }};     location / {         root {{ document_root }};         index index.html;     } } 

在 Playbook 中使用:


			   yaml--- - name: Deploy Nginx config   hosts: webservers   become: yes   vars:     nginx_port: 80     server_name: example.com     document_root: /usr/share/nginx/html   tasks:     - name: Deploy config from template       template:         src: nginx.conf.j2         dest: /etc/nginx/conf.d/example.conf         owner: root         group: root         mode: 0644       notify: Restart Nginx   handlers:     - name: Restart Nginx       service:         name: nginx         state: restarted 

使用 Handlers

Handlers 是特殊的任务,只有在被 notify 触发时才执行,且只执行一次。


			   yaml--- - name: Update config and restart service   hosts: webservers   become: yes   tasks:     - name: Copy config file       copy:         src: /tmp/nginx.conf         dest: /etc/nginx/nginx.conf       notify: Restart Nginx     - name: Copy another config       copy:         src: /tmp/vhost.conf         dest: /etc/nginx/conf.d/vhost.conf       notify: Restart Nginx   handlers:     - name: Restart Nginx       service:         name: nginx         state: restarted 

即使两个任务都触发了 Restart Nginx,Nginx 也只会重启一次。


错误处理

忽略错误:


			   yaml--- - name: Run command and ignore errors   hosts: all   tasks:     - name: Stop service       service:         name: non_existent_service         state: stopped       ignore_errors: yes 

注册变量并判断:


			   yaml--- - name: Check if file exists   hosts: all   tasks:     - name: Check file       stat:         path: /etc/myconfig.conf       register: file_status     - name: Print message if file exists       debug:         msg: "Config file exists"       when: file_status.stat.exists 

第七步:使用 Role 组织可复用任务

Role 是 Ansible 的最佳实践,将任务、变量、模板、文件、Handlers 组织成目录结构,便于复用和维护。

Role 目录结构


			   roles/ └── nginx/     ├── tasks/     │   └── main.yml     ├── handlers/     │   └── main.yml     ├── templates/     │   └── nginx.conf.j2     ├── files/     │   └── index.html     ├── vars/     │   └── main.yml     ├── defaults/     │   └── main.yml     └── meta/         └── main.yml 
  • tasks/main.yml:任务列表
  • handlers/main.yml:Handlers
  • templates/:Jinja2 模板
  • files/:静态文件
  • vars/main.yml:变量(优先级高)
  • defaults/main.yml:默认变量(优先级低)
  • meta/main.yml:元信息(依赖关系)

创建 Role

使用 ansible-galaxy 初始化 Role:


			   bashansible-galaxy init roles/nginx 

输出:


			   - Role roles/nginx was created successfully 

编写 Role

编辑 roles/nginx/tasks/main.yml


			   yaml--- - name: Install Nginx   yum:     name: nginx     state: present - name: Deploy config   template:     src: nginx.conf.j2     dest: /etc/nginx/nginx.conf     owner: root     group: root     mode: 0644   notify: Restart Nginx - name: Deploy index.html   copy:     src: index.html     dest: /usr/share/nginx/html/index.html     owner: nginx     group: nginx     mode: 0644 - name: Start and enable Nginx   service:     name: nginx     state: started     enabled: yes 

编辑 roles/nginx/handlers/main.yml


			   yaml--- - name: Restart Nginx   service:     name: nginx     state: restarted 

编辑 roles/nginx/defaults/main.yml


			   yaml--- nginx_port: 80 server_name: localhost document_root: /usr/share/nginx/html 

创建 roles/nginx/templates/nginx.conf.j2


			   nginxuser nginx; worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; events {     worker_connections 1024; } http {     include /etc/nginx/mime.types;     default_type application/octet-stream;     server {         listen {{ nginx_port }};         server_name {{ server_name }};         location / {             root {{ document_root }};             index index.html;         }     } } 

创建 roles/nginx/files/index.html


			   htmlhtml> <html> <head>     <title>Welcometitle> head> <body>     <h1>Nginx is runningh1> body> html> 

使用 Role

创建 site.yml


			   yaml--- - name: Deploy Nginx   hosts: webservers   become: yes   roles:     - nginx 

执行:


			   bashansible-playbook site.yml 

覆盖 Role 变量

在 Playbook 中覆盖变量:


			   yaml--- - name: Deploy Nginx   hosts: webservers   become: yes   roles:     - role: nginx       nginx_port: 8080       server_name: example.com 

第八步:组织多个 Role

创建 site.yml


			   yaml--- - name: Configure web servers   hosts: webservers   become: yes   roles:     - common     - nginx     - php     - monitoring - name: Configure database servers   hosts: dbservers   become: yes   roles:     - common     - mysql     - backup     - monitoring 

第九步:使用 Ansible Vault 加密敏感数据

Ansible Vault 用于加密敏感数据,如密码、密钥、证书。

加密文件

创建 secrets.yml


			   yaml--- db_password: MySecretPassword123 api_key: abc123xyz456 

加密文件:


			   bashansible-vault encrypt secrets.yml 

输入密码后,文件内容被加密。


查看加密文件


			   bashansible-vault view secrets.yml 

编辑加密文件


			   bashansible-vault edit secrets.yml 

解密文件


			   bashansible-vault decrypt secrets.yml 

在 Playbook 中使用加密文件


			   yaml--- - name: Deploy application   hosts: webservers   become: yes   vars_files:     - secrets.yml   tasks:     - name: Configure database connection       template:         src: db_config.j2         dest: /etc/app/db.conf 

执行时需要提供密码:


			   bashansible-playbook site.yml --ask-vault-pass 

或使用密码文件:


			   bashecho "MyVaultPassword" > .vault_pass chmod 600 .vault_pass ansible-playbook site.yml --vault-password-file .vault_pass 

第十步:使用动态 Inventory

动态 Inventory 从云平台 API 或 CMDB 获取主机列表。

使用脚本生成 Inventory

创建 dynamic_inventory.py


			   python#!/usr/bin/env python3 import json inventory = {     "webservers": {         "hosts": ["192.168.1.10""192.168.1.11"]     },     "dbservers": {         "hosts": ["192.168.1.12"]     },     "_meta": {         "hostvars": {}     } } print(json.dumps(inventory)) 

赋予执行权限:


			   bashchmod +x dynamic_inventory.py 

测试:


			   bash./dynamic_inventory.py 

使用动态 Inventory:


			   bashansible -i dynamic_inventory.py all -m ping 

使用插件获取云主机

Ansible 支持多种云平台插件,如 AWS EC2、Azure、GCP、阿里云。

安装插件:


			   bashansible-galaxy collection install amazon.aws 

创建 aws_ec2.yml


			   yaml--- plugin: amazon.aws.aws_ec2 regions:   - us-east-1 filters:   tag production keyed_groups:   - key: tags.Role     prefix: role 

使用:


			   bashansible-inventory -i aws_ec2.yml --list ansible -i aws_ec2.yml all -m ping 

配置示例

Ansible 配置文件

编辑 /etc/ansible/ansible.cfg


			   ini[defaults] # Inventory 文件路径 inventory = /etc/ansible/hosts # 并发执行数量 forks = 10 # SSH 超时时间 timeout = 30 # 日志文件 log_path = /var/log/ansible.log # 禁用主机密钥检查 host_key_checking = False # 使用的传输方式 transport = ssh # 重试文件路径 retry_files_enabled = False # 角色路径 roles_path = /etc/ansible/roles # Gathering facts 策略 gathering = smart fact_caching = jsonfile fact_caching_connection = /tmp/ansible_facts fact_caching_timeout = 86400 [privilege_escalation] # 使用 sudo become = True become_method = sudo become_user = root become_ask_pass = False [ssh_connection] # SSH 参数 ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no pipelining = True 

完整的 Playbook 示例:部署 Web 应用

创建 deploy_webapp.yml


			   yaml--- - name: Deploy web application   hosts: webservers   become: yes   vars:     app_name: myapp     app_version: v1.0.0     app_dir: /opt/{{ app_name }}     app_user: www-data     nginx_port: 80   tasks:     - name: Install dependencies       yum:         name:           - nginx           - python3           - python3-pip           - git         state: present     - name: Create app user       user:         name: "{{ app_user }}"         system: yes         shell: /sbin/nologin         state: present     - name: Create app directory       file:         path: "{{ app_dir }}"         state: directory         owner: "{{ app_user }}"         group: "{{ app_user }}"         mode: 0755     - name: Clone application from Git       git:         repo: https://github.com/example/myapp.git         dest: "{{ app_dir }}"         version: "{{ app_version }}"       become_user: "{{ app_user }}"     - name: Install Python dependencies       pip:         requirements: "{{ app_dir }}/requirements.txt"         executable: pip3     - name: Deploy Nginx config       template:         src: nginx_webapp.conf.j2         dest: /etc/nginx/conf.d/{{ app_name }}.conf         owner: root         group: root         mode: 0644       notify: Reload Nginx     - name: Deploy systemd service       template:         src: webapp.service.j2         dest: /etc/systemd/system/{{ app_name }}.service         owner: root         group: root         mode: 0644       notify: Restart webapp     - name: Start and enable webapp service       systemd:         name: "{{ app_name }}"         state: started         enabled: yes         daemon_reload: yes     - name: Start and enable Nginx       service:         name: nginx         state: started         enabled: yes   handlers:     - name: Reload Nginx       service:         name: nginx         state: reloaded     - name: Restart webapp       systemd:         name: "{{ app_name }}"         state: restarted         daemon_reload: yes 

创建模板 templates/nginx_webapp.conf.j2


			   nginxupstream {{ app_name }} {     server 127.0.0.1:8000; } server {     listen {{ nginx_port }};     server_name {{ ansible_hostname }};     access_log /var/log/nginx/{{ app_name }}_access.log;     error_log /var/log/nginx/{{ app_name }}_error.log;     location / {         proxy_pass http://{{ app_name }};         proxy_set_header Host $host;         proxy_set_header X-Real-IP $remote_addr;         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;     }     location /static/ {         alias {{ app_dir }}/static/;     } } 

创建模板 templates/webapp.service.j2


			   ini[Unit] Description={{ app_name }} web application After=network.target [Service] Type=simple User={{ app_user }} Group={{ app_user }} WorkingDirectory={{ app_dir }} ExecStart=/usr/bin/python3 {{ app_dir }}/app.py Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target 

执行部署:


			   bashansible-playbook deploy_webapp.yml 

回滚 Playbook 示例

创建 rollback_webapp.yml


			   yaml--- - name: Rollback web application   hosts: webservers   become: yes   vars:     app_name: myapp     app_dir: /opt/{{ app_name }}     app_user: www-data     rollback_version: v0.9.0   tasks:     - name: Stop webapp service       systemd:         name: "{{ app_name }}"         state: stopped     - name: Checkout previous version       git:         repo: https://github.com/example/myapp.git         dest: "{{ app_dir }}"         version: "{{ rollback_version }}"         force: yes       become_user: "{{ app_user }}"     - name: Install Python dependencies       pip:         requirements: "{{ app_dir }}/requirements.txt"         executable: pip3     - name: Start webapp service       systemd:         name: "{{ app_name }}"         state: started     - name: Wait for service to be ready       wait_for:         port: 8000         delay: 2         timeout: 30     - name: Test service health       uri:         url: http://127.0.0.1:8000/health         status_code: 200       register: health_check       retries: 3       delay: 2     - name: Print rollback status       debug:         msg: "Rollback to {{ rollback_version }} successful"       when: health_check.status == 200 

执行回滚:


			   bashansible-playbook rollback_webapp.yml -e "rollback_version=v0.9.0" 

日志和指标观察方法

查看执行日志

Ansible 日志默认不开启,需要在配置文件中启用:


			   ini[defaults] log_path = /var/log/ansible.log 

查看日志:


			   bashtail -f /var/log/ansible.log 

增加详细输出

使用 -v-vv-vvv 增加详细程度:


			   bashansible-playbook site.yml -vvv 
  • -v:显示任务执行结果
  • -vv:显示任务执行详情和连接信息
  • -vvv:显示完整调试信息,包括 SSH 命令
  • -vvvv:显示连接插件调试信息

检查模式(Dry Run)

使用 --check 模拟执行,不实际修改目标主机:


			   bashansible-playbook site.yml --check 

使用 --diff 显示配置文件的变更内容:


			   bashansible-playbook site.yml --check --diff 

限制执行范围

只对部分主机执行:


			   bashansible-playbook site.yml --limit webservers ansible-playbook site.yml --limit web1,web2 ansible-playbook site.yml --limit 192.168.1.10 

查看任务执行时间

使用 profile_tasks 回调插件:

编辑 /etc/ansible/ansible.cfg


			   ini[defaults] callback_whitelist = profile_tasks 

执行 Playbook 后会显示每个任务的执行时间。


生成执行报告

使用 json 回调插件生成 JSON 格式报告:


			   bashANSIBLE_STDOUT_CALLBACK=json ansible-playbook site.yml > report.json 

排查路径


			   执行 Playbook     ↓ 任务失败     ↓ 查看错误信息     ↓ 如果是连接失败:     检查 SSH 连接     检查防火墙     检查 Inventory 配置     ↓ 如果是模块执行失败:     检查目标主机是否满足前置条件     检查模块参数是否正确     检查权限是否足够     使用 -vvv 查看详细日志     ↓ 如果是幂等性问题:     检查任务是否重复执行     使用 changed_when 控制变更状态     ↓ 如果是性能问题:     调整并发数 forks     开启 pipelining     开启 fact caching     ↓ 记录问题和解决方案 

风险提醒

风险 1:批量操作影响生产环境

风险

批量执行命令可能影响生产环境。

预防措施

  1. 使用 --check 和 --diff 模拟执行
  2. 使用 --limit 限制执行范围,先在测试环境验证
  3. 在 Playbook 中增加确认步骤:

			   yaml- name: Confirm before proceeding   pause:     prompt: "Press Enter to continue or Ctrl+C to abort" 
  1. 使用 serial 控制批量执行的并发数:

			   yaml--- - name: Deploy application   hosts: webservers   serial: 1   tasks:     - name: Deploy code       git:         repo: https://github.com/example/myapp.git         dest: /opt/myapp 

这样每次只操作一台主机,发现问题可及时中止。


风险 2:SSH 密钥泄露

风险

SSH 私钥泄露会导致所有受控节点被入侵。

预防措施

  1. 为 Ansible 创建专用 SSH 密钥
  2. 设置严格的文件权限:

			   bashchmod 600 ~/.ssh/ansible_rsa 
  1. 定期轮换 SSH 密钥
  2. 使用堡垒机或跳板机
  3. 限制 SSH 密钥的使用范围

风险 3:Playbook 语法错误

风险

YAML 格式错误导致执行失败。

预防措施

  1. 使用 ansible-playbook --syntax-check 检查语法:

			   bashansible-playbook site.yml --syntax-check 
  1. 使用 ansible-lint 检查最佳实践:

			   bashpip3 install ansible-lint ansible-lint site.yml 
  1. 使用编辑器的 YAML 语法高亮和检查插件

风险 4:变量作用域混乱

风险

变量优先级不清楚,导致使用错误的值。

优先级(从高到低)

  1. 命令行参数 -e
  2. Playbook 中的 vars
  3. Role 中的 vars/main.yml
  4. Inventory 中的主机变量
  5. Inventory 中的组变量
  6. Role 中的 defaults/main.yml

预防措施

使用 ansible-playbook site.yml -e "debug=yes" 打印变量:


			   yaml- name: Debug variables   debug:     var: nginx_port 

风险 5:幂等性问题

风险

多次执行 Playbook 产生不同结果。

预防措施

  1. 使用声明式模块(yumservicecopy)而非命令式模块(shellcommand
  2. 使用 creates 和 removes 参数控制执行条件:

			   yaml- name: Extract archive   unarchive:     src: /tmp/app.tar.gz     dest: /opt/     creates: /opt/app/bin/app 
  1. 使用 changed_when 控制变更状态:

			   yaml- name: Check service status   shell: systemctl is-active nginx   register: result   changed_when: false   failed_when: result.rc != 0 

验证方式

验证连通性


			   bashansible all -m ping 

验证软件包安装


			   bashansible webservers -m shell -a "rpm -qa | grep nginx" 

验证服务状态


			   bashansible webservers -m shell -a "systemctl status nginx" 

验证配置文件


			   bashansible webservers -m shell -a "nginx -t" 

验证应用可用性


			   bashansible webservers -m uri -a "url=http://localhost:80 status_code=200" 

验证数据一致性

对比配置文件内容:


			   bashansible webservers -m shell -a "md5sum /etc/nginx/nginx.conf" 

所有主机的 MD5 应该一致。


回滚方案

回滚配置文件

在修改配置文件前备份:


			   yaml- name: Backup config before change   copy:     src: /etc/nginx/nginx.conf     dest: /etc/nginx/nginx.conf.bak.{{ ansible_date_time.epoch }}     remote_src: yes - name: Update config   template:     src: nginx.conf.j2     dest: /etc/nginx/nginx.conf   notify: Reload Nginx 

回滚时恢复备份:


			   yaml- name: Restore config from backup   copy:     src: /etc/nginx/nginx.conf.bak.1234567890     dest: /etc/nginx/nginx.conf     remote_src: yes   notify: Reload Nginx 

回滚软件包

卸载软件包:


			   bashansible webservers -m yum -a "name=nginx state=absent" 

安装指定版本:


			   bashansible webservers -m yum -a "name=nginx-1.20.1 state=present" 

回滚应用版本

使用 Git 回滚到指定版本,参考前文回滚 Playbook 示例。


生产环境注意事项

注意 1:使用版本控制管理 Playbook

将 Playbook、Role、Inventory 纳入 Git 管理:


			   bashgit init git add . git commit -m "Initial commit" 

每次变更提交到 Git,便于追溯和回滚。


注意 2:区分环境

使用不同的 Inventory 文件管理不同环境:


			   inventories/ ├── production/ │   ├── hosts │   └── group_vars/ │       └── all.yml ├── staging/ │   ├── hosts │   └── group_vars/ │       └── all.yml └── development/     ├── hosts     └── group_vars/         └── all.yml 

执行时指定 Inventory:


			   bashansible-playbook site.yml -i inventories/production/hosts 

注意 3:使用标签控制执行

在任务中添加标签:


			   yaml- name: Install Nginx   yum:     name: nginx     state: present   tags: install - name: Configure Nginx   template:     src: nginx.conf.j2     dest: /etc/nginx/nginx.conf   tags: configure 

只执行特定标签的任务:


			   bashansible-playbook site.yml --tags install ansible-playbook site.yml --tags configure ansible-playbook site.yml --skip-tags install 

注意 4:监控 Ansible 执行结果

集成监控系统,记录 Playbook 执行结果:

使用 json 回调插件输出结果,解析后写入监控系统。

使用 Ansible Tower 或 AWX 提供 Web 界面和 API。


注意 5:定期审计 Inventory 和 Playbook

检查:

  1. 是否有未使用的主机
  2. 是否有过时的任务
  3. 是否有未加密的敏感数据
  4. 是否有不符合最佳实践的代码

注意 6:限制并发数避免压垮目标主机

在配置文件中设置:


			   ini[defaults] forks = 10 

或在 Playbook 中设置:


			   yaml--- - name: Deploy application   hosts: webservers   serial: 5   tasks:     - name: Deploy code       git:         repo: https://github.com/example/myapp.git         dest: /opt/myapp 

注意 7:使用 Ansible Tower 或 AWX 管理大规模环境

Ansible Tower(商业版)和 AWX(开源版)提供:

  • Web 界面和 API
  • RBAC 权限管理
  • 任务调度和审计日志
  • 动态 Inventory
  • 通知集成

安装 AWX:


			   bashgit clone https://github.com/ansible/awx.git cd awx/installer ansible-playbook -i inventory install.yml 

常用命令汇总

Ad-Hoc 命令


			   bash# 测试连通性 ansible all -m ping # 查看主机信息 ansible all -m setup ansible all -m setup -a "filter=ansible_distribution*" # 执行命令 ansible all -m shell -a "uptime" ansible all -m command -a "df -h" # 安装软件 ansible webservers -m yum -a "name=nginx state=present" # 启动服务 ansible webservers -m service -a "name=nginx state=started enabled=yes" # 复制文件 ansible webservers -m copy -a "src=/tmp/index.html dest=/usr/share/nginx/html/" # 创建用户 ansible all -m user -a "name=deploy state=present" # 创建目录 ansible all -m file -a "path=/data state=directory" 

Playbook 命令


			   bash# 执行 Playbook ansible-playbook site.yml # 检查语法 ansible-playbook site.yml --syntax-check # 模拟执行 ansible-playbook site.yml --check # 显示变更内容 ansible-playbook site.yml --check --diff # 限制执行范围 ansible-playbook site.yml --limit webservers ansible-playbook site.yml --limit web1 # 指定标签 ansible-playbook site.yml --tags install ansible-playbook site.yml --skip-tags configure # 传递变量 ansible-playbook site.yml -e "nginx_port=8080" ansible-playbook site.yml -e "@vars.yml" # 使用 Vault ansible-playbook site.yml --ask-vault-pass ansible-playbook site.yml --vault-password-file .vault_pass # 增加详细输出 ansible-playbook site.yml -v ansible-playbook site.yml -vvv # 指定 Inventory ansible-playbook site.yml -i inventories/production/hosts 

Inventory 命令


			   bash# 列出所有主机 ansible-inventory --list # 列出主机(YAML 格式) ansible-inventory --list -y # 列出指定组的主机 ansible-inventory --list --limit webservers # 查看主机变量 ansible-inventory --host web1 

Vault 命令


			   bash# 加密文件 ansible-vault encrypt secrets.yml # 查看加密文件 ansible-vault view secrets.yml # 编辑加密文件 ansible-vault edit secrets.yml # 解密文件 ansible-vault decrypt secrets.yml # 修改密码 ansible-vault rekey secrets.yml 

Galaxy 命令


			   bash# 初始化 Role ansible-galaxy init roles/nginx # 安装 Role ansible-galaxy install geerlingguy.nginx # 从文件安装 Role ansible-galaxy install -r requirements.yml # 列出已安装 Role ansible-galaxy list # 删除 Role ansible-galaxy remove geerlingguy.nginx 

总结

Ansible 是运维自动化的利器,无需 Agent,基于 SSH,易于上手。

核心概念:

  • Inventory:定义受控节点
  • Module:执行具体任务
  • Playbook:任务编排
  • Role:可复用任务集合

实施路径:

  1. 安装 Ansible
  2. 配置 SSH 免密登录
  3. 编写 Inventory
  4. 测试连通性
  5. 使用 Ad-Hoc 命令快速执行任务
  6. 编写 Playbook 执行复杂任务
  7. 使用 Role 组织可复用任务
  8. 使用 Vault 加密敏感数据
  9. 集成版本控制和 CI/CD
  10. 监控和日志收集

最佳实践:

  • 使用版本控制管理代码
  • 区分环境(开发、测试、生产)
  • 使用 --check 和 --diff 模拟执行
  • 使用 --limit 限制执行范围
  • 使用 serial 控制并发
  • 使用标签控制任务执行
  • 使用 Vault 加密敏感数据
  • 定期审计 Inventory 和 Playbook
  • 监控执行结果和异常

风险控制:

  • 先在测试环境验证
  • 备份配置文件
  • 使用 changed_when 控制幂等性
  • 限制 SSH 密钥权限
  • 记录变更历史

Ansible 的核心是幂等性和声明式,描述期望状态,Ansible 自动判断是否需要变更。多练习,积累经验,才能驾驭大规模环境的批量管理。

 


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分