Ansible剧本介绍

什么是剧本

playbook(剧本)是一个持久化的批量执行方法的模型,它要求使用yaml语法,具有简介明了,流程结构清晰明确等特点.playbook的配置文件类似于shell脚本,能持久化保存针对特殊需求的任务列表.单使用ansible命令,它虽然可以完成各种任务和需求,单丝配置复杂任务的时候,使用ansible命令就特别繁琐,而且效率特别低下,最有效的办法就是根据playbook给出的规范搭建剧本环境,并写入任务流程,最后使用ansible-playbook执行命令.


playbook剧本的优势

1,减少重复的书写指令: ansible backup -m file -a
2,看起来简洁清晰
3,功能强大,可以控制流程,比如:判断,循环,变量,标签
4,其他剧本可以复用
5,提供语法检查以及模拟执行


剧本的格式书写要求

playbook的语法,即yaml的语法,ansible采用了jinja2样式模版引擎来渲染yaml文件,我们不仅可以使用变量,也可以使用简单的结构化语句.

YAML格式特点

1,严格的缩进表示层级关系
2,不要使用tab缩进
3,:后面一定要有空格
4,-后面一定要有空格
5,YAML文件内容和Linux系统大小写判断方式保持一致,是区分大小写的,k/v的值均需大小写敏感
6,k/v的值可同行写也可以换行写,同行使用:分隔;
7,v可以是个字符,也可以是一个列表;
8,一个完整的代码块功能需要最少元素包括name: task;
9,文件后缀名需要改为yaml或yml,vim可以只能高亮提示

剧本的组成

hosts: 需要执行的主机
tasks: 需要执行的任务
name:  任务名称


playbook项目目录结构

以下是一个完整的playbook项目目录结构,图片中的一些概念术语,我们先介绍下.

  • role: 从1.2版本开始,ansible加入role(角色)的概念,可以将role看成单独一个服务的概念,在执行流程中,我们即可执行多个role的任务,也可以单独使用一个role的信息;
  • files: 该目录应该存放软件包,通用的文件等不用再流程需要更改的文件.在yaml文件中,我们可以不用指明这些文件的具体路径,只使用文件名即可传输;
  • tasks: 该目录会存放我们任务的具体流程语句,当playbook执行该角色时会执行tasks下的main.yml文件,所以tasks必须存在main.yml文件;
  • vars: 该目录会用于存全局变量,当一个值存在复用的情况,我们可以在vars的main.yml中定义变量,方便后面的扩展和修改;
  • meta: meta文件可以引用其他role的流程,并先于当前流程执行.
  • default:默认变量,在1.3版本添加中的特性,如果一个变量没有被定义,但在default中被定义了,那就会使用default中的变量,但如果被定义了过,则不会使用,意思就是说,default的变量的优先级别为最低.
  • templates:该目录可以存放文本文件,但跟files不同的是,它可以使用jinja2语法.
  • handlers: 存放一些额外的方法,但是这些方法不会被自动执行,只有tasks里触发该方法时才可以.

ansible-playbook wiki_ansible

简单编写一个nginx安装剧本

- hosts: "{{ server }}"
  remote_user: root
  gather_facts: True
 
  tasks:
    - name: Gather the service facts
      ansible.builtin.service_facts:
    - name: yum安装nginx
      yum:
        name: [ "nginx" ]
        state: latest
      when: "'nginx.service' not in ansible_facts.services"
    - name: 配置服务启动
      service:
        name: nginx 
        state: started 
        enabled: yes
  1. hosts: "{{ server }}": 这指定了该剧本将在哪些主机上运行,{{ server }} 是一个变量,表示目标主机的列表。在这里,剧本将在指定的主机上执行。
  2. remote_user: root: 这指定了远程主机上用于连接的用户。在这里,剧本将使用 root 用户连接到远程主机。
  3. gather_facts: True: 这指定了是否在执行任务之前收集远程主机的事实信息。在这里,将会收集有关主机的信息,如操作系统、IP 地址等。
  4. tasks: 这部分包含了具体的任务列表。
  • Gather the service facts: 这是一个任务,它使用 ansible.builtin.service_facts 模块收集远程主机上的服务信息。
  • yum安装nginx: 这是一个任务,它使用 yum 模块安装 Nginx 软件包。条件 when 指定了只有当 Nginx 服务未安装时才执行此任务。
  • 配置服务启动: 这是一个任务,它使用 service 模块配置 Nginx 服务。它将确保 Nginx 服务已启动并设置为开机自启动。

