3.Ansible编写和运行playbook

发布于:2025-08-15 ⋅ 阅读:(14) ⋅ 点赞:(0)

3.Ansible编写和运行 Playbook

Playbook 介绍

如果把 Ansible 的ad-hoc命令比作 “一次性脚本”(适合临时执行单个简单任务),那么Playbook就是 “可重复执行的程序”(适合复杂、多步骤的管理流程)。

举个例子:

  • 用 ad-hoc 创建一个用户可以这样写(临时执行一次):

    # 在node1上创建用户newbie,UID为4000
    [bq@controller web]$ ansible -m user -a "name=newbie uid=4000 state=present" node1
    
  • 但如果需要在多台主机上重复这个操作,或者后续还要修改用户属性,用 Playbook 更合适。把上述操作写成 Playbook(保存为create_user.yml):

    ---
    - name: 统一配置用户(描述这个Play的作用)
      hosts: node1  # 目标主机(从清单中选)
      tasks:
        - name: 确保newbie用户存在且UID为4000(描述这个任务的作用)
          user:  # 使用user模块
            name: newbie  # 用户名
            uid: 4000     # UID
            state: present  # 状态:存在(present)或删除(absent)
    ...
    

Playbook 本质是一个YAML格式的文本文件(通常以.yaml.yml为扩展名),核心作用是:

  1. 按顺序定义多个Play(每个 Play 是一组针对特定主机的操作);
  2. 每个 Play 包含多个任务(按顺序执行的具体操作,如安装软件、修改配置等);
  3. 可重复执行,结果可预测(第二次执行相同 Playbook 时,未变更的任务会显示 “OK”)。

Vim 编辑器设置

用 Vim 编辑 Playbook 时,建议配置自动缩进和 Tab 转换,避免格式错误。

在用户家目录的.vimrc文件中添加配置:

# 方法1:全局生效(所有文件都用2个空格缩进,显示行号)
set ai ts=2 number

# 方法2:仅YAML文件生效(更推荐)
autocmd FileType yaml set ai ts=2  # 打开YAML文件时,自动启用2个空格缩进

配置说明:

  • ai:自动缩进(新行继承上一行的缩进);
  • ts=2:Tab 键自动转换为 2 个空格;
  • number:显示行号(方便定位错误)。

YAML 格式基础

基本格式:

---  # YAML 文件起始标识(建议添加)
- name: 第一个Play(描述该Play的作用,如"部署Web服务")
  hosts: web_servers  # 目标主机/主机组(来自inventory)
  remote_user: devops  # 远程执行用户
  become: yes  # 是否提权(yes/no)
  become_method: sudo  # 提权方式(默认sudo)
  become_user: root  # 提权后的目标用户(通常为root)
  vars:  # 定义变量(可在任务中引用)
    app_name: "myapp"
    app_port: 8080

  tasks:  # 任务列表(按顺序执行)
    - name: 任务1描述(如"安装依赖包")
      yum:  # 模块名称(根据操作选择,如yum/apt/copy等)
        name: "{{ item }}"  # 引用变量或列表项
        state: present
      loop:  # 循环执行(可选)
        - package1
        - package2

    - name: 任务2描述(如"复制配置文件")
      copy:
        src: ./local_config.conf  # 本地文件路径
        dest: /etc/remote_config.conf  # 目标路径
        mode: '0644'  # 文件权限
      notify:  # 触发处理器(配置变更时执行,可选)
        - 重启服务

  handlers:  # 处理器(仅在被notify触发时执行)
    - name: 重启服务
      systemd:
        name: "{{ app_name }}"
        state: restarted


- name: 第二个Play(可定义多个Play,按顺序执行)
  hosts: db_servers
  tasks:
    - name: 数据库相关任务
      # 具体模块和参数...

核心结构说明

  1. Play 定义:每个 - name: ... 开头的块是一个 Play,包含对特定主机的操作
  2. 主机与用户配置hosts 指定目标,remote_userbecome 控制执行权限
  3. 变量(vars):集中定义可复用的值,通过 {{ 变量名 }} 引用
  4. 任务(tasks):具体操作列表,每个任务包含 name(描述)、模块和参数
  5. 处理器(handlers):用于响应任务通知的操作(如配置变更后重启服务)

