ansible之playbook&角色
playbook-剧本
简介
Playbook与ad-hoc相比,是一种完全不同的运用ansible的方式,类似与saltstack的state状态文件。ad-hoc无法持久使用,playbook可以持久使用。
playbook是由一个或多个play组成的列表,play的主要功能在于将事先归并为一组的主机装扮成事先通过ansible中的task定义好的角色。从根本上来讲,所谓的task无非是调用ansible的一个module。将多个play组织在一个playbook中,即可以让它们联合起来按事先编排的机制完成某一任务
Playbook核心元素 | |
---|---|
Hosts | 执行的远程主机列表 |
Tasks | 任务集 |
Varniables | 内置变量或自定义变量在playbook中调用 |
Templates | 模板,即使用模板语法的文件,比如配置文件等 |
Handlers 和notity结合使用 | 由特定条件触发的操作,满足条件方才执行,否则不执行 |
tags 标签 | 指定某条任务执行,用于选择运行playbook中的部分代码。 |
Playbook语法
playbook使用yaml语法格式,后缀可以是yaml,也可以是yml。
在单一一个playbook文件中,可以连续三个连子号(---)区分多个play。还有选择性的连续三个点好(...)用来表示play的结尾,也可省略。
次行开始正常写playbook的内容,一般都会写上描述该playbook的功能。
使用#号注释代码。
缩进必须统一,不能空格和tab混用。
缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行实现的。
YAML文件内容和Linux系统大小写判断方式保持一致,是区分大小写的,k/v的值均需大小写敏感
k/v的值可同行写也可以换行写。同行使用:分隔。
v可以是个字符串,也可以是一个列表
一个完整的代码块功能需要最少元素包括 name: task
ansible-playbook参数
–check or -C | 只检测可能会发生的改变,但不真正执行操作 |
–list-hosts | 列出运行任务的主机 |
–list-tags | 列出playbook文件中定义所有的tags |
–list-tasks | 列出playbook文件中定义的所以任务集 |
–limit | 主机列表 只针对主机列表中的某个主机或者某个组执行 |
-f | 指定并发数,默认为5个 |
-t | 指定tags运行,运行某一个或者多个tags。(前提playbook中有定义tags) |
-v | 显示过程 -vv -vvv更详细 |
playbook组件用法
tasks
每一个task必须有一个名称name,这样在运行playbook时,从其输出的任务执行信息中可以很清楚的辨别是属于哪一个task的,如果没有定义name,action的值将会用作输出信息中标记特定的task。
每一个playbook中可以包含一个或者多个tasks任务列表,每一个tasks完成具体的一件事,(任务模块)比如创建一个用户或者安装一个软件等,在hosts中定义的主机或者主机组都将会执行这个被定义的tasks。
tasks:
- name: run df -h
sudo_user: test #定义要执行的用户组
sudo: yes #配置sudo
shell: name=df -h
tasks:
- name: create new file
file: path=/tmp/test01.txt state=touch
- name: create new user
user: name=test001 state=present
tags
useradd.yml
hosts: websrvs
remote_user: root
tasks :
name: create new file
file: name=/data/newfile state=touch
name: create new user
user: name=test2 system=yes shel1=/sbin/nologin
name: instal1 package
yum: name=httpd
name: copy html
copy: src=/van/www/htm1/index.htm] dest=/var /www/htm1/
name: start service
service: name=httpd state=started enab led=yes
ansible-playbook -t tagsname useradd.yml
- hosts: 192.168.1.31
remote_user: root
tasks:
- name: install httpd
yum: name=httpd state=installed
tags: inhttpd
- name: start httpd
service: name=httpd state=started
tags: sthttpd
- name: restart httpd
service: name=httpd state=restarted
tags:
- rshttpd
- rs_httpd
#通过-t指定tags名称,多个tags用逗号隔开
ansible-playbook -t rshttpd httpd.yml
#通过--skip-tags选项排除不执行的tags
ansible-playbook --skip-tags inhttpd httpd.yml
play的主体部分是task list。task list中的各任务按次序逐个在hosts中指定的所有主机_上执行,即在所有主机上完成第一个任务后再开始第 二个。在运行自下而下某playbook时,如果中途发生错误,所有已执行任务都将回滚,因此在更正playbook后重新执行一次即可
➢task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致
➢每个task都应该有其name ,用于playbook的执行结果输出,建议其内容尽可 能清晰地描述任务执行步骤。如果未提供name,则action的结果将用于输出
如果命令或脚本的退出码不为零, 可以使用ignore_ errors来忽略错误信息
#如果命令或脚本的退出码不为零, 可以使用ignore_ errors来忽略错误信息
name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true
tasks: .
name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True
Handlers
是task列表,这些task与前述的task并没有本质上的不同,用于当关注的资源发生化时,才会采取一定的操作
Notify此action可用于在每个play的最后被触发,这样可避免多次有改变发生时每次都执行指定的操作,仅在所有的变化发生完成后一次性地执行指定操作。在notify中列出的操作称为handler
,也即notify中调用handler中定义的操作。
#可以定义多个
- hosts: websrvs
remote_user: root
tasks:
- name: add group nginx
tags: user
user: name= nginx state= present
- name: add user nginx
user: name=nginx state=present group=nginx
- name: Install Nginx
yum: name=nginx state=present
- name: config
copy: src=/root/config.txt dest=/etc/nginx/nginx.conf
notify:
- Restart Nginx
- Check Nginx Progess
handlers:
- name: Restart Nginx
service: name=nginx state=restarted enabled=yes
- name: Check Nginx process
shell: kllall -0 nginx > /tmp/nginx.log
template
该模板为我们提供了动态配置服务,使用jinja2语言,里面支持多种条件判断、循环、逻辑运算、比较操作等。其实说白了也就是一个文件,和之前配置文件使用copy一样,只是使用copy,不能根据服务器配置不一样进行不同动态的配置。这样就不利于管理。
1、多数情况下都将template文件放在和playbook文件同级的templates目录下(手动创建),这样playbook文件中可以直接引用,会自动去找这个文件。如果放在别的地方,也可以通过绝对路径去指定。
2、模板文件后缀名为.j2。
playbook变量
变量名:仅能由字母、数字和下划线组成,且只能以字母开头
变量来源:
➢ansible setup facts远程主机的所有变量都可直接调用
➢在/etc/ansible/hosts中定义,
➢在role中定义
➢在playbook中定义
➢在命令行中定义,优先级最高
#优先级从下到上依次降低,其中,在/etc/ansible/hosts文件中主机组中主机单独定义,优先级高于公共变量
cat app.yml
- hosts: appsrvs
remote_user: root
tasks:
- name :install package
yum: name={{ pkname }}
- name: start service
service: name={{ pkname }} state=started enab led=yes
ansible-playbook -e ' pkname=vsftpd' app.yml
#执行playbook时候通过参数-e传入变量,这样传入的变量在整个playbook中都可以被调用,属于全局变量
#playbook中变量的定义
- hosts: all
remote_user: root
vars: #定义变量
pkg: nginx #变量1
dir: /tmp/test1 #变量2
/etc/ansible/hosts文件中的变量
[websrvs]
192.168.30.101 http_ port=81 #每个主机的变量
192.168.30.102 http_ .port=82
[websrvs:vars ] #该组的公共变量
nodename=www
domai nname=magedu . com
playbook语句
when
条件测试:如果需要根据变量、facts或此前任务的执行结果来做为某task执行与否的前提时要用到条件测试,通过when语句执行,
在task中使用jinja2的语法格式、
when语句:在task后添加when子句即可使用条件测试;when语句支持jinja2表达式语法。
- hosts: all
remote_user: root
vars:
- listen_port: 88
tasks:
- name: Install Httpd
yum: name=httpd state=installed
- name: Config System6 Httpd
template: src=httpd6.conf.j2 dest=/etc/httpd/conf/httpd.conf
when: ansible_distribution_major_version == "6" #判断系统版本,为6便执行上面的template配置6的配置文件
notify: Restart Httpd
- name: Config System7 Httpd
template: src=httpd7.conf.j2 dest=/etc/httpd/conf/httpd.conf
when: ansible_distribution_major_version == "7" #判断系统版本,为7便执行上面的template配置7的配置文件
notify: Restart Httpd
- name: Start Httpd
service: name=httpd state=started
handlers:
- name: Restart Httpd
service: name=httpd state=restarted
with_items
with_items迭代,当有需要重复性执行的任务时,可以使用迭代机制。
对迭代项的引用,固定变量名为“item”,要在task中使用with_items给定要迭代的元素列表。
列表格式:
字符串
字典
- hosts: all
remote_user: root
tasks:
- name: Install Package
yum: name={{ item }} state=installed #引用item获取值
with_items: #定义with_items
- httpd
- vsftpd
- nginx
#with_items嵌套子变量
---
- hosts: all
remote_user: root
tasks:
- name: Create New Group
group: name={{ item }} state=present
with_items:
- group1
- group2
- group3
- name: Create New User
user: name={{ item.name }} group={{ item.group }} state=present
with_items:
- { name: 'user1', group: 'group1' }
- { name: 'user2', group: 'group2' }
- { name: 'user3', group: 'group3' }
for if
通过使用for,if可以更加灵活的生成配置文件等需求,还可以在里面根据各种条件进行判断,然后生成不同的配置文件、或者服务器配置相关等。
cat templates/nginx.conf.j2
{% for port in nginx_vhost_port %}
server{
listen: {{ port }};
server_name: localhost;
}
{% endfor %}
#执行效果
ansible-playbook testfor01.yml
# 去到一个节点看下生成的结果发现自动生成了三个虚拟主机
cat /tmp/nginx_test.conf
server{
listen: 81;
server_name: localhost;
}
server{
listen: 82;
server_name: localhost;
}
server{
listen: 83;
server_name: localhost;
}
cat templates/nginx.conf.j2
{% for vhost in nginx_vhosts %}
server{
listen: {{ vhost.listen }};
server_name: {{ vhost.server_name }};
root: {{ vhost.root }};
}
{% endfor %}
#执行效果
cat /tmp/nginx_vhost.conf
server{
listen: 8081;
server_name: web1.example.com;
root: /var/www/nginx/web1;
}
server{
listen: 8082;
server_name: web2.example.com;
root: /var/www/nginx/web2;
}
server{
listen: 8083;
server_name: web3.example.com;
root: /var/www/nginx/web3;
}
roles-角色
简介
角色(roles)是ansible自1.2版本开始引入的新特性,用于层次性,结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单的说,roles就是通过分别将变量、文件、任务、模块及处理器放置于单独的目录中、并可以便捷地include他们的一种机制。角色一般用于基于主机构建服务的场景中、但也可以是用于构建守护进程等场景中。
yml文件 | 用于定义此角色用到的各handler:在handler中使用include包含的其他的handler文件也应该位于此目录中; |
files目录 | 存放由copy或script等模块调用的文件; |
templates目录 | templates模块会自动在此目录中寻找Jinja2模板文件; |
tasks目录 | 至少应该包含一个名为main.yml的文件,其定义了此角色的任务列表;此文件可以使用include包含其他的位于此目录中的task文件; |
handlers目录 | 此目录中应当包含一个main; |
vars目录 | 应当包含一个main.yml文件,用于定义此角色用到的变量; |
meta目录 | 应当包含一个main.yml文件,用于定义此角色的特殊设定及其依赖关系;ansible 1.3及其以后的版本才支持 |
default目录 | 为当前角色设定默认变量时使用此目录;应当包含一个main.yml文件; |
用法
角色中一个playbook就可以这样写
---
- hosts: webservers
roles:
- common
- webservers
这个 playbook 为一个角色 ‘x’ 指定了如下的行为:
如果 roles/x/tasks/main.yml 存在, 其中列出的 tasks 将被添加到 play 中
如果roles/x/handlers/main.yml 存在, 其中列出的 handlers 将被添加到 play 中
如果roles/x/vars/main.yml 存在, 其中列出的 variables 将被添加到 play 中
如果roles/x/meta/main.yml 存在, 其中列出的 “角色依赖” 将被添加到 roles 列表中 (1.3 andlater)
所有 copy tasks 可以引用 roles/x/files/ 中的文件,不需要指明文件的路径。
所有 scripttasks 可以引用 roles/x/files/ 中的脚本,不需要指明文件的路径。
所有 template tasks 可以引用roles/x/templates/ 中的文件,不需要指明文件的路径。
所有 include tasks 可以引用roles/x/tasks/ 中的文件,不需要指明文件的路径。
如果 roles 目录下有文件不存在,这些文件将被忽略。比如 roles 目录下面缺少了 ‘vars/’ 目录,这也没关系。
注意:你仍然可以在 playbook 中松散地列出 tasks,vars_files 以及 handlers,这种方式仍然可用,但 roles 是一种很好的具有组织性的功能特性,我们强烈建议使用它。
如果你在 playbook 中同时使用 roles 和 tasks,vars_files 或者 handlers,roles 将优先执行。
而且,也可以使用参数化的 roles,这种方式通过添加变量来实现,比如:
- hosts: webservers
roles:
- common
- { role: foo_app_instance, dir: '/opt/a', port: 5000 }
- { role: foo_app_instance, dir: '/opt/b', port: 5001 }
当一些事情不需要频繁去做时,也可以为 roles 设置触发条件,像这样:
---
- hosts: webservers
roles:
- { role: some_role, when: "ansible_os_family == 'RedHat'" }
它的工作方式是:将条件子句应用到 role 中的每一个 task 上。
也可以给role分配指定的标签,比如
---
- hosts: webservers
roles:
- { role: foo, tags: ["bar", "baz"] }
如果 play 仍然包含有 ‘tasks’ section,这些 tasks 将在所有 roles 应用完成之后才被执行。
也可定义一些 tasks,让它们在 roles 之前以及之后执行,可以这样做:
---
- hosts: webservers
pre_tasks:
- shell: echo 'hello'
roles:
- { role: some_role }
tasks:
- shell: echo 'still busy'
post_tasks:
- shell: echo 'goodbye'
实战
部署nginx
建立目录架构
cd /etc/ansible
mkdir -p roles/nginx
cd /roles/nginx
mkdir tasks templates
编写任务文件
cd tasks
vim group.yml
- name: create group
group: name=nginx gid=80
vim user.yml
- name: create user
user: name=nginx uid=80 group=nginx system=yes shell=/sbin/nologin
vim yum.yml
- name: install package
yum: name=nginx
vim start.yml
- name: start service
service: name=nginx state=started enabled=yes
vim restart.yml
- name: restart service
service: name=nginx state=restarted
vim template.yml
- name: copy conf
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
vim main.yml
- include: group.yml
- include: user.yml
- include: yum.yml
- include: template.yml
- include: start.yml
编写模板文件
cd templates
cp /etc/nginx/nginx.conf nginx.conf.j2
vim nginx.conf.j2
worker_processes {{ ansible_processor_vcpus+2 }};
编写执行文件
注意该文件和roles目录是同级的,即都在/etc/ansible下
cd /etc/ansible
vim nginx.yml
- host: webserver
remote_user: root
roles:
- role: nginx
ansible-playbook nginx.yml