ansible playbook基础

ansible playbook基础

相关文档:

https://docs.ansible.com/ansible/latest/(更新更详细)

http://ansible.com.cn/docs/

playbook简介和运行

ad-hoc(点对点模式)每次只能执行一个任务,又不能持久化使用,只适合执行一些临时的简单任务。

使用ansible做自动化的是它的灵魂,playbook,就如同saltstack的.sls状态文件一样。

ansible的playbook也叫剧本

  • playbook是一个文本文件,其中包含由一个或多个按特定顺序运行的play组成的列表。
  • 剧本中包含多个任务
  • YAML格式书写,随时调用
  • 根据编写的方式记录任务所需步骤,适合执行复杂任务

运行playbook

absible-playbook命令可用于运行playbook。该命令在控制节点上执行,要运行的playbook的名称则作为参数传递。

ansible-playbook site.yml

在运行playbook时,将生成输出来显示所执行的play和任务。输出中也会报告执行的每一项任务的结果。

#输出示例
[root@ansible ~]# ansible-playbook test.yaml

PLAY [webserver] ************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************
ok: [nginx]
ok: [apache]
ok: [tomcat]

TASK [this is my first playbook] ********************************************************************************
ok: [apache]
ok: [nginx]
ok: [tomcat]

PLAY RECAP ******************************************************************************************************
apache                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
nginx                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
tomcat                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


请注意,在playbook运行时,屏幕中会显示每个play和任务的name键的值。(Gathering Facts任务是一项特别的任务,setup模块通常在play启动时自动运行这项任务。)对于含有多个play和任务的playbook,设置name属性后可以更加轻松地监控playbook执行的进展。

通常而言,Ansible Playbook中的任务是幂等的,而且能够安全地多次运行playbook。如果目标受管主机已处于正确的状态,则不应进行任何更改。如果再次运行这个playbook,所有任务都会以状态OK传递,且不报告任何更改。

输出的详细程度

ansible-playbook命令提供的默认输出不提供详细的任务执行信息。ansible-playbook -v命令提供了额外的信息,总共有四个级别。

配置Playbook执行的输出详细程序

选项 描述
-v 显示任务结果
-vv 任务结果和任务配置都会显示
-vvv 包含关于与受管主机连接的信息
-vvvv 增加了连接插件相关的额外详细程序选项,包括受管主机上用于执行脚本的用户以及所执行的脚本

语法检查

在执行playbook之前,最好要进行验证,确保其内容的语法正确无误。ansible-playbook命令提供了一个--syntax-check选项,可用于验证playbook的语法。

语法检查正确时:

[root@ansible ~]# ansible-playbook --syntax-check test.yaml

playbook: test.yaml

语法检查到错误时:

[root@ansible ~]# ansible-playbook --syntax-check test.yaml
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: No JSON object could be decoded

Syntax Error while loading YAML.
  mapping values are not allowed in this context

The error appears to be in '/root/test.yaml': line 3, column 8, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

- hosts:webserver
  tasks:
       ^ here

#yaml语法错误,hosts:后少一个空格

空跑

尝试运行,不会在受控机器上做任何更改

可以使用-C选项对playbook执行空运行。这会使Ansible报告在执行该playbook时将会发生什么更改,但不会对受管主机进行任何实际的更改。

下例演示了一个playbook的空运行,它包含单项任务,可确保在受管主机上安装了最新版本的httpd软件包。注意该空运行报告此任务会对受管主机产生的更改。

ansible-playbook -C webserver.yml 

playbook的元素

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

还有用户元素,很少使用,

指定用户

---
- hosts: 192.168.1.31
  remote_user: root

除了上面的定义外,还可以在某一个tasks中定义要执行该任务的远程用户

tasks: 
  - name: run df -h
    remote_user: test
    shell: name=df -h

还可以定义使用sudo授权用户执行该任务

tasks: 
  - name: run df -h
    sudo_user: test
    sudo: yes
    shell: name=df -h

playbook的语法

语法格式:

  1. 使用yaml编写

  2. playbook文件有一个和多个play组成

  3. 每个play中可以包含:

    hosts(主机)、tasks(任务)

    variables(变量)、roles(角色)、handlers等元素组成

hosts由一个或多个组或者主机组成,逗号分隔

tasks由一个或多个任务组成,多个任务按顺序执行

-f 自定义并发量,默认5

[root@ansible ~]# cat test.yaml
---
- hosts: all
  tasks:
    - name: this is my first playbook
      ping:
    - name: touch a file
      shell:
        cmd: touch a.txt

#执行
[root@ansible ~]# ansible-playbook test.yaml

PLAY [all] ******************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************
ok: [tomcat]
ok: [nginx]
ok: [apache]

