Asible的脚本 ---playbook剧本

Posted 爱show的小卤蛋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Asible的脚本 ---playbook剧本相关的知识,希望对你有一定的参考价值。

文章目录

一: playbooks 简述

1.1 什么是playbooks

Playbooks 是 Ansible的配置,部署,编排语言.他们可以被描述为一个需要希望远程主机执行命令的方案,或者一组IT程序运行的命令集合.

在基础层面, playbooks 可以被用来管理用于部署到远程主机的配置文件.在更高的层面上,playbooks 可以依次对多层式架构上的服务器执行上线包括滚动更新在内的操作并可以将操作委托给其他主机包括在此过程中发生的与监视服务器,负载均衡服务器的交互操作在内.

Playbooks 的格式是YAM,语法做到最小化,意在避免 playbooks 成为一种编程语言或是脚本,但它也并不是一个配置模型或过程的模型.

playbook 由一个或多个 ‘plays’ 组成.它的内容是一个以 ‘plays’ 为元素的列表.

在 play 之中,一组机器被映射为定义好的角色.在 ansible 中,play 的内容,被称为 tasks,即任务.在基本层次的应用中,一个任务是一个对 ansible 模块的调用,这在前面章节学习过.

‘plays’ 好似音符,playbook 好似由 ‘plays’ 构成的曲谱,通过 playbook,可以编排步骤进行多机器的部署,比如在 webservers 组的所有机器上运行一定的步骤, 然后在 database server 组运行一些步骤,最后回到 webservers 组,再运行一些步骤,诸如此类.

“plays” 算是一个体育方面的类比,你可以通过多个 plays 告诉你的系统做不同的事情,不仅是定义一种特定的状态或模型.你可以在不同时间运行不同的 plays.


1.2 playbooks的组成

playbooks本身由以下各部分组成

  1. Tasks:任务,即通过task调用ansible的模板将多个操作组织在一个playbook中运行
  2. Variables:变量
  3. Templates:模板
  4. Handlers:处理器,当changed状态条件满足时, (notify)触发执行的操作
  5. Roles:角色


1.3 playbooks剧本示例

1.3.1 先修改/etc/ansible/hosts主机清单

[root@host103 ~]# vim /etc/ansible/hosts
...
#将主机ip或者主机名(要做主机名映射) 加入webservers组
[webservers]
192.168.23.105
....

1.3.2 编写安装并启动httpd服务的剧本

#剧本名应该是 .yml 或者 .yaml 结尾
[root@host103 opt]# vim daemon1.yaml
---                          #yaml文件以 三个短横杠--- 开头,表明是一个yaml文件。可以省略
- name: first palybook       # 定义一个play 的名称。可以省略
  gather_facts: false        #设置不进行facts信息收集。这样可以加快执行速度。默认是true。可以省略
  hosts: webservers          #指定要执行任务的被管理主机,多个主机组用冒号: 隔开。
  remote_user: root          #指定被管理主机上执行任务的用户
  tasks:                     #定义任务列表。任务列表中的各个任务安次序在hosts定义的主机上执行
    - name: test connection  #自定义任务名称。可以省略。但是为了排查,不建议省略
      ping:                  #使用module: [options] 格式来定义任务。这里使用ping 模块

    - name: disable selinux
      command: '/sbin/setenforce 0'    #command和shell 模块无需使用key=value的模式。引号可省
      #忽略失败任务。如果命令返回值不为0,就会报错,退出playboos。所以使用此选项,忽略失败的任务
      ignore_errors: true   

    - name: disable firewalld
      #使用module: options的格式来定义任务.option 使用key=value的格式
      service: name=firewalld state=stopped enabled=no

    - name: install apache
      yum: name=httpd state=latest

    - name: start apache service
      service: name=httpd state=started enabled=yes

    - name: modify httpd configuration file
      #需要事先准备好/opt/httpd.conf文件
      copy: src=/opt/httpd.conf dest=/etc/httpd/conf/httpd.conf
      #设置触发器。如果以上操作后为change状态,会通过notify 指定的名称触发对应名称的handlers操作
      notify: "restart httpd"

  #handlers中定义的就是任务
  handlers:
   #设置任务名。应该和notify指定的任务名相同
    - name: restart httpd
      #设置执行的动作。这里使用service模块,执行重启httpd的动作
      service: name=httpd state=restarted

