自动化运维工具ansible 3playbook

1 介绍

1.1 简介

Playbook是由一个或多个“play”组成的列表,可以让它们联同起来按事先编排的机制执行;所谓task无非是调用ansible的一个module,而在模块参数中可以使用变量;模块执行是幂等的,这就意味着多次执行是安全的,因为其结果均一致。
就好像一个剧本一样,安排每个演员(每天被控制的主机)什么时候做什么事情(执行命令命令)

1.2 使用了YAML

1.2.1yaml特点

YAML的可读性好
YAML和脚本语言的交互性好
YAML使用实现语言的数据类型
YAML有一个一致的信息模型
YAML易于实现
YAML可以基于流来处理
YAML表达能力强,扩展性好
YAML不像其他的信息传输格式。json具有标记简单,体积小的优点但是不容易读。xml具有可读性好的优点,但是标记冗长,体积大。YAML融合了这两者的优点,用缩进和空格表示。下面是一个json和xml实例对比。

1.2.2 yaml语法

1。基本格式要求
YAML大小写敏感;
使用缩进代表层级关系;
缩进只能使用空格,不能使用TAB,不要求空格个数,只需要相同层级左对齐(一般2个或4个空格)
yaml用#作为注释
(详见这个链接)
https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html#yaml-syntax
2.我们这里只注重yaml里对playbook最重要的两部分:列表,字典
(1)列表,每个列表项前面有一个“-”和空格符号。
例如,表示一个个水果的数组,相当于[apple,orange,pear]

- apple
- orange
- pear

(2)字典,每个字典项只要对齐就行了。
例如,表示一个个水果和他颜色的字典{apple:red,orange:orange,pear:yellow}

apple:red
orang:orange
pear:yellow

