linux中级_Ansible

TJCcc 发布于 2026-01-24 100 次阅读


目录

  1. Ansible 概述与安装
  2. 主机清单 (Inventory)
  3. Ad-hoc 临时命令与常用模块
  4. Playbook 剧本基础
  5. 变量 (Variables)
  6. 逻辑控制 (When & Loop)
  7. Handlers 触发器
  8. Jinja2 模板
  9. Roles 角色编排
  10. ansible配置nginx例子(无roles角色编排)
  11. ansible配置nginx例子(roles角色编排)
  12. ansible当中选择连接远程服务器的用户的优先级
  13. Ansible 的ping不是 ICMP ping(不是网络 ping)
  14. 不同 ping 失败类型,对应 “能否执行其他操作” 的结论

1. Ansible 概述与安装

1.1 简介

Ansible 是一个开源的自动化运维工具,基于 Python 开发。

  • 无 Agent (Agentless): 不需要被管理节点安装客户端,通过 SSH 协议通信。
  • 幂等性 (Idempotency): 大多数模块具有幂等性,即多次执行相同的操作,结果是一致的,不会产生副作用。
  • 声明式: 描述“想要达到什么状态”,而不是“如何做”。

1.2 安装

以 CentOS/RHEL 为例:

# 安装 EPEL 源
sudo yum install epel-release -y

# 安装 Ansible
sudo yum install ansible -y

# 验证安装
ansible --version

1.3 基础配置

配置文件通常位于 /etc/ansible/ansible.cfg。建议在项目目录下创建自定义 ansible.cfg

[defaults]
inventory      = ./hosts
remote_user    = root
host_key_checking = False  # 关闭 SSH 主机密钥检查

2. 主机清单 (Inventory)

Inventory 文件定义了 Ansible 管理的主机。默认位置 /etc/ansible/hosts,也可以通过 -i 指定。

2.1 基础格式

# 未分组的主机
192.168.1.10

# 分组 [webservers]
[webservers]
192.168.1.11 192.168.1.12 # 连续 IP 写法

[dbservers]
db-[1:3].example.com # 对应 db-1, db-2, db-3

2.2 主机变量与组变量

[webservers]
web1 ansible_host=192.168.1.11 ansible_port=2222

[webservers:vars]
http_port=80

2.3 组嵌套 (Children)

[apache]
web1

[nginx]
web2

[all_web:children]
apache nginx

3. Ad-hoc 临时命令与常用模块

Ad-hoc 适用于临时执行一次性任务。
语法: ansible <主机模式> -m <模块名> -a "<参数>"

3.1 常用模块详解

模块描述示例命令
ping测试连通性 (非 ICMP ping)ansible all -m ping
command(默认模块) 执行 Shell 命令,不支持管道/重定向ansible web -m command -a "uptime"
shell执行 Shell 命令,支持管道/重定向ansible web -m shell -a "ps -ef | grep nginx"
copy复制文件到远程主机ansible web -m copy -a "src=./idx.html dest=/var/www/html/"
file管理文件/目录属性 (创建、删除、权限)ansible web -m file -a "path=/tmp/dir state=directory mode=755"
yum/apt软件包管理ansible web -m yum -a "name=nginx state=present"
service服务管理 (启动、停止、重启)ansible web -m service -a "name=nginx state=started enabled=yes"
user用户管理ansible web -m user -a "name=deploy uid=1001"
setup收集远程主机信息 (Facts)ansible web -m setup

4. Playbook 剧本基础

Playbook 是 Ansible 的核心,使用 YAML 格式定义一系列任务。

4.1 核心结构

  • Hosts: 指定执行的主机
  • Tasks: 任务列表
  • Remote_user: 远程执行用户

4.2 示例:部署 Web 服务器

创建文件 web_deploy.yml:

---
- name: 部署 Nginx Web 服务器
  hosts: webservers
  remote_user: root
  gather_facts: yes  # 收集主机信息

  tasks:
    - name: 安装 Nginx
      yum:
        name: nginx
        state: present

    - name: 确保 Nginx 正在运行并开机自启
      service:
        name: nginx
        state: started
        enabled: yes

    - name: 部署自定义首页
      copy:
        src: index.html
        dest: /usr/share/nginx/html/index.html

执行 Playbook:

ansible-playbook web_deploy.yml

5. 变量 (Variables)

5.1 变量定义位置与优先级

优先级从低到高:

  1. Inventory 文件中的变量
  2. Playbook 中的 vars
  3. 命令行传递 -e "var=value"

5.2 在 Playbook 中定义

- hosts: all
  vars:
    package_name: httpd
    service_port: 8080
  tasks:
    - name: 安装包
      yum:
        name: "{{ package_name }}"

5.3 注册变量 (Register)