#Ansible在执行完某个任务之后并不会立即去执行对应的handler,而是在当前play中所有普通任务都执行完后再去执行handler,这样的好处是可以多次触发notify,但最后只执行一次对应的handler,从而避免多次重启。


1.3.3 准备/opt/httpd.conf文件

root@host103 opt]# vim httpd.conf
#配置监听端口和域名(正常是使用80端口)
42// Listen 8080
95// ServerName www.mynet.com:8080


1.4 执行ploybooks剧本

格式:

ansible-playbook 剧本名


补充参数:

-k (-ask-pass):用来交互输入ssh密码

-K (-ask-become-pass):用来交互输入sudo密码
-u :指定用户

#管理主机执行playbooks剧本
[root@host103 opt]# ansible-playbook demo1.yaml

#访问测试
[root@host105 ~]# firefox http://192.168.23.105:8080



1.5 检查yaml文件,和指定task开始运行

1.5.1 yaml 文件语法检查

#检查yaml 文件的语法是否正确
[root@host103 opt]# ansible-playbook demo1.yaml --syntax-check

#检查task任务
[root@host103 opt]# ansible-playbook demo1.yaml --list-task

#检查生效的主机
[root@host103 opt]# ansible-playbook demo1.yaml --list-hosts


1.5.2 指定从某个task开始运行

#将192.168.23.105 主机的firewalld服务开启
[root@host105 ~]# systemctl  start firewalld
[root@host105 ~]# systemctl  is-active firewalld
active
#192.168.23.105 停止httpd服务,并移除httpd
[root@host105 ~]# systemctl  stop httpd
[root@host105 ~]# yum -y remove httpd


#先查看有哪些tasks
[root@host103 opt]# ansible-playbook demo1.yaml --list-task
playbook: demo1.yaml

  play #1 (webservers): first palybook	TAGS: []
    tasks:
      test connection	TAGS: []
      disable selinux	TAGS: []
      disable firewalld	TAGS: []
      install apache	TAGS: []
      start apache service	TAGS: []
      modify httpd configuration file	TAGS: []

#指定从 install apache 这个任务开始执行
[root@host103 opt]# ansible-playbook demo1.yaml  --start-at-task='install apache'


#此时,192.168.23.105主机,重新安装了httpd,并启动了服务
[root@host105 ~]# rpm -q httpd
httpd-2.4.6-67.el7.centos.x86_64
[root@host105 ~]# systemctl is-active  httpd
active

#由于disale firewalld任务在install apache 任务之前,因此,没有被执行
[root@host105 ~]# systemctl is-active  firewalld
active


二: 定义和引用变量

2.1 定义变量

2.1.1 合法的变量名

在使用变量之前最好先知道什么是合法的变量名==. 变量名可以为字母,数字以及下划线.变量始终应该以字母开头.==

“foo_port”是个合法的变量名.”foo5”也是.

“foo-port”, “foo port”, “foo.port” 和 “12”则不是合法的变量名.


2.1.2 playbook中定义变量的格式

可以使用vars定义变量,多个变量应该组成列表的格式。变量的格式为key: value的格式

- hosts: dbservers
  vars:
    - groupname: mysql  #定义变量groupname,值为mysql
    - username: nginx   #定义变量username,值为nginx


2.2 使用变量

Ansible允许你使用Jinja2模板系统在playbook中引用变量.借助Jinja你能做很多复杂的操作,

2.2.1 变量的替换

#在简单的模板中可以如下操作,这也是变量替换的最基本形式
My amp goes to {{ max_amp_value }}

