Skip to content

官当

https://ansible-tran.readthedocs.io/en/latest/docs/playbooks.html

https://docs.ansible.com/ansible/latest/getting_started/get_started_playbook.html

1. Ansible Playbook

1.1 Playbook简介

Playbookad-hoc相比,是一种完全不同的运用ansible的方式,类似与saltstackstate状态文件。ad-hoc无法持久使用,playbook可以持久使用。

playbook是由一个或多个play组成的列表,play的主要功能在于将事先归并为一组的主机装扮成事先通过ansible中的task定义好的角色。从根本上来讲,所谓的task无非是调用ansible的一个module。将多个play组织在一个playbook中,即可以让它们联合起来按事先编排的机制完成某一任务

1.2 Playbook核心元素

  • hosts 执行的远程主机列表
  • tasks 任务集
  • varniables 内置变量或自定义变量在playbook中调用
  • templates 模板,即使用模板语法的文件,比如配置文件等
  • handlers 和notity结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行
  • tags 标签,指定某条任务执行,用于选择运行playbook中的部分代码。

1.3 Playbook语法

playbook使用yaml语法格式,后缀可以是yaml,也可以是yml

  • 在单一一个playbook文件中,可以连续三个连子号(---)区分多个play。还有选择性的连续三个点好(...)用来表示play的结尾,也可省略。
  • 次行开始正常写playbook的内容,一般都会写上描述该playbook的功能。
  • 使用#号注释代码。
  • 缩进必须统一,不能空格和tab混用。
  • 缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行实现的。
  • YAML文件内容和Linux系统大小写判断方式保持一致,是区分大小写的,k/v的值均需大小写敏感
  • k/v的值可同行写也可以换行写。同行使用:分隔。
  • v可以是个字符串,也可以是一个列表
  • 一个完整的代码块功能需要最少元素包括 name: task

案例

sh
# 创建playbook文件
[root@ansible ~/ansible]$ cat playbook01.yml
---                       #固定格式
- hosts: node1     #定义需要执行主机
  remote_user: root       #远程用户
  vars:                   #定义变量
    http_port: 8088       #变量

  tasks:                             #定义一个任务的开始
    - name: create new file          #定义任务的名称
      file: name=/tmp/playtest.txt state=touch   #调用模块,具体要做的事情
    - name: create new user
      user: name=test02 system=yes shell=/sbin/nologin
    - name: install package
      yum: name=httpd
    - name: config httpd
      template: src=./httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify:                 #定义执行一个动作(action)让handlers来引用执行,与handlers配合使用
        - restart apache      #notify要执行的动作,这里必须与handlers中的name定义内容一致
    - name: copy index.html
      copy: src=/var/www/html/index.html dest=/var/www/html/index.html
    - name: start httpd
      service: name=httpd state=started
  handlers:                                    #处理器:更加tasks中notify定义的action触发执行相应的处理动作
    - name: restart apache                     #要与notify定义的内容相同
      service: name=httpd state=restarted      #触发要执行的动作
# 创建playbook文件
[root@ansible ~/ansible]$ cat playbook01.yml
---                       #固定格式
- hosts: node1     #定义需要执行主机
  remote_user: root       #远程用户
  vars:                   #定义变量
    http_port: 8088       #变量

  tasks:                             #定义一个任务的开始
    - name: create new file          #定义任务的名称
      file: name=/tmp/playtest.txt state=touch   #调用模块,具体要做的事情
    - name: create new user
      user: name=test02 system=yes shell=/sbin/nologin
    - name: install package
      yum: name=httpd
    - name: config httpd
      template: src=./httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify:                 #定义执行一个动作(action)让handlers来引用执行,与handlers配合使用
        - restart apache      #notify要执行的动作,这里必须与handlers中的name定义内容一致
    - name: copy index.html
      copy: src=/var/www/html/index.html dest=/var/www/html/index.html
    - name: start httpd
      service: name=httpd state=started
  handlers:                                    #处理器:更加tasks中notify定义的action触发执行相应的处理动作
    - name: restart apache                     #要与notify定义的内容相同
      service: name=httpd state=restarted      #触发要执行的动作

1.4 Playbook运行方式

通过ansible-playbook命令运行