编写时需注意

  • 严格使用空格缩进(推荐 2 个空格),同一层级缩进必须一致

  • 注释用##后面的内容会被忽略,建议多写注释提高可读性)

  • 模块参数需符合对应模块的语法要求(可通过 ansible-doc 模块名 查看文档)

  • 可通过 ansible-playbook --syntax-check 剧本名.yml 检查语法正确性

  • 冒号右侧(key: value):大部分可修改,少数不可,右侧是 “值”,用于填充具体内容、配置或引用,能否修改取决于其是否为固定枚举值。

      1. 可修改:动态值或自定义内容

      大部分情况下,右侧是用户根据需求设置的具体值,可自由修改:

      • 字符串 / 路径:如 hosts: node1 中,node1 可改为其他主机名(如 node2all);home: /home/joe 中,/home/joe 可改为 /home/bob
      • 变量引用:如 name: "{{ user }}" 中,{{ user }} 会动态引用变量值,变量值修改后这里也会同步变化。
      • 文本内容:如 debug 模块的 msg 内容,username is {{ user }} 可改为任意文本(如 用户名为 {{ user }})。
      1. 不可修改:固定枚举值

      部分模块参数的右侧值是 Ansible 规定的枚举值(只能从指定选项中选择),不可随意修改,否则会报错。
      例如:

      • user 模块的 state 参数:只能是 present(创建)或 absent(删除),不能改为 createdelete
      • service 模块的 state 参数:只能是 startedstoppedrestarted 等,不可自定义其他值。
  • 冒号(:)左侧的内容固定的关键字不可修改,有些是自定义名称(可修改)**。具体区分如下:

    • 不可修改:固定关键字

      这些是 Ansible 或 YAML 语法规定的固定 “键”,必须严格按原样书写,修改后会导致语法错误或功能失效。
      例如:

      • Playbook 结构关键字:name(定义任务 / 剧本名称)、hosts(指定目标主机)、vars(定义变量)、tasks(定义任务列表)等。
        例:hosts: node1 中,hosts 不可修改,修改为 host_list: node1 会报错。

      • 模块名:如 user:(用户管理模块)、debug:(调试模块),是 Ansible 内置模块的唯一标识,不可修改。
        例:user: name: joe 中,user 不可修改,改为 add_user: name: joe 会提示 “模块不存在”。

      • 模块参数:模块内置的配置项,如 user 模块的 name(用户名)、home(家目录)、state(状态);debug 模块的 msg(输出信息)等。
        例:name: "{{ user }}" 中,name 不可修改,是 user 模块规定的参数名,改为 username: "{{ user }}" 会导致参数无效。

      • 可修改:自定义标识 / 变量名

        如果左侧是用户自定义的变量名、标签或标识符,则可以修改,只要符合 YAML 命名规范(字母、数字、下划线,不建议用特殊字符)。
        例如:

        • 自定义变量名:vars 下的变量,如 user: joe 中,user 是自定义变量名,可改为 username: joe(后续引用需同步改为 {{ username }})。
        • 自定义标签:如果使用 tags 关键字,标签名可自定义,如 tags: create_user 中,create_user 可修改为 add_user 等。
      2. 可修改:自定义标识 / 变量名

      如果左侧是用户自定义的变量名、标签或标识符,则可以修改,只要符合 YAML 命名规范(字母、数字、下划线,不建议用特殊字符)。
      例如:

      • 自定义变量名:vars 下的变量,如 user: joe 中,user 是自定义变量名,可改为 username: joe(后续引用需同步改为 {{ username }})。
      • 自定义标签:如果使用 tags 关键字,标签名可自定义,如 tags: create_user 中,create_user 可修改为 add_user 等。

实验环境搭建

下面通过一个 “部署 Web 服务” 的案例,实际演示 Playbook 的编写和运行。先搭建实验环境:

1:创建工作目录

# 创建web目录(用于存放Playbook相关文件),并进入该目录
[bq@controller ~]$ mkdir web && cd web