模拟执行

ansible-playbook -C nginx_install.yml  -e "server=10.10.10.236"

用-C 或者--check都行

ansible-playbook wiki_ansible_02

剧本高级特性-循环

官方文档

https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html

应用场景

安装多个软件
创建多个目录
复制多个目录
复制多个文件到不同的目录
不同的文件权限不一样

循环书写风格:单行模式

- name: create_data
    file: path=/data state=directory owner=www group=www

- name: create_backup
    file: path=/backup state=directory owner=www group=www

循环书写风格:缩进模式

以前的写法:

- name: create_data
    file:
      path: /data
      state: directory
      owner: www
      group: www

- name: create_backup
    file:
      path: /backup
      state: directory
      owner: www
      group: www

循环实现:

- name: create_dir
    file:
      path: {{ item }}
      state: directory
      owner: www
      group: www
    loop:
      - /data
      - /backup

循环书写风格:多参数循环模式

- name: create_dir
    file:
      path: {{ item.path }}
      state: directory
      owner: www
      group: www
      mode: "{{ item.mode }}"
    loop:
      - { path: '/data', mode: '755'}
      - { path: '/backup', mode: '644'}

循环写法之with_items

- name: 创建nginx日志目录
    file: name={{ item }} state=directory owner=www group=www mode: 0755 recurse=yes
    with_items:
      - "{{ NGINX_DIR }}/logs/admin"
      - "{{ NGINX_DIR }}/logs/adminapi"

是不是见过这种with_items,目前的话已经被loop所替代了


另类写法达到和循环一样的效果

- hosts: "{{ server }}"
  remote_user: root
  gather_facts: True

  tasks:
    - name: yum安装php环境依赖包
      yum:
        name: "{{ packages }}"
        state: latest
      vars:
        packages:
          - pcre
          - pcre-devel
          - zlib
          - zlib-devel
          - gd
          - gd-devel

ansible-playbook wiki_角色_03


循环配合条件when使用

- hosts: "{{ server }}"
  remote_user: root
  gather_facts: True
  tasks:
    - name: check if /data directory does not exist
      ansible.builtin.stat:
        path: "/data"
      register: data_dir_stat

    - name: check if /backup directory does not exist
      ansible.builtin.stat:
        path: "/backup"
      register: backup_dir_stat

    - name: create_dir
      ansible.builtin.file:
        path: "{{ item.path }}"
        state: directory
        owner: www
        group: www
        mode: "{{ item.mode }}"
      loop:
        - { path: '/data', mode: '755'}
        - { path: '/backup', mode: '644'}
      when: data_dir_stat.stat.exists == false and backup_dir_stat.stat.exists == false

ansible-playbook wiki_playbook_04

当然根据官方文档还有其他更多的用法,这个具体我们查看官方文档好了,还是比较有趣的


剧本高级特性-变量

应用场景

自定义某个变量,在任务中被多次引用
从主机收集到系统信息里提取某个变量,比如ip地址,主机名

自定义变量并引用

- hosts: backup 
  vars:
    data_path: /data/
    dest_path: /etc/
    file_path: /etc/rsync.passwd

  tasks:
  - name: 01mkdir
    file:
      path: "{{ data_path }}"
      state: directory
      
  - name: 02copy 
    copy:
      src: "{{ file_path }}"
      dest: "{{ dest_path }}"

使用内置变量获取主机信息

比如我们需要获取主机的ip和主机名信息来修改配置,这种需求场景也是较常见

- hosts: "{{ server }}"
  remote_user: root
  gather_facts: True
  tasks:
    - name: gen info
      ansible.builtin.debug:
        msg: "{{ ansible_hostname }}: {{ ansible_default_ipv4.address }}"