(3)看起来简单的数据格式yaml好像规定这么多,好麻烦,但是当数据关系复杂了之后,这种格式就非常清晰了。在后面的例子里我们将会发现他的好处。
(4)yaml也可以用流式数据作为文件数据的交换格式(这就丧失了他简单明了的优越性,所以不推荐。

1.3优点

1.我们之前的ansible命令都是一个个功能一条命令,想要重复使用命令还得一条条去找。用playbook把所有的ansible命令放到一个文件里,啥时候用直接到用这个文件就行了
2.不过这和普通的脚本文件不同,因为这个文件把每个对每个节点分开设置命令,享用哪个节点的设置就直接传入相应的参数就行。
3.标记tag一些命令,当传入tag时就只执行被tag过后的命令。

2 基础

2.1 基本格式

2.1.1 创建playbook文件(yaml格式)

创建后缀名为.yaml的文件,并且按照yaml语法指定文件。
1.这是最基础的文件格式
相当于在命令行里执行了
ansible 选择的主机 -m 使用的模块名 -a 参数1=参数的值1 参数2= 参数的值2

- hosts:选择的主机组
  remote_user:以什么身份在被控制的主机上执行命令
  tasks:
  - name: 第一条命令的描述
	使用的模块名: 
	  参数1: 参数的值1
	  参数2: 参数的值2
  - name: 第二条命令的描述
	  .....
		

2.注意:
(1)参数1: 参数的值1之间的: 可以用=代替。
(2)像shell,command,script这样的模块可以直接给参数值,就不需要:分割了

2.1.2 检测playbook

测试yaml文件语法错误和预先排练一遍

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

2.1.3 运行playbook文件

ansible-playbook <filename.yml> … [options],没有参数就可以直接运行了

其他参数
<filename.yml>:yaml格式的playbook文件路径,必须指明
[options]: 选项
-C, --check:并不在远程主机上执行,只是测试。
-i PATH, --inventory=PATH:资产的文件路径
–flush-cache:清楚fact缓存
–list-hosts:列出匹配的远程主机,并不执行任何动作
-t, TAGS, --tags=TAGS:运行指定的标签任务
–skip-tags:跳过指定的notify,后面详细介绍。

2.2 实例

1.下面以创建用户为例,写一个playbook在node2上设置hostname,上创建一个user1用户,并且把该用户的信息输出到1.txt里。
2.步骤
(1)创建test.yaml

[root@node1 ~]# cat test.yaml 
- hosts: node2
  remote_user: root
  tasks: 
  - name: hostname
    command: hostnamectl set-hostname node2
  - name: create user
    user: name:user1
  - name: show user
    shell: id user1 >> 1.txt

如果把他写成流式的形式非常难看,yaml使得我们我们这样可以非常清晰地看到数据项之间的关系

	
[{hosts: node2,remote_user: root,tasks: [{name: hostname,command: hostnamectl set-hostname node2},{name: create user,user: name:user1},{- name: show user,shell: id user1 >> 1.txt}]]

(2)检查语法错误,并且预先排练一遍

(3)正式执行成功

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

3 其他功能

3.1 设置和使用变量var

3.1.1 设置变量

(如果有同名情况,优先级从上往下)
1.在调用playbook时传入参数。通过-e 传入参数

[root@node1 ~]# ansible-playbook -e test=2 test.yaml

(1)在/etc/ansible/hosts 定义变量,在主机组中的主机单独定义
(2)在/etc/ansible/hosts 定义变量,针对主机组中的所有主机集中定义变量
3.在yaml文件里var。
4.调用被控制机器的信息。

3.1.2 使用变量

要用{{ 变量名 }}来使用,要注意空格

  - name: use var
    shell: echo {{ var_in_yaml }} > 2.txt

3.2 条件语句

notify语句和handler语句

如果有一条task没有改变(changed)对应被控制主机的结果。那么就notify指向handler中的某一条处理。经常使用在看一个文件的配置文件改变了没有,改变的话就重启服务。

  - name: 任务名称
    使用的模块名:
    参数1: 参数的值1
    参数2: 参数的值2
    notify:
    - restart apache
  handler:
  - name: 处理名称
    使用的模块名:
    参数1: 参数的值1
    参数2: 参数的值2

实例
我们先在node2节点上开启httpd服务,然后在node1上运行这个程序,发现

- hosts: node2
  remote_user: root
  tasks:
  - name: start service
    service:
      name: httpd
      state: started
    notify:
    - show
  handler:
  - name: show
    shell: echo "http服务第一启动" > message.txt

when语句

当满足某个条件时,才执行这条任务

  - name: 任务名称
    使用的模块名:
    参数1: 参数的值1
    参数2: 参数的值2
    when:条件

3.3 遍历迭代

任务中可以通过{{ item }}使用这个变量

  - name: 任务名称
    使用的模块名:
    参数1: 参数的值1
    参数2: 参数的值2
    with_itmes:
   	- 元素1
   	- 元素2
   	- 元素3

3.4 模板文件

思考这样的情景:我们小时候学校发奖状,会从头到尾手写奖状内容吗?当然不会。
一般会是这样:先制作一个模板奖状,把名字,日期,奖项空出来。然后复印这个模板,等到发奖状的时候把需要填的内容填上就可以把奖状发给同学了。
因此模板文件也有这样的功能,提供一个模板,在分发任务的时候根据具体的变量值把模板填充完整。

3.4.1 书写模板文件,以j2结尾

需要使用变量名的时候用:{{ 变量名 }}
下面我们还是使用这个发奖状的例子,给每个主机发一个奖状,对不同的主机需要写不同的名字。

[root@node1 test_dir]# cat jiangzhuang.j2 
host {{ hostname }}:
	恭喜你获得了”最棒的主机"称号

3.4.2使用模板文件

在yaml文件里需要为模板文件提供变量,对node2和node3写yaml文件。执行这个yaml文件。会发现node2,node3收到了文件。

[root@node1 test_dir]# cat  test.yaml 
- hosts: node2
  remote_user: root
  vars: 
    hostname: "主机2"
  tasks:
  - name: send jiangzhuang
    template: 
      src=jiangzhuang.j2 dest=/root/jiangzhuang.txt

- hosts: node3
  remote_user: root
  vars: 
    hostname: "主机3"
  tasks:
  - name: send jiangzhuang
    template: 
      src=jiangzhuang.j2 dest=/root/jiangzhuang.txt

3.5 标记功能

3.5.1 标记语句

- name: 任务名称
    使用的模块名:
      参数1: 参数的值1
      参数2: 参数的值2
    tags:标记名 

3.5.2 使用标记

仅执行被标记的task,跳过执行的task

ansible-playbook --tags 标记名 testtag.yml
ansible-playbook --skip-tags 标记名 testtag.ymll

3.6 角色功能

把每个功能模块化,需要的时候在site里调用这个功能

3.6.1 文件结构及其功能

创建这样的文件结构。

roles/
├── http
│   ├── defaults
│   │   └── main.yaml
│   ├── files
│   │   └── index.html
│   ├── headlers
│   │   └── main.yaml
│   ├── meta
│   │   └── main.yaml
│   ├── tasks
│   │   └── main.yaml
│   ├── templates
│   │   └── httpd.conf.j2
│   └── vars
│       └── main.yaml
└── site.yaml

tasks/:此目录中至少应该有一个名为main.yml的文件,用于定义各task;其它的文件需要由main.yml进行“包含”调用;
handlers/:此目录中至少应该有一个名为main.yml的文件,用于定义各handler;其它的文件需要由main.yml进行“包含”调用;
vars/:用于存放数据文件;
templates/:存储由template模块调用的模板文本;
meta/:此目录中至少应该有一个名为main.yml的文件,定义当前角色的特殊设定及其依赖关系;其它的文件需要由main.yml进行“包含”调用;
default/:此目录中至少应该有一个名为main.yml的文件,用于设定默认变量;
files目录:存放由copy或script等模块调用的文件;

3.6.2实例:部署一个远程配置http添加8080端口服务

1.创建文件目录,并且在site里写主任务

[root@node1 ansible]# tree roles/
roles/
├── http
│   ├── defaults
│   │   └── main.yaml
│   ├── files
│   │   └── index.html
│   ├── headlers
│   │   └── main.yaml
│   ├── meta
│   │   └── main.yaml
│   ├── tasks
│   │   └── main.yaml
│   ├── templates
│   │   └── httpd.conf.j2
│   └── vars
│       └── main.yaml
└── site.yaml
[root@node1 roles]# cat site.yaml 
- hosts: node2
  remote_user: root
#  vars:
#    httpd_ports: 8080
  roles:
  - http

2.在files/index.html文件

[root@node1 http]# cat files/index.html 
hello eagleslab!

3.在tasks/main.yaml文件设置任务

[root@node1 http]# cat tasks/main.yaml 
- name: installed httpd service
  yum: name=httpd state=latest
  when: ansible_os_family  == "RedHat"
- name: start httpd server
  service: name=httpd state=restarted 
- name: write httpd config
  template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
  notify:
  - restart httpd
- name: config httpd index
  copy: src=index.html dest=/var/www/html/index.html

4.写模板文件。

[root@node1 http]# cat templates/httpd.conf.j2 | grep -Ev "^[[:space:]]|^#"
ServerRoot "/etc/httpd"
Listen {{ httpd_ports }}
Include conf.modules.d/*.conf
User apache
Group apache
ServerAdmin root@localhost
<Directory />
</Directory>
DocumentRoot "/var/www/html"
<Directory "/var/www">
</Directory>
<Directory "/var/www/html">
</Directory>
<IfModule dir_module>
</IfModule>
<Files ".ht*">
</Files>
ErrorLog "logs/error_log"
LogLevel warn
<IfModule log_config_module>
</IfModule>
<IfModule alias_module>
</IfModule>
<Directory "/var/www/cgi-bin">
</Directory>
<IfModule mime_module>
</IfModule>
AddDefaultCharset UTF-8
<IfModule mime_magic_module>
</IfModule>
EnableSendfile on
IncludeOptional conf.d/*.conf
[root@node1 http]# cat headlers/main.yaml 
- name: restart httpd
  service: name=httpd state=restarted

5.在vars/main.yaml文件里写端口号

[root@node1 http]# cat vars/main.yaml 
httpd_ports: 8000

6.运行

[root@node1 http]# ansible site.yaml

猜你喜欢

转载自blog.csdn.net/weixin_44055272/article/details/88770457