2:配置 Ansible 默认参数(ansible.cfg)

创建ansible.cfg文件,定义远程用户、主机清单路径、提权方式等(避免每次执行命令重复输入参数):

[bq@controller web]$ cat > ansible.cfg <<'EOF'
[defaults]
remote_user = bq  # 默认远程登录用户(要在目标主机存在)
inventory = ./inventory  # 主机清单路径(指定为当前目录的inventory文件)

[privilege_escalation]
become = True  # 默认启用提权(执行需要root权限的操作)
become_user = root  # 提权到root用户
become_method = sudo  # 提权方式为sudo
become_ask_pass = False  # 提权时不询问密码(需提前配置sudo免密)
EOF

3:定义主机清单(inventory)

创建inventory文件,列出需要管理的主机(每行一个主机名,需确保 controller 能通过 SSH 连接这些主机):

[bq@controller web]$ cat > inventory <<'EOF'
controller  # 控制节点(可选,可用于本地测试)
node1       # 被管理节点1(示例中主要操作的节点)
node2       # 被管理节点2(预留,本案例暂不使用)
node3
node4
EOF

Playbook 编写(实战案例)

我们编写一个 Playbook(web_deploy.yml),实现以下功能:

  1. 在 node1 上安装并启动 httpd(Web 服务)和 firewalld(防火墙);
  2. 配置防火墙允许 http 访问;
  3. 创建一个测试网页;
  4. 在本地(controller)验证 node1 的 Web 服务是否可用。

Playbook 示例

playbook.yaml 内容如下:

# YAML文件起始标识(建议添加,明确文件格式)
---

# 第一个Play:部署Web服务到node1
- name: 在node1上部署Web服务(描述Play的作用)
  hosts: node1  # 目标主机(从inventory中选node1)
  # 提权配置(覆盖ansible.cfg的默认设置,这里使用默认即可)
  # remote_user: bq
  # become: True

  tasks:  # 任务列表(按顺序执行)
    # 任务1:安装httpd和firewalld(确保是最新版本)
    - name: 安装最新版的httpd和firewalld
      yum:  # 使用yum模块(适用于CentOS/RHEL系统)
        name:  # 要安装的软件包列表
          - httpd  # Web服务软件
          - firewalld  # 防火墙软件
        state: latest  # 状态:确保为最新版本(若已安装则升级)

    # 任务2:创建测试网页
    - name: 生成测试网页内容
      copy:  # 使用copy模块(复制内容到目标文件)
        content: "Welcome to bq WebSite!\n"  # 网页内容(\n是换行)
        dest: /var/www/html/index.html  # 目标文件路径(httpd默认首页路径)
        mode: '0644'  # 文件权限(所有者可读可写,其他用户只读)

    # 任务3:启动并设置firewalld开机自启
    - name: 确保firewalld服务启动并开机自启
      service:  # 使用service模块(管理系统服务)
        name: firewalld  # 服务名称
        enabled: true  # 开机自启(true=启用,false=禁用)
        state: started  # 服务状态(started=启动,stopped=停止,restarted=重启)

    # 任务4:防火墙放行http服务
    - name: 配置防火墙允许http访问(80端口)
      firewalld:  # 使用firewalld模块(管理防火墙规则)
        service: http  # 预定义服务(对应80端口)
        permanent: true  # 规则永久生效(重启防火墙后不丢失)
        state: enabled  # 启用规则
        immediate: yes  # 立即生效(无需等待防火墙重启)

    # 任务5:启动并设置httpd开机自启
    - name: 确保httpd服务启动并开机自启
      service:
        name: httpd
        enabled: true
        state: started

# 第二个Play:在本地验证Web服务
- name: 验证node1的Web服务是否可用
  hosts: localhost  # 目标主机为本地(controller自身)
  become: no  # 不需要提权(本地访问无需root)
  tasks:
    - name: 访问node1的Web页面,检查是否正常响应
      uri:  # 使用uri模块(发送HTTP请求并验证)
        url: http://node1  # 要访问的URL(node1的http服务)
        return_content: yes  # 返回页面内容(可选,用于调试)
        status_code: 200  # 期望的HTTP状态码(200=成功)

