介绍
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 格式
YMAL 语言格式
1、在单一档案中,可用连续三个连字号(---)区分多个档案。另外,还有选择性的连续三个点号( ... )用来表示档案结尾
2、次行开始正常写Playbook的内容,一般建议写明该Playbook的功能
3、使用#号注释代码
4、缩进必须是统一的,不能空格和tab混用
5、缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的
6、YAML文件内容是区别大小写的,k/v的值均需大小写敏感
7、k/v的值可同行写也可换行写。同行使用:分隔
8、v可是个字符串,也可是另一个列表
9、一个完整的代码块功能需最少元素需包括 name: task
10、一个name只能包括一个task
11、YAML文件扩展名通常为yml或yamlList 列表
列表由多个元素组成, 且所有元素前均使用”-“打头
范例:
# A list of tasty fruits
- Apple
- Orange
- Strawberry
- Mango
[Apply, Orange, Strawberry, Mango]Dictionary 字典
字典通常由多个 key 与 value 构成
范例:
# An example record
name: Example Developer
job: Developer
skill: Elite
# 也可以将key:value置于{}中进行表示, 用,分割多个key:value
# An example record
{name: "Example developer", job: "developer", skill: "Elite"}Playbooks 配置文件的基础组件
hosts
运行指定任务的目标主机;
使用hosts指示使用哪个主机或主机组来运行下面的tasks,每个playbook都必须指定hosts,hosts也可以使用通配符格式。
主机或主机组在inventory清单中指定,可以使用系统默认的/etc/ansible/hosts,也可以自己编辑,在运行的时候加上-i选项,指定清单的位置即可。
在运行清单文件的时候,-list-hosts选项会显示那些主机将会参与执行task的过程中。
范例:
- hosts: websrvs:appsrvsremoute_user
在远程主机上执行任务的用户;
指定远端主机中的哪个用户来登录远端系统,在远端系统执行task的用户
可用于 host 和 task 中, 也可以通过指定其通过 sudo 的方式在远程主机上执行任务, 其可用于 play 全局或某个任务; 此外, 甚至可以在 sudo 时使用 sudo_user 指定 sudo 时切换的用户
范例:
- hosts: websrvs
remote_user: root
tasks:
- name: test connection
ping:
remote_user: test_user
sudo: yes # 默认sudo为root
sudo_user: wang # sudo为wangtask 列表和 action 组件
play 的主体部分是 task list, task list 中有一个或多个 task, 各个 task 按次序逐个在 hosts 中指定的所有主机上执行, 即在所有主机上完成第一个 task 后, 再开始第二个 task. task 的目的是使用指定的参数执行模块, 而再模块参数中可以使用变量. 模块执行是幂等的, 这意味着多次执行是安全的, 因为其结果均一致. 每个 task 都应该有其 name, 用于 playbook 的执行结果输出, 建议其内容能清晰地描述任务执行步骤. 如果未提供 name, 则 action 的结果将用于输出
task 两种格式
(1) action: moudule arguments (2) module: arguments 建议使用
注意: shell 和 command 模块后面跟命令, 而非 key=value
范例:
---
- hosts: websrvs
remote_user: root
tasks:
- name: install httpd
yum: name=httpd
- name: start httpd
service: name=httpd start=started enabled=yes其它组件
某任务的状态在运行后为 changed 时, 可以通过 notify 通知给相应的 handlers 任务可以通过 tags 打标签, 可在 ansible-playbook 命令上使用-t 指定进行调用
ShellScript VS Playbook 案例
# SHELL脚本实现
#!/bin/bash
# 安装apache
yum install --quiet -y httpd
# 复制配置文件
cp /tmp/httpd.conf /etc/httpd/conf/httpd.conf
cp /tmp/vhosts.conf /etc/httpd/conf.d/
systemctl enable --now httpdplaybook 实现
---
- hosts: websrvs
remote_user: root
tasks:
- name: "安装Apache"
yum: name=httpd
- name: "复制配置文件"
copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/
- name: "复制配置文件"
copy: src=/tmp/vhosts.conf dest=/etc/httpd/conf.d/
- name: "启动Apache, 并设置开机启动"
service: name=httpd state=started enabled=yesplaybook 命令
语法格式:
ansible-playbook <filename.yml> ... [options]常见选项:
--check -C # 只检查可能发生的改变, 但不真正执行操作
--list-hosts # 列出运行任务的主机
--list-tags # 列出tag
--list-tasks # 列出task
--limit 主机列表 # 只针对列表中的主机执行
-v -vv -vvv # 显示过程范例
ansible-playbook file.yml --check #只检测
ansible-playbook file.yml
ansible-playbook file.yml --limit websrvsplaybook 初步案例
利用 playbook 安装 nginx
---
# install nginx
- hosts: servers
remote_user: root
gather_facts: no
tasks:
- name: add group nginx
group: name=nginx state=present
- name: add user nginx
user: name=nginx state=present
- name: install nginx
apt: name=nginx state=present
- name: html page
copy: src=/root/demo1/files/index.html dest=/usr/share/nginx/html/index.html
- name: start nginx
service: name=nginx state=started enabled=yes
- name: add ufw rule
shell: ufw allow 80/tcp利用 playbook 安装 mysql
- hosts: websrvs
remote_user: root
tasks:
- name: install packages
yum: name=libaio,perl-Data-Dumper,per-Getopt-Long
- name: create mysql group
group: name=mysql gid=306
- name: create mysql user
user: name=mysql uid=306 group=mysql shell=/sbin/nologin system=yes create_home=no home=/data/mysql
- name: copy tar to remote host and file mode
unarchive: src=/data/ansible/files/mysql-5.6.46-linux-glibc2.12-x86_64.tar.gz dest=/usr/local/ owner=root group=root
- name: crate link /usr/local/mysql
file: src=/usr/local/mysql-5.6.46-linux-glibc2.12-x86_64 dest=/usr/local/mysql state=link
- name: data dir
shell: chdir=/usr/local/mysql/ ./script/mysql_install_db --datadir=/data/mysql -- user=mysql
tags: data
- name: config my.conf
copy: src=/data/ansible/files/my.conf dest=/etc/my.cnf
- name: service script
shell: /bin/cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
- name: enable service
shell: /etc/init.d/mysqld start: chkconfig --add mysqld; chkconfig mysql on
tags: service
- name: PATH variable
copy: content='PATH=/usr/local/mysql/bin:$PATH' dest=/etc/profile.d/mysql.sh
- name: secure script
script: /data/ansible/files/secure_mysql.sh
tags: script
Playbook 中使用 handlers 和 notify
Handlers 本质是 task list, 类似于 mysql 中的触发器触发行为, 其中的 task 与前述的 task 并没有本质上的不同, 主要用于当关注的资源发生变化时, 才会采取一定的操作, 而 Notify 对应的 action 可用于在每个 play 的最后触发, 这样可以避免多次有改变发生时每次都执行指定的操作, 尽在所有的变化发生完成后一次性地执行指定操作. 在 notify 中列出的操作称为 handler, 也即 notify 中调用 handler 中定义的操作
案例:
---
- hosts: websrvs
remote_user: root
tasks:
- name: install httpd
yum: name=httpd state=present
- name: install configure file
copy: src=files/httpd.conf dest=/etc/httpd/conf
notify: restart httpd
handlers:
- name: restart httpd
service: name=httpd state=restartedplaybook 中使用 tags 组件
在 playbook 文件中, 可以利用 tags 组件, 为特定 task 指定标签, 当在执行 playbook 时, 可以只执行特定 tags 的 task, 而非整个 playbook 文件
案例: httpd.yml
---
# tags example
- hosts: websrvs
remote_user: root
gater_facts: no
tasks:
- name: install httpd
yum: name=httpd state=present
- name: install configure file
copy: src=files/httpd.conf dest=/etc/httpd/conf/
tags: conf
- name: start httpd service
tags: service
service: name=httpd state=started enabled=yes执行指定的 tags
ansible-playbook -t conf,service httpd.ymlplaybook 中使用变量
变量名: 仅能由字母, 数字和下划线, 且只能以字母开头
变量定义
key=value范例:
http_port=80变量调用方式:
通过 调用变量, 且变量名前后建议加空格, 有时用”“ 才生效
变量来源:
ansible 的 setup facts 远程主机的所有变量都可直接调用
通过命令行指定变量, 优先级最高
在 playbook 文件中定义变量
范例: var2.yml
---
- hosts: servers
remote_user: root
vars:
- username: xiaolv
- groupname: xiaolv
tasks:
- name: add group
group: name={{ groupname }} state=present
- name: add user
user: name={{ username }} state=present
# 注意:name= 后面不要加空格在 playbook 文件中定义变量
范例:
- hosts: websrvs
remote_user: root
tasks:
- name: create group
group: name={{ groupname }} state=present
- name: create user
user: name={{ username }} group={{ groupname }} state=present
ansible-playbook -e "username=user2 groupname=group2" var3.yml使用变量文件
可以在一个独立的 playbook 文件中定义变量, 在另一个 playbook 文件中引用变量文件中的变量,比 playbook 中定义的变量优先级更高
vars.yml
package_name: mysql-server
service_name: mysqlinstall_app.yml
---
- hosts: servers
remote_user: root
vars_files:
- vars.yml
tasks:
- name: install package
apt: name={{ package_name }}
tags: install
- name: start service
service: name={{ service_name }} state=started主机清单中定义变量
主机变量
在 inventory 主机清单文件中为指定的主机定义变量以便于在 playbook 中使用
范例:
[websrvs]
www1.example.com http_port=80 maxRequestPerChild=808
www2.example.com http_port=8080 maxRequestPerChild=909组(公共)变量
在 inventory 主机清单文件中赋予给指定组内所有主机上的在 playbook 中可用的变量
范例:
[websrvs]
www1.example.com
www2.example.com
[websrvs:vars]
domain=example.comtemplate 模板
模板是一个文本文件, 可以作为生成文件的模板, 并且模板文件中可以嵌套 jinja2 语法
jinja2 语法
jinja2 语言使用字面量, 有下面形式
字符串: 使用单引号或双引号
数字: 整数, 浮点数 列表: [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 语法详细语法可参考https://doc.yonyoucloud.com/doc/jinja2-docs-cn/index.html
template
template 功能: 可以根据和参考模块文件, 动态生成相似的配置文件 template 文件必须存放于 template 目录下, 且命名为.j2 结尾 yaml/yml 文件需和 template 目录平级, 目录结构如下:
./ |—temnginx.yml |—templates |___nginx.conf.j2
范例: 利用 template 同步 nginx 配置文件
# 准备templates/nginx.conf.j2文件
# vim temnginx.yam
---
- hosts: websrvs
remote_user: root
tasks:
- name: template config to remote hosts
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conftemplate 模块只能在 playbook 中使用,不能在 ansible 命令中单独使用 template 的作用根据模板生成文件并拷贝到目标位置
执行:
ansible-playbook temnginx.ymltemplate 变更替换
范例:
修改文件 nginx.conf.j2
vim templates/nginx.conf.j2
worker_processes {{ ansible_processor_vcpus }}变量来源:
setup 模块的变量(系统变量)
主机清单中的变量
变量文件中的变量
yaml 文件中定义的变量
ansible-playbook 中使用-e 选项传递的变量
角色中定义的变量
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
notify: restart nginx
- name: start service
service: name=nginx state=started enabled=yes
handlers:
- name: restart nginx
service: name=nginx state=restartedplaybook 使用 when
when 语句,可以实现条件测试,如果需要根据变量,facts 或此前任务的执行结果来作为某 task 执行与否的前提时,
要用到条件测试,通过在 task 后添加 when 子局即可使用条件测试,jinja2 的语法格式
范例:
---
- hosts: websrvs
remote_user: root
tasks:
- name: "shutdown RedHat flavored systems"
command: /sbin/shutdown -h now
when: ansible_os_family == "RedHat"范例:
---
- 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: restart Nginx
service: name=nginx state=restarted
when: ansible_distribution_major_version == "6"playbook 使用迭代 with_items
迭代:当有需要重复性执行的任务时,可以使用迭代机制 对迭代项的引用,固定变量名为”item” 要在 task 中使用 with items 给定要迭代的元素列表
列表元素格式:
字符串
字典
范例:
---
- hosts: websrvs
remote_user: root
tasks:
- name: add serveral users
user: name={{ item }} state=present group=wheel
with_items:
- testuser1
- testuser2范例:
---
- hosts: websrvs
remote_user: root
tasks:
- name: add some groups
group: name={{ item }} state=present
with_items:
- nginx
- mysql
- apache
- name: add some users
user: name={{ item.name }} group={{ item.group }} state=present
with_items:
- { name: "nginx", group: "nginx" }
- { name: "mysql", group: "mysql" }
- { name: "apache", group: "apache" }