#在playbook中替换:
template: src=foo.cfg.j2 dest={{ remote_install_path }}/foo.cfg

#在上述的例子中,我们使用变量来决定文件放置在哪里. 
#在模板中你自动会获取在主机范围之内的所有变量的访问权


2.3 yaml陷阱

Ansible 使用 “{{ var }}” 来引用变量. 如果一个值以 “{” 开头, YAML 将认为它是一个字典, 所以我们必须引用它, 像这样:foo: “{{ variable }}”

#错误的例子
- hosts: app_servers
  vars:
      app_path: {{ base_path }}/22
      
#正确的例子
- hosts: app_servers
  vars:
       app_path: "{{ base_path }}/22"


2.4 使用Facts获取的信息

还有其它地方可以获取变量,这些变量是自动发现的,而不是用户自己设置的.

Facts通过访问远程系统获取相应的信息. 一个例子就是远程主机的IP地址或者操作系统是什么. 使用以下命令可以查看哪些信息是可用的:

ansible hostname -m setup

[root@host103 opt]# ansible webservers -m setup
[root@host103 opt]# ansible webservers -m setup -a 'filter=*ipv4'


2.5 在命令行定义变量

在命令行,可以使用 -e 指定变量。但是会覆盖playbooks里的变量

[root@host103 opt]#ansible-playbook demo1 -e "username=nginx"


2.6 ansible提供的变量

Ansible会自动提供给你一些变量,即使你并没有定义过它们.这些变量中重要的有 ==‘hostvars’,’group_names’,和 ‘groups’.==由于这些变量名是预留的,所以用户不应当覆盖它们. ‘environmen’ 也是预留的.

2.6.1 hostvars变量

hostvars可以让你访问其它主机的变量,包括哪些主机中获取到的facts.如果你还没有在当前playbook或者一组playbook的任何play中访问那个主机,那么你可以获取变量,但无法看到facts值. 如果数据库服务器想使用另一个节点的某个 ‘fact’ 值,或者赋值给该节点的一个inventory变量.可以在一个模板中甚至命令行中轻松实现:

{{ hostvars['test.example.com']['ansible_distribution'] }}

2.6.2groups变量

group_names 是当前主机所在所有群组的列表(数组).所以可以使用Jinja2语法在模板中根据该主机所在群组关系(或角色)来产生变化:

{% if 'webserver' in group_names %}
   # some part of a configuration file that only applies to webservers
{% endif %}

2.6.3 groups 变量

groups 是inventory中所有群组(主机)的列表.可用于枚举群组中的所有主机.例如:

{% for host in groups['app_servers'] %}
   # something that applies to all app servers.
{% endfor %}

一个经常使用的范式是找出该群组中的所有IP地址:

{% for host in groups['app_servers'] %}
   {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}


2.7 变量文件的分隔

把playbook置于源代码管理之下是个很好的注意,当你可能会想把playbook源码公开之余还想保持某些重要的变量私有.有时你也想把某些信息放置在不同的文件中,远离主playbook文件.

你可以使用外部的变量文件来实现:

---
- hosts: all
  remote_user: root
  vars:
    favcolor: blue
  vars_files:
    - /vars/external_vars.yml

  tasks:

  - name: this is just a placeholder
    command: /bin/echo foo

这可以保证你共享playbook源码时隔离敏感数据的风险.

每个变量文件的内容是一个简单的YAML文件,如下所示:

---
# in the above example, this would be vars/external_vars.yml
somevar: somevalue
password: magic


2.8 变量的优先级

* extra vars (在命令行中使用 -e)优先级最高
* 然后是在inventory中定义的连接变量(比如ansible_ssh_user)
* 接着是大多数的其它变量(命令行转换,play中的变量,included的变量,role中的变量等)
* 然后是在inventory定义的其它变量
* 然后是由系统发现的facts
* 然后是 "role默认变量", 这个是最默认的值,很容易丧失优先权


2.9playbook变量使用示例

[root@host103 opt]# vim demo2.yaml 
- name: second playbook
  #启动facts,默认为true(或者yes).如果想要关闭,使用false或者no
  gather_facts: yes
  hosts: webservers
  remote_user: root
  #定义变量。groupname变量,值为test ; username变量,值为zhangsan
  vars:
    - groupname: test
    - username: zhangsan
  tasks:
    - name: create group
      #引用变量groupname
      group: name={{groupname}} system=yes gid=1024

    - name: create user for group
      #引用变量username和groupname
      user: name={{username}} system=yes uid=9527 group={{groupname}}

    - name: test copy file
     #从facts里引用变量。但是要求facts必须是开启.
     #ansible_defaule_ipv4为其中的一个key。
      copy: content="{{ansible_default_ipv4}}" dest=/opt/test.txt


#执行剧本
[root@host103 opt]# ansible-playbook demo2.yaml


#在192.168.23.103 上使用ansible 的setup模块,并使用filter过滤键值对信息
[root@host103 opt]# ansible webservers -m setup -a 'filter=ansible_default_ipv4'

#在192.168.23.105主机查看zhangsan用户是否生成,组是否是test
[root@host105 opt]# id zhangsan
#查看/opt/test.txt
[root@host105 opt]# cat /opt/test.txt


三 指定远程主机sudo切换用户

---
- hosts: dbservers
  remote_user: zhangsan            
  become: yes	                 #2.6版本以后的参数,之前是sudo,意思为切换用户运行
  become_user: root              #指定sudo用户为root

如果你需要在使用 sudo 时指定密码,可在运行 ansible-playbook 命令时加上选项 --ask-sudo-pass (-K). 如果使用 sudo 时,playbook 疑似被挂起,可能是在 sudo prompt 处被卡住,这时可执行 Control-C 杀死卡住的任务,再重新运行一次.

当使用 sudo_user 切换到 非root 用户时,模块的参数会暂时写入 /tmp 目录下的一个随机临时文件. 当命令执行结束后,临时文件立即删除.这种情况发生在普通用户的切换时,比如从 ‘bob’ 切换到 ‘timmy’, 切换到 root 账户时,不会发生,如从 ‘bob’ 切换到 ‘root’,直接以普通用户或root身份登录也不会发生. 如果你不希望这些数据在短暂的时间内可以被读取(不可写),请避免在 sudo_user 中传递未加密的密码. 其他情况下,’/tmp’ 目录不被使用,这种情况不会发生.Ansible 也有意识的在日志中不记录密码参数.


四:when 条件判断

在Ansible中,提供的唯一一个通用的条件判断是when指令,当when指令的值为true时,则该任务执行,否则不执行该任务。

when一个比较常见的应用场景是实现跳过某个主机不执行任务或者只有满足条件的主机执行任务

4.1 跳过主机,或者让满足条件的主机执行任务

vim demo3.yaml
---
- hosts: all
  remote_user: root
  tasks:
   - name: reboot
     command: /usr/sbin/shutdown -r now
     #when指令中的变量名不需要手动加上 {{}}
     #次任务效果为:当主机位192.168.23.105时,执行/usr/sbin/shutdown -r now命令,重启主机
     when: ansible_default_ipv4.address == "192.168.23.105"      
或 
     when: inventory_hostname == "<主机名>"
	
	
ansible-playbook demo3.yaml


4.2 迭代

Ansible提供了很多种循环结构,一般都命名为with_items,作用等同于 loop 循环。

[root@host103 opt]# vim demo4.yaml
---
- name: play4
  hosts: webservers
  gather_facts: false
  tasks:
    - name: create directories
      #创建目录。目录名使用with_items里的循环
      file:
      #由于值是{{....}} ,所以为了防止被认为是字典,要加上双引号.
        path: "{{item}}"
        state: directory
      #with_items循环,等同于loop循环  
      with_items:
        - /opt/zhangsan
        - /opt/lisi
    - name: add users
     #使用循环创建用户,并添加附加组。
      user:
       #以下两种方法都可以调用with_iptems里的值
        name: "{{item.name}}"
        groups: "{{item['test_groups']}}"
      
      with_items:
        #以下两种方法都可以
        - name: test01
          test_groups: aaa
        - {name: test02, test_groups: bbb}                                         

#在管理机上运行剧本
[root@host103 opt]# ansible-playbook demo4.yaml 

#在被管理主机上查看执行结果
[root@host105 opt]# ls 
lisi  zhangsan
[root@host105 opt]# id test01
uid=9528(test01) gid=9528(test01)=9528(test01),2222(aaa)
[root@host105 opt]# id test02
uid=9529(test02) gid=9529(test02)=9529(test02),3333(bbb


五 templates模块

Jinja是基于Python的模板引擎。Template类是Jinja的一个重要组件,可以看作是一个编译过的模板文件,用来产生目标文本,传递Python的变量给模板去替换模板中的标记。

5.1 准备模板

准备一个一 .j2 为后缀的template模板文件,设置引用的变量

[root@host103 opt]# cp httpd.conf httpd.conf.j2
[root@host103 opt]# vim httpd.conf.j2 
 #42//设置监听端口为变量。引用变量http_port的值
 42// Listen {{http_port}}
 #95// 设置域名为引用变量server_name的值
 95 ServerName {{server_name}}
 #119// 设置网站根目录为引用root_dir的值
 119// DocumentRoot "{{root_dir}}"
 #131// 配置目录访问权限
 131// <Directory "{{root_dir}}">


5.2 修改主机清单文件

修改主机清单文件,使用主机变量定义一个变量名相同,而值不同的变量

[root@host103 opt]# vim /etc/ansible/hosts

#使用主机变量,对变量进行单个的定义
[webservers]
192.168.23.105 http_port=192.168.23.105:80 server_name=www.mynet.com:80 root_dir=/opt/mynet

[dbservers]
192.168.23.106 http_port=192.168.23.106:8080 server_name=www.zhi.com:8080 root_dir=/opt/zhi


5.3 编写playbook

---
- name: for diff apache
  hosts: webservers dbservers
  remote_user: root
  vars:
    - Package: httpd
    - Service: httpd
    - mynet: /opt/mynet
    - zhi: /opt/zhi
  tasks:
    - name: install httpd
      yum: name={{Package}} state=latest

    - name: install configure file
      template: src=/opt/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
      notify:
        - restart httpd

    - name: create root dir for mynet
      file: path={{mynet}} state=directory
      when: ansible_default_ipv4.address == "192.168.23.105"

    - name: for mynet index.html
      copy: content='this is mynet.com' dest={{mynet}}/index.html
      when: ansible_default_ipv4.address == "192.168.23.105"

    - name: create root dir for zhi
      file: path={{zhi}} state=directory
      when: ansible_default_ipv4.address == "192.168.23.106"

    - name: for zhi index.html
      copy: content='this is zhi.com' dest={{zhi}}/index.html
      when: ansible_default_ipv4.address == "192.168.23.106"

    - name: start httpd
      service: name={{Service}} enabled=true state=started
  handlers:
    - name: restart httpd
      service: name={{Service}} state=restarted
  
  
  
  [root@host103 opt]# ansible-playbook demo5.yaml

#访问测试
[root@host103 opt]# curl http://192.168.23.105:80
this is mynet.com 

[root@host103 opt]# curl http://192.168.23.106:8080
this is zhi.com


六: tags模块

可以在一个playbook中为某个或某些任务定义“标签”,在执行此playbook时通过ansible-playbook命令使用–tags选项能实现仅运行指定的tasks。

playbook还提供了一个特殊的tags为always。作用就是当使用always作为tags的task时,无论执行哪一个tags时,定义有always的tags都会执行。

6.1 指定tags标签执行

[root@host104 opt]# vim webhosts.yaml
---
- name: for test tags
  hosts: webservers
  remote_user: root
  tasks:
    - name: copy hosts file
      copy: src=/etc/hosts dest=/opt/hosts
      #使用tags设置标签
      tags:
        - only
    - name: touch file
      file: path=/opt/testhost state=touch
 
#使用--tags指定标签。 
[root@host104 opt]# ansible-playbook webhosts.yaml --tags="only"

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ciP4sjyg-1635247577641)(image-20211025111004200.png)]