TASK [this is my first playbook] ********************************************************************************
ok: [nginx]
ok: [tomcat]
ok: [apache]

TASK [touch a file] *********************************************************************************************
changed: [apache]
changed: [nginx]
changed: [tomcat]

PLAY RECAP ******************************************************************************************************
apache                     : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
nginx                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
tomcat                     : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


指定执行play的目标主机

1.直接使用主机名或组名,多个用逗号隔开(或者冒号)

hosts: webserver
hosts: nginx,apache
hosts: all

2.使用组的索引来精确到组中某个主机

hosts: webserver[0]		#索引从0开始,-1是最后一个

3.范围表示

hosts: webserver[1:2]	#webserver组的第2和第3个主机

#也可以字母
hosts: webserver[A:F]	#webserverA-webserverF的主机

4.使用通配符

#匹配web开头的主机或主机组
hosts: web*

5.使用正则表达式,~放在开头用来表示使用正则

hosts: ~(web|db)server

6.&!

pattern前面加一个&符号表示取交集

pattern1:&pattern2
#要求同时存在于pattern1和pattern2中的主机

pattern前面加一个!符号表示排除

pattern1:!pattern2
#要求出现在pattern1中但未出现在pattern2中

tasks 任务列表和 gather_facts

http://ansible.com.cn/docs/playbooks_intro.html#tasks

tasks指令用来指定这个play中包含的任务,可以是一个或多个任务,任务也需要放在play的数组中,所以tasks指令内使用- xxx:的方式来表示每一个任务

gather_facts是一个play级别的指令设置,它是一个负责收集目标主机信息的任务,由setup模块提供。默认情况下,每个play都会先执行这个特殊的任务,收集完信息之后才开始执行其它任务。但是,收集目标主机信息的效率很低,如果能够确保playbook中不会使用到所收集的信息,可以显式指定gather_facts: no来禁止这个默认执行的收集任务,这对效率的提升是非常可观的。

此外每个play和每个task都可以使用name指令来命名,也建议尽量为每个play和每个task都命名,且名称具有唯一性。

例如:

---
- hosts: nginx
  gather_facts: false

  tasks: 
    - copy: src=/etc/passwd dest=/tmp

加上name

---
- name: first play
  hosts: nginx
  gather_facts: false

  tasks: 
    - name: copy /etc/passwd to /tmp
      copy: src=/etc/passwd dest=/tmp

每一个task必须有一个名称name,这样在运行playbook时,能从输出信息中明确辨别是属于哪一个task的,如果没有定义 nameaction的值将会用作输出信息中标记特定的task

tasks:
  - name: create new file
    file: path=/tmp/test01.txt state=touch
  - name: create new user
    user: name=tom state=present

每一个playbook中可以包含一个或者多个tasks任务列表,每一个tasks中多个动作组合完成具体的一件事,,在hosts中定义的主机或者主机组都将会执行这个被定义的tasks

示例:

---
- name: play to setup web server
  hosts: webservers
  tasks:
  - name: latest httpd version installed
    yum:
      name: httpd
      state: latest
      
  - name: service is enabled
    service:
      name: httpd
      enabled: true

Handlers与Notify

http://ansible.com.cn/docs/playbooks_intro.html#handlers

官方文档资料更全:https://docs.ansible.com/ansible/latest/user_guide/playbooks_handlers.html

handler,用来执行某些条件下的任务,比如当配置文件发生变化的时候,通过notify触发处理程序去重启服务。

注意:

  • handlers和tasks是同级别的
  • notify在tasks的下级
  • notify可以指定handlers的name或者listen(一般用name)
  • meta: flush_handlers 想立即执行所有的 handler 命令

示例:

当apache配置文件发生改变时,notify语句显示调用处理程序restart apache

---
- hosts: localhost
  tasks:
    - name: copy configuratioon
      template:
        src: /root/httpd.conf
        dest: /etc/httpd/conf/httpd.conf
      notify:
          - restart apache

  handlers:
    - name: restart apache
      service:
        name: httpd
        state: restarted

上面的例子中,restart apache处理程序只有在template任务通知已发生更改时才会触发。一个任务可以在其notify部分中调用多个处理程序。Ansible将notify语句视为数组,并且迭代处理程序名称:

---
- hosts: localhost
  tasks:
    - name: copy configuratioon
      template:
        src: /root/httpd.conf
        dest: /etc/httpd/conf/httpd.conf
      notify:
          - restart apache
          - restart redis

  handlers:
    - name: restart apache
      service:
        name: httpd
        state: restarted

    - name: restart redis
      service:
        name: redis
        state: restarted

多个paly

Playbook是一个YAML文件,含有由一个或多个play组成的列表。记住一个play按顺序列出了要对清单中的选定主机执行的任务。因此,如果一个playbook中有多个play,每个play可以将其任务应用到单独的一组主机。