格式:ansible-playbook <filename.yml> ... [options]

sh
[root@ansible ~/ansible]$ ansible-playbook -h
#ansible-playbook常用选项:
--check  or -C    #只检测可能会发生的改变,但不真正执行操作
--list-hosts      #列出运行任务的主机
--list-tags       #列出playbook文件中定义所有的tags
--list-tasks      #列出playbook文件中定义的所以任务集
--limit           #主机列表 只针对主机列表中的某个主机或者某个组执行
-f                #指定并发数,默认为5个
-t                #指定tags运行,运行某一个或者多个tags。(前提playbook中有定义tags)
-v                #显示过程  -vv  -vvv更详细
[root@ansible ~/ansible]$ ansible-playbook -h
#ansible-playbook常用选项:
--check  or -C    #只检测可能会发生的改变,但不真正执行操作
--list-hosts      #列出运行任务的主机
--list-tags       #列出playbook文件中定义所有的tags
--list-tasks      #列出playbook文件中定义的所以任务集
--limit           #主机列表 只针对主机列表中的某个主机或者某个组执行
-f                #指定并发数,默认为5个
-t                #指定tags运行,运行某一个或者多个tags。(前提playbook中有定义tags)
-v                #显示过程  -vv  -vvv更详细

2. 定义模版

bash
cd /etc/ansible/roles

mkdir hello

cd hello

mkdir templates defaults  files  handlers  meta  tasks  vars
cd /etc/ansible/roles

mkdir hello

cd hello

mkdir templates defaults  files  handlers  meta  tasks  vars

3.模块

1.正则

2.lineinfile

yaml
---
- hosts: all
  gather_facts: true
  remote_user: root
  tasks:
    - name: "修改ssh配置文件的安全选项"
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^#?UseDNS'
        line: 'UseDNS no'
      notify:
        - restart sshd

  handlers:
    - name: restart sshd
      service:
        name: sshd
        state: restarted
---
- hosts: all
  gather_facts: true
  remote_user: root
  tasks:
    - name: "修改ssh配置文件的安全选项"
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^#?UseDNS'
        line: 'UseDNS no'
      notify:
        - restart sshd

  handlers:
    - name: restart sshd
      service:
        name: sshd
        state: restarted

4. 流程控制

4.1 handler

在Ansible中,handler是一个特殊的任务,它不会在playbook的主执行流程中直接运行,而是根据某些条件被触发执行。这些条件通常是由其他任务(task)的状态变化触发的,比如当某个任务修改了目标系统的状态(例如文件、服务或配置等)。

handler的主要作用是允许您在playbook的末尾一次性地执行某些操作,而不是在每个任务之后都执行。这可以提高效率,并避免不必要的重复操作。

handler通常与notify关键字一起使用。当您在一个任务中定义了notify,并且该任务的状态发生了改变(例如从未改变变为已改变),那么与notify相关联的handler就会在playbook的末尾被触发。

yaml
- hosts: web
  gather_facts: false
  tasks:
    - name: copy ngx conf file
      copy:
        src: /tmp/backend.conf
        dest: /etc/nginx/conf.d/
        backup: yes
      notify:
        - ngx reload
  handlers:
    - name: ngx reload
      systemd:
        name: nginx
        state: reloaded
- hosts: web
  gather_facts: false
  tasks:
    - name: copy ngx conf file
      copy:
        src: /tmp/backend.conf
        dest: /etc/nginx/conf.d/
        backup: yes
      notify:
        - ngx reload
  handlers:
    - name: ngx reload
      systemd:
        name: nginx
        state: reloaded

4.2 when

在Ansible中,when关键字用于条件判断,根据特定的条件来决定是否执行某个任务(task)。当条件为真时,任务将会执行;当条件为假时,任务将被跳过。

when关键字的使用非常简单,只需在单个任务的后面添加when条件判断语句即可。在when语句中,变量不需要使用

image-20241218135347848表达式来包裹。

在Ansible中,when语句用于条件判断,其判断语法关键字和表达式与Python的语法非常相似。Ansible的when语句可以支持多种条件表达式和逻辑运算符,以下是一些常用的判断语法关键字和表达式:

1.比较运算符