#在被管理主机上查看
[root@host105 opt]# ls
hosts  


6.2 使用always标签

playbook还提供了一个特殊的tags为always。作用就是当使用always作为tags的task时,无论执行哪一个tags时,定义有always的tags都会执行。

#先将被管理主机上的复制文件hosts删除
[root@host105 opt]# rm -rf hosts

[root@host104 opt]# vim webhosts.yaml
---
- name: for test tags
  hosts: webservers
  remote_user: root
  tasks:
    - name: copy hosts file
      copy: src=/etc/hosts dest=/opt/hosts
      tags:
        - only
    - name: touch file
      file: path=/opt/testhost state=touch
      #添加tags标签always
      tags:
        - always
        
 #依旧是指定标签执行       
[root@host104 opt]# ansible-playbook webhosts.yaml --tags="only"


[root@host105 opt]# ls
hosts  testhost


七:role 模块

Ansible为了层次化、结构化地组织Playbook,使用了角色(roles),roles可以根据层次型结构自动装载变量文件、task以及handlers等。简单来讲,roles就是通过分别将变量、文件、任务、模块及处理器放置于单独的目录中,并可以便捷地include它们。roles一般用于基于主机构建服务的场景中,但也可以用于构建守护进程等场景中。

7.1 roles的目录结构

