Ansible 自动化
Ansible 是一款无代理(Agentless)的 IT 自动化工具,通过 SSH 连接远程主机并执行配置管理、应用部署和任务编排。它使用 YAML 格式编写 Playbook,学习曲线平缓,是 Enterprise Linux 环境中最流行的自动化方案之一。
安装 Ansible
Section titled “安装 Ansible”在控制节点上安装
Section titled “在控制节点上安装”Ansible 只需要安装在控制节点(你执行命令的机器)上,被管理的远程主机不需要安装任何额外软件,只需要有 SSH 和 Python。
# 方法一:通过 DNF 安装(推荐)sudo dnf install -y epel-releasesudo dnf install -y ansible-core
# 方法二:通过 pip 安装(获取最新版本)sudo dnf install -y python3 python3-pippip3 install --user ansible
# 验证安装ansible --version安装额外集合
Section titled “安装额外集合”Ansible 的模块以集合(Collection)形式分发:
# 安装常用集合ansible-galaxy collection install ansible.posixansible-galaxy collection install community.generalansible-galaxy collection install community.mysql
# 查看已安装集合ansible-galaxy collection listInventory 主机清单
Section titled “Inventory 主机清单”Inventory 文件定义了 Ansible 要管理的主机和分组信息。
INI 格式
Section titled “INI 格式”创建 /etc/ansible/hosts 或项目目录下的 inventory.ini:
# 单独的主机10.0.0.100
# 定义组[webservers]web01.example.comweb02.example.comweb03.example.com
[dbservers]db01.example.com ansible_port=2222db02.example.com
[appservers]app[01:05].example.com # app01 到 app05
# 组变量[webservers:vars]ansible_user=opsansible_become=truehttp_port=80
[dbservers:vars]ansible_user=dbaansible_become=true
# 父组(包含多个子组)[production:children]webserversdbserversappserversYAML 格式
Section titled “YAML 格式”all: children: webservers: hosts: web01.example.com: web02.example.com: vars: ansible_user: ops http_port: 80
dbservers: hosts: db01.example.com: ansible_port: 2222 db02.example.com: vars: ansible_user: dba
production: children: webservers: dbservers:验证 Inventory
Section titled “验证 Inventory”# 查看所有主机ansible-inventory -i inventory.yml --list
# 以图形方式展示ansible-inventory -i inventory.yml --graph
# 查看特定主机的变量ansible-inventory -i inventory.yml --host web01.example.comAd-Hoc 命令
Section titled “Ad-Hoc 命令”Ad-Hoc 命令适合执行一次性的快速操作。
# 测试所有主机连通性ansible all -i inventory.yml -m ping
# 查看远程主机信息ansible webservers -i inventory.yml -m setup
# 执行 Shell 命令ansible all -i inventory.yml -m shell -a "uptime"ansible all -i inventory.yml -m shell -a "free -h"ansible all -i inventory.yml -m shell -a "df -h /"
# 管理软件包ansible webservers -i inventory.yml -m dnf -a "name=nginx state=present" --becomeansible webservers -i inventory.yml -m dnf -a "name=* state=latest" --become
# 管理服务ansible webservers -i inventory.yml -m service -a "name=nginx state=started enabled=yes" --become
# 复制文件ansible webservers -i inventory.yml -m copy -a "src=/tmp/app.conf dest=/etc/app.conf owner=root mode=0644" --become
# 管理用户ansible all -i inventory.yml -m user -a "name=deploy state=present groups=wheel" --become
# 重启主机ansible dbservers -i inventory.yml -m reboot --become
# 限制执行范围ansible webservers -i inventory.yml -m shell -a "hostname" --limit web01.example.com
# 并行控制(默认 5 个并行)ansible all -i inventory.yml -m ping -f 20编写 Playbook
Section titled “编写 Playbook”Playbook 是 Ansible 的核心配置文件,使用 YAML 格式定义一系列有序的任务。
基础 Playbook
Section titled “基础 Playbook”# site.yml - 基础 Web 服务器配置---- name: 配置 Web 服务器 hosts: webservers become: true vars: http_port: 80 doc_root: /var/www/myapp
tasks: - name: 安装必要软件包 ansible.builtin.dnf: name: - nginx - firewalld - vim-enhanced state: present
- name: 创建网站目录 ansible.builtin.file: path: "{{ doc_root }}" state: directory owner: nginx group: nginx mode: '0755'
- name: 部署首页 ansible.builtin.copy: content: | <!DOCTYPE html> <html> <head><title>Welcome</title></head> <body><h1>Server: {{ ansible_hostname }}</h1></body> </html> dest: "{{ doc_root }}/index.html" owner: nginx group: nginx mode: '0644'
- name: 启动并启用 Nginx ansible.builtin.service: name: nginx state: started enabled: true
- name: 启动并启用 Firewalld ansible.builtin.service: name: firewalld state: started enabled: true
- name: 开放 HTTP 端口 ansible.posix.firewalld: service: http permanent: true state: enabled immediate: true运行 Playbook
Section titled “运行 Playbook”# 执行 Playbookansible-playbook -i inventory.yml site.yml
# 模拟运行(不实际执行变更)ansible-playbook -i inventory.yml site.yml --check
# 显示详细输出ansible-playbook -i inventory.yml site.yml -vansible-playbook -i inventory.yml site.yml -vvv
# 限制在特定主机上运行ansible-playbook -i inventory.yml site.yml --limit web01.example.com
# 从指定任务开始执行ansible-playbook -i inventory.yml site.yml --start-at-task="启动并启用 Nginx"
# 逐步确认执行ansible-playbook -i inventory.yml site.yml --step
# 指定额外变量ansible-playbook -i inventory.yml site.yml -e "http_port=8080"常用模块详解
Section titled “常用模块详解”dnf 模块
Section titled “dnf 模块”tasks: # 安装单个软件包 - name: 安装 Nginx ansible.builtin.dnf: name: nginx state: present
# 安装多个软件包 - name: 安装开发工具 ansible.builtin.dnf: name: - gcc - make - python3-devel state: present
# 安装指定版本 - name: 安装指定版本 ansible.builtin.dnf: name: nginx-1.20.1 state: present
# 更新所有软件包 - name: 系统更新 ansible.builtin.dnf: name: '*' state: latest
# 安装软件包组 - name: 安装开发工具组 ansible.builtin.dnf: name: '@Development Tools' state: present
# 卸载软件包 - name: 移除旧软件 ansible.builtin.dnf: name: httpd state: absentservice 模块
Section titled “service 模块”tasks: - name: 启动并启用服务 ansible.builtin.service: name: nginx state: started enabled: true
- name: 重启服务 ansible.builtin.service: name: nginx state: restarted
- name: 重新加载服务配置 ansible.builtin.service: name: nginx state: reloaded
- name: 停止并禁用服务 ansible.builtin.service: name: postfix state: stopped enabled: falsecopy 模块
Section titled “copy 模块”tasks: # 从控制节点复制文件 - name: 复制配置文件 ansible.builtin.copy: src: files/nginx.conf dest: /etc/nginx/nginx.conf owner: root group: root mode: '0644' backup: true notify: 重载 Nginx
# 直接写入内容 - name: 写入配置 ansible.builtin.copy: content: | server { listen 80; server_name {{ ansible_fqdn }}; root /var/www/html; } dest: /etc/nginx/conf.d/default.conf owner: root group: root mode: '0644'template 模块
Section titled “template 模块”Jinja2 模板是 Ansible 最强大的功能之一,可以根据变量动态生成配置文件。
创建模板文件 templates/nginx.conf.j2:
# Ansible managed - Do not edit manuallyworker_processes {{ ansible_processor_vcpus }};
events { worker_connections {{ nginx_worker_connections | default(1024) }};}
http { include /etc/nginx/mime.types; default_type application/octet-stream;
sendfile on; keepalive_timeout 65;
{% for vhost in virtual_hosts %} server { listen {{ vhost.port | default(80) }}; server_name {{ vhost.server_name }}; root {{ vhost.doc_root }};
{% if vhost.ssl | default(false) %} listen 443 ssl; ssl_certificate {{ vhost.ssl_cert }}; ssl_certificate_key {{ vhost.ssl_key }};{% endif %} }{% endfor %}}在 Playbook 中使用模板:
- name: 配置 Web 服务器 hosts: webservers become: true vars: nginx_worker_connections: 2048 virtual_hosts: - server_name: app.example.com doc_root: /var/www/app port: 80 - server_name: api.example.com doc_root: /var/www/api port: 8080
tasks: - name: 部署 Nginx 配置 ansible.builtin.template: src: templates/nginx.conf.j2 dest: /etc/nginx/nginx.conf owner: root group: root mode: '0644' validate: nginx -t -c %s notify: 重载 Nginx
handlers: - name: 重载 Nginx ansible.builtin.service: name: nginx state: reloaded其他常用模块
Section titled “其他常用模块”tasks: # 文件和目录管理 - name: 创建目录 ansible.builtin.file: path: /data/app state: directory owner: app group: app mode: '0755'
- name: 创建符号链接 ansible.builtin.file: src: /data/app/current dest: /var/www/app state: link
# 管理计划任务 - name: 添加定时备份任务 ansible.builtin.cron: name: "数据库备份" minute: "0" hour: "2" job: "/usr/local/bin/backup.sh >> /var/log/backup.log 2>&1" user: root
# 管理 SELinux 布尔值 - name: 允许 Nginx 连接网络 ansible.posix.seboolean: name: httpd_can_network_connect state: true persistent: true
# 管理 sysctl 参数 - name: 设置内核参数 ansible.posix.sysctl: name: net.ipv4.ip_forward value: '1' sysctl_set: true reload: true
# 从 URL 下载文件 - name: 下载应用包 ansible.builtin.get_url: url: https://example.com/releases/app-1.0.tar.gz dest: /tmp/app-1.0.tar.gz checksum: sha256:abc123...
# 解压文件 - name: 解压应用 ansible.builtin.unarchive: src: /tmp/app-1.0.tar.gz dest: /opt/ remote_src: true条件判断与循环
Section titled “条件判断与循环”tasks: - name: 仅在 AlmaLinux 上执行 ansible.builtin.dnf: name: almalinux-release state: latest when: ansible_distribution == "AlmaLinux"
- name: 仅在 EL9 上执行 ansible.builtin.debug: msg: "Running on EL9" when: ansible_distribution_major_version == "9"
- name: 当服务存在时重启 ansible.builtin.service: name: nginx state: restarted when: "'nginx' in ansible_facts.packages"tasks: - name: 创建多个用户 ansible.builtin.user: name: "{{ item.name }}" groups: "{{ item.groups }}" state: present loop: - { name: 'alice', groups: 'wheel' } - { name: 'bob', groups: 'developers' } - { name: 'charlie', groups: 'developers' }
- name: 启动多个服务 ansible.builtin.service: name: "{{ item }}" state: started enabled: true loop: - nginx - firewalld - fail2banHandlers
Section titled “Handlers”Handlers 是一种特殊任务,仅在被 notify 触发时才执行,且在所有任务执行完毕后才运行:
- name: 配置服务器 hosts: webservers become: true
tasks: - name: 更新 Nginx 配置 ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf notify: - 验证 Nginx 配置 - 重载 Nginx
- name: 更新 sysctl 配置 ansible.builtin.copy: src: sysctl.conf dest: /etc/sysctl.d/99-custom.conf notify: 应用 sysctl
handlers: - name: 验证 Nginx 配置 ansible.builtin.command: nginx -t
- name: 重载 Nginx ansible.builtin.service: name: nginx state: reloaded
- name: 应用 sysctl ansible.builtin.command: sysctl --systemRoles 角色
Section titled “Roles 角色”Role 是组织 Playbook 的最佳实践,将相关的任务、变量、模板、文件等按功能归类到标准化目录结构中。
创建 Role
Section titled “创建 Role”# 使用 ansible-galaxy 创建 role 骨架ansible-galaxy role init roles/webserver
# 生成的目录结构tree roles/webserver/# roles/webserver/# ├── defaults/# │ └── main.yml # 默认变量(优先级最低)# ├── files/ # 静态文件# ├── handlers/# │ └── main.yml # Handlers# ├── meta/# │ └── main.yml # Role 元数据和依赖# ├── tasks/# │ └── main.yml # 主要任务# ├── templates/ # Jinja2 模板# └── vars/# └── main.yml # 角色变量(优先级较高)编写 Role
Section titled “编写 Role”---nginx_port: 80nginx_worker_connections: 1024doc_root: /var/www/html---- name: 安装 Nginx ansible.builtin.dnf: name: nginx state: present
- name: 部署 Nginx 配置 ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf notify: 重载 Nginx
- name: 创建网站目录 ansible.builtin.file: path: "{{ doc_root }}" state: directory owner: nginx group: nginx mode: '0755'
- name: 启动 Nginx ansible.builtin.service: name: nginx state: started enabled: true---- name: 重载 Nginx ansible.builtin.service: name: nginx state: reloaded使用 Role
Section titled “使用 Role”---- name: 配置 Web 服务器 hosts: webservers become: true roles: - webserver
- name: 配置数据库服务器 hosts: dbservers become: true roles: - role: database vars: db_port: 3306从 Ansible Galaxy 安装 Role
Section titled “从 Ansible Galaxy 安装 Role”# 搜索 roleansible-galaxy search nginx --platforms EL
# 安装 roleansible-galaxy role install geerlingguy.nginx
# 从 requirements 文件批量安装cat > requirements.yml << 'EOF'---roles: - name: geerlingguy.nginx - name: geerlingguy.mysql
collections: - name: community.general - name: ansible.posixEOF
ansible-galaxy install -r requirements.yml项目目录结构推荐
Section titled “项目目录结构推荐”ansible-project/├── ansible.cfg # Ansible 配置├── inventory/│ ├── production.yml # 生产环境主机│ └── staging.yml # 测试环境主机├── group_vars/│ ├── all.yml # 所有主机的变量│ ├── webservers.yml # Web 服务器组变量│ └── dbservers.yml # 数据库组变量├── host_vars/│ └── web01.example.com.yml├── roles/│ ├── common/│ ├── webserver/│ └── database/├── site.yml # 主 Playbook├── webservers.yml└── dbservers.yml# ansible.cfg[defaults]inventory = inventory/production.ymlremote_user = opsroles_path = roleshost_key_checking = Falseretry_files_enabled = Falsestdout_callback = yaml
[privilege_escalation]become = Truebecome_method = sudobecome_ask_pass = FalseAnsible Vault 加密敏感数据
Section titled “Ansible Vault 加密敏感数据”# 加密文件ansible-vault encrypt group_vars/dbservers.yml
# 编辑加密文件ansible-vault edit group_vars/dbservers.yml
# 解密文件ansible-vault decrypt group_vars/dbservers.yml
# 运行包含加密数据的 Playbookansible-playbook site.yml --ask-vault-pass
# 使用密码文件ansible-playbook site.yml --vault-password-file ~/.vault_passAnsible 是 Enterprise Linux 运维自动化的利器,从简单的 Ad-Hoc 命令到复杂的多角色 Playbook,都能高效地管理大规模服务器集群。结合 Cloud-Init 进行初始配置、再用 Ansible 完成后续部署是非常经典的工作流。