Ansible剧本(playbook)编写

2023-04-24 17:08:48 浏览数 (2)

Playbook简介

playbook是ansible用于配置,部署,和管理被控节点的剧本。 通过playbook的详细描述,执行其中的一系列tasks,可以让远端主机达到预期的状态。playbook就像Ansible控制器给被控节点列出的的一系列to-do-list,而被控节点必须要完成。 也可以这么理解,playbook 字面意思,即剧本,现实中由演员按照剧本表演,在Ansible中,这次由计算机进行表演,由计算机安装,部署应用,提供对外服务,以及组织计算机处理各种各样的事情。

Playbook的使用优势

  1. 功能比ansible命令更强大
  2. 能很好的控制先后执行顺序, 以及依赖关系
  3. 语法展现更加的直观
  4. ansible命令无法持久使用, playbook 可以持久使用

剧本的书写格式要求

语法格式

ansible剧本格式:遵循yaml语法格式(类似python脚本编写格式) rsync配置文件格式:ini语法格式 sersync配置文件格式:xml语法格式(标签格式)

注意缩进

合理的信息缩进,两个空格表示一个缩进关系 一定不要使用tab

代码语言:javascript复制
标题一
_ _ 标题二
_ _ _ _ 标题三

冒号

所有冒号后面都要加上空格

代码语言:javascript复制
- hosts: 10.1.1.20
  tasks:
    yum: name=rsync state=installed

短横线 - 列表功能

使用短横线构成列表信息,短横线后面需要有空格

代码语言:javascript复制
- boysec
  man
- 爱好
  台球

剧本书写

文件名格式

剧本文件拓展名为xxx.yaml

  1. 方便识别文件是一个剧本文件
  2. 文件编写时会有颜色提示

练习: 写一个剧本,使用yum/copy/service模块安装部署启动rsync服务

代码语言:javascript复制
---
- hosts: rsync                     # 指定主机组或主机
  remote_user: root                # 远程用户
  gather_facts:  no                # 是否先执行setup模块获取主机相关信息。
  tasks:                           # 固定格式
    - name: "01-add-user"          # 指定名称
      user: name=rsync create_home=no shell=/sbin/nologin uid=2021   # user模块
    - name: "02-installed-rsync"
      yum: name=rsync state=installed
    - name: "03-copy rsync.conf"
      copy: src=/playbook/rsync/rsyncd.conf dest=/etc/
    - name: "04-create passwd conf"
      copy: content="rsync_backup:123456" dest=/etc/rsync.passwd mode=0600
    - name: "05-create rsync dir"
      file: path=/backup  state=directory owner=rsync group=rsync
    - name: "06-started rsync service"
      service: name=rsyncd state=started
    - name: "07-enabled rsync service"
      service: name=rsyncd enabled=yes

剧本检查模拟

代码语言:javascript复制
## 检查
ansible-playbook --syntax-check rsync.yaml 

## 模拟执行
ansible-playbook -C rsync.yaml

## 执行
ansible-playbook  rsync.yaml

nfs服务剧本

服务端
代码语言:javascript复制
- hosts: nfs_server
  tasks:
  - name: 01-add user
    user:
      name: www
      shell: /sbin/nologin
      create_home: no
      state: present
      uid: 1010
  - name: 02-installed nfs server
    yum: name=nfs-utils state=latest
  - name: 03-copy nfs exports
    copy: src=/playbook/exports dest=/etc/
  - name: 04-create data dir
    file: path=/nfs-data state=directory owner=www group=www
  - name: 05-start rpcbind
    service: name=rpcbind state=started enabled=yes
  - name: 06-start nfs
    service: name=nfs state=started enabled=yes
客户端
代码语言:javascript复制
- hosts: nfs_client
  tasks:
  - name: 01-add user
    user: name=www create_home=no shell=/sbin/nologin uid=1010
  - name: 02-installed nfs-client
    yum: name=nfs-utils state=latest
  - name: 03-create mount data
    file: path=/mnt/data state=directory owner=www
  - name: 04-mount data
    mount: path=/mnt/data src=10.1.1.60:/nfs-data fstype=nfs opts=defaults state=mounted

剧本高级特性