将任务的输出保存到变量中,常用于后续调试或判断。

  tasks:
    - name: 检查文件是否存在
      shell: ls /tmp/flag
      register: file_check
      ignore_errors: yes

    - name: 输出检查结果
      debug:
        msg: "文件检查结果: {{ file_check.rc }}"

6. 逻辑控制 (When & Loop)

6.1 条件判断 (When)

使用 when 关键字进行条件判断。支持 and, or, not, ==, != 等逻辑。

  tasks:
    - name: 仅在 CentOS 系统安装 httpd
      yum:
        name: httpd
        state: present
      when: ansible_distribution == "CentOS"

    - name: 逻辑组合示例
      command: /bin/something
      when: 
        - ansible_distribution == "CentOS"
        - ansible_memtotal_mb > 1024
      # 列表形式默认为 AND 关系
      # 或者: when: ansible_distribution == "CentOS" and ansible_memtotal_mb > 1024

6.2 循环语句 (Loop)

推荐使用 loop (旧版本为 with_items)。

  tasks:
    - name: 创建多个用户
      user:
        name: "{{ item }}"
        state: present
      loop:
        - user1
        - user2
        - user3

    - name: 循环字典列表
      user:
        name: "{{ item.name }}"
        uid: "{{ item.id }}"
      loop:
        - { name: 'alice', id: 1010 }
        - { name: 'bob', id: 1011 }

7. Handlers 触发器

Handlers 是特殊的任务,只有在被其他任务通过 notify 触发且该任务状态为 changed 时才会执行。常用于重启服务。

  tasks:
    - name: 修改配置文件
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: Restart Nginx  # 触发 Handler

  handlers:
    - name: Restart Nginx    # 名称必须与 notify 一致
      service:
        name: nginx
        state: restarted

8. Jinja2 模板

使用 template 模块可以动态生成配置文件。Ansible 使用 Python 的 Jinja2 模板引擎。

8.1 模板语法

  • {{ variable }}: 变量输出
  • {% control %}: 逻辑控制 (if, for)
  • {# comment #}: 注释

8.2 示例

Playbook 变量:

vars:
  worker_processes: 4
  vhosts:
    - port: 8080
      name: site1
    - port: 8081
      name: site2

模板文件 nginx.conf.j2:

worker_processes {{ worker_processes }};

http {
    {% for vhost in vhosts %}
    server {
        listen {{ vhost.port }};
        server_name {{ vhost.name }}.example.com;
    }
    {% endfor %}
}

9. Roles 角色编排

Roles 是 Ansible 组织 Playbook 的最佳实践方式,它将变量、任务、文件、模板等按目录结构分离,实现代码复用。

9.1 标准目录结构

roles/
└── common/
    ├── tasks/
    │   └── main.yml      # 主任务入口
    ├── handlers/
    │   └── main.yml      # Handlers
    ├── templates/        # 存放 .j2 模板
    ├── files/            # 存放静态文件 (copy 模块用)
    ├── vars/
    │   └── main.yml      # 角色内部变量
    ├── defaults/
    │   └── main.yml      # 默认变量 (优先级最低)
    └── meta/
        └── main.yml      # 依赖关系

9.2 创建 Role

可以使用 ansible-galaxy init <role_name> 快速创建目录结构。

9.3 使用 Roles

在主 Playbook (site.yml) 中调用:

---
- hosts: webservers
  roles:
    - common
    - nginx
    - { role: php, php_version: 7.4 }  # 传递参数给 Role

9.4 任务重用与 Tags

# roles/common/tasks/main.yml
- name: 安装基础工具
  yum:
    name: ["wget", "curl", "vim"]
    state: present

总结: 本笔记构建了 Ansible 的核心知识体系。建议按照 安装 -> Ad-hoc -> Playbook -> Roles 的路径进行实践,逐步掌握自动化运维的能力。

10.例子1:nginx_deploy

本示例将演示如何编写一个完整的 Ansible Playbook,用于在远程主机上自动化部署 Nginx Web 服务器。整个流程包含三个核心步骤:安装 (Install) -> 配置 (Configure) -> 启动 (Start)


1. 项目目录结构

建议的目录结构如下,保持清晰和规范:

nginx-deploy/
├── hosts                   # 主机清单文件
├── deploy_nginx.yml        # 主 Playbook 剧本
└── templates/
    └── nginx.conf.j2       # Nginx 配置文件模板

2. 编写主机清单 (hosts)

定义目标主机及相关变量。

文件内容: hosts

[webservers]
192.168.1.101
192.168.1.102

[webservers:vars]
http_port=80 server_name=example.com

3. 编写配置文件模板 (templates/nginx.conf.j2)

使用 Jinja2 模板语法,将变量嵌入配置文件中。当变量改变时,Ansible 会自动更新远程主机的配置。

文件内容: templates/nginx.conf.j2

user 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;

    # 使用变量定义日志格式
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    keepalive_timeout   65;

    server {
        # 使用 Inventory 中定义的变量
        listen       {{ http_port }};
        server_name  {{ server_name }};
        root         /usr/share/nginx/html;

        location / {
        }
    }
}