在编排可能涉及对不同主机执行不同任务的复杂部署时,这会大有帮助。我们可以这样进行编写:对一组主机运行一个play,完成后再对另一组主机运行另一个play。

缩写包含多个play的playbook非常简单。Playbook中的各个play编写为playbook中的顶级列表项。各个play是含有常用play关键字的列表项。

以下示例显示了含有两个play的简单playbook。第一个play针对10.11.114.65运行,第二个play则针对10.11.114.66运行。

---
# This is a simple playbook with two plays

- name: first play
  hosts: 10.11.114.65
  tasks:
    - name: first task
      yum:
        name: httpd
        status: present

    - name: second task
      service:
        name: httpd
        enabled: true

- name: second play
  hosts: 10.11.114.66
  tasks:
    - name: first task
      service:
        name: mariadb
        enabled: true

playbook中模块的参数传递

用 copy模块做示例:

tasks: 
  - name: copy /etc/passwd to /tmp
    copy: src=/etc/passwd dest=/tmp

键值对形式和使用arg传递

---
- name: first play
  hosts: nginx
  gather_facts: false
  tasks: 
    - name: copy1
      copy: 
        src: /etc/passwd
        dest: /tmp

    - name: copy2
      copy: 
      args:
        src: /etc/passwd
        dest: /tmp

怎样书写都可以,但要追求简洁、易读、美观

关于变量的简单使用

一些简单的变量使用,不包含jinja和条件控制等

定义变量

子母数字下划线组成,子母开头。

可以在Ansible项目中的多个位置定义变量。不过,这些变量大致可简化为三个范围级别:

  • 全局范围:从命令行或Ansible配置设置的变量
  • Play范围:在play和相关结构中设置的变量
  • 主机范围:由清单、事实收集或注册的任务,在主机组和个别主机上设置的变量

更细的也可以在任务中取细分,但都遵循范围越小,优先级越高。

如果在多个相同名称的变量,则采用优先级别最高的变量。窄范围优先于更广泛的范围:由清单定义的变量将被playbook定义的变量覆盖,后者将被命令行中定义的变量覆盖。

如从inventory、role、play、task、block、facts的信息变量等等

命令行传入指定变量

执行playbook时-e传入变量,属于全局的变量,在整个playbook都可以使用。

清单变量可被playbook中设置的变量覆盖,这两种变量又可通过在命令行中传递参数到ansible或ansible-playbook命令来覆盖。在命令行上设置的变量称为额外变量。

当需要覆盖一次性运行的playbook的变量的已定义值时,额外变量非常有用。

# 定义单个变量
[root@ansible ~]# ansible-playbook -e 'var1="value1"' xxx.yml

# 定义多个变量
[root@ansible ~]# ansible-playbook -e 'var1="value1" var2="value2"' xxx.yml

# 引入单个变量文件
[root@ansible ~]# ansible-playbook -e '@varfile1.yml' xxx.yml

# 引入多个变量文件
[root@ansible ~]# ansible-playbook -e '@varfile1.yml' -e '@varfile2.yml' xxx.yml

小示例:

[root@ansible ~]# cat var.yaml
---
- hosts: all
  tasks:
    - name: install pkg
      yum: name={{ pkg }}



[root@ansible ~]# ansible-playbook -e "pkg=wget" var.yaml

hosts中主机和主机组变量(inventory变量)

也就是在清单文件中定义变量

直接应用于主机的清单变量分为两在类:

  • 主机变量,应用于特定主机
  • 组管理,应用于一个主机组或一组主机组中的所有主机

主机变量优先于组变量,但playbook中定义的变量的优先级比这两者更高。

若要定义主机变量和组变量:

一种方法是直接在清单文件中定义

还有一种是和前面清单文件一样,用目录的形势

清单文件中定义

由于默认配置在/etc/ansible/hosts文件中,

示例:

# 编辑hosts文件定义变量
[root@ansible ~]# vim /etc/ansible/hosts
[apache]
10.11.114.65 webdir=/opt/test     #定义单个主机的变量
10.11.114.66

[apache:vars]      #定义整个组的统一变量
webdir=/web/test

[nginx]
10.100.21.1[1:5]
[nginx:vars]
webdir=/opt/web


# 编辑playbook文件
[root@ansible ~]# cat variables.yml 
---
- hosts: all
  tasks:
    - name: create webdir
      file: name={{ webdir }} state=directory   #引用变量


# 执行playbook
[root@ansible ~]# ansible-playbook variables.yml

分文件定义 Host 和 Group 变量

http://ansible.com.cn/docs/intro_inventory.html#host-group

例子:

[root@ansible project]# pwd
/root/project
[root@ansible project]# tree
.
├── group_vars
│   └── webserver
├── host_vars
│   └── nginx
├── inventory
└── playbook.yaml

[root@ansible project]# cat ~/project/group_vars/webserver
dir_name: /tmp/webserver_test
[root@ansible project]# cat ~/project/host_vars/nginx
dir_name: /tmp/nginx_test
[root@ansible project]# cat ~/project/inventory
[webserver]
nginx ansible_ssh_host=10.11.114.65
apache ansible_ssh_host=10.11.114.66
tomcat ansible_ssh_host=10.11.114.67

[webserver:vars]
ansible_ssh_port=22
ansible_ssh_user=root
ansible_ssh_pass=123456


#playbook中,变量位于开头元素时一定要加引号,除非是下面这种等号方式
[root@ansible project]# cat ~/project/playbook.yaml
---
- hosts: all
  gather_facts: false
  tasks:
    - name: create dir
      file:
#      file: name={{ dir_name }} state=directory 
        path: "{{ dir_name }}"
        state: directory

#nginx主机中将会创建/tmp/nginx_test,其他则创建/tmp/webserver_test
[root@ansible project]# ansible-playbook -i inventory playbook.yaml

playbook中使用变量

编写playbook时,可以定义自己的变量,然后在任务中调用这些值。例如,名为web_package的变量可以使用值httpd来定义。然后,任务可以使用yum模块调用该变量来安装httpd软件包。

Playbook变量可以通过多种方式定义。一种常见的方式是将变量放在playbook开头的vars块中:

- hosts: all
  vars:
    user: joe
    home: /home/joe

也可以在外部文件中定义playbook变量。此时不使用playbook中的vars块,可以改为使用vars_files指令,后面跟上相对于playbook位置的外部变量文件名称列表:

- hosts: all
  vars_files:
    - vars/users.yml

而后,可以使用YAML格式在这一/这些文件中定义playbook变量:

user: joe
home: /home/joe

示例:

引用变量就是将变量名放在双大括号内。在任务执行时,Ansible会将变量替换为其值。

---
- hosts: all
  gather_facts: false
  vars:
    user: tom
  tasks:
    - name: create a user
      user:
        name: "{{ user }}"

注意:当变量用作开始一个值的第一元素时,必须使用引号。这可以防止Ansible将变量引用视为YAML字典的开头。

使用来自gather_facts的变量

http://ansible.com.cn/docs/playbooks_variables.html?highlight=facts#facts

https://www.cnblogs.com/shipment/p/15555335.html#setup模块

也就是使用setup模块的变量

示例:

---
- hosts: all
  tasks:
    - name: create a file
      file: name={{ ansible_hostname }}.txt state=touch

playbook中标签的使用

playbook中可以对每个任务集打标签(tags)

执行的时候可以通过-t选择指定标签执行

还可以通过--skip-tags选择除了某个标签外全部执行等。

示例:

---
- hosts: localhost
  tasks:
    - name: task1
      file: name=t1.txt state=touch
      tags: t1
    - name: task2
      file: name=t2.txt state=touch
      tags: t2



#只运行t1标签的tasks
[root@ansible ~]# ansible-playbook -t t1 var.yaml

运行方式和执行策略

-f并发连接数

在配置文件中有一项配置forks,默认值是5

[root@ansible ~]# grep 'fork' /etc/ansible/ansible.cfg
#forks          = 5

forks的值也代表了最多有几个节点同时执行任务,每个节点默认对应一个ansible-playbook进程和ssh进程,

默认的forks是5,运行一个playbook对10台主机操作,查看一下进程

[root@ansible ~]# pstree -c | grep ansible
        |      |-sshd-+-bash---ansible-playboo-+-ansible-playboo---sshpass---ssh
        |      |      |                        |-ansible-playboo---sshpass---ssh
        |      |      |                        |-ansible-playboo---sshpass---ssh
        |      |      |                        |-ansible-playboo---sshpass---ssh
        |      |      |                        |-ansible-playboo---sshpass---ssh
        |      |      |                        `-{ansible-playboo}


#有6个进程,其中父进程是ansible的主控进程,负责监控fork出的子进程
#如果由于连接失败或者执行某个任务失败,则该节点不会执行当前paly的后续任务,但是仍会执行后续play

也就是说:

forks=5是指同时有5个节点执行任务,可能有的节点执行的快,有的执行的慢。比如上例的10个节点,第一批同时执行5个,当某个执行的快,已经完成返回退出时。ansible的主控进程会在开一个进程,让第6个主机执行,也不是去等待其它节点执行完毕。始终保持有5个进程在工作。

posted @ 2021-11-19 09:34  EverEternity  阅读(278)  评论(0编辑  收藏  举报