Ansible Playbook
一、Host Inventory(主机清单)
Inventory支持对主机进行分组,每个组内可以定义多个主机,每个主机都可以定义在任何一个或多个主机组内。
inventory 文件
默认路径为/etc/ansible/hosts
Ansible Inventory 文件是一个纯文本文件,用于定义 Ansible 执行命令的目标主机和组,以及这些主机和组的变量和属性。
Inventory 文件的构成包括:
1.主机与组
-
主机:一个主机就是目标机器,可以是 IP 地址、域名或在 SSH 配置中定义的别名,也可以使用
ansible_host
指定。 -
组:可以将多个主机划分到同一个组中,并可以对组进行操作。
[WebServer] 192.168.1.10 192.168.1.11 192.168.1.12 [DatabaseServer] 192.168.1.20 192.168.1.21
2.变量
-
主机变量:为单个主机指定变量,使用主机名或 IP 地址作为标识符,变量可以设置为一个或多个值。
[ServerA] 192.168.1.10 http_port=80 https_port=443 [ServerB] serverb.example.com ansible_user=admin ansible_password=1234
- 组变量:在组名之后,使用
vars
关键字设置变量,可以在组间共享变量。示例:
#所有主机指定 `ansible_ssh_user` 变量,这意味着每个主机都具有该变量。 #为每个主机定义唯一的 `name` 变量。 [WebServer] 192.168.1.10 name=webserver1 192.168.1.11 name=webserver2 192.168.1.12 name=webserver3 [DatabaseServer] 192.168.1.20 name=dbserver1 192.168.1.21 name=dbserver2 [all:vars] ansible_ssh_user=centos
3.组的嵌套
将一个组嵌套到另一个组中,在 Inventory 文件中使用 :children
关键字。
[WebServer] 192.168.1.10 192.168.1.11 192.168.1.12 [DatabaseServer] 192.168.1.20 [Production:children] WebServer DatabaseServer
4.别名
使用 Alias(别名)来引用 Inventory 中的主机。
#为 `WebServer` 和 `DatabaseServer` 组定义别名,并将它们作为两个分离的组 `Web` 和 `Db` 的成员 #创建了一个名为 `Production` 的组,该组由这两个组的别名组成。 [WebServer] 192.168.1.10 192.168.1.11 192.168.1.12 [DatabaseServer] 192.168.1.20 [Web:children] WebServer [Db:children] DatabaseServer [Production] @Web @Db
inventory 中的变量
在 hosts 文件
中为主机或组定义变量,在 playbook 中
可以直接调用变量。
Inventory变量名 | 含义 |
---|---|
ansible_host | ansible连接节点时的IP地址 |
ansible_port | 连接对方的端口号,ssh连接时默认为22 |
ansible_user | 连接对方主机时使用的用户名。不指定时,将使用执行ansible或ansible-playbook命令的用户 |
ansible_password | 连接时的用户的ssh密码,仅在未使用密钥对验证的情况下有效 |
ansible_ssh_private_key_file | 指定密钥认证ssh连接时的私钥文件 |
ansible_ssh_common_args | 提供给ssh、sftp、scp命令的额外参数 |
ansible_become | 允许进行权限提升 |
ansible_become_method | 指定提升权限的方式,例如可使用sudo/su/runas等方式 |
ansible_become_user | 提升为哪个用户的权限,默认提升为root |
ansible_become_password | 提升为指定用户权限时的密码 |
二、Playbook 剧本
1.Playbook的组成部分
1)Tasks:任务,即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行;
2)Variables:变量;
3)Templates:模板;
4)Handlers:处理器,当changed状态条件满足时,(notify)触发执行的操作;
5)Roles:角色。
2.编写Playbook
基本格式
- 在单一文件第一行,用连续三个连字号“-” 开始,还有选择性的连续三个点号( … )用来表示文件的结尾;
- 次行开始正常写Playbook的内容,一般建议写明该Playbook的功能;
- 使用#号注释代码;
- 缩进必须是统一的,不能空格和tab混用;
- 缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的;
- YAML文件内容是区别大小写的,key/value的值均需大小写敏感;
- 多个key/value可同行写也可换行写,同行使用,分隔;
- v可是个字符串,也可是另一个列表;
- 一个完整的代码块功能需最少元素需包括 name 和 task;
- 一个name只能包括一个task;
- YAML文件扩展名通常为yml或yaml
xxx.yaml/xxx.yml --- #表示开始 - name: #指定play的名称 hosts: #指定主机清单中定义的主机组名 remote_user: #指定远程主机的执行用户 grather_facts: ture|fales #指定是否要收集远程主机的facts信息 vars: #自定义变量,只能在当前play有效 - 变量1: 值1 #格式为key: value - 变量2: 值2 tasks: #定义任务列表,默认从上往下依次执行 - name: #定义任务的名称 模块名: 模块参数 ignore errors: true #忽略任务的失败 - name: #可以定义多个任务 模块名: 模块参数 notify: 任务名 #如以上操作后为changed的状态时,会通过notify指定的名称触发对应名称的handlers操作 ##条件判断## - name: 模块名: 模块参数 when: #定义条件表达式(== != > < >= <=),条件成立时执行此task任务,否则不执行任务 ##循环## - name: 模块名: 模块参数={{item}} with_items: #定义循环列表 ##tags模块,标签## - name: 模块名: 模块参数 tags: - 标签1 - 标签2 handlers: - name: 任务名 #和notify中的任务名相同 模块名: 模块参数 #无注释版 --- - name: hosts: remote_user: grather_facts: ture|fales vars: - 变量1: 值1 - 变量2: 值2 tasks: - name: 模块名: 模块参数 ignore errors: true - name: 模块名: notify: 任务名 - name: 模块名: when: - name: 模块名: 模块参数={{item}} with_items: handlers: - name: 任务名 模块名: 模块参数
语句的横向/纵向写法
横向写法一般是数组类型
纵向写法一般是列表类型
task任务的模块语法格式 横向格式: 模块名: 参数1=值 参数2={{变量名}} ... 纵向格式: 模块名: 参数1: 值 参数2: "{{变量名}}" ... with_items 和 vars 的语法格式 横向格式: with_items: ["值1", "值2", "值3", ...] 值为对象(键值对字段)时: with_items: - {key1: "值1", key2: "值2"} - {key1: "值3", key2: "值4"} ... 纵向格式: with_items: - 值1 - 值2 - 值3 ... 值为对象(键值对字段)时: with_items: - key1: "值1" key2: "值2" - key1: "值3" key2: "值4" ...
三、Playbook实例
1.编写yum安装nginx的playbook
#编写yaml文件,安装nginx的剧本 vim test.yaml --- - name: first play hosts: dbservers remote_user: root gather_facts: false tasks: - name: firewalld service: name=firewalld state=stopped enabled=no - name: selinux command: '/usr/sbin/setenforce 0 ' ignore_errors: true - name: mount mount: src=/dev/sr0 path=/mnt state=mounted fstype=iso9660 - name: local.repo copy: src=/etc/yum.repos.d/local.repo dest=/etc/yum.repos.d/ - name: epel.repo copy: src=/etc/yum.repos.d/epel.repo dest=/etc/yum.repos.d/ - name: nginx install yum: name=nginx state=latest - name: configuration file copy: src=/usr/local/nginx/conf/nginx.conf dest=/etc/nginx/nginx.conf notify: "reload nginx" - name: start nginx service: name=nginx state=started enabled=yes handlers: - name: reload nginx service: name=nginx state=reloaded
2.参数补充
#执行playbook
ansible-playbook xx.yaml/yml [参数]
常用参数 | 描述 | |
---|---|---|
--syntax-check |
检查yaml文件的语法是否正确 | |
--list-task |
检查tasks任务 | |
--list-hosts |
检查生效的主机 | |
--start-at-task=' name ' |
指定从某个task开始运行 | 一般用于剧本较长且不想从头重复执行的场景 |
-k |
用来交互输入ssh密码 | -ask-pass |
-K | 用来交互输入sudo密码 | -ask-become-pass |
-u | 指定用户 |
3.变量的定义和引用
vars: #自定义变量,只能在当前play有效 - 变量1: 值1 #格式为key: value - 变量2: 值2 tasks: #在任务列表中引用变量 -name: module: {{变量1}}
方式一:在yaml文件中定义和引用
vim test2.yml --- - name: second play hosts: dbservers remote_user: root gather_facts: true vars: - groupname: mysql - username: nginx tasks: - name: create group group: name={{groupname}} system=yes gid=306 #使用 {{key}} 引用变量的值 - name: create user user: name={{username}} uid=306 group={{groupname}} - name: copy file copy: content="{{ansible_default_ipv4.network}}" dest=/opt/vars.txt #在setup模块中可以获取facts变量信息 #ansible_default_ipv4为facts变量信息中的字段 #ansible_default_ipv4.network中的 .network表示只提取信息中network部分
ansible-play test2.yml
方式二:在命令行定义
ansible-playbook test1.yaml -e "username=nginx" #通过 -e 参数传递一个额外的变量 "username=nginx" 给 playbook #playbook 将会使用变量 "username" 的值设置为 "nginx"
4.指定远程主机sudo切换用户
使用-k
和 -K
参数实现。
编写剧本
vim test2.yaml --- - hosts: webservers remote_user: test2 become: yes #2.6版本以后的参数,之前是sudo,意思为切换用户运行 become_user: root #指定sudo用户为root tasks: - name: ts command: ls ./
执行剧本
#执行playbook,加上参数-k和-K
ansible-playbook test2.yml -k -K
5.when条件判断
用法
在Ansible中,提供的唯一一个通用的条件判断是when指令
。
当when指令的值为true
时,则该任务执行,否则不执行该任务。
When指令一个比较常见的应用场景是实现跳过某个主机不执行任务 或者 只有满足条件的主机执行任务。
编写剧本,使用条件判断语句
#如果条件判断成功,则对应主机关机 vim test3.yaml --- - hosts: all remote_user: root tasks: - name: poweroff host command: /usr/sbin/poweroff -r now when: ansible_default_ipv4.address == "192.168.2.102" #when指令中的变量名不需要手动加上 {{}} 或 when: inventory_hostname == "<主机名>"
执行剧本
#执行剧本
ansible-playbook test3.yaml
6.迭代(循环结构)
Ansible提供了很多种循环结构,一般都命名为with_items
,作用等同于 loop 循环。
vim test4.yaml --- - name: play1 hosts: dbservers gather_facts: false tasks: - name: create file file: path: "{{item}}" state: touch with_items: [ /opt/a, /opt/b, /opt/c, /opt/d ] - name: play2 hosts: dbservers gather_facts: false vars: test: - /tmp/test1 - /tmp/test2 - /tmp/test3 - /tmp/test4 tasks: - name: create directories file: path: "{{item}}" state: directory with_items: "{{test}}" - name: play3 hosts: dbservers gather_facts: false tasks: - name: add users user: name={{item.name}} state=present groups={{item.groups}} with_items: - name: test1 groups: wheel - name: test2 groups: root 或 with_items: - {name: 'test1', groups: 'wheel'} - {name: 'test2', groups: 'root'} ansible-playbook test3.yaml
四、Playbook的模块
Templatetags
Roles
1.Template配置模板模块
用于生成配置模板文件,配合hosts中定义的变量,或者剧本中定义的变量,为不同主机生成不同的配置参数。
Jinja是基于Python的模板引擎。
Template类是Jinja的一个重要组件,可以看作是一个编译过的模板文件,用来产生目标文本,传递Python的变量给模板去替换模板中的标记。
怎么使用
1)先准备一个xxx.j2
配置模板文件,在文件中使用{{变量名}}
引用主机变量或者vars字段
自定义的变量 以及 facts信息字段
做变量的值;
2)在playbook剧本中的task任务定义template模块配置。
template: src--xxx.j2 文件路径 dest-远程主机文件路径
实例
1)先准备一个以 .j2
为后缀的 template 模板文件,设置引用的变量
mkdir /etc/ansible/playbook cp /etc/nginx/nginx.conf /etc/ansible/playbook/nginx.conf.j2
2)修改主机清单文件,使用主机变量定义一个变量名相同,而值不同的变量
#在Template 模板中定义的变量
vim /etc/ansible/hosts [webservers] 192.168.100.102 http_port=192.168.100.102:80 server_name=www.test1.com:80 root_dir=/var/www/html 192.168.100.103 http_port=192.168.100.103:80 server_name=www.test2.com:80 root_dir=/var/www/html [dbservers] 192.168.100.106 http_port=192.168.100.103:80 server_name=www.test3.com:80 root_dir=/var/www/html
3)编写 playbook并执行
#编写剧本 vim nginx1.yaml --- - name: new play hosts: webservers:dbservers remote_user: root gather_facts: yes tasks: - name: disable firewalld systemd: name: firewalld state: stopped enabled: no - name: disable selinux command: '/usr/sbin/setenforce 0' ignore_errors: true - name: copy local repo file copy: src: /etc/yum.repos.d/repo.bak/local.repo dest: /etc/yum.repos.d/ - name: mount cdrom mount: src=/dev/sr0 path=/mnt state=mounted fstype=iso9660 - name: install dependent packages with_items: - gcc - gcc-c++ - make - pcre-devel - zlib-devel - openssl-devel yum: name={{item}} state=present #name: "gcc,gcc-c++,make,pcre-devel,zlib-devel,openssl-devel" #state: present - name: unarchive nginx package unarchive: copy=yes src=/opt/nginx-1.24.0.tar.gz dest=/opt/ - name: install nginx shell: chdir=/opt/nginx-1.24.0/ ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_stub_status_module && make && make install - name: copy nginx config file template: src=/etc/ansible/playbook/nginx.conf.j2 dest=/usr/local/nginx/conf/nginx.conf notify: "reload nginx" - name: copy nginx systemd control script copy: src=/etc/ansible/playbook/nginx.service dest=/usr/lib/systemd/system/ - name: create nginx user user: name=nginx shell=/sbin/nologin create_home=no - name: start nginx service: name=nginx state=started enabled=yes - name: create root dir file: path={{root_dir}} state=directory - name: create index.html for 2.102 copy: content="this is test1" dest={{root_dir}}/index.html when: ansible_default_ipv4.address == "192.168.2.102" - name: create index.html for 2.103 copy: content="this is test2" dest={{root_dir}}/index.html when: ansible_default_ipv4.address == "192.168.2.103" - name: create index.html for 2.106 copy: content="this is test3" dest={{root_dir}}/index.html when: ansible_default_ipv4.address == "192.168.2.106" handlers: - name: reload nginx service: name=nginx state=reloaded
#执行剧本
ansible-playbook nginx1.yaml
2.tags模块
可以在一个playbook中为某个或某些任务定义“标签”,在执行此playbook时通过ansible-playbook命令使用–tags选项能实现仅运行指定的tasks。
playbook还提供了一个特殊的tags为always。
作用就是当使用always作为tags的task时,无论执行哪一个tags时,定义有always的tags都会执行。
3.roles模块
Roles用于层次性、结构化地组织playbook,适用于代码复用度较高的场景。
roles模块的作用(重要)
将playbook剧本中的各个play视作一个角色,将各个角色的tasks任务
、vars变量
、templates模板
、files文件
等内容放置在指定角色的目录中统一管理。
需要的时候可以在playbook中使用roles角色直接调用,即roles角色可以在playbook中实现代码的复用。
roles 的目录结构
cd /etc/ansible/ tree roles/ roles/ ├── web/ #相当于 playbook 中的 每一个 play 主题 │ ├── files/ │ ├── templates/ │ ├── tasks/ │ ├── handlers/ │ ├── vars/ │ ├── defaults/ │ └── meta/ └── db/ ├── files/ ├── templates/ ├── tasks/ ├── handlers/ ├── vars/ ├── defaults/ └── meta/
roles 内各目录含义解释
目录 | 含义 |
---|---|
files |
用来存放由 copy 模块或 script 模块调用的文件 |
templates |
用来存放 jinjia2 模板,template 模块会自动在此目录中寻找 jinjia2 模板文件 |
tasks | 此目录应当包含一个 main.yml 文件,用于定义此角色的任务列表,此文件可以使用 include 包含其它的位于此目录的 task 文件 |
handlers |
此目录应当包含一个 main.yml 文件,用于定义此角色中触发条件时执行的动作 |
vars | 此目录应当包含一个 main.yml 文件,用于定义此角色用到的变量 |
defaults |
此目录应当包含一个 main.yml 文件,用于为当前角色设定默认变量。 这些变量具有所有可用变量中最低的优先级,并且可以很容易地被任何其他变量覆盖。所以生产中我们一般不在这里定义变量 |
meta | 此目录应当包含一个 main.yml 文件,用于定义此角色的元数据信息及其依赖关系 |
在一个 playbook 中使用 roles 的步骤
1)创建以 roles 命名的目录;
#举个例子 mkdir /etc/ansible/roles/ -p #yum装完默认就有
2)创建全局变量目录(可选);
#举个例子 mkdir /etc/ansible/group_vars/ -p touch /etc/ansible/group_vars/all #文件名自己定义,引用的时候注意
3)在 roles 目录中分别创建以各角色名称命名的目录,如 httpd、mysql;
#举个例子 mkdir /etc/ansible/roles/httpd mkdir /etc/ansible/roles/mysql
4)在每个角色命名的目录中分别创建files、handlers、tasks、templates、meta、defaults和vars目录,用不到的目录可以创建为空目录,也可以不创建;
#举个例子 mkdir /etc/ansible/roles/httpd/{files,templates,tasks,handlers,vars,defaults,meta} mkdir /etc/ansible/roles/mysql/{files,templates,tasks,handlers,vars,defaults,meta}
5)在每个角色的 handlers、tasks、meta、defaults、vars 目录下创建 main.yml
文件,千万不能自定义文件名;
#举个例子 touch /etc/ansible/roles/httpd/{defaults,vars,tasks,meta,handlers}/main.yml touch /etc/ansible/roles/mysql/{defaults,vars,tasks,meta,handlers}/main.yml
6)修改 site.yml
文件,针对不同主机去调用不同的角色;
#举个例子 vim /etc/ansible/site.yml --- - hosts: webservers remote_user: root roles: - httpd - hosts: dbservers remote_user: root roles: - mysql
7)ansible-playbook
运行剧本。
#举个例子 cd /etc/ansible ansible-playbook site.yml
实例
1) 创建各角色的目录和main.yaml
文件
mkdir /etc/ansible/roles/httpd/{files,templates,tasks,handlers,vars,defaults,meta} -p mkdir /etc/ansible/roles/mysql/{files,templates,tasks,handlers,vars,defaults,meta} -p mkdir /etc/ansible/roles/php/{files,templates,tasks,handlers,vars,defaults,meta} -p touch /etc/ansible/roles/httpd/{defaults,vars,tasks,meta,handlers}/main.yml touch /etc/ansible/roles/mysql/{defaults,vars,tasks,meta,handlers}/main.yml touch /etc/ansible/roles/php/{defaults,vars,tasks,meta,handlers}/main.yml
2)编写httpd模块
vim /etc/ansible/roles/mysql/tasks/main.yml - name: install mysql yum: name={{pkg}} state=latest - name: start mysql service: enabled=true name={{svc}} state=started vim /etc/ansible/roles/mysql/vars/main.yml pkg: - mariadb - mariadb-server svc: mariadb
4)编写php模块
vim /etc/ansible/roles/php/tasks/main.yml - name: install php yum: name={{pkg}} state=latest - name: start php-fpm service: enabled=true name={{svc}} state=started vim /etc/ansible/roles/php/vars/main.yml pkg: - php - php-fpm svc: php-fpm
5)编写roles示例
vim /etc/ansible/site.yml --- - hosts: webservers remote_user: root roles: - httpd - mysql - php
cd /etc/ansible
ansible-playbook site.yml