符号作用
==相等
!=不等
>大于
<小于
>=大于等于
<=小于等于
yaml
- hosts: all  
  tasks:  
    - debug:  
        msg: "当前主机名 {{ ansible_hostname }} 可用内存大于 1000MB {{ ansible_memfree_mb }}"  
      when: ansible_memfree_mb >= 1000
- hosts: all  
  tasks:  
    - debug:  
        msg: "当前主机名 {{ ansible_hostname }} 可用内存大于 1000MB {{ ansible_memfree_mb }}"  
      when: ansible_memfree_mb >= 1000

2.逻辑运算符

符号作用
and逻辑与
or逻辑或
not逻辑否
yaml
- hosts: all  
  tasks:  
    - debug:  
        msg: "当前主机名 {{ ansible_hostname }} 可用内存大于 200MB 小于1000MB {{ ansible_memfree_mb }}"  
      when: ansible_memfree_mb <= 1000 and ansible_memfree_mb >= 200
- hosts: all  
  tasks:  
    - debug:  
        msg: "当前主机名 {{ ansible_hostname }} 可用内存大于 200MB 小于1000MB {{ ansible_memfree_mb }}"  
      when: ansible_memfree_mb <= 1000 and ansible_memfree_mb >= 200

3.组合条件

可以使用括号()来组合多个条件,以改变运算的优先级

4.is判断

符号作用
is exists文件、目录存在
is not exists文件、目录不存在
is number判断是否为数字
is string判断是否为字符串
is defined判断变量是否已定义,已定义则返回真
is undefined判断变量是否未定义,未定义则返回真
is none判断变量的值是否为空,如果变量已定义且值为空,则返回真
is match判断正则是否匹配
is file判断指定路径是否为一个文件,是则为真
is directory判断指定路径是否为一个目录,是则为真
is link判断指定路径是否为一个软链接,是则为真
is mount判断指定路径是否为一个挂载点,是则为真
yaml
- host: all
  tasks:
    - name: add ngx yum repo
      yum_repository:
        name: nginx-stable
        description: " nginx yum repo"
        baseurl: "http://nginx.org/packages/centos/$releasever/$basearch/"
        enable: yes
        gpgcheck: no
        file: nginx
      when: ( ansible_hostname is match("web|lb") )
- host: all
  tasks:
    - name: add ngx yum repo
      yum_repository:
        name: nginx-stable
        description: " nginx yum repo"
        baseurl: "http://nginx.org/packages/centos/$releasever/$basearch/"
        enable: yes
        gpgcheck: no
        file: nginx
      when: ( ansible_hostname is match("web|lb") )

5. register

拿到执行命令结果

yaml
- name: Nginx Check
  hosts: all

  tasks:
    - name: Check if Nginx is installed
      command: ls /sbin/nginx
      register: ngx_exist
      ignore_errors: true

    - name: ngx status
      debug:
        msg: "{{ ngx_exist }}"

    - name: Nginx service failure
      debug:
        msg: ngx is not installed
      when: ngx_exist is failure
      
    - name: Nginx service success
      debug:
        msg: ngx is  installed
      when: ngx_exist is failure
- name: Nginx Check
  hosts: all

  tasks:
    - name: Check if Nginx is installed
      command: ls /sbin/nginx
      register: ngx_exist
      ignore_errors: true

    - name: ngx status
      debug:
        msg: "{{ ngx_exist }}"

    - name: Nginx service failure
      debug:
        msg: ngx is not installed
      when: ngx_exist is failure
      
    - name: Nginx service success
      debug:
        msg: ngx is  installed
      when: ngx_exist is failure

❌ 注意

要使用failure判断,就要开启ignore_errors。默认情况下ignore_errors为false,如果脚本tasks有错误,会忽略接下来的任务,也就会使playbook获取不到failed的结果。所以,要使用 is failure 一定要开启 ignore_errors

yaml
- name: Check if Tomcat Webapp Exists
  stat:
    path: /opt/tomcat/webapps/ROOT/WEB-INF/web.xml
  register: tomcat_extracted

- name: Download Apache Tomcat
  get_url:
    url: "https://downloads.apache.org/tomcat/tomcat-9/v{{ tomcat_version }}/bin/apache-tomcat-{{ tomcat_version }}.tar.gz"
    dest: "/tmp/apache-tomcat-{{ tomcat_version }}.tar.gz"
  when: not tomcat_extracted.stat.exists