ansible-playbook wiki_角色_05


剧本高级特性-注册变量

应用场景

调式,回显命令执行的内容

把状态保存成变量,其他任务可以进行判断或引用

比较常见的是,当我们安装一个服务时先判断服务是否已经被安装了,如果被安装了那就跳过不执行

- hosts: "{{ server }}"
  remote_user: root
  gather_facts: False
  tasks:
    - name: Gather the package facts
      ansible.builtin.package_facts:
        manager: auto

    - name: filebeat_install
      ansible.builtin.debug:
        msg: "start filebat_install step"
      when: "'filebeat' not in ansible_facts.packages"

ansible-playbook wiki_角色_06

剧本高级特性-服务状态管理

应用场景

有时我们希望仅在计算机上发生更改时运行,例如,如果任务更新了服务的配置,我们希望重新启动该服务,但如果配置未更改,则不需要重新启动,ansible使用handlers来处理这个问题

官方文档:https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_handlers.html

通知处理程序

任务可以使用关键字指示一个或多个处理程序执行notify,该notify关键字可以应用于任务并接受在任务更改时收到通知的处理程序名称列表,或者也可以提供包含单个处理程序名称的字符串

tasks:
- name: Template configuration file
  ansible.builtin.template:
    src: template.j2
    dest: /etc/foo.conf
  notify:
    - Restart apache
    - Restart memcached

handlers:
  - name: Restart memcached
    ansible.builtin.service:
      name: memcached
      state: restarted

  - name: Restart apache
    ansible.builtin.service:
      name: apache
      state: restarted

剧本高级特性-选择标签

应用场景

调试,选择性执行任务

打印出playbook里要执行的所有标签

ansible-playbook --list-tags rsync_install.yaml

指定运行某个标签

ansible-playbook -t '03-install nfs service' rsync_install_tag.yaml 

指定运行多个标签

ansible-playbook -t 01-add-group,02-add-user,05-create-data-dir rsync_install_tag.yaml

指定不运行某个标签

ansible-playbook --skip-tags 01-add-group rsync_install_tag.yaml

指定不运行多个标签

ansible-playbook --skip-tags 01-add-group,02-add-user,04-copy-nfs-exports rsync_install_tag.yaml

剧本高级特性-选择tasks

应用场景

调式任务,从某个任务开始往下执行

查看task列表

ansible-playbook --list-tasks php.yml -e "server=10.10.10.236"

ansible-playbook wiki_角色_07

选择从哪一个task开始运行

ansible-playbook --start-at-task 'Gather the service facts' php.yml  -e "server=10.10.10.236"

ansible-playbook wiki_ansible_08

Ansible角色介绍

为什么需要使用角色

写成一个yaml不太灵活,臃肿
全部写在一起,修改不太方便
配置文件随便放,不标准

角色解决了什么问题

把剧本拆分
解耦,结构更清晰,调试更方便

编写角色的最佳实践

初级阶段,不要直接写就角色,先写好剧本,然后拆分
一开始不要想一步到位,不用拆的很细,尤其是变量

角色目录规划

官方说明

https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html

目录说明

注意,这里的目录结构必须按照官方定义的要求来做,不要自己随便乱起,可以看我最上面发的

我们还可以在某些目录中添加其yaml文件,例如你可以将特定于平台的任务放在单独的文件中,并在tasks/main.yml文件中引用它们

ansible-playbook wiki_playbook_09

ansible-playbook wiki_playbook_10


如何使用角色

可以通过三种方式使用角色:

  • 在剧本级别,可以使用roles,这是在剧本中使用角色的经典方式
  • 在任务级别可以使用include_role, 可以动态地在任务剧本部分的任何地方使用include_role
  • 在任务级别还可以使用import_role,可以静态地在任务剧本中的任何位置使用import_role

这在企业运维中使用剧本是非常常见的


未来规划

目前有个任务中心的子系统,分为系统调用公共部分和平时处理工作的特殊部分,大概脑图是这样,大佬只给了图,没有具体源码

ansible-playbook wiki_角色_11

这个让新手练练手我觉得还是不错的