4. 编写 Playbook (deploy_nginx.yml)

这是核心执行文件,串联了安装、配置、启动的逻辑,并使用 Handlers 处理服务重启。

文件内容: deploy_nginx.yml

---
- name: 自动化部署 Nginx 服务
  hosts: webservers
  remote_user: root
  gather_facts: yes  # 收集系统信息,用于判断系统类型等

  vars:
    nginx_package_name: nginx
    nginx_service_name: nginx
    nginx_config_path: /etc/nginx/nginx.conf

  tasks:
    # -------------------------------------------------------
    # 1. 安装阶段 (Install)
    # -------------------------------------------------------
    - name: 安装 EPEL 源 (CentOS/RHEL 需要)
      yum:
        name: epel-release
        state: present
      when: ansible_os_family == "RedHat"

    - name: 安装 Nginx 软件包
      yum:
        name: "{{ nginx_package_name }}"
        state: present

    # -------------------------------------------------------
    # 2. 配置阶段 (Configure)
    # -------------------------------------------------------
    - name: 分发 Nginx 配置文件
      template:
        src: templates/nginx.conf.j2
        dest: "{{ nginx_config_path }}"
        owner: root
        group: root
        mode: '0644'
        validate: 'nginx -t -c %s'  # 分发前先验证配置文件的语法正确性
      notify: Reload Nginx  # 如果配置文件发生变化,通知 Handler

    # -------------------------------------------------------
    # 3. 启动阶段 (Start)
    # -------------------------------------------------------
    - name: 确保 Nginx 服务已启动并设置为开机自启
      service:
        name: "{{ nginx_service_name }}"
        state: started
        enabled: yes

  # -------------------------------------------------------
  # Handlers: 仅在被 notify 且状态改变时触发
  # -------------------------------------------------------
  handlers:
    - name: Reload Nginx
      service:
        name: "{{ nginx_service_name }}"
        state: reloaded

5. 执行 Playbook

在终端中运行以下命令来执行部署:

5.1 语法检查

在执行前,建议先检查语法是否有误:

ansible-playbook -i hosts deploy_nginx.yml --syntax-check

5.2 模拟执行 (Dry Run)

查看将会发生什么改变,但不实际执行:

ansible-playbook -i hosts deploy_nginx.yml --check

5.3 正式执行

ansible-playbook -i hosts deploy_nginx.yml

6. 关键点解析

  1. 幂等性 (Idempotency):
    • yum 模块:如果 Nginx 已经安装,不会重复安装。
    • template 模块:如果远程文件与模板生成的内容一致,不会覆盖,也不会触发 Handler。
    • service 模块:如果服务已经启动,不会重复启动。
  2. Handlers (触发器):
    • 我们在配置文件的任务中使用了 notify: Reload Nginx
    • 这意味着,只有当 nginx.conf 的内容真正发生改变时,Ansible 才会执行 Reload Nginx 这个 Handler。
    • 这避免了每次运行 Playbook 都重启服务,保证了服务的稳定性。
  3. Validate (配置验证):
    • validate: 'nginx -t -c %s' 是一个非常实用的技巧。
    • 它会在新配置文件覆盖远程文件之前,先用 Nginx 的语法检查工具测试新生成的临时文件。
    • 如果语法检查失败,Ansible 会终止任务,不会覆盖旧的配置文件,从而避免因配置错误导致服务挂掉。

11.例子2:nginx_deploy(roles)

本示例演示如何将之前的 Nginx 单文件 Playbook 重构为 Ansible Roles 结构。Roles 能够实现任务、变量、文件和处理器的分离,极大提高了代码的可读性与复用性。


1. 目录结构设计

标准的 Roles 项目结构如下:

ansible-project/
├── hosts                   # 主机清单
├── site.yml                # 总入口 Playbook
└── roles/
    └── nginx/              # Nginx 角色目录
        ├── tasks/
        │   └── main.yml    # 核心任务逻辑
        ├── handlers/
        │   └── main.yml    # 触发器逻辑
        ├── templates/
        │   └── nginx.conf.j2 # 配置文件模板
        ├── vars/
        │   └── main.yml    # 角色专用变量 (优先级较高)
        └── defaults/
            └── main.yml    # 默认变量 (优先级最低,方便用户覆盖)

2. 编写角色文件

2.1 默认变量 (roles/nginx/defaults/main.yml)

定义角色的默认值,使用者可以在 Playbook 中轻松覆盖这些值。