- name: Extract Tomcat
  unarchive:
    src: "/tmp/apache-tomcat-{{ tomcat_version }}.tar.gz"
    dest: /opt/tomcat
    remote_src: true
    extra_opts: ["--strip-components=1"]
  when: not tomcat_extracted.stat.exists
- name: Check if Tomcat Webapp Exists
  stat:
    path: /opt/tomcat/webapps/ROOT/WEB-INF/web.xml
  register: tomcat_extracted

- name: Download Apache Tomcat
  get_url:
    url: "https://downloads.apache.org/tomcat/tomcat-9/v{{ tomcat_version }}/bin/apache-tomcat-{{ tomcat_version }}.tar.gz"
    dest: "/tmp/apache-tomcat-{{ tomcat_version }}.tar.gz"
  when: not tomcat_extracted.stat.exists

- name: Extract Tomcat
  unarchive:
    src: "/tmp/apache-tomcat-{{ tomcat_version }}.tar.gz"
    dest: /opt/tomcat
    remote_src: true
    extra_opts: ["--strip-components=1"]
  when: not tomcat_extracted.stat.exists

4.3 with_items

在Ansible中,with_items 是一个循环迭代模块,用于在playbook的任务中处理一个列表或字典中的元素。他将遍历一个列表或字典,并对每个元素执行一次任务。

with_items 后面跟着的是要处理的列表或字典数据。任务会按照列表顺序依次执行,每次迭代时,列表中的一个元素会被传递给任务作为变量(通常使用 image-20241218141425016 来引用当前迭代的元素)。

1.迭代列表

- hosts: all
  tasks:
  - name: 创建多个目录
    file:
      path: "{{ item }}"
      state: directory
    with_items:
      - /tmp/dir1
      - /tmp/dir2
      - /tmp/dir3
- hosts: all
  tasks:
  - name: 创建多个目录
    file:
      path: "{{ item }}"
      state: directory
    with_items:
      - /tmp/dir1
      - /tmp/dir2
      - /tmp/dir3

2.迭代字典

- hosts: all
  tasks:
  - name: 创建多个目录
    file:
      path: "{{ item.path }}/{{ item.name}}"
      state: touch
    with_items:
      - {path: /tmp/dir1, name: gg}
      - {path: /tmp/dir2, name: uu}
      - {path: /tmp/dir3, name: oo}
- hosts: all
  tasks:
  - name: 创建多个目录
    file:
      path: "{{ item.path }}/{{ item.name}}"
      state: touch
    with_items:
      - {path: /tmp/dir1, name: gg}
      - {path: /tmp/dir2, name: uu}
      - {path: /tmp/dir3, name: oo}

4.4 loop

在Ansible Playbook中,loop关键字用于迭代一个列表或字典,并对每个元素执行一次特定的任务

通过ansible-doc -t lookup 查看文档

1.迭代列表

- hosts: all  
  tasks:  
    - name: Install packages  
      yum:  
        name: "{{ item }}"  
        state: present  
      loop:  
        - httpd  
        - mysql-server  
        - php
- hosts: all  
  tasks:  
    - name: Install packages  
      yum:  
        name: "{{ item }}"  
        state: present  
      loop:  
        - httpd  
        - mysql-server  
        - php

2.迭代字典

- hosts: all  
  tasks:  
    - name: Create directories  
      file:  
      path: "{{ item.path }}/{{ item.name}}"
      state: touch 
      loop:  
        - { path: "/tmp/directory1", name: "file1.txt" }  
        - { path: "/tmp/directory2", name: "file2.txt" }  
        - { path: "/tmp/directory3", name: "file3.txt" }
- hosts: all  
  tasks:  
    - name: Create directories  
      file:  
      path: "{{ item.path }}/{{ item.name}}"
      state: touch 
      loop:  
        - { path: "/tmp/directory1", name: "file1.txt" }  
        - { path: "/tmp/directory2", name: "file2.txt" }  
        - { path: "/tmp/directory3", name: "file3.txt" }

文档:

https://docs.ansible.com/ansible/2.9/user_guide/playbooks_conditionals.html