ansible入门讲解--playbook(二)

Ansible playbook简介

playbook是ansible用于配置、部署和管理被控节点的剧本。

通过playbook的详细描述,执行其中的一系列tasks,可以让远端主机达到预期的状态。playbook就像Ansible控制器给被控节点列出的一系列to-do-list,而被控节点必须要完成。

也可以这么理解,playbook字面意思,即剧本,现实中有演员按照剧本表演,在Ansible中,这次有计算机进行表演,由计算机安装、部署应用、提供对外服务、以及组织计算机处理各种各样的事情。

Ansible playbook使用场景

执行一些简单的任务,使用ad-hoc命令可以方便的解决问题,但是有时一个设施过于复杂,需要大量操作的时候,执行ad-hoc命令是不合适的,这时最好使用playbook。

就像执行shell命令与写shell脚本一样,也可以理解为批处理任务,不过playbook有自己的语法格式。

使用playbook你可以方便的重用这些代码,可以移植到不同的机器上面,向函数一样,最大的利用代码。在你使用Ansible的过程中,你也会发现,你处理的大部分操作都是编写playbook。可以把常见的应用都编写成playbook,之后管理服务器会变得十分简单。

Ansible playbook格式

1)格式简介

playbook由YMAL语言编写。YAML( /ˈjæməl/ )参考了其他多种语言,包括:XML、C语言、Python、Perl以及电子邮件格式RFC2822,Clark Evans在2001年5月在首次发表了这种语言,另外Ingy döt Net与OrenBen-Kiki也是这语言的共同设计者。
  YMAL格式是类似于JSON的文件格式,便于人理解和阅读,同时便于书写。首先学习了解一下YMAL的格式,对我们后面书写playbook很有帮助。以下为playbook常用到的YMAL格式:

  • 1、文件的第一行应该以 "---" (三个连字符)开始,表明YMAL文件的开始。
  • 2、在同一行中,#之后的内容表示注释,类似于shell,python和ruby。
  • 3、YMAL中的列表元素以”-”开头然后紧跟着一个空格,后面为元素内容。
  • 4、同一个列表中的元素应该保持相同的缩进。否则会被当做错误处理。
  • 5、play中hosts,variables,roles,tasks等对象的表示方法都是键值中间以":"分隔表示,":"后面还要增加一个空格。

下面是一个举例:

---
#安装与运行mysql服务
- hosts: node1
  remote_user: root
  tasks:
  
    - name: install mysql-server package
      yum: name=mysql-server state=present
    - name: starting mysqld service
      service: name=mysql state=started

我们的文件名称应该以.yml结尾,像我们上面的例子就是mysql.yml。其中,有三个部分组成:

  • hosts :使用hosts指示使用哪个主机或主机组来运行下面的tasks,每个playbook都必须指定hosts,hosts也可以使用通配符格式。主机或主机组在inventory清单中指定,可以使用系统默认的/etc/ansible/hosts,也可以自己编辑,在运行的时候加上-i选项,指定清单的位置即可。在运行清单文件的时候,-list-hosts选项会显示哪些主机将会参与执行task的过程中。
    remote_user:指定远端主机中的哪个用户来登入远端系统,在远端系统执行task的用户,可以任意指定,也可以使用sudo,但是用户必须要有执行相应task的权限。
    tasks指定远端主机将要执行的一系列动作。task的核心为Ansible的模块,前面已经提到模块的用法。tasks 包含 name 和要执行的模块,name 是可选的,只是为了便于用户阅读,不过还是建议加上去,模块是必须的,同时也要给予模块相应的参数。
    使用ansible-playbook运行playbook文件,得到如下输出信息,输出内容为JSON格式。并且由不同颜色组成,便于识别。一般而言
  • 绿色代表执行成功,系统保持原样
  • 黄色代表系统代表系统状态发生改变
  • 红色代表执行失败,显示错误输出
    执行有三个步骤:1、收集facts 2、执行tasks 3、报告结果

2)核心元素

Playbook的核心元素:

  • Hosts:主机组;
  • Tasks:任务列表;
  • Variables:变量,设置方式有四种;
  • Templates:包含了模板语法的文本吧文件
  • Handlers:有特定条件出发的任务;

3)基本组件