我们已经体验了使用剧本来安装服务,但是上述的简单ansible剧本存在一定的局限性

  1. 全部写成一行虽然看起来整洁,但是有一些特性没办法使用
  2. 比如同时需要创建多个目录,启动多个服务,需要重复写多条语句
  3. 参数不直观,不好修改
  4. 剧本里写的是启动服务,如果配置文件发生变化,重复执行不会重启服务

不过没有关系,等学习了下面的高级特性,然后我们可以换一种写法

循环(loop)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html

使用情景:

  1. 需要创建多个目录
  2. 需要启动多个服务

具体实现: 同时创建2个目录/data和/backup

代码语言:javascript复制
- hosts: 10.1.1.20
  tasks:
  - name: 01-create dir data and backuo
    file:
      path: "{{ item }}"
      state: directory
      owner: www
      group: www
    loop: 
    - /data
    - /backup

同时创建多个用户(字典类型)

代码语言:javascript复制
---
- name: create user
  hosts: 10.1.1.20
  tasks: 
  - name: create user and group
    user:
        name: "{{ item.name }}"
        group: "{{ item.groups }}"
    loop:
    - {name: 'user01', groups: 'wheel'}
    - {name: 'user02', groups: 'root'}
变量(vars)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html 使用情景:

  1. 自定义某个名称,在任务中会多次引用
  2. 从主机收集的系统信息中提取某个变量并引用,例如网卡信息

具体实现:

自定义一个文件名变量,创建文件时引用:

代码语言:javascript复制
- hosts: 10.1.1.20
  vars:
    file_name: boysec
  tasks:
  - name: 01-create vars dir
    file:
      path: "/data/{{ file_name }}"
      state: directory
      owner: www
      group: www

使用变量获取主机的eth1地址:

代码语言:javascript复制
- hosts: 10.1.1.20
  tasks:
  - name: 01-get ip address
    shell: "echo {{ ansible_facts.eth0.ipv4.address }}" >/root/ip.txt

在主机hosts中指定变量:

代码语言:javascript复制
tail -5 /etc/ansible/hosts
[backup]
10.1.1.30

[backup:vars]
file_name="boysec"

软件安装:

代码语言:javascript复制
- hosts 10.1.1.20
  vars:
    - web_packages: nginx-1.15.2
    - ftp_packages: vsftp
    
  tasks:
    - name: Installed {{ web_packages }} and {{ ftp_packages }}
      yum:
        name:
          - "{{ web_packages }}"
          - "{{ ftp_packages }}"
        state: present
注册变量(register)

使用情景:将配置文件的状态注册成一个变量,方便其他任务引用 具体实现: 1.将配置文件的状态注册成一个服务变量并打印出来

代码语言:javascript复制
- hosts: 10.1.1.20
  tasks:
  - name: 01-register nginx status
    shell: netstat -lnpt|grep nginx
    register: nginx_port
  - name: 02-out nginx status
    debug:
      msg: "{{ nginx_port.stdout_lines }}"

2.打印多个信息:

代码语言:javascript复制
- hosts: rsync
  tasks:
  - name: 01-echo hostname
    shell: echo $(hostname)
    register: rsync_hostname
    
  - name: debug rsync_hostname
    debug:
      msg: "{{ item }}"
    loop:
      - "{{ rsync_hostname.stdout }}"
      - "{{ rsync_hostname.cmd }}"
服务管理(service)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html?highlight=handlers 使用情景:如果配置文件发生了变化,就重启服务,否则什么都不操作 具体实现:

代码语言:javascript复制
- hosts: 10.1.1.80
  tasks:
  - name: 01-if nfs conf changed,then restart nfs service
    copy:
      src: /playbook/exports
      dest: /etc/
    notify: Restart_nfs
  handlers:
    - name: Restart_nfs
      service:
        name: nfs
        state: restarted
标签(tags)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html 使用情景:从我们指定的任务开始执行,而不是从头到尾执行一遍

