Ansible playbook的使用

xiaolv
4
2025-11-07

介绍

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或yaml
List 列表

列表由多个元素组成, 且所有元素前均使用”-“打头

范例:

 # 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:appsrvs

remoute_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为wang

task 列表和 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 httpd

playbook 实现

 ---
 - 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=yes

playbook 命令

语法格式:

 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 websrvs

playbook 初步案例

利用 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=restarted

playbook 中使用 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.yml

playbook 中使用变量

变量名: 仅能由字母, 数字和下划线, 且只能以字母开头

变量定义

 key=value

范例:

 http_port=80

变量调用方式:

通过 调用变量, 且变量名前后建议加空格, 有时用”“ 才生效

变量来源:

  1. ansible 的 setup facts 远程主机的所有变量都可直接调用

  2. 通过命令行指定变量, 优先级最高

在 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: mysql

install_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.com

template 模板

模板是一个文本文件, 可以作为生成文件的模板, 并且模板文件中可以嵌套 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.conf

template 模块只能在 playbook 中使用,不能在 ansible 命令中单独使用 template 的作用根据模板生成文件并拷贝到目标位置

执行:

 ansible-playbook temnginx.yml

template 变更替换

范例:

修改文件 nginx.conf.j2

vim templates/nginx.conf.j2

 worker_processes {{ ansible_processor_vcpus }}

变量来源:

  1. setup 模块的变量(系统变量)

  2. 主机清单中的变量

  3. 变量文件中的变量

  4. yaml 文件中定义的变量

  5. ansible-playbook 中使用-e 选项传递的变量

  6. 角色中定义的变量

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=restarted

playbook 使用 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" }


动物装饰