Playbooks配置文件的基础组件:

    Hosts:运行指定任务的目标主机
    remoute_user:在远程主机上执行任务的用户;
    sudo_user:
    tasks:任务列表
            格式:
            tasks:
              – name: TASK_NAME
               module: arguments
               notify: HANDLER_NAME
               handlers:
              – name: HANDLER_NAME
               module: arguments






    模块,模块参数:

          格式:
            (1) action: module arguments
            (2) module: arguments
            注意:shell和command模块后面直接跟命令,而非key=value类的参数列表;

handlers:任务,在特定条件下触发;接收到其它任务的通知时被触发;

(1) 某任务的状态在运行后为changed时,可通过“notify”通知给相应的handlers;
(2) 任务可以通过“tags“打标签,而后可在ansible-playbook命令上使用-t指定进行调用;

举例

① 定义playbook

[root@server ~]# cd /etc/ansible
---
- hosts: web
  remote_user: root
  tasks:

     - name: install nginx
       yum: name=nginx state=present
     - name: copy nginx.conf
       copy: src=/tmp/nginx.conf dest=/etc/nginx/nginx.conf backup=yes
       notify: reload       #当nginx.conf发生改变时,通知给相应的handlers
       tags: reloadnginx   #打标签
     - name:start nginx service
       service:name=nginx state=started
       tags: startnginx    打标签

  handlers:   #注意,前面没有-,是两个空格
     - name: reload
       service: named=nginx state=restarted   #为了在进程中可以看出来,在此使用重启

② 测试运行结果
写完了以后,我们就可以运行了

[root@server ansible]# ansible-playbook nginx.yml


现在我们可以看看两台机器的端口是否开启:

[root@server ansible]#ansible web -m shell -a 'ss -nutlp|grep nginx'
192.168.37.122 | SUCCESS | rc=0 >>
tcp    LISTEN     0      128       *:80                    *:*                   users:(("nginx",pid=8304,fd=6),("nginx",pid=8303,fd=6))

192.168.37.133 | SUCCESS | rc=0 >>
tcp    LISTEN     0      128       *:80                    *:*                   users:(("nginx",pid=9671,fd=6),("nginx",pid=9670,fd=6))

③ 测试标签
我们在里面已经打上了一个标签,所以可以直接引用标签,但是我们需要先把服务关闭,在来运行剧本并引用标签

[root@server ansible]# ansible web -m shell -a 'systemctl stop nginx'
[root@server ansible]# ansible-playbook nginx.yml -t startnginx


④ 测试notify
我们还做了一个notify,来测试一下:
首先,它的触发条件是配置文件被改变,所以我们去把配置文件中的端口改一下:

[root@server ansible]# vim /tmp/nginx.conf
	listen       8080;

然后我们重新加载一下这个剧本:

发现我们执行的就是reload段以及我们定义的notify部分。
我们来看一看我们的端口号:

[root@server ansible]# ansible web -m shell -a 'ss -ntlp | grep nginx'
192.168.37.122 | SUCCESS | rc=0 >>
LISTEN     0      128          *:8080                     *:*                   users:(("nginx",pid=2097,fd=6),("nginx",pid=2096,fd=6))

192.168.37.133 | SUCCESS | rc=0 >>
LISTEN     0      128          *:8080                     *:*                   users:(("nginx",pid=3061,fd=6),("nginx",pid=3060,fd=6))

可以看出,我们的nginx端口已经变成了8080。

4)vriables部分

上文中,我们说到了variables是变量,有四种定义方法,现在我们就来说说这四种定义方法:

① facts :可直接调用

上一篇中,我们有说到setup这个模块,这个模块就是通过调用facts组件来实现的。我们这里的variables也可以直接调用facts组件。
  具体的facters我们可以使用setup模块来获取,然后直接放入我们的剧本中调用即可。

② 用户自定义变量

我们也可以直接使用用户自定义变量,想要自定义变量有以下两种方式:

  • 通过命令行传入
    ansible-playbook命令的命令行中的-e VARS, --extra-vars=VARS,这样就可以直接把自定义的变量传入。
  • 在playbook中定义变量
    我们也可以直接在playbook中定义我们的变量:
vars:
  - var1: value1
  - - var2: value2

举例

① 定义剧本
我们就使用全局替换把我们刚刚编辑的文件修改一下:

[root@server ansible]# vim nginx.yml


这样一来,我们的剧本就定义完成了。

② 拷贝配置文件
我们想要在被监管的机器上安装什么服务的话,就直接在我们的server端上把该服务的配置文件拷贝到我们的/tmp/目录下。这样我们的剧本才能正常运行。
  
我们就以keepalived服务为例:

[root@server ansible]# cp /etc/keepalived/keepalived.conf /tmp/keepalived.conf