# YAML文件结束标识(可省略)
...

核心结构说明

  1. Play:每个- name: ...开头的块是一个 Play,用于定义对特定主机的操作(如第一个 Play 针对 node1,第二个针对localhost)。
  2. 任务(tasks):每个任务是具体的操作(如安装软件、修改配置),由name(描述)和模块(如yumservice)组成。
  3. 模块:Ansible 的功能单元,每个模块实现特定功能(如yum管理软件包,service管理服务),参数需符合模块要求(可通过ansible-doc 模块名查文档,如ansible-doc yum)。

Playbook 运行与验证

1:检查语法

在运行前先检查 Playbook 语法是否正确:

[bq@controller web]$ ansible-playbook web_deploy.yml --syntax-check
# 成功输出:playbook: web_deploy.yml(无报错说明语法正确)
2:空运行

-C选项模拟运行,不实际修改,查看会执行哪些操作(适合测试):

[bq@controller web]$ ansible-playbook web_deploy.yml -C
# 输出中会显示每个任务的预期状态(如"changed"表示会修改,"ok"表示无变化)
3:实际运行
[bq@controller web]$ ansible-playbook web_deploy.yml

#首次运行成功的输出(部分):
PLAY [在node1上部署Web服务] ******************************************
TASK [Gathering Facts] **********************************************
ok: [node1]

TASK [安装最新版的httpd和firewalld] ************************************
changed: [node1]  # 状态为changed,表示执行了安装操作

...(中间省略其他任务)

PLAY [验证node1的Web服务是否可用] **************************************
TASK [Gathering Facts] **********************************************
ok: [localhost]

TASK [访问node1的Web页面,检查是否正常响应] ******************************
ok: [localhost]  # 状态为ok,表示访问成功(状态码200)

PLAY RECAP **********************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    ...
node1                      : ok=5    changed=3    ...

# 第二次运行时,所有任务会显示ok(因为系统状态已符合预期,无需修改),这体现了 Ansible 的 "幂等性"(重复执行结果一致)。
4:手动验证

在 controller 上直接访问 node1 的网页,确认内容正确:

[bq@controller web]$ curl http://node1
Welcome to bq WebSite!  # 输出预期内容,说明部署成功
运行选项补充
  • 详细输出:用-v系列选项查看更多信息(调试时常用):

    ansible-playbook web_deploy.yml -v  # 显示任务结果
    ansible-playbook web_deploy.yml -vv  # 显示任务结果+配置
    ansible-playbook web_deploy.yml -vvv- #包含关于与受管主机连接的信息。
    ansible-playbook web_deploy.yml -vvvv- #增加了连接插件相关的额外详细程度选项,包括受管主机上用于执行脚本的用户,以及所执行的脚本。
    
  • 指定主机:用-l选项仅对部分主机执行(覆盖 Play 中的hosts配置):

    ansible-playbook web_deploy.yml -l node1  # 仅在node1上执行
    
YAML 注释