代码语言:javascript复制
- hosts: rsync
  tasks: 
    - name: "01-add-user"
      user: name=rsync create_home=no shell=/sbin/nologin uid=2021
      tags: 01-add-user
    - name: "02-installed-rsync"
      yum: name=rsync state=installed
      tags: 02-install-rsync
    - name: "03-copy rsync.conf"
      copy: src=/playbook/rsync/rsyncd.conf dest=/etc/
      tags: 03-copy-rsync.conf
    - name: "04-create passwd conf"
      copy: content="rsync_backup:123456" dest=/etc/rsync.passwd mode=0600
      tags: 04-create passwd conf
    - name: "05-create rsync dir"
      file: path=/backup  state=directory owner=rsync group=rsync
      tags: 05-create dir
    - name: "06-started rsync service"
      service: name=rsyncd state=started enabled=yes
      tags: 06-start rsync

调用标签: 1.打印出playbook里要执行的所有标签

代码语言:javascript复制
ansible-playbook --list-tags tags.yaml

2.指定运行某个标签

代码语言:javascript复制
ansible-playbook -t "04-create passwd conf" tags.yaml

3.指定运行多个标签,使用逗号隔开

代码语言:javascript复制
ansible-playbook -t "04-create passwd conf","06-start rsync" tags.yaml

4.指定不运行某个标签

代码语言:javascript复制
ansible-playbook --skip-tags="04-create passwd conf" tags.yaml

5.指定不运行多个标签

代码语言:javascript复制
ansible-playbook --skip-tags="04-create passwd conf","06-start rsync" tags.yaml
触发器(handlers)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_handlers.html

handlers(触发器): 定义一些task列表,与之前剧本中task没有关系,只有资源发送变化才会采取一定的操作;

notify(告警器):notify中调用handler中定义的操作;

例如: 我们来试试,此处我们使用httpd作为示例,虽然httpd可以使用systemctl restart httpd命令重载配置,但是此处的示例中并不会使用这个命令,而是用httpd类比那些需要重启生效的应用。

假设我们想要将httpd的端口从80改成8088,并且在修改配置以后重启httpd,那么我们可以编写如下剧本。

代码语言:javascript复制
---
- hosts: webserver
  remote_user: root
  tasks:
  - name: Modif the configuration
    lineinfile:
      path=/etc/httpd/conf/httpd.conf
      regexp="^Listen 80"
      line="Linsten 8080"
      backrefs=yes
      backup=yes
    notify:
      - restart httpd
  handlers:
   - name: restart httpd
     service:
       name=httpd
       state=restarted

注意:无论多少个task通知相同的handlers,handlers仅会在tasks结束后运行一次。

条件判断(when)

官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html#basic-conditionals-with-when 通常,您希望根据事实执行或跳过任务。事实是单个主机的属性,包括 IP 地址、操作系统、文件系统的状态等等。基于事实的条件:

  • 只有在操作系统是特定版本时,才能安装特定包。
  • 您可以跳过在具有内部 IP 地址的主机上配置防火墙。
  • 只有在文件系统已满时,才能执行清理任务。

实例: 1.只关闭关闭Debian系统

代码语言:javascript复制
tasks:
  - name: Shut down Debian flavored systems
    ansible.builtin.command: /sbin/shutdown -t now
    when: ansible_facts['os_family'] == "Debian"

2.关闭系统为CentOS系统版本为6的

代码语言:javascript复制
tasks:
  - name: Shut down CentOS 6 systems
    ansible.builtin.command: /sbin/shutdown -t now
    when:
      - ansible_facts['distribution'] == "CentOS"
      - ansible_facts['distribution_major_version'] == "6"

回顾运行检查规范与补充

00.检查剧本拼写规范

代码语言:javascript复制
ansible-playbook --syntax-check check.yaml

01.检查这个任务执行的主机对象

代码语言:javascript复制
ansible-playbook --list-host check.yaml

02.检查这个剧本需要执行哪些任务

代码语言:javascript复制
ansible-playbook --list-tasks check.yaml 

03.检查这个剧本执行哪些tag

代码语言:javascript复制
ansible-playbook --list-tags check.yaml

04.模拟执行剧本

代码语言:javascript复制
ansible-playbook -C check.yaml

05.针对某台主机执行剧本

代码语言:javascript复制
ansible-playbook --limit 10.1.1.60 check.yaml

template 模板

模板是一个文本文件,可以做为生成文件的模版,并且模板文件中还可嵌套jinja语法

jinja2语言

网站:https://jinja.palletsprojects.com/en/2.11.x/

jinja2 语言使用字面量,有下面形式:

代码语言:javascript复制
字符串:使用单引号或双引号
数字:整数,浮点数
列表:[item1, item2, …]
元组:(item1, item2, …)
字典:{key1:value1, key2:value2, …}
布尔型:true/false
算术运算: , -, *, /, //, %, **
比较操作:==, !=, >, >=, <, <=
逻辑运算:and,or,not
流表达式:For,If,When

字面量:

表达式最简单的形式就是字面量。字面量表示诸如字符串和数值的 Python 对象。如“Hello World” 双引号或单引号中间的一切都是字符串。无论何时你需要在模板中使用一个字符串(比如函数调用、过滤器或只是包含或继承一个模板的参数),如42,42.23 数值可以为整数和浮点数。如果有小数点,则为浮点数,否则为整数。在 Python 里, 42 和 42.0 是不一样的

算术运算:

Jinja 允许用计算值。支持下面的运算符

代码语言:javascript复制
 :把两个对象加到一起。通常对象是素质,但是如果两者是字符串或列表,你可以用这 种方式来衔接它们。无论如何这不是首选的连接字符串的方式!连接字符串见 ~ 运算符。 {{ 1   1 }} 等于 2
-:用第一个数减去第二个数。 {{ 3 – 2 }} 等于 1
/:对两个数做除法。返回值会是一个浮点数。 {{ 1 / 2 }} 等于 {{ 0.5 }}
//:对两个数做除法,返回整数商。 {{ 20 // 7 }} 等于 2
%:计算整数除法的余数。 {{ 11 % 7 }} 等于 4
*:用右边的数乘左边的操作数。 {{ 2* 2 }} 会返回 4 。也可以用于重 复一个字符串多次。 {{ ‘=’ *80 }} 会打印 80 个等号的横条
**:取左操作数的右操作数次幂。 {{ 2**3 }} 会返回 8

比较操作符

代码语言:javascript复制
== 比较两个对象是否相等
!= 比较两个对象是否不等

如果左边大于右边,返回 true
= 如果左边大于等于右边,返回 true
< 如果左边小于右边,返回 true
<= 如果左边小于等于右边,返回 true

逻辑运算符

代码语言:javascript复制
对于 if 语句,在 for 过滤或 if 表达式中,它可以用于联合多个表达式
and 如果左操作数和右操作数同为真,返回 true
or 如果左操作数和右操作数有一个为真,返回 true
not 对一个表达式取反
(expr)表达式组
true / false true 永远是 true ,而 false 始终是 false
template

template功能:可以根据和参考模块文件,动态生成相类似的配置文件 template文件必须存放于templates目录下,且命名为 .j2 结尾 yaml/yml 文件需和templates目录平级,目录结构如下示例:

代码语言:javascript复制
./
├── temnginx.yml
└── templates
└── nginx.conf.j2

范例:利用template 同步nginx配置文件

代码语言:javascript复制
#准备templates/nginx.conf.j2文件
vim temnginx.yml
---
- hosts: websrvs
  remote_user: root

  tasks:
    - name: template config to remote hosts
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf

 ansible-playbook temnginx.yml
template变更替换

范例:

代码语言:javascript复制
#修改文件nginx.conf.j2 
mkdir templates
vim templates/nginx.conf.j2
worker_processes {{ ansible_processor_vcpus }};

vim temnginx2.yml
---
- hosts: websrvs
  remote_user: root

  tasks:
    - name: install nginx
      yum: name=nginx
    - name: template config to remote hosts
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf 
    - name: start service
      service: name=nginx state=started enable=yes

ansible-playbook temnginx2.yml
template算术运算

范例:

代码语言:javascript复制
vim nginx.conf.j2 
worker_processes {{ ansible_processor_vcpus**2 }};    
worker_processes {{ ansible_processor_vcpus 2 }}; 
template中使用流程控制for和if

for单一值范例:

代码语言:javascript复制
vim nginx.conf2.j2
{% for vhost in nginx_vhosts %}
server {
    listen {{ vhost }}
}
{% endfor %}

for键值对范例:

代码语言:javascript复制
vim nginx.conf3.j2
{% for vhost in nginx_vhosts %}
server {
    listen {{ vhost.listen }}
}
{% endfor %}

0 人点赞