③ 运行剧本,变量由命令行传入

[root@server ansible]# ansible-playbook nginx.yml -e rpmname=keepalived


④ 修改剧本,直接定义变量
同样的,我们可以直接在剧本中把变量定义好,这样就不需要在通过命令行传入了。以后想要安装不同的服务,直接在剧本里把变量修改一下即可。

[root@server ansible]# vim nginx.yml


⑤ 运行定义过变量的剧本
我们刚刚已经把变量定义在剧本里面了。现在我们来运行一下试试看:

[root@server ansible]# ansible-playbook nginx.yml


发现这样也是可以的~

③ 通过roles传递变量

具体的,我们下文中说到 roles 的时候再详细说明.

④ Host Inventory

我们也可以直接在主机清单中定义。
定义的方法如下:

  • 向不同的主机传递不同的变量:
IP/HOSTNAME varaiable=value var2=value2
  • 向组中的主机传递相同的变量:
[groupname:vars]
  variable=value

5)模板 templates

模板是一个文本文件,嵌套有脚本(使用模板编程语言编写)。
jinjia2:Jinjia2是python的一种模板语言,以Djiango的模板语言为原本。
模板支持:

字符串:使用单引号或双引号;
数字:整数,浮点数;
列表:[item1,item2,...]
元组:[item1,item2,...]
字典: [key:value1,key2:value2,...]
布尔型: true/false
算术运算:
    +  -  *  /  //  %  **
比较操作:
    ==  !=  >  >=  <  <=
逻辑运算:
    and  or  not

通常来说,模板都是通过引用变量来运用的。

举例

① 定义模板
我们直接把之前定义的/tmp/nginx.conf改个名,然后编辑一下,就可以定义成我们的模板文件了:

[root@server ansible]# cd /tmp
[root@server tmp]# mv nginx.conf nginx.conf.j2
[root@server tmp]# vim nginx.conf.j2
        worker_processes  {{ ansible_processor_vcpus }};
	listen       {{ nginxport }};

② 修改剧本
我们现在需要去修改剧本来定义变量:

[root@server ansible]# vim nginx.yml


需要修改的部分如图所示。

运行剧本
上面的准备工作完成后,我们就可以去运行剧本了:

[rooot@server ansible]# ansible-playbook nginx.yml -t reloadnginx
PLAY [web] *********************************************************************

TASK [setup] *******************************************************************
ok: [192.168.37.122]
ok: [192.168.37.133]

TASK [copy nginx.conf] *********************************************************
ok: [192.168.37.122]
ok: [192.168.37.133]

PLAY RECAP *********************************************************************
192.168.37.122             : ok=2    changed=0    unreachable=0    failed=0   
192.168.37.133             : ok=2    changed=0    unreachable=0    failed=0 

6)条件测试

  • when语句:在task中使用,jinja2的语法格式。
    举例如下:
tasks:
- name: install conf file to centos7
  template: src=files/nginx.conf.c7.j2
  when: ansible_distribution_major_version == "7"
- name: install conf file to centos6
  template: src=files/nginx.conf.c6.j2
  when: ansible_distribution_major_version == "6"
  • 循环:迭代,需要重复执行的任务;
    对迭代项的引用,固定变量名为"item",而后在task中使用with_items给定要迭代的元素列表;
    要迭代的元素列表;
    举例如下:
tasks:
- name: unstall web packages
  yum: name={{ item }} state=absent
  with_items:
  - httpd
  - php
  - php-mysql

7)字典

ansible playbook还支持字典功能。举例如下:

- name: install some packages
  yum: name={{ item }} state=present
  with_items:
    - nginx
    - memcached
    - php-fpm
- name: add some groups
  group: name={{ item }} state=present
  with_items:
    - group11
    - group12
    - group13
- name: add some users
  user: name={{ item.name }} group={{ item.group }} state=present
  with_items:
    - { name: 'user11', group: 'group11' }
    - { name: 'user12', group: 'group12' }
    - { name: 'user13', group: 'group13' }

8)角色订制:roles

① 简介

对于以上所有的方式有个弊端就是无法实现复用假设在同时部署Web、db、ha 时或不同服务器组合不同的应用就需要写多个yml文件。很难实现灵活的调用。
  
roles 用于层次性、结构化地组织playbook。roles 能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单来讲,roles就是通过分别将变量(vars)、文件(file)、任务(tasks)、模块(modules)及处理器(handlers)放置于单独的目录中,并可以便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中。

② 什么场景下会使用roles

