Skip to content
Kurisu
Go back

Ansible 自动化运维

Ansible 是开源自动化平台:用 YAML 写 Playbook,被管节点无需装代理,通过 SSH 完成配置管理、应用部署与工作流编排。


一、自动化与 Ansible 概述

1.1 自动化与基础架构即代码(IaC)

手动操作(图形界面或命令行)的问题:容易跳过或做错步骤、对执行结果验证有限、多台服务器易出现微小差异导致环境不一致。基础架构即代码 (IaC) 用自动化语言定义所需的 IT 基础架构状态:配置是简单文本文件(可纳入版本控制),开发人员定义配置、操作员审阅反馈,通过自动化重复确保系统处于期望状态。

1.2 Ansible 特性

特性说明
SimplePlaybook 易读易改,无需编码技能
Powerful应用部署、配置管理、工作流与网络自动化
Agentless用 OpenSSH/WinRM 连接,被管节点无需装代理
跨平台Linux、Windows、UNIX、网络设备与容器
幂等性同一主机可重复安全运行 Playbook,不会重复更改
版本控制配置均为文本文件,可纳入 Git

1.3 Ansible 架构

两类计算机:

角色说明
控制节点 (Control Node)安装 Ansible,维护全部组件与配置
受管主机 (Managed Hosts)在 Inventory 中列出,通过 SSH 接收任务

工作流程:管理员写 Playbook(YAML 格式)→ 控制节点 SSH 连接受管主机 → 推送 Ansible 模块(Python 小程序)→ 执行后模块自动删除。


二、部署 Ansible

2.1 安装

# 安装 Ansible
dnf install ansible -y

# 查看版本信息
ansible --version
# ansible [core 2.13.3]
#   config file = /etc/ansible/ansible.cfg
#   configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']

2.2 安装额外模块

# 安装 Ansible Galaxy 集合
ansible-galaxy collection install ansible.posix

三、Inventory 清单管理

3.1 清单概述

清单定义被管理的批主机;可分组、组可嵌套,一台主机可属多组。

默认清单文件 /etc/ansible/hosts。

3.2 INI 格式清单

基本规则:

# 没有指定组的主机要写在所有组的最前面
green.example.com
blue.example.com
192.168.100.1

# webservers 组
[webservers]
alpha.example.org
beta.example.org
192.168.1.100
www[001:006].example.com

# dbservers 组
[dbservers]
db01.intranet.mydomain.net
db02.intranet.mydomain.net
10.25.1.56

# test 组包含其他组(组嵌套)
[test:children]
webservers
dbservers

3.3 创建清单文件

# 配置本地解析
cat /etc/hosts
# 192.168.115.128 web1 web1.example.com
# 192.168.115.133 web2 web2.example.com

# 创建自定义清单
cat inventory
# [webserver]
# web1.example.com
# web2.example.com
#
# [dbserver]
# 192.168.115.128
# 192.168.115.133
#
# [test:children]
# webserver
# dbserver

# 查看清单结构
ansible-inventory --graph -i inventory
# @all:
#   |--@test:
#   |  |--@dbserver:
#   |  |  |--192.168.115.128
#   |  |  |--192.168.115.133
#   |  |--@webserver:
#   |  |  |--web1.example.com
#   |  |  |--web2.example.com
#   |--@ungrouped:

3.4 内置主机组

组名说明
all清单中明确列出的每个主机
ungrouped清单中列出但不属于任何组的主机

四、ansible.cfg 配置文件

4.1 配置文件优先级

配置文件按以下优先级选择(只取其一):

  1. ANSIBLE_CONFIG 环境变量指定的文件
  2. 当前目录的 ./ansible.cfg
  3. 用户家目录的 ~/.ansible.cfg
  4. /etc/ansible/ansible.cfg(默认)

不融合多个配置文件,只用选定的一个。

4.2 常用配置项

[defaults]
inventory = ./inventory        # 清单文件路径
remote_user = root             # SSH 远程用户
ask_pass = false               # 是否提示输入 SSH 密码

[privilege_escalation]
become = true                  # 是否自动提权
become_method = sudo           # 提权方法
become_user = root             # 提权目标用户
become_ask_pass = false        # 是否提示输入 sudo 密码

4.3 生成示例配置

# 生成禁用的默认配置
ansible-config init --disabled > ansible2.cfg

# 生成所有配置项
ansible-config init --disabled -t all > ansible3.cfg

五、临时命令 (Ad-Hoc Commands)

5.1 基本格式

ansible host-pattern -m module [-a 'module arguments'] [-i inventory]