---
nginx_port: 80
nginx_server_name: localhost
nginx_user: nginx
nginx_config_path: /etc/nginx/nginx.conf

2.2 任务文件 (roles/nginx/tasks/main.yml)

只包含具体的执行步骤,不再包含 hosts 等定义。

---
- name: 安装 EPEL 源 (CentOS/RHEL)
  yum:
    name: epel-release
    state: present
  when: ansible_os_family == "RedHat"

- name: 安装 Nginx
  yum:
    name: nginx
    state: present

- name: 配置 Nginx
  template:
    src: nginx.conf.j2
    dest: "{{ nginx_config_path }}"
    owner: root
    group: root
    mode: '0644'
    validate: 'nginx -t -c %s'
  notify: Reload Nginx  # 触发 Handler

- name: 启动 Nginx 并设置开机自启
  service:
    name: nginx
    state: started
    enabled: yes

2.3 处理器文件 (roles/nginx/handlers/main.yml)

定义被 notify 触发的操作。

---
- name: Reload Nginx
  service:
    name: nginx
    state: reloaded

2.4 模板文件 (roles/nginx/templates/nginx.conf.j2)

使用变量占位。注意这里使用了 defaults/main.yml 中定义的变量名。

user {{ nginx_user }};
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;

    sendfile            on;
    keepalive_timeout   65;

    server {
        listen       {{ nginx_port }};
        server_name  {{ nginx_server_name }};
        root         /usr/share/nginx/html;

        location / {
            index index.html index.htm;
        }
    }
}

3. 编写入口文件

3.1 主机清单 (hosts)

[webservers]
192.168.1.101
192.168.1.102

3.2 总 Playbook (site.yml)

这是执行的入口,负责将主机和角色关联起来。我们可以在这里覆盖默认变量。

---
- name: 部署 Web 服务集群
  hosts: webservers
  remote_user: root
  gather_facts: yes

  roles:
    - role: nginx
      # 在调用角色时覆盖默认变量
      vars:
        nginx_port: 8080
        nginx_server_name: www.example.com

4. 执行部署

在项目根目录下执行:

# 检查目录结构是否正确
tree .

# 执行 Playbook
ansible-playbook -i hosts site.yml

5. 为什么使用 Roles?

对比之前的单文件 Playbook,Roles 的优势在于:

  1. 解耦: 任务、变量、配置分离,修改配置文件不需要去翻找任务代码。
  2. 复用: 这个 nginx 角色可以被多个项目共用。例如,你可以同时部署一个 web 集群和一个 api 集群,只需在 site.yml 中调用两次该角色并传入不同的端口变量即可。
  3. 清晰: site.yml 非常简洁,只描述了“谁做什么”,而“怎么做”的细节被封装在 Roles 内部。

12.ansible当中选择连接远程服务器的用户的优先级

当同时配置时,Ansible 会按以下优先级选择用户(从高到低):

  1. 任务级别(task)的 remote_user
  2. Play 级别(play)的 remote_user
  3. 命令行指定的 ansible_user
  4. 主机清单 / 变量文件里的 ansible_user
  5. Ansible 配置文件(ansible.cfg)里的 remote_user
  6. 执行 ansible 命令的本地系统用户

举个例子:

  • 清单里配置 ansible_user=admin
  • Playbook 里配置 remote_user=root → 最终会用 root 连接(Play 级别 remote_user 优先级更高)。

13. Ansible 的ping不是 ICMP ping(不是网络 ping)

Ansible 的ansible zabbix -m ping和你之前用的ping 172.16.1.81完全是两回事:

  • 系统ping:只验证网络层可达(ICMP 协议);
  • Ansible ping:验证「网络可达 + SSH 登录成功 + Python 环境兼容 + Ansible 模块能执行」的全链路,是 Ansible 操作的 “通行证”。

14. 不同 ping 失败类型,对应 “能否执行其他操作” 的结论

Ansible ping 失败类型其他操作能否执行?核心原因
Connection timed out(端口 22 超时)完全不能网络 / 防火墙拦截,连 SSH 都连不上,任何操作都无法传输到目标主机
Permission denied(SSH 认证失败)完全不能密钥 / 密码不对,SSH 登录失败,模块无法传输到目标主机
ModuleNotFoundError(Python 依赖)完全不能模块能传输到目标主机,但执行时 Python 依赖缺失,所有模块都会报类似错误
SUCCESS但ping返回pong(成功)可以正常执行全链路兼容,copy、apt、service 等模块都能正常运行

简单说:Ansible ping 失败 = 目标主机不在 Ansible 的 “可管理列表” 里,所有依赖 Ansible 的操作(配置管理、软件安装、命令执行等)都无法在这台 zabbix 服务器上完成。

唯有极致沉淀,才能造就辉煌。
最后更新于 2026-03-01