在 YAML中, 编号或井号符号(#)右侧的所有内容都是注释。如果注释的左侧有内容, 请在该编号符号的前面加一个空格。注释可用于提高可读性。

示例:

# This is YAML comment
Some data # This is also a YAML comment
注意事项与不足

本案例的潜在问题

  1. 环境依赖
    • 假设目标主机是 CentOS/RHEL(使用yum模块),若为 Ubuntu 需替换为apt模块;
    • 需确保 controller 能解析node1的主机名(可在/etc/hosts中配置);
    • become_ask_pass = False要求目标主机的sudo已配置免密(否则会提权失败,需手动输入密码)。
  2. 错误处理
    • 未添加错误判断(如软件安装失败、服务启动失败时,Playbook 会直接报错终止);
    • 可通过ignore_errors: yes忽略特定错误,或failed_when定义失败条件。
  3. 扩展性
    • 若需部署到多台主机,需在inventory中定义主机组(如[web_servers]),并在 Play 中设置hosts: web_servers
    • 重复任务可提取为变量(vars)或角色(Roles),减少代码冗余。
编写 Playbook 的最佳实践
  1. 多写注释:每个 Play 和任务都用name描述作用,复杂逻辑加#注释;
  2. 先测试再执行:用--syntax-check-C验证后再实际运行;
  3. 利用幂等性:尽量使用支持幂等的模块(如yumstate: present不会重复安装);
  4. 拆分复杂任务:一个 Playbook 只做一件事(如部署 Web、部署数据库分开),便于维护。

Playbook 提权

在playbook中指定此关键字将覆盖/etc/ansible/ansible.cfg文件中的设置特权升级属性

  • remote_user,指定ssh用户

  • become,启用或禁用特权升级

  • become_method,启用特权升级的方法

  • become_user,特殊升级的帐户

---
- name: Enable intranet services
  hosts: node1
  
  remote_user: bq
  become: true
  become_method: sudo
  become_user: root
  
  tasks:
    - name: latest version of httpd and firewalld installed
      yum:
        name:
          - httpd
          - firewalld
        state: latest

YAML属性补充

YAML 单行字符串

YAML中的字符串通常不需要放在引号里,即使字符串中包含空格。

字符串也可以用双引号或单引号括起。

this is a string
'this is another string'
"this is yet another a string"
YAML 多行字符串
  • 可以使用竖线(I)字符表示,保留字符串中的换行字符。

    示例:

    ---
    - name: test string
      hosts: node1
      tasks:
        - name: test string
          # 用户显示变量值或者字符串
          debug:
            msg: |
              Example Company
              123 Main Street
              Atlanta, GA 30303
    
  • 也可以使用大于号(>)字符表示换行字符。执行时换行符使用空格代替,并且行内的引导空白将被删除。

    示例:

    ---
    - name: test string
      hosts: node1
      tasks:
        - name: test string
          # 用户显示变量值或者字符串
          debug:
            msg: >
              This is an example
              of a long string,
              that will become
              a single sentence once folded.
    

    这种方法通常用于将很长的字符串在空格字符处断行,使它们跨占多行来提高可读性。

YAML 字典

一组键值对的集合,又称为映射(mapping)和哈希(hashes)。

以缩进块的形式编写键值对集合,如下方所示:user属性是字典格式,是多个键值对集合。

user:
  name: bq 
  uid: 1088
  state: absent

字典也可以使用以花括号括起的内联块格式编写,如下方所示:

user: {name: bq, uid: 1088, state: absent}

大多数情形中应避免内联块格式,其可读性较差。不过,当playbook中包含角色列表时,使用这种语法,更加容易区分play中包含的角色和传递给角色的变量。

某些 playbook 可能使用较旧的简写(shorthand)格式,通过将模块的键值对放在与模块名称相同的行上来定义任务。

示例:

- name: shorhand form
  user: name=bq uid=1088 state=absent

普通格式:

- name: shorhand form
  user:
    name: bq 
    uid: 1088
    state: absent

两者格式总结:

  • 通常您应避免简写格式,而使用普通格式。
  • 普通格式的行数较多,更容易操作。任务的关键字垂直堆叠,更容易区分。 阅读play时,您的眼睛直接向下扫视,左右运动较少。
  • 普通格式是原生的YAML,现代文本编辑器中的语法突出显示工具可以识别,简写形式则不支持。
  • 可能会在文档和他人提供的旧playbook中看到这种语法,而且这种语法仍然可以发挥作用。
YAML 列表

一组按次序排列的值,又称为序列(sequence)和数组(array)。

以缩进块的形式编写的键值对集合,如下方所示:

- name: latest version of httpd and firewalld installed
  yum:
    name:
      - httpd
      - firewalld
    state: latest
- name: test html page is installed
  copy:
    content: "Welcome to the example.com intranet!\n"
    dest: /var/www/html/index.html

以上有两个任务,每个任务都是多个键值对描述。其中yum模块操作的软件包是一个简单的名称列表。

内联格式:

name: [httpd, firewalld]

尽量避免内联格式。


网站公告

今日签到

点亮在社区的每一天
去签到