linux中级_Ansible

TJCcc 发布于 16 天前 28 次阅读


Ansible 详细学习笔记

这份笔记旨在提供一个系统性的 Ansible 学习框架,涵盖从基础安装到高级角色编排的核心知识点。

目录

  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角色编排)

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 内部。
唯有极致沉淀,才能造就辉煌。
最后更新于 2026-02-01