5.2 常用模块

模块类别模块用途
文件模块copy复制文件到远程
file文件属性与权限
lineinfile管理文件中的行
软件包模块package通用包管理
yumYUM 包管理
dnfDNF 包管理
pipPython 包管理
系统模块firewalld防火墙管理
reboot重启系统
service服务管理
user用户管理
group组管理
网络模块get_url下载文件
nmcliNetworkManager 管理
uriHTTP 请求

5.3 临时命令示例

# 测试连通性
ansible all -m ping

# 创建用户
ansible -m user -a 'name=haha uid=4000 state=present' web1.example.com

# 执行 shell 命令
ansible -m shell -a 'id haha' web1.example.com

# 安装软件包
ansible webserver -m dnf -a 'name=httpd state=present'

5.4 执行命令的三种模块

模块说明
shell按 Shell 方式运行,支持管道与重定向
command单纯执行命令,不支持管道
raw目标无 Python 环境时使用

尽量少用这三个模块:专用模块多为幂等且可追踪更改。


六、Playbook 编写

6.1 YAML 基础

YAML 两条规则:同层级数据元素缩进量必须相同;子项缩进量必须大于父项。

只能用空格缩进,不能用制表符。

Vim 配置:autocmd FileType yaml setlocal ai ts=2 sw=2 et

6.2 YAML 语法速查

# 注释
# This is a YAML comment

# 字符串(通常不需要引号)
this is a string
'this is a string'
"this is yet another string"

# 多行字符串(保留换行)
include_newlines: |
  Example Company
  123 Main Street
  Atlanta, GA 30303

# 多行字符串(折叠为单行)
fold_newlines: >
  This is an example
  of a long string,
  that will become
  a single sentence once folded.

# 字典(缩进格式)
name: svcrole
svcservice: httpd
svcport: 80

# 字典(内联格式)
{name: svcrole, svcservice: httpd, svcport: 80}

# 列表(短横线格式)
hosts:
  - servera
  - serverb
  - serverc

# 列表(内联格式)
hosts: [servera, serverb, serverc]

6.3 Playbook 基本结构

---
- name: play 名称
  hosts: 目标主机或组
  tasks:
    - name: 任务描述
      module_name:
        param1: value1
        param2: value2

6.4 简单 Playbook 示例

---
- name: create user
  hosts: web1.example.com
  tasks:
    - name: create user haha
      user:
        name: haha
        uid: 4000
        state: present

6.5 多任务 Playbook

---
- name: configure web server
  hosts: webservers
  tasks:
    - name: web server is enabled
      service:
        name: httpd
        enabled: true

    - name: ntp server is enabled
      service:
        name: chronyd
        enabled: true

    - name: postfix is enabled
      service:
        name: postfix
        enabled: true

6.6 多 Play 的 Playbook

---
- name: first play
  hosts: web1.example.com
  tasks:
    - name: install httpd
      dnf:
        name: httpd
        state: present

    - name: start httpd
      service:
        name: httpd
        enabled: true

- name: second play
  hosts: 192.168.115.128
  tasks:
    - name: start mariadb
      service:
        name: mariadb
        enabled: true

6.7 运行 Playbook

# 检查语法
ansible-playbook --syntax-check site.yml

# 运行 Playbook
ansible-playbook user.yml

# 增加详细程度
ansible-playbook user.yml -v      # 显示任务结果
ansible-playbook user.yml -vv     # 显示任务结果和配置
ansible-playbook user.yml -vvv    # 包含连接信息
ansible-playbook user.yml -vvvv   # 包含额外详细选项

七、变量管理

7.1 变量定义位置

变量分三个范围级别:

范围说明优先级
全局范围命令行或配置里设置的变量最高
Play 范围在 play 和相关结构中设置的变量中
主机范围清单、事实收集或注册任务设置的变量最低

7.2 在 Playbook 中定义变量

- hosts: all
  vars:
    user: joe
    home: /home/joe

7.3 使用外部变量文件

# Playbook 中引用
- hosts: all
  vars_files:
    - vars/users.yml

# vars/users.yml 内容
user: joe
home: /home/joe

7.4 在任务中使用变量

vars:
  user: joe
tasks:
  - name: Create the user {{ user }}
    user:
      name: "{{ user }}"

变量作为值的第一个元素时必须加引号,否则会被当作 YAML 字典开头。

7.5 主机变量和组变量(推荐做法)

推荐做法:建 group_vars 和 host_vars 目录:

project/
├── group_vars
│   ├── datacenter1
│   ├── datacenter2
│   └── datacenters
├── host_vars
│   ├── demo1.example.com
│   ├── demo2.example.com
│   ├── demo3.example.com
│   └── demo4.example.com
└── inventory
# group_vars/datacenters
package: httpd

# group_vars/datacenter1(覆盖父组变量)
package: httpd

# group_vars/datacenter2
package: apache

# host_vars/demo1.example.com(最高优先级)
package: httpd

7.6 从命令行覆盖变量

ansible-playbook main.yml -e "package=apache"

7.7 使用注册变量捕获命令输出

---
- name: Install a package and prints the result
  hosts: all
  tasks:
    - name: Install the package
      dnf:
        name: httpd
        state: installed
      register: install_result

    - debug:
        var: install_result

八、Ansible Facts(事实)

8.1 事实概述

事实是 Ansible 自动检测到的受管主机变量;每个 play 首个任务前会先跑 setup 模块收集。

事实变量
短主机名ansible_facts['hostname']
完全限定域名ansible_facts['fqdn']
主要 IPv4 地址ansible_facts['default_ipv4']['address']
所有网络接口名称ansible_facts['interfaces']
DNS 服务器列表ansible_facts['dns']['nameservers']
内核版本ansible_facts['kernel']

8.2 在 Playbook 中使用事实

---
- hosts: all
  tasks:
    - name: Print various Ansible facts
      debug:
        msg: >
          The default IPv4 address of {{ ansible_facts.fqdn }}
          is {{ ansible_facts.default_ipv4.address }}

8.3 关闭事实收集

---
- name: This play gathers no facts automatically
  hosts: large_farm
  gather_facts: no

九、循环与条件

9.1 简单循环

- name: Postfix and Dovecot are running
  service:
    name: "{{ item }}"
    state: started
  loop:
    - postfix
    - dovecot

9.2 使用变量列表循环

vars:
  mail_services:
    - postfix
    - dovecot
tasks:
  - name: Postfix and Dovecot are running
    service:
      name: "{{ item }}"
      state: started
    loop: "{{ mail_services }}"

9.3 循环散列/字典列表

- name: Users exist and are in the correct groups
  user:
    name: "{{ item.name }}"
    state: present
    groups: "{{ item.groups }}"
  loop:
    - name: jane
      groups: wheel
    - name: joe
      groups: root

9.4 when 条件语句

操作示例
等于(字符串)ansible_machine == "x86_64"
等于(数字)max_memory == 512
小于min_memory < 128
大于min_memory > 256
不等于min_memory != 512
变量存在min_memory is defined
变量不存在min_memory is not defined
布尔变量为 truememory_available
布尔变量为 falsenot memory_available
值在列表中ansible_distribution in supported_distros

9.5 when 使用示例

# 条件安装
- name: "{{ my_service }} package is installed"
  dnf:
    name: "{{ my_service }}"
  when: my_service is defined

# 布尔条件
- name: httpd package is installed
  dnf:
    name: httpd
  when: run_my_task

# 判断发行版
- name: Install httpd using dnf
  dnf:
    name: httpd
    state: present
  when: ansible_distribution in supported_distros

9.6 组合条件

# OR 条件
when: ansible_distribution == "Redhat" or ansible_distribution == "Fedora"

# AND 条件
when: ansible_distribution_version == "7.5" and ansible_kernel == "3.10.0-327.el7.x86_64"

# 列表形式(默认 AND)
when:
  - ansible_distribution_version == "7.5"
  - ansible_kernel == "3.10.0-327.el7.x86_64"

# 括号分组
when: >
  ( ansible_distribution == "Redhat" and
    ansible_distribution_major_version == "7" )
  or
  ( ansible_distribution == "Fedora" and
    ansible_distribution_major_version == "28" )

9.7 组合循环与条件

# 仅在根分区有足够空间时安装
- name: install mariadb-server if enough space on root
  dnf:
    name: mariadb-server
    state: latest
  loop: "{{ ansible_mounts }}"
  when: item.mount == "/" and item.size_available > 300000000

十、最佳实践

  1. 版本控制与命名:Playbook、清单、配置纳入 Git;变量用下划线(web_server)不用连字符(web-server)
  2. 变量与密钥:用 group_vars/host_vars 目录,别混进清单文件;敏感变量用 ansible-vault 加密
  3. 运行纪律:优先专用模块(比 shell/command 更可靠且幂等);先用临时命令验连通性再写 Playbook;运行前 --syntax-check;复杂时用 Roles 组织


Share this post:

Previous Post
路由策略
Next Post
RHCSA 系统管理要点