[root@host104 ansible]# cd /etc/ansible/
[root@host104 ansible]# tree roles/
#创建roles下创建的第一层目录为角色目录。
#角色目录里需要包含files,templates,tasks,handlers,vars,defaults,meta目录
roles/
├── web/
│   ├── files/
│   ├── templates/
│   ├── tasks/
│   ├── handlers/
│   ├── vars/
│   ├── defaults/
│   └── meta/
└── db/
    ├── files/
    ├── templates/
    ├── tasks/
    ├── handlers/
    ├── vars/
    ├── defaults/
    └── meta/


7.2 roles 目录的含义解释

  • files

    • 用来存放由 copy 模块或 script 模块调用的文件。
  • templates

    • 用来存放 jinjia2 模板,template 模块会自动在此目录中寻找 jinjia2 模板文件。
  • tasks

    • 此目录应当包含一个 main.yml 文件,用于定义此角色的任务列表,此文件可以使用 include 包含其它的位于此目录的 task 文件。
  • handlers

    • 此目录应当包含一个 main.yml 文件,用于定义此角色中触发条件时执行的动作。
  • vars

    • 此目录应当包含一个 main.yml 文件,用于定义此角色用到的变量。
  • defaults

    • 此目录应当包含一个 main.yml 文件,用于为当前角色设定默认变量。
  • meta

    • 此目录应当包含一个 main.yml 文件,用于定义此角色的特殊设定及其依赖关系。


7.3 在一个playbook中使用roles的步骤

1 创建以roles命名的目录

#使用yum 安装的默认就有
mkdir -p /etc/ansible/rules

以上是关于Asible的脚本 ---playbook剧本的主要内容,如果未能解决你的问题,请参考以下文章

Ansible作为python模块库使用

什么叫做脚本?如何使用脚本?再如如何制作脚本?

自动化脚本之共享脚本

linux shell脚本被包含是什么意思?.命令和source命令(在脚本中运行脚本,脚本中调用脚本)(脚本包含,父子脚本)

linux shell脚本被包含是什么意思?.命令和source命令(在脚本中运行脚本,脚本中调用脚本)(脚本包含,父子脚本)

什么是脚本语言脚本语言的优缺点