一.Ansible Playbook基本介绍
1.Playbook 介绍
Ansible Playbook 是 Ansible 的核心组件之一,它是一个用于配置管理、应用部署和任务自动化的文本文件,使用 YML格式编写。YML 的语法简洁明了,易于阅读和编写,使得用户可以方便地描述自动化任务的步骤和目标。
playbook是由一个或者多个play组成的列表,可以让这些列表按事先编排的机制执行;所谓task是调用ansible的具体模块,在模块参数中可以使用变量。模块执行是幂等性的,意味着多次执行结果相同。使用yaml语言编写playbook,后缀名一般为.yml
Playbook优势:
可重复性和一致性
一旦 Playbook 编写完成并经过测试,就可以在不同的环境中重复使用。无论是开发环境、测试环境还是生产环境,只要目标主机符合 Playbook 的要求,就可以通过运行 Playbook 来实现相同的配置和部署操作,保证了配置的一致性。
易于阅读和维护
由于采用 YAML 语言编写,Playbook 的结构清晰、语法简单。相比于复杂的脚本语言,非开发人员也能够相对容易地理解和修改 Playbook 的内容。而且可以方便地对 Playbook 进行版本控制,便于团队协作开发和维护自动化流程。
幂等性
Ansible Playbook 中的任务通常是幂等的,这意味着多次运行相同的 Playbook(在没有外部状态改变的情况下)不会产生额外的副作用。
2.Playbook 文件格式
文件的第一行应该以 "---" (三个连字符)开始,表明YMAL文件的开始。
在同一行中,#之后的内容表示注释,类似于shell
YMAL中的列表元素以”-”开头然后紧跟着一个空格,后面为元素内容。
同一个列表中的元素应该保持相同的缩进。否则会被当做错误处理。
play中hosts,variables,roles,tasks等对象的表示方法都是键值中间以":"分隔表示,":"后面还要增加一个空格。
缩进:使用空格缩进(而非Tab),通常使用2个空格
执行playbook文件格式为:
ansible-playbook playbook.yml |
在我们编写完playbook文件后,可以使用文件检查命令:
ansible-playbook --syntax-check playbook.yml |
3.Playbook 核心元素
(1)Hosts(主机组):定义了Playbook将要操作的目标机器集合。可以是单个主机名、IP地址,也可以是主机组名。
(2)Tasks(任务列表):构成Play的主要部分,定义了在目标主机上执行的具体操作序列。每个任务通常关联一个特定的Ansible模块及参数。
(3)Variables(变量):用于在Playbook中传递和管理配置信息,支持多种设置方式(如直接赋值、变量文件、环境变量、角色默认变量等),增加了Playbook的灵活性和可重用性。
(4)Templates(模板):使用Jinja2模板引擎动态生成配置文件。允许根据变量插入动态内容,适用于需要根据不同环境定制配置的情况。
(5)Handlers(处理程序):特殊类型的任务,仅当被其他任务通过notify属性明确告知时才会执行。常用于服务重启等需要在特定条件满足时进行的操作。
二.Ansible Playbook 剧本编写
1.Playbook 基本组件格式
1.1 Hosts
--- - hosts: node1 remote_user: root |
hosts是一个主机组名称或IP,Ansible后续会在定义的主机组或主机执行后续定义的任务,remote_user指定了主机在执行这些任务时所用的用户。
1.2 Tasks
--- - hosts: node1 remote_user: root tasks: - name: test_playbook shell: touch /opt/hzyfile.txt |
Tasks在hosts字段之后,通过tasks关键字来定义一系列要执行的任务,其下面的name参数同样是对任务的描述,在执行过程中会打印出来,shell是ansible模块名字。
执行该文件的反馈:
[root@server playbook]# ansible-playbook file.yml |
1.3 Handlers
在 Playbook 中,handlers部分用于定义处理器。通常在tasks部分通过notify关键字来触发处理器。执行task之后,服务器发生变化之后要执行的一些操作,就会使用到handlers。
Handlers触发条件:当某任务状态变为changed时,可以触发一个或多个预先定义好的处理程序。使用tags为任务和处理程序打标签,可以在执行Playbook时通过--tags或-t选项选择性地执行带有特定标签的任务。
2.Playbook 基本使用
在node1节点安装nginx
2.1 yml文件编写
[root@server playbook]# vi nginx1.yml --- - name: nginx hosts: node1 remote_user: root tasks: - name: Install epel yum: name: epel-release.noarch state: latest - name: Install nginx yum: name: nginx state: present - name: Copy nginx configure file copy: src: /etc/ansible/playbook/nginx.conf dest: /etc/nginx/nginx.conf backup: yes - name: Create index.html shell: echo "hzy AnsiblePlaybook used!" > /usr/share/nginx/html/index.html - name: Start nginx service: name: nginx state: restarted - name: firewalld stoped service: name: firewalld state: stopped |
2.2 Nginx.conf
events { worker_connections 1024; } http { server { listen 8080; server_name 192.168.100.20:8080; location / { index index.html; } } } |
2.3 检查nginx.yml文件:
[root@server playbook]# ansible-playbook --syntax-check nginx2.yml [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details playbook: nginx2.yml |
2.4 执行nginx.yml文件
在浏览器中进行访问:
3.Playbook Handlers Tags
在 Playbook 中,handlers部分用于定义处理器。通常在tasks部分通过notify关键字来触发处理器。执行task之后,服务器发生变化之后要执行的一些操作,就会使用到handlers。
Handlers触发条件:当某任务状态变为changed时,可以触发一个或多个预先定义好的处理程序。使用tags为任务和处理程序打标签,可以在执行Playbook时通过--tags或-t选项选择性地执行带有特定标签的任务。
--- - name: Install nginx hosts: node2 remote_user: root tasks: - name: Install epel yum: name: epel-release.noarch state: latest - name: Install nginx yum: name: nginx state: present - name: Copy nginx configure file copy: src: /etc/ansible/playbook/nginx2.conf dest: /etc/nginx/nginx.conf backup: yes notify: reload #当配置文件发生改变时,通知相应的reload tags: reloadnginx - name: Start nginx service: name: nginx state: restarted tags: startnginx - name: Create index.html shell: echo "hzy nginx_handlers" > /usr/share/nginx/html/index.html handlers: - name: reload service: name: nginx state: restarted |
Nginx2.conf文件:
events { worker_connections 1024; } http { server { listen 8080; server_name 192.168.100.30:8080; location / { index index.html; } } } |
检查并执行nginx2.yml文件
测试:
3.1 测试1:测试Tag
先关闭node2的nginx
[root@server playbook]# ansible -m shell -a "systemctl stop nginx" node2 |
运行剧本并引用标签startnginx:
[root@server playbook]# ansible-playbook nginx2.yml -t startnginx |
可以发现,直接跳过了剧本中的前面所有过程,直接通过标签运行startnginx。
3.2 测试2:测试notify
修改配置文件:
events { worker_connections 1024; } http { server { listen 9090; server_name 192.168.100.30:9090; location / { index index.html; } } } |
重新执行playbook并运行剧本并引用标签reloadnginx
[root@server playbook]# ansible-playbook nginx2.yml -t reloadnginx |
此时,我们可以发现,执行的就是我们yml文件中定义的notify字段和reload,当配置文件发生变化时,只有copy模块真正执行后,才会去调用下面的handlers相关的操作,通知了handlers,重启nginx。
访问端口查看是否重启成功:
4.Playbook variables
4.1 Playbook中定义变量
在Ansible剧本中,vars 是用来定义变量的一个关键字,它允许你为任务、play或整个剧本设定变量值。这些变量可以在后续的任务中被引用,以提高剧本的灵活性和重用性。
在模块下使用变量,作用域仅限于当前模块。
- hosts: node1 vars: my_var: "Hello, World!" tasks: - name: Use playbook-defined variable shell: echo "{{ my_var }}" > /root/hello.txt |
--- - name: yum tools hosts: node1 tasks: - name: install yum: name: "{{pkg}}" state: present vars: pkg: - net-tools - unzip |
若定义在配置清单全局,那么当前hosts下所有的模块全部可以使用
--- - name: install vars nginx hosts: node2 remote_user: root vars: web: - nginx nginx_vars: - "this is nginx_vars" tasks: - name: install nginx yum: name: "{{web}}" state: present - name: Copy nginx configure file copy: src: /etc/ansible/playbook/nginx.conf dest: /etc/nginx/nginx.conf backup: yes - name: change html shell: echo "{{nginx_vars}}" > /usr/share/nginx/html/index.html - name: start nginx service: name: nginx state: started |
4.2 Playbook使用vars_files
我们可以在playbook文件内通过vars_files字段引用变量,首先把所有的变量定义到某个文件内,然后在playbook文件内使用vars_files参数引用这个变量文件
定义变量文件var_file.yml:
pkg: - mariadb - mariadb-server - net-tools |
编写playbook:
--- - hosts: node3 name: install mariadb vars_files: /etc/ansible/playbook/var_file.yml tasks: - name: install mariadb and server yum: name: "{{pkg}}" state: latest - name: start mariadb service: name: mariadb state: started |
检查是否执行成功:
4.3 Playbook Inventory主机清单
Ansible Inventory 文件是一个纯文本文件,用于定义 Ansible 执行命令的目标主机和组,以及这些主机和组的变量和属性。
在/etc/ansible/hosts文件下可以设置主机组,也可以设置主机组变量:
--- [group] host1 host2 [group:vars] dir=hzy file=socool |
编写yml文件:
--- - hosts: group name: hzyfile tasks: - name: mkdir dir file: path: /root/{{dir}} state: directory - name: touch file file: path: /root/{{dir}}/{{file}} state: touch |
主机清单中定义变量,只要hosts配置的是主机清单中设置变量的组,可以直接使用变量。如果hosts配置的不是主机清单中设置变量的组,变量不可识别。
/etc/ansible/hosts定义变量(针对单个主机定义,针对主机组进行定义)playbook中定义的变量,主机组定义的变量优先级高于整合组定义的变量3.主机定义的变量优先级高于主机组定义的变量。
4.4 通过同名组-目录定义变量
[group] host1 host2 [group:vars] dir=hzy file=socool |
在ansible的hosts文件下,我们定义了主机组,主机组的名为group,并且在文件中设置了变量vars,调用变量的方式为:在ansible目录下创建和主机组同名的目录,在该目录下创建的yml文件可以直接调用hosts目录下同名主机组设置的变量。
例如,我在hosts文件下定义了group主机组,那么我只需要在ansible目录下创建group目录,在group目录下创建的文件就都可以调用group主机组的变量。
[root@server ansible]# mkdir group [root@server ansible]# cd group/ [root@server group]# vi 111.yml --- - hosts: group name: hzyfile tasks: - name: touch file file: path: /root/{{dir}}/111 state: touch [root@server group]# ansible-playbook 111.yml |
4.5 命令行设置变量
这个就简单直接了:
--- - hosts: node1 name: hzyfile tasks: - name: touch file file: path: /root/{{dir}} state: directory [root@server playbook]# ansible-playbook 222.yml -e "dir=user" |
若有多个变量,可以使用-e “vars1” -e “vars2”这样的方式进行书写。
5.Playbook 循环
5.1 with_item
with_items是最常用的循环方法,适用于列表中的每个元素执行任务。
--- - hosts: node1 name: while touch file remote_user: root tasks: - name: file file: path: "/tmp/{{ item }}" mode: "600" state: touch with_item: - cris - aino - rond |
5.2 with_dict
with_dict用于迭代字典的键值对
--- - hosts: node1 name: while touch file remote_user: root tasks: - name: create user: name: "{{ item.key }}" uid: "{{ item.value.uid }}" with_dict: ron: uid: 1007 hzy: uid: 1008 |
5.3 loop
处理复杂数据结构时,loop更加灵活
--- - hosts: node2 name: while touch file remote_user: root tasks: - name: create user: name: "{{ item.name }}" uid: "{{ item.uid }}" loop: - { name: 'hzy', uid: 1008 } - { name: 'ron', uid: 1007 } |
6.Playbook 条件判断
--- - hosts: all-servers user: root gather_facts: True tasks: - name: use when shell: touch /tmp/when.txt when: ansible_ens33.ipv4.address == "192.168.100.30" |
判断主机ens32网卡的IP,若是IP为192.168.100.30的主机,就创建/tmp/when.txt文件
报错原因:host3主机的网卡名为ens32,检测不到ens33网卡。
when的值是一个条件表达式,如果条件判断成立,这个task就执行,如果判断不成立,则task不执行。
7.Playbook Template
在 Ansible 的角色管理中,Templates 模块是用来处理模板文件的模块,它允许我们在角色中使用模板文件来生成配置文件或其他文本文件。通过 Templates 模块可以将变量和逻辑结构化地嵌入到模板文件中,实现更加灵活和动态的文件生成过程。其主要作用如下。
创建模板文件:在角色的 templates 目录下创建模板文件,通常以 .j2 扩展名结尾,例如 config_file.conf.j2。
在playbook中运行:在任务中使用 Templates 模块引用模板文件,并指定生成的目标文件路径。在模板文件中使用变量:在模板文件中使用Jinja2模板语法,引用Ansible变量并添加逻辑控制语句
参数化模板文件通过在 Tasks 中传递变量给 template 模块来定制每个部分的值。
7.1 Jinja2
Jinja2 是一个 Python 模板引擎,Ansible 使用 Jinja2 模板来动态生成配置文件、脚本等内容。Jinja2 模板允许在模板中使用变量、条件判断、循环等功能,从而可以根据不同的环境和需求生成不同的输出。
#循环表达式 {% for i in EXPR %} {% endfor %} #条件判断 {% if EXPR %} {% elif EXPR %} {% else %} {% endif %} #注释 {# COMMENT #} |
[root@server ~]# vi jinja2.yml - hosts: node1 tasks: - name: copy template file template: src: motd.j2 dest: /etc/motd |
[root@server ~]# vi motd.j2 Welcome to {{ ansible_fqdn }} This system total mem is : {{ ansible_memtotal_mb }} MB This system free mem is : {{ ansible_memfree_mb }} MB |
{{ ansible_fqdn }}ansible_fqdn 是 Ansible 内置的一个事实变量(Ansible facts),它会获取目标主机的完全限定域名,{{ ansible_memtotal_mb }}同样属于 Ansible 的事实变量,ansible_memtotal_mb 会获取目标主机的总内存大小,单位是兆字节(MB)。{{ ansible_memfree_mb }}这也是基于 Ansible 事实收集得到的变量,它表示目标主机当前可用的空闲内存大小,单位同样为兆字节(MB)。
7.2 Jinja2管理nginx
Playbook文件:
- hosts: node2 vars: http_port: 80 server_name: www.baidu.com tasks: - name: copy template: src: proxy_7.conf.j2 #src:指定了 Jinja2 模板文件为 proxy_7.conf.j2 dest: /etc/nginx/conf.d/proxy_7.conf notify: reload nginx handlers: - name: reload nginx systemd: name: nginx state: reloaded |
准备配置文件
upstream {{ server_name }} { {% for n in range(21) %} server 192.168.100.{{ n }}:{{ http_port }} {% endfor %} } server{ listen 80; server_name {{ server_name }}; location / { root /code index index.html proxy_pass http://{{ server_name }} proxy_set_header Host $http_host } |
通过 {% for n (range(21) 会生成 0 到 20 的整数序列)。在每次循环中,会生成一条类似 server 192.168.100.[具体数字]:80 的后端服务器配置语句(其中端口号使用了前面定义的 http_port 变量,即 80),从逻辑上看是想配置 21 个位于 192.168.100.x 网段的后端服务器指向这个 upstream,但实际应用中要确保这些 IP 对应的服务器确实提供了相应的服务且端口正确开放等情况。
执行playbook文件
查看/etc/nginx/conf.d/prosy_7.conf
8.Playbook Roles
8.1 Roles介绍
roles(⻆⾊): 就是通过分别将variables, tasks及handlers等放置于单独的⽬录中,并可以便捷地调⽤它们的⼀种机制。
假设我们要写⼀个playbook来安装管理lamp环境,那么这个playbook就会写很⻓。所以我们希望把这个很⼤的⽂件分成多个功能拆分, 分成apache管理,php管理,mysql管理,然后在需要使⽤的时候直接调⽤就可以了,以免重复写。就类似编程⾥的模块化的概念,以达到代码复⽤的效果。
Roles模块含有files、templates、tasks、handlers、vars、defaults、meta七个目录,每个目录的作用如下。
l files:用来存放由 copy 模块或script模块调用的文件。
l templates:用来存放jinjia2模板,template模块会 自动在此目录中寻找jinjia2模板文件。
l tasks:此目录应当包含一个main.yml文件, 用于定义此角色的任务列表,此文件可以使用include包含其它的位于此目录的 task 文件。
l handlers:此目录应当包含一个main.yml文件,用于定义此角色中触发条件时执行的动作。
l vars:此目录应当包含一个main.yml文件,用于定义此角色用到的变量。
l defaults:此目录应当包含一个main.yml文件,用于为当前角色设定默认变量。
l meta:此目录应当包含一个main.yml文件,用于定义此角色的特殊设定及其依赖关系
先创建roles命令的目录以及创建全局变量目录,然后在roles目录中分别创建以各角色名称命令的目录(如httpd在每个角色命令的目录中分别创建file、handlers、tasks、templates、meta、defaults和vars目录,用不到的目录可以创建为空),接着在每个角色的handlers、tasks、meta、defaults、vars、目录下创建main.yml文件,不能自定义。最后在playbook文件中调用各角色。
8.2搭建LAMP架构
在roles目录下创建相应的目录
[root@server roles]# mkdir /etc/ansible/roles/{httpd,mysql,php}/{files,templates,tasks,handlers,vars,defaults,meta} -p |
创建应用的yml文件:
[root@server roles]# touch /etc/ansible/roles/{httpd,mysql,php}/{tasks,handlers,vars,defaults,meta}/main.yml |
查看目录结构:
[root@server roles]# tree . ├── httpd │ ├── defaults │ │ └── main.yml │ ├── files │ ├── handlers │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ ├── templates │ └── vars │ └── main.yml ├── mysql │ ├── defaults │ │ └── main.yml │ ├── files │ ├── handlers │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ ├── templates │ └── vars │ └── main.yml └── php ├── defaults │ └── main.yml ├── files ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── tasks │ └── main.yml ├── templates └── vars └── main.yml 24 directories, 15 files |
编写roles模版:
[root@server roles]# vi httpd/tasks/main.yml - name: ensure apache is at the latest version yum: pkg={{pkg}} state=latest [root@server roles]# vi httpd/vars/main.yml pkg: httpd [root@server roles]# vi mysql/tasks/main.yml - name: ensure php is at the latest version yum: pkg={{pkg}} state=latest [root@server roles]# vi mysql/vars/main.yml pkg: mariadb* [root@server roles]# vi php/tasks/main.yml - name: ensure php is at the latest version yum: pkg={{pkg}} state=latest [root@server roles]# vi php/vars/main.yml pkg: php 编写roles总脚本 [root@server ansible]# vi web.yml - hosts: node3 remote_user: root roles: - httpd - mysql - php |
执行playbook