假如我们现在有3个被管理主机,第一个要配置成httpd,第二个要配置成php服务器,第三个要配置成MySQL服务器。我们如何来定义playbook?

第一个play用到第一个主机上,用来构建httpd,第二个play用到第二个主机上,用来构建php,第三个play用到第三个主机上,用来构建MySQL。这些个play定义在playbook中比较麻烦,将来也不利于模块化调用,不利于多次调。比如说后来又加进来一个主机,这个第4个主机既是httpd服务器,又是php服务器,我们只能写第4个play,上面写上安装httpd和php。这样playbook中的代码就重复了。

为了避免代码重复,roles能够实现代码重复被调用。定义一个角色叫websrvs,第二个角色叫phpappsrvs,第三个角色叫dbsrvs。那么调用时如下来调用:

hosts: host1
role:
- websrvs

hosts: host2
role:
- phpappsrvs
        
hosts: host3
role:
- dbsrvs
        
hosts: host4
role:
- websrvs
- phpappsrvs

这样代码就可以重复利用了,每个角色可以被独立重复调用。下面举例说明使用方式。

③ roles实例

假设有3台主机,172.16.7.151主机上安装MySQL,172.16.7.152上安装httpd,172.16.7.153上安装MySQL和httpd。我们建立两个角色websrvs和dbsrvs,然后应用到这几个主机上。

1. 创建roles的必需目录

[root@node1 opt]# mkdir -pv ansible_playbooks/roles/{websrvs,dbsrvs}/{tasks,files,templates,meta,handlers,vars}

每个role下面有个目录叫meta,在里面可以新建文件main.yml,在文件中可以设置该role和其它role之前的关联关系。

2. 配置角色

(1)配置角色websrvs

[root@node1 opt]# cd ansible_playbooks/roles/
[root@node1 roles]# cd websrvs/
[root@node1 websrvs]# ls
files  handlers  meta  tasks  templates  vars
  • a、将httpd配置文件上传到files目录下,我这里假设httpd.conf每台主机都是一样的,实际上应该用模板,先用一样的配置文件举例
[root@node1 websrvs]# cp /etc/httpd/conf/httpd.conf files/
  • b、编写任务列表tasks
[root@node1 websrvs]# vim tasks/main.yml
- name: install httpd package
  yum: name=httpd
- name: install configuration file
  copy: src=httpd.conf dest=/etc/httpd/conf
  tags:
  - conf
  notify:
  - restart httpd
- name: start httpd
  service: name=httpd state=started

  • c、由于上面的tasks中定义了notify,所以要定义handlers
[root@node1 websrvs]# vim handlers/main.yml
- name: restart httpd
  service: name=httpd state=restarted

如果需要定义变量,则在vars目录下创建main.yml文件,在文件中写入变量,以key:value的形式定义,比如:

http_port: 8080

(2)配置角色dbsrvs

[root@node1 roles]# cd dbsrvs/
[root@node1 dbsrvs]# ls
files  handlers  meta  tasks  templates  vars
  • a、将MySQL配置文件上传到files目录下。
  • b、编写任务列表tasks
[root@node1 dbsrvs]# vim tasks/main.yml
- name: install mysql-server package
  yum: name=mysql-server state=latest
- name: install configuration file
  copy: src=my.cnf dest/etc/my.cnf
  tags:
  - conf
  notify:
  - restart mysqld
- name:
  service: name=mysqld enabled=true state=started
  • c、定义handlers
[root@node1 dbsrvs]# vim handlers/main.yml
- name: restart mysqld
  service: name=mysqld state=restarted

定义playbook
【注意】:要在roles目录同级创建playbook。

[root@node1 ansible_playbooks]# vim web.yml
- hosts: 172.16.7.152
  roles:
  - websrvs 

[root@node1 ansible_playbooks]# vim db.yml
- hosts: 172.16.7.151
  roles:
  - dbsrvs 

[root@node1 ansible_playbooks]# vim site.yml
- hosts: 172.16.7.153
  roles:
  - websrvs
  - dbsrvs 

运行:

[root@node1 ansible_playbooks]# ansible-playbook web.yml
[root@node1 ansible_playbooks]# ansible-playbook db.yml
[root@node1 ansible_playbooks]# ansible-playbook site.yml

当然也可以把这些内容写入同一个playbook中。playbook的名字可以自定义。
注:本文来源于网络

posted @ 2020-05-11 22:06  我不是教主  阅读(549)  评论(0编辑  收藏  举报