为什么引入playbook?
一般运维人员完成一个任务, 比如安装部署一个httpd服务会需要多个模块(一个模块也可以称之为task)提供功能来完成。而playbook就是组织多个task的容器,它的实质就是一个文件,有着特定的组织格式,它采用的语法格式是YAML(Yet Another Markup Language)。YAML语法能够简单的表示散列表,字典等数据结构。简单来说, playbook是由一个或多个模块组成的,使用多个不同的模块,完成一件事情。
Ansible核心功能
- pyYAML用于ansible编写剧本所使用的语言格式(saltstack---python);
- rsync-ini语法, sersync-xml语法, nsible-pyYAML语法;
- paramiko远程连接与数据传输;
- Jinja2用于编写ansible的模板信息;
YAML三板斧
缩进: YAML使用一个固定的缩进风格表示层级结构,每个缩进由两个空格组成, 不能使用tabs;
冒号: 以冒号结尾的除外,其他所有冒号后面所有必须有空格;
短横线: 表示列表项,使用一个短横杠加一个空格。多个项使用同样的缩进级别作为同一列表;
YAML基本语法
Ansible-playbook采用YAML语法编写。连续的项目(即列表)用 -
减号来表示,key/value(字典)用冒号:
分隔。
列表:每一个列表成员前面都要有一个短横线和一个空格
1 2 3 4 5 6 7 8 |
fruits: - Apple - Orange - Strawberry - Mango 或者: fruits: [ 'Apple' , 'Orange' , 'Strawberry' , 'Mango' ] |
字典:每一个成员由键值对组成,注意冒号后面要有空格
1 2 3 4 5 6 |
martin: name: Martin D'vloper job: Developer skill: Elite 或者 martin: {name: Martin D'vloper, job: Developer, skill: Elite} |
列表和字典可以混合使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
- martin: name: Martin D'vloper job: Developer skills: - python - perl - pascal - tabitha: name: Tabitha Bitumen job: Developer skills: - lisp - fortran - erlang |
示例如下:
[root@localhost ~]# cat httpd.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
--- - hosts: control-node #将要执行任务的主机,已经在hosts文件中定义好了,可是单个主机或主机组 remote_user: root #在目标主机上执行任务时的用户身份 vars: - pkg: httpd tasks: - name: "install httpd package." yum: name={ { pkg }} state=installed - name: "copy httpd configure file to remote host." copy: src= /root/conf/httpd .conf dest= /etc/httpd/conf/httpd .conf notify: restart httpd #当这个任务执行状态发生改变时,触发handlers执行. - name: "boot httpd service." service: name=httpd state=started handlers: #handlers与tasks是同一级别 - name: restart httpd service: name=httpd state=restarted |
playbook语法特性
1. 以 --- (三个减号)开始,必须顶行写;
2. 次行开始写Playbook的内容,但是一般要求写明该playbook的功能;
3. 严格缩进,并且不能用Tab键缩进;
4. 缩进级别必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的;
5. K/V的值可同行写,也可换行写。同行使用 :分隔,换行写需要以 - 分隔;
playbook基础组件
Hosts:运行执行任务(task)的目标主机
remote_user:在远程主机上执行任务的用户
tasks:任务列表
handlers:任务,与tasks不同的是只有在接受到通知时才会被触发
templates:使用模板语言的文本文件,使用jinja2语法。
variables:变量,变量替换{ { variable_name }}
整个playbook是以task为中心,表明要执行的任务。hosts和remote_user表明在远程主机以何种身份执行,其他组件让其能够更加灵活。下面介绍插件:
1. variable
变量定义在资产 (inventory) 中, 默认就是/etc/ansible/hosts文件中
1 2 3 4 5 6 7 8 9 10 11 12 |
主机变量: 192.168.200.136 http_port=808 maxRequestsPerChild=808 192.168.200.137 http_port=8080 maxRequestsPerChild=909 主机组变量: [websers] 192.168.200.136 192.168.200.137 [websers:vars] ntp_server=ntp.exampl.com proxy=proxy.exampl.com |
变量定义在playbook中
1 2 3 |
- hosts: webservers vars: http_port: 80 |
使用facts变量
1 2 3 4 |
facts变量是由setup模块获取远程主机的信息。 用法: # ansible 192.168.200.136 -m setup |
在roles中定义变量, 这个后面会介绍到.
ansible-playbook 命令中传入参数
1 2 |
使用 -e选项传入参数 # ansible-playbook 192.168.200.136 -e "httpd_port=808" httpd04.yml |
变量的引用
2. templates
它是一个模块功能,与copy不同的是他的文本文件采用了jinga2语法,jinga2基本语法如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
字面量: 字符串:使用单引号或双引号 数字:整型,浮点数 列表:{item1,item2,...} 字典:{key1:value1,key2:value2,...} 布尔型: true /false 算术运算: +,-,*,/, // ,%,** 比较运算: ==,!=,>,>=,<,<= 逻辑运算: and,or,not |
注意:template只能在palybook中使用。
3. tasks
执行的模块命令
1 2 3 4 5 6 7 8 9 10 11 12 13 |
格式: action:模块参数(此种方式只在较新的版本中出现) module:参数(已键值对的形式出现) 每一个task都有一个名称,用于标记此任务。任务示例: name: install httpd yum: name=httpd state=present 注意:shell和 command 没有参数,可在后面直接跟命令 shell: ss -tnl | grep :80 1)某任务的运行状态为changed后,可通过相应的notify通知相应的handlers 2)任务可以通过tags打标签,然后通过palybook命令-t选项调用. |
playbook命令及调用方式
用法:
ansible-playbook ... [options]
: yaml格式的playbook文件路径,必须指明
[options]: 选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
Options: --ask-vault-pass #ask for vault password #加密playbook文件时提示输入密码 -C, --check #don't make any changes; instead, try to predict some of the changes that may occur #模拟执行,不会真正在机器上执行(查看执行会产生什么变化)。即并不在远程主机上执行,只是测试。 -D, -- diff #when changing (small) files and templates, show the differences in those files; works great with --check #当更新的文件数及内容较少时,该选项可显示这些文件不同的地方,该选项结合-C用会有较好的效果 -e EXTRA_VARS, --extra-vars=EXTRA_VARS #set additional variables as key=value or YAML/JSON #在Playbook中引入外部参数变量 --flush-cache #clear the fact cache #清理fact缓存,将fact清除到的远程主机缓存 --force-handlers #run handlers even if a task fails #强制运行handlers的任务,即使在任务失败的情况下 -f FORKS, --forks=FORKS #specify number of parallel processes to use(default=5) #并行任务数。FORKS被指定为一个整数,默认是5 -h, --help #show this help message and exit #打开帮助文档API -i INVENTORY, --inventory- file =INVENTORY #specify inventory host path (default=/etc/ansible/hosts) or comma separated host list. #指定要读取的Inventory清单文件 -l SUBSET, --limit=SUBSET #further limit selected hosts to an additional pattern #限定执行的主机范围 --list-hosts #outputs a list of matching hosts; does not execute anything else #列出执行匹配到的主机,但并不会执行任何动作。 --list-tags #list all available tags #列出所有可用的tags --list-tasks #list all tasks that would be executed #列出所有即将被执行的任务 -M MODULE_PATH, --module-path=MODULE_PATH #specify path(s) to module library (default=None) #要执行的模块的路径 --new-vault-password- file =NEW_VAULT_PASSWORD_FILE #new vault password file for rekey # --output=OUTPUT_FILE #output file name for encrypt or decrypt; use - for stdout # --skip-tags=SKIP_TAGS #only run plays and tasks whose tags do not match these values #跳过指定的tags任务 --start-at-task=START_AT_TASK #start the playbook at the task matching this name #从第几条任务(START_AT_TASK)开始执行 --step #one-step-at-a-time: confirm each task before running #逐步执行Playbook定义的任务,并经人工确认后继续执行下一步任务 --syntax-check #perform a syntax check on the playbook, but do not execute it #检查Playbook中的语法书写,并不实际执行 -t TAGS, --tags=TAGS #only run plays and tasks tagged with these values #指定执行该tags的任务 --vault-password- file =VAULT_PASSWORD_FILE #vault password file # - v , --verbose #verbose mode (-vvv for more, -vvvv to enable connection debugging) #执行详细输出 --version #show program's version number and exit #显示版本 ############Connection Options,即下面时连接权限############ control as whom and how to connect to hosts -k, --ask-pass #ask for connection password # --private-key=PRIVATE_KEY_FILE, --key- file =PRIVATE_KEY_FILE #use this file to authenticate the connection # -u REMOTE_USER, --user=REMOTE_USER #connect as this user (default=None) #指定远程主机以USERNAME运行命令 -c CONNECTION, --connection=CONNECTION #connection type to use (default=smart) #指定连接方式,可用选项paramiko (SSH)、ssh、local,local方式常用于crontab和kickstarts -T TIMEOUT, --timeout=TIMEOUT #override the connection timeout in seconds(default=10) #SSH连接超时时间设定,默认10s -- ssh -common-args=SSH_COMMON_ARGS #specify common arguments to pass to sftp/scp/ssh (e.g.ProxyCommand) # -- sftp -extra-args=SFTP_EXTRA_ARGS #specify extra arguments to pass to sftp only (e.g. -f, -l) # -- scp -extra-args=SCP_EXTRA_ARGS #specify extra arguments to pass to scp only (e.g. -l) # -- ssh -extra-args=SSH_EXTRA_ARGS #specify extra arguments to pass to ssh only (e.g. -R) # ############Privilege Escalation Options, 即下面时权限提升权限############ control how and which user you become as on target hosts -s, -- sudo #run operations with sudo (nopasswd) (deprecated, use become) #相当于Linux系统下的sudo命令 -U SUDO_USER, -- sudo -user=SUDO_USER #desired sudo user (default=root) (deprecated, use become) #使用sudo,相当于Linux下的sudo命令 -S, -- su #run operations with su (deprecated, use become) # -R SU_USER, -- su -user=SU_USER #run operations with su as this user (default=root)(deprecated, use become) -b, --become #run operations with become (does not imply password prompting) # --become-method=BECOME_METHOD #privilege escalation method to use (default=sudo),valid choices: [ sudo | su | pbrun | pfexec | doas |dzdo | ksu | runas ] # --become-user=BECOME_USER #run operations as this user (default=root) # --ask- sudo -pass #ask for sudo password (deprecated, use become) #传递sudo密码到远程主机,来保证sudo命令的正常运行 --ask- su -pass #ask for su password (deprecated, use become) # -K, --ask-become-pass #ask for privilege escalation password # |
ansible-playbook需要注意的两个命令
1)检查语法,只检查是否是yaml语法格式。并不做逻辑校验。(记住这个要经常使用, 它是判断语法是否正确!!!)
# ansible-playbook --syntax-check kevin.yml
2)模拟执行(不是真的执行)
# ansible-playbook -C kevin.yml
关闭Facts
如果不需要使用主机的任何fact数据,可以选择关闭fact数据的获取,这样有利于增强Ansible面对大量系统的push模块。
在playbook中关闭Facts方法(gather_facts: no):
1 2 3 |
--- - hosts: webserver gather_facts: no |
palybook书写格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
--- # 也可以不使用这一行。可以省略。 - hosts: 172.16.60.211 #处理指定服务器. - (空格)hosts:(空格)172.16.20.211 task: #剧本所要干的事情; (空格)(空格)task: - name: #(两个空格)-(空格)name。 command : echo hello clsn linux #(四个空格)command:(空格) 需要注意: Task任务里的name可以省略不写,将-(空格)放到下一行模块墙面。例如: --- - hosts: 172.16.60.211 task: - command : echo hello clsn linux 小示例: [root@localhost ansible] # cat haha.yaml --- - hosts: test_host remote_user: root gather_facts: no tasks: - file : path= /opt/task1 .txt state= touch |
palybook格式示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[root@ansible-server ~] # vim /etc/ansible/test.yaml - hosts: 172.16.60.213 tasks: - name: Install Rsync yum: name= rsync state=installed playbook检查方法 [root@ansible-server ~] # ansible-playbook --syntax-check /etc/ansible/test.yaml playbook: /etc/ansible/test .yaml [root@ansible-server ~] # ansible-playbook -C /etc/ansible/test.yaml PLAY [172.16.60.213] ******************************************************************************************************************* TASK [Gathering Facts] ***************************************************************************************************************** ok: [172.16.60.213] TASK [Install Rsync] ******************************************************************************************************************* ok: [172.16.60.213] PLAY RECAP ***************************************************************************************************************************** 172.16.60.213 : ok=2 changed=0 unreachable=0 failed=0 上面两个检查命令, 第一个是进行playbook剧本配置信息语法检查; 第二个是模拟playbook剧本执行(彩排) |
palybook剧本文件示例
ansible-playbook编写内容扩展:剧本任务编写多个任务
1 2 3 4 5 6 |
- hosts: all tasks: - name: restart-network cron : name= 'restart network' minute=00 hour=00 job= '/usr/sbin/ntpdate time.nist.gov >/dev/null 2>&1' - name: sync time cron : name= 'sync time' minute=* /5 job= "/usr/sbin/ntpdate pool.ntp.com >/dev/null 2>&1" |
剧本编写内容扩展:剧本任务编写多个主机
1 2 3 4 5 6 7 8 9 10 11 |
- hosts: 172.16.60.7 tasks: - name: restart-network cron : name= 'restart network' minute=00 hour=00 job= '/usr/sbin/ntpdate time.nist.gov >/dev/null 2>&1' - name: sync time cron : name= 'sync time' minute=* /5 job= "/usr/sbin/ntpdate pool.ntp.com >/dev/null 2>&1" - hosts: 172.16.60.31 tasks: - name: show ip addr to file shell: echo $( hostname -i) >> /tmp/ip .txt |
playbook剧本编写方式
- 多主机单任务编写方式
- 多主机多任务编写方式
- 不同主机多任务编写方式
来看一个比较完整的ansible的yml文件写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
--- - host: webservers ###要管理的远程服务器组名称, 服务器地址维护在/etc/ansible/hosts 里, 也可以直接写地址 vars: port: 8081 ###定义了一个变量 端口号 remote_user: root ###远程登录后用什么用户执行 pre_tasks: ###执行正式 task 之前执行的任务 - name: pre task ###任务名称 shell: echo 'execute pre task' ###执行一行 shell 命令, 支持 >> 等符号 roles: ###引入 roles, 可以理解为引用了一个其他项目 ansible 包, 引用的 roles 可以是另一个完整的 ansible 脚本 - role: my_role ###要引用的 role 名称 when: "ansible_os_family == 'RedHat'" ###判断条件, ansible_os_family 是一个内置变量, 可直接使用 tasks: ###按顺序执行以下 task - include: my_tasks /some_task .yml ###可以引入其他 yml 文件 - name: get hostname ###这是一个 task, 名称 command : cat log.log ###执行一行 command , 和 shell 类似, 但是不支持 >> 等操作符 register: result ###执行的结果, 设到 result 这个变量中, 后面可以使用 - name: set hostname shell: cat { {result.stdout}} >> host.text - name: task1 command : echo 'execute task1' - name: task2 start apache service: ###启动 httpd 服务器, service 是一个 ansible 内置模块, 读者可以自行查看更多模块, 包括下载复制等等 name: httpd state: started tags: - apache ###这是一个标签, 可以用 ansible-playbook main.yml --tags "apache" 指定只执行这个任务 - name: copy and set value of index.html template: ###这是一个复制方法, 也叫模块, 并且.j2文件中可以使用{ {}}来设置需要替换的变量 src: templates /index .html.j2 dest: /etc/httpd/index .html notify: ###唤醒执行后面的 handlers 中名字叫 restart apache 的任务 - restart apache post_tasks: ###最后需要执行的任务 - name: posy task shell: echo 'execute post task' handlers: - name: restart apache debug: ###这是一个打印模块 msg: start restart apche |
palybook剧本中的方法
1. handlers 任务触发
在需要被监控的任务(tasks)中定义一个notify,只有当这个任务被执行时,才会触发notify对应的handlers去执行相应操作。例如配置文件被修改后,有可能需要重启程序,此时我们可以配置一个handlers,类似触发器。注意:handlers下的name名称必须要和它对应的notify名称相同!否则不会执行!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[root@localhost ~] # cat httpd.yaml --- - hosts: control-node remote_user: root vars: - pkg: httpd tasks: - name: "install httpd package." yum: name={ { pkg }} state=installed - name: "copy httpd configure file to remote host." copy: src= /root/conf/httpd .conf dest= /etc/httpd/conf/httpd .conf notify: restart httpd - name: "boot httpd service." service: name=httpd state=started handlers: - name: restart httpd service: name=httpd state=restarted |
######## 在使用handlers的过程中,需要注意下面几点 ########
1. handlers只有在其所在的任务被执行完时,它才会被运行;如果一个任务中定义了notify调用Handlers,但由于条件判断等原因,该任务未被执行,则Handlers同样不会被执行。
2. handlers只会在Play的末尾运行一次;如果想在一个Playbook的中间运行handlers,则需要使用meta模块来实现,例如:-meta: flush_handlers。
3. 可以直接在Handlers中使用notify选项,实现Handlers调用Handlers。
4. 可以使用listen关键字,在一个tasks任务中一次性notify多个handler。即将多个handler分为"一组",使用相同的"组名"即可,当notify对应的值为"组名"时,"组"内的所有handler都会被notify。
5. 如果一个Play在运行到调用handlers的语句之前失败了,那么这个handlers将不会被执行。但是可以使用mega模块的--force-handlers选项来强制执行handlers,即使在handlers所在Play中途运行失败也能执行。需要注意:--force-handlers参数主要针对即使playbook执行失败,也要执行代码块成功了的handlers(即执行成功的task任务), 如果代码块本身执行失败(即执行失败的task任务),那么它所对应的handlers应当不会被执行!
handlers可以理解成另一种tasks,handlers是另一种"任务列表",可以理解handlers和tasks是"平级关系",所以他们的缩进相同。handlers的任务会被tasks中的任务进行"调用",但是,被"调用"并不意味着一定会执行,只有当tasks中的任务"真正执行"以后,handlers中被调用的任务才会执行,如果tasks中的任务并没有做出任何实际的操作,那么handlers中的任务即使被"调用",也并不会执行。handlers中可以有多个任务,被tasks中不同的任务notify。
场景1:headlers在所有tasks任务被执行完时才执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
[root@localhost ansible] # cat haha.yaml --- - hosts: test_host remote_user: root become: yes become_method: sudo tasks: - name: make file task1 file : path= /opt/task1 .txt state= touch notify: task1 - name: make file task2 file : path= /opt/task2 .txt state= touch notify: task2 handlers: - name: task1 file : path= /opt/task1 .txt mode=777 owner=root group=root - name: task2 file : path= /opt/task2 .txt src= /opt/task2 .txt dest= /opt/heihei state=link force= yes 执行结果: [root@localhost ansible] # ansible-playbook haha.yaml PLAY [test_host] ********************************************************************************************************************************* TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.233] ok: [172.16.60.234] TASK [ make file task1] *************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] TASK [ make file task2] *************************************************************************************************************************** changed: [172.16.60.233] changed: [172.16.60.234] RUNNING HANDLER [task1] ************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] RUNNING HANDLER [task2] ************************************************************************************************************************** changed: [172.16.60.233] changed: [172.16.60.234] PLAY RECAP *************************************************************************************************************************************** 172.16.60.233 : ok=5 changed=4 unreachable=0 failed=0 172.16.60.234 : ok=5 changed=4 unreachable=0 failed=0 |
从上面运行结果看出,Handlers执行的顺序与Handlers在playbook中定义的顺序是相同的,与"handler"被notify的顺序无关。
场景2:使用meta模块,headlers会在它所对应的task任务执行完后立即被触发并执行,即在playbook的中间环节运行。
默认情况下,所有的task执行完毕后,才会执行各个handles,并不是执行完某个task后,立即执行相应的handler,如果想要在执行完某些task以后立即执行对应的handlre,那么需要使用meta模块。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
[root@localhost ansible] # cat haha.yaml --- - hosts: test_host remote_user: root become: yes become_method: sudo tasks: - name: make file task1 file : path= /opt/task1 .txt state= touch notify: task1 - meta: flush_handlers - name: make file task2 file : path= /opt/task2 .txt state= touch notify: task2 handlers: - name: task1 file : path= /opt/task1 .txt mode=777 owner=root group=root - name: task2 file : path= /opt/task2 .txt src= /opt/task2 .txt dest= /opt/heihei state=link force= yes 执行结果: [root@localhost ansible] # ansible-playbook haha.yaml PLAY [test_host] ********************************************************************************************************************************* TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.234] ok: [172.16.60.233] TASK [ make file task1] *************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] RUNNING HANDLER [task1] ************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] TASK [ make file task2] *************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] RUNNING HANDLER [task2] ************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] PLAY RECAP *************************************************************************************************************************************** 172.16.60.233 : ok=5 changed=4 unreachable=0 failed=0 172.16.60.234 : ok=5 changed=4 unreachable=0 failed=0 |
上面使用了meta模块后,注意它的执行顺序于场景1做下对比!
场景3:Handlers调用Handlers
若实现Handlers调用Handlers,则直接在Handlers中使用notify选项即可以。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
[root@localhost ansible] # cat haha.yaml --- - hosts: test_host remote_user: root become: yes become_method: sudo tasks: - name: make file task1 file : path= /opt/task1 .txt state= touch notify: task1 - name: make file task2 file : path= /opt/task2 .txt state= touch handlers: - name: task1 file : path= /opt/task1 .txt mode=777 owner=root group=root notify: task2 - name: task2 file : path= /opt/task2 .txt src= /opt/task2 .txt dest= /opt/heihei state=link force= yes 执行结果: [root@localhost ansible] # ansible-playbook haha.yaml PLAY [test_host] ********************************************************************************************************************************* TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.234] ok: [172.16.60.233] TASK [ make file task1] *************************************************************************************************************************** changed: [172.16.60.233] changed: [172.16.60.234] TASK [ make file task2] *************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] RUNNING HANDLER [task1] ************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] RUNNING HANDLER [task2] ************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] PLAY RECAP *************************************************************************************************************************************** 172.16.60.233 : ok=5 changed=4 unreachable=0 failed=0 172.16.60.234 : ok=5 changed=4 unreachable=0 failed=0 注意:上面执行的顺序是: make file task1 > make file task2 > task1 > task2 ==================================================================== 也可以改成下面的方式:实现Handlers调用Handlers [root@localhost ansible] # cat haha.yaml --- - hosts: test_host remote_user: root become: yes become_method: sudo tasks: - name: make file task1 file : path= /opt/task1 .txt state= touch notify: task1 handlers: - name: task1 file : path= /opt/task1 .txt mode=777 owner=root group=root notify: task2 - name: task2 file : path= /opt/task2 .txt state= touch notify: task3 - name: task3 file : path= /opt/task2 .txt src= /opt/task2 .txt dest= /opt/heihei state=link force= yes 执行结果: [root@localhost ansible] # ansible-playbook haha.yaml PLAY [test_host] ********************************************************************************************************************************* TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.233] ok: [172.16.60.234] TASK [ make file task1] *************************************************************************************************************************** changed: [172.16.60.233] changed: [172.16.60.234] RUNNING HANDLER [task1] ************************************************************************************************************************** changed: [172.16.60.233] changed: [172.16.60.234] RUNNING HANDLER [task2] ************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] RUNNING HANDLER [task3] ************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] PLAY RECAP *************************************************************************************************************************************** 172.16.60.233 : ok=5 changed=4 unreachable=0 failed=0 172.16.60.234 : ok=5 changed=4 unreachable=0 failed=0 注意:上面的执行顺序是: make file task1 > task1 > task2 > task3 |
场景4:使用listen关键字,在一个tasks任务中一次性notify多个handler
怎么才能一次性notify多个handler呢?如果尝试将多个handler使用相同的name呢?其实这样并不可行!因为当多个handler的name相同时,只有一个handler会被执行。要想实现一次notify多个handler,需要借助一个关键字,它就是"listen",可以把listen理解成"组名",可以把多个handler分成"组",当需要一次性notify多个handler时,只要将多个handler分为"一组",使用相同的"组名"即可,当notify对应的值为"组名"时,"组"内的所有handler都会被notify。需要注意:listen的名称要和notify名称保持一致!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
[root@localhost ansible] # cat haha.yaml --- - hosts: test_host remote_user: root become: yes become_method: sudo tasks: - name: make file task1 file : path= /opt/task1 .txt state= touch notify: group1_handler handlers: - name: task1 listen: group1_handler file : path= /opt/task1 .txt mode=777 owner=root group=root - name: task2 listen: group1_handler file : path= /opt/task1 .txt src= /opt/task1 .txt dest= /opt/heihei state=link force= yes - name: task3 listen: group1_handler shell: echo "this is test,haha...." >> /opt/task1 .txt 执行结果: [root@localhost ansible] # ansible-playbook haha.yaml PLAY [test_host] ********************************************************************************************************************************* TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.233] ok: [172.16.60.234] TASK [ make file task1] *************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] RUNNING HANDLER [task1] ************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] RUNNING HANDLER [task2] ************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] RUNNING HANDLER [task3] ************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] PLAY RECAP *************************************************************************************************************************************** 172.16.60.233 : ok=5 changed=4 unreachable=0 failed=0 172.16.60.234 : ok=5 changed=4 unreachable=0 failed=0 |
场景5:使用--force-handlers选项来强制执行handlers
当playbook剧本执行失败以后,handlers可能并没有被触发,也就不会执行了!如果想不管task任务是否成功执行,都强制执行handlers。在这个时候,可以在执行playbook的时候,添加--force-handlers来强制执行handlers!但是必须要注意的是:--force-handlers参数主要针对即使playbook执行失败,也要执行代码块成功了的handlers(即执行成功的task任务), 如果代码块本身执行失败(即执行失败的task任务),那么它所对应的handlers应当不会被执行!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
[root@localhost ansible] # cat haha.yaml --- - hosts: test_host remote_user: root become: yes become_method: sudo tasks: - name: make file task1 file : path= /opt/task1 .txt state= touch notify: task1 - name: make file task2 file : path= /opt/kevin/task2 .txt state= touch notify: task2 handlers: - name: task1 file : path= /opt/task1 .txt mode=777 owner=root group=root - name: task2 shell: ln -s /opt/task1 .txt /opt/task2 .txt 执行结果: [root@localhost ansible] # ansible-playbook haha.yaml PLAY [test_host] ********************************************************************************************************************************* TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.234] ok: [172.16.60.233] TASK [ make file task1] *************************************************************************************************************************** changed: [172.16.60.233] changed: [172.16.60.234] TASK [ make file task2] *************************************************************************************************************************** fatal: [172.16.60.234]: FAILED! => { "changed" : false , "module_stderr" : "" , "module_stdout" : "Traceback (most recent call last):\r\n File \"/tmp/ansible_iNMDpU/ansible_module_file.py\", line 474, in \r\n main()\r\n File \"/tmp/ansible_iNMDpU/ansible_module_file.py\", line 448, in main\r\n open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n" , "msg" : "MODULE FAILURE" , "rc" : 0} fatal: [172.16.60.233]: FAILED! => { "changed" : false , "module_stderr" : "" , "module_stdout" : "Traceback (most recent call last):\r\n File \"/tmp/ansible_OvTacW/ansible_module_file.py\", line 474, in \r\n main()\r\n File \"/tmp/ansible_OvTacW/ansible_module_file.py\", line 448, in main\r\n open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n" , "msg" : "MODULE FAILURE" , "rc" : 0} RUNNING HANDLER [task1] ************************************************************************************************************************** to retry, use: --limit @ /etc/ansible/haha .retry PLAY RECAP *************************************************************************************************************************************** 172.16.60.233 : ok=2 changed=1 unreachable=0 failed=1 172.16.60.234 : ok=2 changed=1 unreachable=0 failed=1 如上执行结果,由于 /opt/kevin 目录不存在,导致task的第二个任务执行失败,这个时候handler根本没有被触发,也就不会执行。 即使第一个任务执行成功,但是它对应的第一个handler也不会被执行!! ################################################################################### 接下来使用--force-handlers选项来强制执行handlers(强制执行的是:成功执行的task对应的handler) [root@localhost ansible] # ansible-playbook haha.yaml --force-handlers PLAY [test_host] ********************************************************************************************************************************* TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.234] ok: [172.16.60.233] TASK [ make file task1] *************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] TASK [ make file task2] *************************************************************************************************************************** fatal: [172.16.60.233]: FAILED! => { "changed" : false , "module_stderr" : "" , "module_stdout" : "Traceback (most recent call last):\r\n File \"/tmp/ansible_rEJQHm/ansible_module_file.py\", line 474, in \r\n main()\r\n File \"/tmp/ansible_rEJQHm/ansible_module_file.py\", line 448, in main\r\n open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n" , "msg" : "MODULE FAILURE" , "rc" : 0} fatal: [172.16.60.234]: FAILED! => { "changed" : false , "module_stderr" : "" , "module_stdout" : "Traceback (most recent call last):\r\n File \"/tmp/ansible_7CDxpp/ansible_module_file.py\", line 474, in \r\n main()\r\n File \"/tmp/ansible_7CDxpp/ansible_module_file.py\", line 448, in main\r\n open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n" , "msg" : "MODULE FAILURE" , "rc" : 0} RUNNING HANDLER [task1] ************************************************************************************************************************** changed: [172.16.60.234] changed: [172.16.60.233] to retry, use: --limit @ /etc/ansible/haha .retry PLAY RECAP *************************************************************************************************************************************** 172.16.60.233 : ok=3 changed=2 unreachable=0 failed=1 172.16.60.234 : ok=3 changed=2 unreachable=0 failed=1 如上执行结果,即使playbook执行中有task任务执行失败,但是执行成功的task任务所调用的handler依然会被强制触发并执行!但是执行失败的task任务所调用的handler依然不会被执行。 即handlers中的task1会被执行,task2不会被执行! |
2. tags任务标签
tags用于让用户选择运行playbook中的部分代码。ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。此时如果确信其没有变化,就可以通过tags跳过此些代码片断。tags可以看作是ansible的任务控制!
ansible的标签(Tags)功能可以给角色(Roles)、文件、单独的任务,甚至整个Playbook打上标签,然后利用这些标签来指定要运行Playbook中的个别任务,或不执行指定的任务。如果有一个很大的playbook剧本,而只想运行playbook其中的某个或部分task任务,而不是运行playbook中所有的任务,这个时候tags是你的最佳选择。
2.1 ansible支持"tags:"属性,执行playbook时,可以通过两种方式根据"tags"过滤任务:
1. 在命令行上,使用或选项"--tags或 --skip-tags",后面使用空格或"="都可以。
2. 在ansible配置设置中,使用和选项"TAGS_RUN或TAGS_SKIP";
3. 可以使用"--list-tags"查看playbook中有哪些tags会被执行;
2.2 ansible系统中内置的特殊tags(目前有5个特殊的tags)
到ansible 2.5版本以后,目前系统内置的tags有以下几个:
always: 除非--skip-tags指定这个标签,否则该标记为always的task一直都会执行。"--tags always"只执行标记了always的tasks;
never: 除非--tags指定了这个标签,否则该标记为never的task一直都不会执行。"--tags never"执行标记了always和never的tasks;
tagged: --tags tagged表示执行所有有tags标签的tasks任务,但不包括tags标签是never的tasks任务;--skip-tags tagged表示所有有tags标签的tasks任务都跳过,即不会执行。
untagged: --tags untagged表示执行所有没有tags标签的tasks任务和tags标签为always的tasks任务;--skip-tags untagged效果相反!
all:--tags all表示执行所有的tags标签为非never的task,包括有tags标签和无tags标签的tasks。
执行ansible-playbook命令时,使用下面两个参数的含义(自定义的tags可以是单个,也可以是多个,多个之间使用逗号隔开):
"--tags 自定义的tag" 表示执行tags为指定的标签名的tasks和tags为always的tasks。如果执行命令ansible-playbook site.yml 时不指定tags,则会执行所有tags为非never的tasks
"--skip-tags 自定义tag" 表示执行所有非指定tag和非never的tasks
2.3 tags标签配置语法有下面三种:
1 2 3 4 5 6 7 8 9 |
语法一: tags: - tag_test 语法二: tags: tag_test 语法三: tags: [ 'tag_test' ] |
2.3 ansible的tags使用
1)最常见的使用形式。一个task任务添加一个tags标签。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
官方示例如下: [root@localhost ansible] # vim example.yml --- - hosts: all remote_user: root gather_facts: no tasks: - yum: name={ { item }} state=installed with_items: - httpd - memcached tags: - packages - template: src=templates /src .j2 dest= /etc/foo .conf tags: - configuration |
此时如果希望只run其中的某个task,则run的时候指定tags即可。可以运行多个tags,中间使用逗号隔开;也可以运行单个tags。
1 2 3 4 5 6 7 |
[root@localhost ansible] # ansible-playbook example.yml --tags "configuration,packages" [root@localhost ansible] # ansible-playbook example.yml --tags configuration [root@localhost ansible] # ansible-playbook example.yml --tags packages 或者 [root@localhost ansible] # ansible-playbook example.yml --tags="configuration,packages" [root@localhost ansible] # ansible-playbook example.yml --tags=configuration [root@localhost ansible] # ansible-playbook example.yml --tags=packages |
相反,也可以使用--skip-tags跳过某个task任务。
1 2 3 |
[root@localhost ansible] # ansible-playbook example.yml --skip-tags configuration 或者 [root@localhost ansible] # ansible-playbook example.yml --skip-tags=configuration |
具体看下面示例(tags三种语法都用上):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
[root@localhost ansible] # cat haha.yaml --- - hosts: test_host remote_user: root gather_facts: no tasks: - name: task1 file : path= /opt/task1 .txt state= touch tags: make_task1 - name: task2 file : path= /opt/task2 .txt state= touch tags: - make_task2 - name: task3 file : path= /opt/task2 .txt src= /opt/task2 .txt dest= /opt/heihei state=link force= yes tags: [ 'link_task3' ] 只运行make_task1标签的task任务 [root@localhost ansible] # ansible-playbook haha.yaml --tags make_task1 PLAY [test_host] ********************************************************************************************************************************* TASK [task1] ************************************************************************************************************************************* changed: [172.16.60.234] PLAY RECAP *************************************************************************************************************************************** 172.16.60.234 : ok=1 changed=1 unreachable=0 failed=0 运行多个tags [root@localhost ansible] # ansible-playbook haha.yaml --tags make_task1,make_task2 PLAY [test_host] ********************************************************************************************************************************* TASK [task1] ************************************************************************************************************************************* changed: [172.16.60.234] TASK [task2] ************************************************************************************************************************************* changed: [172.16.60.234] PLAY RECAP *************************************************************************************************************************************** 172.16.60.234 : ok=2 changed=2 unreachable=0 failed=0 跳过make_task2标签的任务,其他任务正常执行 [root@localhost ansible] # ansible-playbook haha.yaml --skip-tags make_task2 PLAY [test_host] ********************************************************************************************************************************* TASK [task1] ************************************************************************************************************************************* changed: [172.16.60.234] TASK [task3] ************************************************************************************************************************************* changed: [172.16.60.234] PLAY RECAP *************************************************************************************************************************************** 172.16.60.234 : ok=2 changed=2 unreachable=0 failed=0 |
2)一个task任务添加多个tags标签。
上面是一个task任务添加一个tags标签,其实一个task任务可以添加多个标签,而且不同的task任务可以使用相同的tags标签。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
一个任务添加多个tags标签的语法仍然也有三种: 语法1: tags: - tag1 - tag2 语法2: tags: tag1,tag2 语法3: tags: [ 'tag1,tag2' ] ======================================================== 具体示例如下: [root@localhost ansible] # vim https.yml --- - hosts: test_host remote_user: root tasks: - name: install httpd package tags: - httpd - package yum: name=httpd state=latest - name: start up httpd service tags: httpd,service service: name: httpd state: started 上面例子中每个任务都有多个标签,而且上例中两个任务都有一个共同的标签,就是httpd标签。 所以当执行 "ansible-playbook httpd.yml --tags=httpd" 时,上面两个task任务都会被执行。 由于上面例子中的所有任务都有共同的httpd标签,所以像这种情况,可以把httpd标签提取出来并写在play剧本中,示例如下: [root@localhost ansible] # vim https.yml --- - hosts: test_host remote_user: root tags:httpd tasks: - name: install httpd package tags: - package yum: name=httpd state=latest - name: start up httpd service tags: [ 'service' ] service: name: httpd state: started |
需要注意:当tags写在play剧本中而非写在task任务中时,play中的所有task任务都会继续当前paly中的tags,就像上例中,两个任务都会继承httpds的tag标签,同时还拥有自己的tag标签。
3)内置的特殊tags的用法
上面已经介绍了5个内置的特殊的tags,每个都有其自身的用意。如下以always关键字的tags为例:如果把任务的tags值指定为always时,那么这个任务就总是被执行,除非使用"--skip-tags"选项明确指定不执行对应任务的tags标签。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
[root@localhost ansible] # cat haha.yaml --- - hosts: test_host remote_user: root gather_facts: no tasks: - name: task1 file : path= /opt/task1 .txt state= touch tags: make_task1 - name: task2 file : path= /opt/task2 .txt state= touch tags: - always - name: task3 file : path= /opt/task2 .txt src= /opt/task2 .txt dest= /opt/heihei state=link force= yes tags: [ 'link_task3' ] 执行1:如下,虽然tags指定了执行标签为make_task1的任务,但是由于任务2的标签有关键字always,所以任务2也会被执行,这就是always的作用! [root@localhost ansible] # ansible-playbook haha.yaml --tags=make_task1 PLAY [test_host] ********************************************************************************************************************************* TASK [task1] ************************************************************************************************************************************* changed: [172.16.60.234] TASK [task2] ************************************************************************************************************************************* changed: [172.16.60.234] PLAY RECAP *************************************************************************************************************************************** 172.16.60.234 : ok=2 changed=2 unreachable=0 failed=0 执行2: 只执行标签为always的任务 [root@localhost ansible] # ansible-playbook haha.yaml --tags always 或者 [root@localhost ansible] # ansible-playbook haha.yaml --tags=always PLAY [test_host] ********************************************************************************************************************************* TASK [task2] ************************************************************************************************************************************* changed: [172.16.60.234] PLAY RECAP *************************************************************************************************************************************** 172.16.60.234 : ok=1 changed=1 unreachable=0 failed=0 执行3: 跳过标签为always关键字的任务,这里明确指出跳过执行always标签。 [root@localhost ansible] # ansible-playbook haha.yaml --skip-tags always PLAY [test_host] ********************************************************************************************************************************* TASK [task1] ************************************************************************************************************************************* changed: [172.16.60.234] TASK [task3] ************************************************************************************************************************************* ok: [172.16.60.234] PLAY RECAP *************************************************************************************************************************************** 172.16.60.234 : ok=2 changed=1 unreachable=0 failed=0 其他四个特殊的tags标签在这里就不做示例说明了。特殊tags标签可以在ansible-playbook命令执行时直接使用。 |
4)tags标签可以和role 结合使用
1 2 3 4 |
[root@localhost ansible] # cat test.yml --- roles: - { role: webserver, port: 5000, tags: [ 'web' , 'foo' ] } |
5)tags和include结合使用。
1 2 3 |
[root@localhost ansible] # cat test.yml --- - include: kevin.yml tags=web,foo |
如上,对一个include任务打了两个tags标签,直接执行"ansible_playbook test.yml" 或 "ansible_playbook test.yml --tags=web" 或 "ansible_playbook test.yml --tags=foo" 命令则会将kevin.yml文件中所有task任务都执行。
再来看看一个include结合tags的示例:通过指定标签(tags),来说明是安装tomcat7还是tomcat8
tomcat.yml文件
1 2 3 4 5 |
--- - include: install_tomcat7.yml tags: tomcat7 - include: install_tomcat8.yml tags: tomcat8 |
install_tomcat7.yml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
--- - name: "复制文件到远程主机" copy: src={ { item.src }} dest={ { item.dest }} with_items: - src: jdk-7u79-linux-x64.rpm dest: /usr/local/src/ - src: java17.sh dest: /etc/profile .d/ - name: "安装jdk" yum: name: /usr/local/src/jdk-7u79-linux-x64 .rpm state: present - name: "重新加载环境变量" shell: "source /etc/profile.d/java17.sh" - name: "复制tomcat文件到远程服务器并解压" unarchive: src=apache-tomcat-7.0.64.zip dest= /data/ copy= yes owner=staplesapp group=admin - name: "对解压后的文件重命名" shell: mv /data/apache-tomcat-7 .0.64 /data/tomcat7 - name: "对tomcat进行相关配置" shell: find /data/tomcat7/bin -name "*.sh" | xargs chmod +x - name: "启动tomcat" shell: 'nohup /data/tomcat7/bin/startup.sh &' |
install_tomcat8.yml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
--- - name: "复制文件到远程主机" copy: src={ { item.src }} dest={ { item.dest }} with_items: - src: jdk-8u111-linux-x64.rpm dest: /usr/local/src/ - src: java18.sh dest: /etc/profile .d/ - name: "安装jdk" yum: name: /usr/local/src/jdk-8u111-linux-x64 .rpm state: present - name: "配置java环境变量" shell: "source /etc/profile.d/java18.sh" - name: "安装tomcat" unarchive: src=apache-tomcat-8.0.30. tar .gz dest= /data/ copy= yes owner=staplesapp group=admin - name: "对解压后的文件重命名" shell: mv /data/apache-tomcat-8 .0.30 /data/tomcat8 - name: "启动tomcat" shell: 'nohup /data/tomcat8/bin/startup.sh &' |
下面开始执行命令:
1 2 3 4 5 |
安装tomcat7: [root@localhost ansible] # ansible-playbook tomcat.yml --tags tomcat7 安装tomcat8: [root@localhost ansible] # ansible-playbook tomcat.yml --tags tomcat8 |
这里需要特别注意:
在之前ansible版本中使用include 整合多个roles至统一入口结合tags标签来管理roles剧本,但在ansible2.8版本之后将会删除include语法,更改为import_playbook。如果还使用include语法也可以,只不过ansible-playbook执行结果中会有告警信息:"DEPRECATION WARNING]:'include' for playbook includes. You should use 'import_playbook' instead. This feature will be removed in version 2.8. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg."。所以,最好将上面tomcat.yml文件中的include语法改成import_playbook,如下:
1 2 3 4 5 6 |
[root@localhost ansible] # cat tomcat.yml --- - import_playbook: install_tomcat7.yml tags: tomcat7 - import_playbook: install_tomcat8.yml tags: tomcat8 |
3. include用法
如果想在playbook中重复使用任务列表,则可以使用include文件来执行此操作。 使用include的任务列表是定义系统将要实现的角色的好方法。主要清楚:ansible2.8版本之后include语法变成了import_playbook。如果还是使用include,则不会影响执行结果,只不过是有告警信息。ansible也可以将变量传递给include。示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
示例1: 通过Include,可以在playbook中引用另一个playbook或者tasks ============================================================== [root@localhost ansible] # cat install_MysqlAndPhp.yml - yum: name: mysql state: present - yum: name: php-fpm state: present [root@localhost ansible] # cat lamp.yml --- - hosts: test_host remote_user: root gather_facts: no tasks: - include: install_MysqlAndPhp.yml - yum: name: httpd state: present [root@localhost ansible] # cat lnmp.yml --- - hosts: test_host remote_user: root gather_facts: no tasks: - include: install_MysqlAndPhp.yml - yum: name: nginx state: present 示例2: 可以在handler中引用include ============================================================== [root@localhost ansible] # cat test_include.yml --- - hosts: test_host remote_user: root gather_facts: no tasks: - file : path: /opt/ttt state: touch notify: test include handlers handlers: - name: test include handlers include: include_handler.yml [root@localhost ansible] # cat include_handler.yml - debug: msg: "task1 of handlers" - debug: msg: "task2 of handlers" - debug: msg: "task3 of handlers" 示例3: when在include中使用 ============================================================== [root@localhost ansible] # cat /etc/ansible/hosts [db] 192.168.24.10 [app] 192.168.24.11 [root@localhost ansible] # cat install_client.yml --- - hosts: '` hosts `' user: ansible sudo : yes sudo_user:root roles: - install_client [root@localhost ansible] # cat roles/install_client/tasks/main.yml --- - include: db.yml when: "hosts == 'db'" - include: app.yml when: "hosts == 'app'" [root@localhost ansible] # cat roles/install_client/tasks/db.yml --- - name: Touchdb file shell: touch /tmp/db .txt [root@localhost ansible] # cat roles/install_client/tasks/app.yml --- - name: Touchdb file shell: touch /tmp/db .txt 执行命令: [root@localhost ansible] # ansible-playbook -i hosts install_client.yml --extra-vars "hosts=db" [root@localhost ansible] # ansible-playbook -i hosts install_client.yml --extra-vars "hosts=app" 示例4: 可以在include中使用tags标签,这个在上面已经介绍过了 ============================================================== |
4. role用法
角色(roles)是ansible自1.2版本开始引入的新特性,用于层次性,结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单的说,roles就是通过分别将变量、文件、任务、模块及处理器放置于单独的目录中、并可以便捷地include他们的一种机制。角色一般用于基于主机构建服务的场景中、但也可以是用于构建守护进程等场景中。role主要作用是重用playbook,例如无论安装什么软件都会安装时间同步服务,那么每个playbook都要编写ntp task,可以将ntp task写好,等到用的时候再调用就行了。ansible中将其组织成role,它有着固定的组织格式,以便playbook调用。
4.1 role层级目录结构
role以特定的层级目录结构进行组织的tasks、variables、handlers、templates、files等;相当于函数的调用把各个功能切割成片段来执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
roles/ role_name/:定义的role的名字 file /: 用于存放copy或script等模块调用的函数 tasks/: 用于定义各种task,此目录一定要有main.yml;其他文件需要main.yml包含调用 handlers/: 用于定义各种handlers,此目录一定要有main.yml;其他文件需要main.yml包含调用 vars/: 用于定义variables,此目录一定要有main.yml;其他文件需要main.yml包含调用 templates/:存储由template模块调用的模板文本; meta/: 定义当前角色的特殊设定及其依赖关系,此目录中至少应该有一个名为main.yml的文件;其它的文件需要由main.yml进行 "包含" 调用; default/: 此目录中至少应该有一个名为main.yml的文件,用于设定默认变量; [root@localhost ansible] # ll roles/ total 40 drwkebor-kebor-kebo 8 root root 4096 Jul 29 22:13 web_Deploy drwkebor-kebor-kebo 8 root root 4096 May 7 2019 web_Deploy_af [root@localhost ansible] # ll roles/web_Deploy total 25 -rw-r--r-- 1 root root 45 May 7 2019 web_Deploy.yml drwkebor-kebor-kebo 2 root root 4096 Jul 10 19:09 defaults drwkebor-kebor-kebo 2 root root 4096 May 7 2019 handlers drwkebor-kebor-kebo 2 root root 4096 May 7 2019 meta drwkebor-kebor-kebo 2 root root 4096 Dec 26 19:42 tasks drwkebor-kebor-kebo 2 root root 4096 May 7 2019 templates drwkebor-kebor-kebo 2 root root 4096 May 7 2019 vars [root@localhost ansible] # ll roles/web_Deploy/tasks/ total 35 -rwkebor-kebor-kebo 1 root root 1542 Jun 24 2019 Auth.yml -rwkebor-kebor-kebo 1 root root 1482 Oct 11 16:13 StartService.yml -rwkebor-kebor-kebo 1 root root 963 Jun 18 2019 main.yml -rwkebor-kebor-kebo 1 root root 1415 May 7 2019 StopService.yml [root@localhost ansible] # cat roles/web_Deploy/tasks/main.yml --- - include_tasks: Auth.yml tags: userauth - include_tasks: StopService.yml tags: stopservice - include_tasks: StartService.yml tags: startservice [root@localhost ansible] # cat roles/web_Deploy/web_Deploy.yml --- - hosts: all roles: - web_Deploy =================================================================================== 再如下一个项目的role目录结构: site.yml webservers.yml fooservers.yml roles/ common/ files/ templates/ tasks/ handlers/ vars/ defaults/ meta/ webservers/ files/ templates/ tasks/ handlers/ vars/ defaults/ meta/ 再看下目录解释: 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为一个角色 "kebo" 指定了如下的行为: 如果 roles /kebo/tasks/main .yml 存在, 其中列出的tasks将被添加到play中 如果roles /kebo/handlers/main .yml 存在, 其中列出的handlers将被添加到play中 如果roles /kebo/vars/main .yml 存在, 其中列出的variables将被添加到play中 如果roles /kebo/meta/main .yml 存在, 其中列出的 "角色依赖" 将被添加到roles列表中 (1.3 andlater) 所有 copy tasks 可以引用 roles /kebo/files/ 中的文件,不需要指明文件的路径。 所有 scripttasks 可以引用 roles /kebo/files/ 中的脚本,不需要指明文件的路径。 所有 template tasks 可以引用roles /kebo/templates/ 中的文件,不需要指明文件的路径。 所有 include tasks 可以引用roles /kebo/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' 注意: pre_tasks: 执行正式 task 之前执行的任务 post_tasks:最后需要执行的任务 |
4.2 在playbook中调用role
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
role存放的路径在配置文件 /etc/ansible/ansible .cfg中定义。如下,发现ansible的roles目录定义到 /root/app/script/ansible/roles 路径下了!! [root@localhost ansible] # cat /etc/ansible/ansible.cfg |grep roles_path roles_path = /etc/ansible/roles : /root/app/script/ansible/roles 在playbook中调用role的方式有三种,如下: 第一种: - hosts: HOSTS remote_user: root roles: - ROLE_NAME1 - ROLE_NAME2 第二种:除了字典第一个元素指明调用的role,后面是传递给role的变量 - hosts: HOSTS remote_user: root roles: - { role: ROLE_NAME1, VARIABLE1: VALUE1, ... } 第三种:when指明role调用的条件 - hosts: HOSTS remote_user: root roles: - { role: ROLE_NAME1, when: CONDITIONS } |
4.3 调用role示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
0) 先来看看role的路径定义 [root@localhost ansible] # cat /etc/ansible/ansible.cfg|grep roles_path roles_path = /etc/ansible/roles : /etc/ansible/roles 1)目录结构 [root@localhost ansible] # tree /etc/ansible/roles /etc/ansible/roles └── httpd #palybook调用时role的名称 ├── defaults ├── files └── handlers │ └── main.yml #所有的目录文件,并不一定要有,用时才创建 └── mata └── tasks │ └── main.yml └── tamplates └── httpd.conf.c6.j2 #centos6,centos7的配置文件 └── httpd.conf.c7.j2 2)tasks文件 [root@localhost ansible] # cat /etc/ansible/roles/httpd/tasks/main.yml - name: install httpd package yum: name=httpd state=present - name: install configure file template: src=httpd.conf.c{ { ansible_distribution_major_version }}.j2 dest= /etc/httpd/conf/httpd .conf tags: instconf notify: restart httpd service - name: start httpd service service: name=httpd state=started enabled= true 3) handlers文件 [root@localhost ansible] # cat /etc/ansible/roles/httpd/handlers/main.yml - name: restart httpd service service: name=httpd state=restarted 4) 模板文件 [root@localhost ansible] # grep ^Listen /etc/ansible/roles/httpd/templates/httpd.conf.c6.j2 Lister { { httpd_port }} 5) 变量 [root@localhost ansible] # cat /etc/ansible/roles/httpd/vars/main.yml httpd_port: 8088 6) playbook文件 [root@localhost ansible] # cat /etc/ansible/httpd_conf.yml --- - hosts: webservers remote_user: root roles: - { role: httpd } 7) 执行playbook文件,并查看httpd端口 [root@localhost ansible] # ansible-playbook -i /etc/ansible/hosts /etc/ansible/httpd_conf.yml [root@localhost ansible] # ansible -i /etc/ansible/hosts webservers -m shell -a "ss -tnlp|grep :80" ======================================================================================================== 再来看一例: 1.group: 创建用户组nginx 2.user: 创建用户nginx 3.yum: 安装nginx 4.template: 配置文件更新nginx.conf 5.service: 启动nginx [root@localhost ~] # cat /etc/ansible/ansible.cfg|grep roles_path roles_path = /etc/ansible/roles : /root/ansible/roles [root@localhost ~] # cd /root/ansible/roles/nginx [root@localhost nginx] # mkdir tasks templates [root@localhost nginx] # cd tasks [root@localhost tasks] # vim group.yml - name: create group nginx group: name=nginx gid=80 [root@localhost tasks] # vim user.yml -name: create user nginx user: name=nginx uid=80 group=nginx system= yes shell= /sbi/nologin [root@localhost tasks] # vim install.yml - name: install package yum: name=nginx [root@localhost tasks] # vim start.yml - name: start service service: name=nginx state=started enabled= yes [root@localhost tasks] # vim restart.yml - name: restart service service: name=nginx state=restarted [root@localhost tasks] # vim templ.yml - name: copy conf template: src=nginx.conf.j2 dest= /etc/nginx/conf/nginx .conf [root@localhost tasks] # vim main.yml - include: group.yml - include: user.yml - include: install .yml - include: templ.yml - include: start.yml [root@localhost tasks] # cd ../templates && ls nginx.conf.j2 [root@localhost tasks] # cd /root/ansible [root@localhost ansible] # vim nginx_role.yml - hosts: websrvs remote_user: root roles: - role: nginx 执行命令: [root@localhost ansible] # ansible-playbook nginx_role.yml |
5. loop列表循环用法
在ansible 2.5版本之前,大多数人习惯使用"with_X"风格的关键字操作循环,从ansible 2.6版本开始,官方开始推荐使用"loop"关键字代替"with_X"风格关键字。下面通过一些小示例来说明使用loop关键字进行的列表循环操作。[loop、with_items、with_list 三者等同,效果是一样的!]。ansible的循环使用,可以参考下面"循环变量"以及参考这里
playbook中的变量设置
######## 变量的优先级 ########
1. extra vars变量(在命令行中使用 -e);优先级最高
2. 在inventory中定义的连接变量(比如ansible_ssh_user);优先级第二
3. 大多数的其他变量(命令行转换,play中的变量,include的变量,role的变量等);优先级第三
4. 在inventory定义的其他变量;优先级第四
5. 有系统发现的facts;优先级第五
6. "role默认变量",这个是最默认的值,很容易丧失优先权。优先级最小。
另外:在inventory清单列表里定义的变量:单个主机定义的变量优先级高于主机组定义的变量
经过实验,ansible使用inventory定义变量的优先级顺序从高到低为:
1. host_vars下定义变量
2. inventory中单个主机定义变量
3. group_vars下定义变量
4. inventory中组定义变量
playbook中定义变量,如下:
1 2 3 |
- hosts: webservers vars: http_port: 80 |
YAML陷阱
YAML语法要求如果值以{ { foo }}开头的话,那么就需要将整行用双引号包起来,这是为了确认你不是想声明一个YAML字典。
如下面配置是不行的!!!
1 2 3 4 |
--- - hosts: app_servers vars: app_path: { { base_path }} /data/web |
应该改成下面这样:
1 2 3 4 |
--- - hosts: app_servers vars: app_path: "{ { base_path }}/data/web" |
######## Ansbile-playbook变量配置方法 ########
1. 在inventory主机清单文件中定义变量
可以直接定义在主机清单文件/etc/ansible/hosts中,表明该变量只对对应的主机或者组有效,对其余的主机和组无效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
有针对单个主机定义变量和组定义变量两种方式。 1)向不同的单个主机传递不同的变量; IP /HOSTNAME var1=value1 var2=value2 2)向组中的主机传递相同的变量; [groupname:vars] var1=value1 var2=value2 但是注意: 组定义变量的作用范围是组下的所有主机。 当两种定义方式同时存在时,ansible会优先采用单个主机定义的变量值! [root@ss-server ansible] # pwd /etc/ansible [root@ss-server ansible] # cat hosts|egrep -v "^#|^$" [kevin] 172.16.60.20 key=20180101 172.16.60.22 ansible_ssh_port=22288 key= "niubility" [root@ss-server ansible] # cat ansi.yml --- - hosts: kevin gather_facts: False tasks: - name: haha debug: msg= "the { { inventory_hostname }} value is { { key }}" 执行结果(注意inventory_hostname代表inventory列表列表里被控节点的主机名): [root@ss-server ansible] # ansible-playbook ansi.yml PLAY [kevin] ************************************************************************************************************************************* TASK [haha] ************************************************************************************************************************************** ok: [172.16.60.20] => { "msg" : "the 172.16.60.20 value is 20180101" } ok: [172.16.60.22] => { "msg" : "the 172.16.60.22 value is niubility" } PLAY RECAP *************************************************************************************************************************************** 172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 172.16.60.22 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 再看下面一示例 [root@ss-server ansible] # tail -n 10 /etc/ansible/hosts [webserver] 172.16.60.51 dir = /root/node2 172.16.60.80 dir = /root/node1 [node1] 172.16.60.80 [webserver:vars] file = hostname .txt [root@ss-server ansible] # cat playbook.yml --- - hosts: webserver remote_user: root tasks: - name: Create New Folder file : name={ { dir }} state=directory - name: Create New Folder shell: echo ` hostname ` > { { dir }}/{ { file }} 执行结果: [root@ss-server ansible] # ansible-playbook playbook.yml PLAY [webserver] ************************************************************************ TASK [Gathering Facts] *************************************************************** ok: [172.16.60.80] ok: [172.16.60.51] TASK [Create New Folder] ************************************************************* changed: [172.16.60.51] changed: [172.16.60.80] TASK [Create New Folder] ************************************************************* changed: [172.16.60.51] changed: [172.16.60.80] PLAY RECAP *************************************************************************** 172.16.60.51 : ok=3 changed=2 unreachable=0 failed=0 172.16.60.80 : ok=3 changed=2 unreachable=0 failed=0 |
此外:ansible还内置了一些固定的主机变量名,在inventory中定义其值, 在另一篇文章中介绍过。
2. 通过host_vars和group_vars目录来定义变量
/etc/ansible/目录是linux系统上ansible默认的配置文件目录(Mac系统上的话,其默认配置目录是在/usr/local/etc/ansible/),在该目录下创建host_vars和group_vars两个目录用来存放定义变量的文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
1)针对单个主机的变量 [root@ss-server ansible] # pwd /etc/ansible [root@ss-server ansible] # cat host_vars/172.16.60.20 --- user: root pass: root@123 2)针对 test 组的变量 [root@ss-server ansible] # cat group_vars/kevin --- user: work pass: work@123 ############ 在inventory清单列表文件里,单个主机定义的变量优先级高于主机组定义的变量 ############ ############ 经过实验,ansible使用变量的优先级顺序从高到低为 ############# 1. host_vars下定义变量 2. inventory中单个主机定义变量 3. group_vars下定义变量 4. inventory中组定义变量 示例如下: [root@ss-server ~] # cat /root/ansible/hosts [kevin] 172.16.60.20 ansible_ssh_port=22222 172.16.60.21 ansible_ssh_port=22288 [root@ss-server ~] # cat /root/ansible/group_vars/kevin key=20191010 [root@ss-server ~] # cat /root/ansible/bo.yml --- - hosts: kevin remote_user: root tasks: - debug: msg= 'The key is { {key}} ' [root@ss-server ~] # ansible-playbook /root/ansible/bo.yml PLAY [kevin] ************************************************************************************************************************************* TASK [haha] ************************************************************************************************************************************** ok: [172.16.60.20] => { "msg" : "The key is 20191010" } ok: [172.16.60.22] => { "msg" : "The key is 20191010" } PLAY RECAP *************************************************************************************************************************************** 172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 172.16.60.22 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 |
3. 通过var_files定义变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
[root@ss-server ansible] # cat laoji.yml --- key: jiayou [root@ss-server ansible] # cat bo.yml --- - hosts: kevin gather_facts: False vars_files: - laoji.yml tasks: - name: display debug: msg= "the { { inventory_hostname }} valus is { { key }}" 执行结果: [root@ss-server ansible] # ansible-playbook bo.yml PALY [kevin] **************************************************** TASK [display] **************************************************** ok: [172.16.60.20] => { "changed" : false "msg" : "the 172.16.60.20 value is jiayou" } PLAY RECAP **************************************************** 172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 |
4. 通过vars_prompt交互式传入变量
在playbook中定义vars_prompt的变量名和交互式提示信息,就可以实现在运行playbook时,通过交互的传入变量值。
private字段:用来定义交互时是否回显输入的值,默认private为yes;
default字段:用来定义变量的默认值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
[root@ss-server ansible] # cat prom.yml --- - hosts: kevin remote_user: root vars_prompt: - name: "var1" prompt: "please input you name" private: no - name: "var2" prompt: "please input you age" private: yes default: 'kevin var' tasks: - name: display var1 debug: msg= "your name of var1 is { { var1 }}" - name: display var2 debug: msg= "you age of var2 is { { var2 }}" 执行结果: [root@ss-server ansible] # ansible-playbook prom.yml your name of var1: wangzhaojun #把输入的内容传递给变量var1。输入的值显示出来!! you age of var2 [kevin var]: #把输入的内容传递给默认变量kevin var。但是输入的值不显示出来!! 比如这里输入的30 PALY [kevin] **************************************************** TASK [Gathering Facts] **************************************************** OK: [172.16.60.20] TASK [display var1] **************************************************** ok: [172.16.60.20] =>{ "msg" : "youn name of var1 is wangzhaojun" } TASK [display var2] **************************************************** ok: [172.16.60.20] =>{ "msg" : "youn name of var2 is 30" } PLAY RECAP **************************************************** 172.16.60.20 : ok=3 changed=0 unreachable=0 failed=0 接下来再来看一个 "ansible 中prompt 交互变量的使用" 的示例 [root@ss-server ansible] # cat haha.yml --- - hosts: kevin remote_user: root vars_prompt: - name: "your_name" prompt: "what is your name" private: no - name: "your_age" prompt: "how old are you" private: no - name: "solution" prompt: "Choose the solution you want \n A: solutionA\n B: solutionB\n C: solutionC\n" private: no default: A - name: "user_name" prompt: "Enter user name" private: no - name: "user_password" prompt: "Enter user password" private: no encrypt: "sha512_crypt" confirm: yes tasks: - name: "output vars" debug: msg= "your name is { { your_name }},you are { { your_age }} years old" - name: "output solution" debug: msg= "the final solution is { {solution}}" - name: "create_user" user: name: "{ {user_name}}" password: "{ {user_password}}" - name: "debug_create user" debug: msg= "create user { {user_name}} in" 执行结果为: [root@ss-server ansible] # ansible-playbook haha.yml what is your name: wangshibo how old are you: 29 Choose the solution you want A: solutionA B: solutionB C: solutionC [A]: C Enter user name: bobo Enter user password: bobo123 confirm Enter user password: bobo123 PLAY [kevin] ************************************************************************************************************************************* TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.20] TASK [output vars] ******************************************************************************************************************************* ok: [172.16.60.20] => { "msg" : "your name is wangshibo,you are 29 years old" } TASK [output solution] *************************************************************************************************************************** ok: [172.16.60.20] => { "msg" : "the final solution is C" } TASK [create_user] ******************************************************************************************************************************* changed: [172.16.60.20] TASK [debug_create user] ************************************************************************************************************************* ok: [172.16.60.20] => { "msg" : "create user bobo in" } PLAY RECAP *************************************************************************************************************************************** 172.16.60.20 : ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 |
5. 通过ansible-playbook命令行定义变量!即参数传入变量
除了"vars_prompt"和"vars_files",也可以通过Ansible命令行发送变量。如果想要编写一个通用的发布playbook时则特别有用!你可以传递应用的版本以便部署。例如下面命令(注意: --extra-vars 相等于 -e)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
[root@ss-server ~] # ansible-playbook release.yml --extra-vars "version=1.78.34 other_variable=fos" 其他场景中,通过参数传入变量也是很有用的。比如为playbook设置主机群组或用户。如下: [root@ss-server ~] # vim exap.yml --- - hosts: '{ {hosts}}' remote_user: '{ {user}}' tasks: - name: "一个测试" debug: msg= "your hosts is { {hosts}}, user is { {user}}" 执行的时候,通过参数传入变量(-e)。变量{ {hosts}}可以是主机群组名称,也可以是主机ip。 [root@ss-server ansible] # ansible-playbook exap.yml -e "hosts=kevin user=root" [WARNING]: Found variable using reserved name: hosts PLAY [kevin] ************************************************************************************************************************************* TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.20] TASK [一个测试] ************************************************************************************************************************************** ok: [172.16.60.20] => { "msg" : "your hosts is kevin, user is root" } PLAY RECAP *************************************************************************************************************************************** 172.16.60.20 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 在命令行里面传值的方法: [root@ss-server ansible] # ansible-playbook amp.yml --extra-vars "hosts=webserver user=root" 还可以使用json格式传递参数: [root@ss-server ansible] # ansible-playbook amp.yml --extra-vars "{'hosts':'webserver', 'user':'root'}" 还可以将参数放在文件里面进行传递(注意命令行里要是用 "@文件名" ): [root@ss-server ansible] # ansible-playbook amp.yml --extra-vars "@anhui.yml" [root@ss-server ansible] # cat anhui.yml --- hosts: webserver user: root 例如下面这个添加用户的plakbook剧本配置,用传递了json文件: [root@ss-server ansible] # cat useradd.yml --- - hosts: "{ { host }}" gather_facts: no remote_user: root vars: UserName: "{ { user }}" UserPassword: "{ { pass }}" tasks: - name: create new user { { UserName }} user: name={ { UserName }} shell= /bin/bash password={ { UserPassword |password_hash( 'sha512' ) }} update_password=always append= yes - name: Config /etc/sudoers lineinfile: dest= /etc/sudoers state=present line= '{ { item }}' validate= 'visudo -cf %s' with_items: - "{ { user }} ALL=(ALL) NOPASSWD:ALL,!/bin/rm,!/bin/su,!/usr/bin/passwd,!/usr/sbin/visudo,!/sbin/shutdown,!/sbin/reboot,!/sbin/halt" - "Defaults: { { user }} !requiretty" [root@ss-server ansible] # ansible-playbook useradd.yml -e "host=172.16.60.20 user=kevin_bo pass=kevin@bo123" 在上例中,变量pass是密码,如果变量pass里有特殊的字符,或者变量pass是一串数组的话,它将被转义。若不想被转义,可以使用如下方法: [root@ss-server ansible] # cat user.json host: webserver #ansible里面定义的主机组或者直接配置主机ip,如172.16.60.20 user: kevin_bo #添加的用户名 pass: 'Fxx6unM$R%I$Jna&' #添加的用户的密码,可以用百度上随机密码生成器生成 指定user,json文件执行剧本 (使用JSON格式的文件即可,-e 传递变量,优先级最高) [root@ss-server ansible] # ansible-playbook useradd.yml -e "@user.json" 删除web组中所有用户 [root@ss-server ansible] # ansible webserver -m user -a 'name=zhangsan state=absent remove=yes' |
6. 在playbook剧本中定义变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
在playbook中定义变量需要用到Ansible的vars模块,可以将所有需要用到的变量统一在vars模块下定义,定义格式需要遵循YAML语言格式: vars: - var1: value1 - var2: value2 - var3: value3 - ....: ..... 示例如下: [root@ss-server ansible] # cat playbook.yml --- - hosts: kevin remote_user: root vars: - dir1: /root/Ansible - dir2: /root/Ansible/test1 - dir3: /root/Ansible/test2 tasks: - name: Create New Folder file : name={ { dir1 }} state=directory - name: Create New Folder file : name={ { dir2 }} state=directory - name: Create New Folder file : name={ { dir3 }} state=directory [root@ss-server ansible] # ansible-playbook playbook.yml PLAY [kevin] ************************************************************************* TASK [Gathering Facts] *************************************************************** ok: [172.16.60.20] TASK [Create New Folder] ************************************************************* changed: [172.16.60.20] TASK [Create New Folder] ************************************************************* changed: [172.16.60.20] TASK [Create New Folder] ************************************************************* changed: [172.16.60.20] PLAY RECAP *************************************************************************** [172.16.60.20] : ok=4 changed=3 unreachable=0 failed=0 |
7. 通过roles角色定义变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
在Ansible的roles中定义变量,需要将变量及值的键值对形式写到roles的vars目录下的main.yml文件中,同样适用YAML语言格式,格式如下: var1: value1 var2: value2 var3: value3 但是请注意: 通过Roles定义的变量只适用于当前roles。如下是roles文件组织结构: [root@ss-server roles] # tree test/ test / ├── files ├── handlers ├── playbook.retry ├── playbook.yml ├── tasks │ └── main.yml ├── templates └── vars └── main.yml 5 directories, 4 files [root@ss-server roles] # cat test/tasks/main.yml #任务文件 - name: Get IP Address shell: echo `{ { cmd }}` >> { { dir }}/{ { file }} [root@ss-server roles] # cat test/vars/main.yml #定义变量cmd cmd: hostname -I [root@ss-server roles] # cat test/playbook.yml --- - hosts: webserver remote_user: root roles: - test hosts清单列表里定义的变量,适用于所有roles。 [root@ss-server roles] # cat /etc/ansible/hosts [webserver] 172.16.60.51 dir = /root/node2 172.16.60.80 dir = /root/node1 [node1] 172.16.60.80 [webserver:vars] file = hostname .txt playbook调用Roles,执行结果为: [root@Centos7T test ] #ansible-playbook playbook.yml PLAY [websvr] ************************************************************************ TASK [Gathering Facts] *************************************************************** ok: [172.16.60.80] ok: [172.16.60.51] TASK [ test : Get IP Address] ********************************************************* changed: [172.16.60.51] changed: [172.16.60.80] PLAY RECAP *************************************************************************** 172.16.60.51 : ok=2 changed=1 unreachable=0 failed=0 172.16.60.80 : ok=2 changed=1 unreachable=0 failed=0 |
8. 使用Facts获取的信息
还有其它地方可以获取变量, 这些变量是自动发现的,而不是用户自己设置的。Facts通过访问远程系统获取相应的信息,一个很好的例子就是远程主机的IP地址或者操作系统是什么。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
使用以下命令可以查看哪些信息是可用的(kevin是上面在 /etc/ansible/hosts 列表文件中配置的主机群组): [root@ss-server ansible] # ansible kevin -m setup [root@ss-server ansible] # ansible kevin -m setup|grep "ansible_python_version" "ansible_python_version" : "2.7.5" , 可以在playbook中这样引用上面被控制主机的python版本: { { ansible_python_version }} [root@ss-server ansible] # ansible kevin -m setup|grep "ansible_nodename" "ansible_nodename" : "ss-server" , 可以在playbook中这样引用上面被控制主机的主机名: { { ansible_nodename }} [root@ss-server ansible] # ansible kevin -m setup|grep "ansible_hostname" "ansible_hostname" : "ss-server" , 被控制主机的主机名变量还可以是: { { ansible_hostname }} 访问复杂变量数据 有些提供的facts, 比如网络信息等, 是一个嵌套的数据结构。访问它们使用简单的 { { foo }} 语法并不够用, 但是也仍然很容易.如下所示: { { ansible_eth0[ "ipv4" ][ "address" ] }} 或者这样写: { { ansible_eth0.ipv4.address }} ############ 如果关闭Facts,可以大大提高ansible的执行速度 ########### 关闭方法如下: [root@ss-server ansible] # cat anhui.yml --- - hosts: kevin gather_facts: no |
9. register注册变量
变量的另一个主要用途是在运行命令时,把命令结果存储到一个变量中,不同模块的执行结果是不同的。运行playbook时使用-v选项可以看到可能的结果值,ansible执行任务的结果值可以保存在变量中,以便稍后使用它。register方式主要用于在task之间传递变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
示例如下: [root@ss-server ansible] # cat /etc/ansible/hosts [kevin] 172.16.60.237 172.16.60.238 [root@ss-server ansible] # cat /root/register.yml --- - hosts: kevin remote_user: root tasks: - name: register bo_test shell: hostname -I register: info - name: display info debug: msg= "this host ip is { { info }}" [root@ss-server ansible] # ansible-playbook /root/register.yml PLAY [kevin] ************************************************************************************************************************************* TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.238] ok: [172.16.60.237] TASK [register bo_test] ************************************************************************************************************************** changed: [172.16.60.238] changed: [172.16.60.237] TASK [display info] ****************************************************************************************************************************** ok: [172.16.60.237] => { "msg" : "this host ip is {'stderr_lines': [], u'changed': True, u'end': u'2019-12-15 22:07:18.431549', 'failed': False, u'stdout': u'172.16.60.237 ', u'cmd': u'hostname -I', u'rc': 0, u'start': u'2019-12-15 22:07:18.408235', u'stderr': u'', u'delta': u'0:00:00.023314', 'stdout_lines': [u'172.16.60.237 ']}" } ok: [172.16.60.238] => { "msg" : "this host ip is {'stderr_lines': [], u'changed': True, u'end': u'2019-12-15 22:07:18.430108', 'failed': False, u'stdout': u'172.16.60.238 ', u'cmd': u'hostname -I', u'rc': 0, u'start': u'2019-12-15 22:07:18.407184', u'stderr': u'', u'delta': u'0:00:00.022924', 'stdout_lines': [u'172.16.60.238 ']}" } PLAY RECAP *************************************************************************************************************************************** 172.16.60.237 : ok=3 changed=1 unreachable=0 failed=0 172.16.60.238 : ok=3 changed=1 unreachable=0 failed=0 这里注意下: register定义的info变量在第二个task中用来查看前一个task中执行的 hostname 命令的结果。 可以看到playbook运行后的结果中,info返回的是一段python字典数据,如果只想看到stdout部分的信息的话,可以通过info[‘stdout’]来引用。 [root@ss-server ansible] # cat /root/register.yml --- - hosts: kevin remote_user: root tasks: - name: register bo_test shell: hostname -I register: info - name: display info debug: msg= "this host ip is { { info['stdout'] }}" [root@ss-server ansible] # ansible-playbook /root/register.yml PLAY [kevin] ************************************************************************************************************************************* TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.238] ok: [172.16.60.237] TASK [register bo_test] ************************************************************************************************************************** changed: [172.16.60.237] changed: [172.16.60.238] TASK [display info] ****************************************************************************************************************************** ok: [172.16.60.237] => { "msg" : "this host ip is 172.16.60.237 " } ok: [172.16.60.238] => { "msg" : "this host ip is 172.16.60.238 " } PLAY RECAP *************************************************************************************************************************************** 172.16.60.237 : ok=3 changed=1 unreachable=0 failed=0 172.16.60.238 : ok=3 changed=1 unreachable=0 failed=0 |
10. hostvars 变量
该变量用于引用其他主机上收集的facts中的数据,或者引用其他主机的主机变量、主机组变量。即从一台远程主机获取另一台远程主机的变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
如下示例: [root@ss-server ansible] # cat /etc/ansible/hosts [kevin] 172.16.60.237 addr=beijing 172.16.60.238 user=shibo age=39 [root@ss-server ansible] # cat test.yml --- - hosts: kevin remote_user: root gather_facts: False tasks: - name: this is test1 debug: msg= "She is come from { { hostvars['172.16.60.237']['addr'] }}" - name: this is test2 debug: msg= "I am { { hostvars['172.16.60.238']['user'] }}, and age is { { hostvars['172.16.60.238']['age'] }}" 或者将 test .yml文件配置如下,即由 "[变量]" " 改为 " .变量" 两种配置的执行结果都是一样的! [root@ss-server ansible] # cat test.yml --- - hosts: kevin remote_user: root gather_facts: False tasks: - name: this is test1 debug: msg= "She is come from { { hostvars['172.16.60.237'].addr }}" - name: this is test2 debug: msg= "I am { { hostvars['172.16.60.238'].user }}, and age is { { hostvars['172.16.60.238'].age }}" 执行结果为: [root@ss-server ansible] # ansible-playbook test.yml PLAY [kevin] ************************************************************************************************************************************* TASK [this is test1] ***************************************************************************************************************************** ok: [172.16.60.237] => { "msg" : "She is come from beijing" } ok: [172.16.60.238] => { "msg" : "She is come from beijing" } TASK [this is test2] ***************************************************************************************************************************** ok: [172.16.60.237] => { "msg" : "I am shibo, and age is 39" } ok: [172.16.60.238] => { "msg" : "I am shibo, and age is 39" } PLAY RECAP *************************************************************************************************************************************** 172.16.60.237 : ok=2 changed=0 unreachable=0 failed=0 172.16.60.238 : ok=2 changed=0 unreachable=0 failed=0 ############################# 这里需要注意下 ########################### 除了上面的配置,还可以如下配置,但是请注意: 1)debug后面必须使用 "var" 字段,即hostvar变量传递给var字段的变量。 2)var=hostvars[....],等于两边不能有空格! [root@ss-server ansible] # cat test.yml --- - hosts: kevin remote_user: root gather_facts: False tasks: - name: this is test1 debug: var=hostvars[ '172.16.60.237' ][ 'addr' ] - name: this is test2 debug: var=hostvars[ '172.16.60.238' ][ 'user' ] 或者将 test .yml文件配置如下,这两个配置方法的执行结果是一样的! [root@ss-server ansible] # cat test.yml --- - hosts: kevin remote_user: root gather_facts: False tasks: - name: this is test1 debug: var=hostvars[ '172.16.60.237' ].addr - name: this is test2 debug: var=hostvars[ '172.16.60.238' ].user 执行结果为: [root@ss-server ansible] # ansible-playbook test.yml PLAY [kevin] ************************************************************************************************************************************* TASK [this is test1] ***************************************************************************************************************************** ok: [172.16.60.237] => { "hostvars['172.16.60.237']['addr']" : "beijing" } ok: [172.16.60.238] => { "hostvars['172.16.60.237']['addr']" : "beijing" } TASK [this is test2] ***************************************************************************************************************************** ok: [172.16.60.237] => { "hostvars['172.16.60.238']['user']" : "shibo" } ok: [172.16.60.238] => { "hostvars['172.16.60.238']['user']" : "shibo" } PLAY RECAP *************************************************************************************************************************************** 172.16.60.237 : ok=2 changed=0 unreachable=0 failed=0 172.16.60.238 : ok=2 changed=0 unreachable=0 failed=0 注意:如果同一个name里配置了多个debug,则默认执行最后一个debug内容! (多个task的话,也是执行最后一个task) [root@ss-server ansible] # cat test.yml --- - hosts: kevin remote_user: root gather_facts: False tasks: - name: this is test1 debug: var=hostvars[ '172.16.60.237' ][ 'addr' ] - name: this is test2 debug: var=hostvars[ '172.16.60.238' ][ 'user' ] debug: var=hostvars[ '172.16.60.238' ][ 'age' ] 执行结果: [root@ss-server ansible] # ansible-playbook test.yml [WARNING]: While constructing a mapping from /etc/ansible/test .yml, line 8, column 7, found a duplicate dict key (debug). Using last defined value only. PLAY [kevin] ************************************************************************************************************************************* TASK [this is test1] ***************************************************************************************************************************** ok: [172.16.60.237] => { "hostvars['172.16.60.237']['addr']" : "beijing" } ok: [172.16.60.238] => { "hostvars['172.16.60.237']['addr']" : "beijing" } TASK [this is test2] ***************************************************************************************************************************** ok: [172.16.60.237] => { "hostvars['172.16.60.238']['age']" : "39" } ok: [172.16.60.238] => { "hostvars['172.16.60.238']['age']" : "39" } PLAY RECAP *************************************************************************************************************************************** 172.16.60.237 : ok=2 changed=0 unreachable=0 failed=0 172.16.60.238 : ok=2 changed=0 unreachable=0 failed=0 |
11. 列表变量、循环变量、字典变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
1)ansible的变量不仅可以是单个的值,也可以为列表,即ansible传列表作为变量。如下示例: ################################################################################################################# [root@ss-server ansible] # vim test.yml --- - hosts: test_host gather_facts: no vars: - list: [1,2,3] tasks: - name: echo debug: msg= "{ { list }}" 执行结果: [root@ss-server ansible] # ansible-palybook test.yml TASK [ echo ] ******************************************************************** ok: [172.16.60.220] => { "msg" : [ 1, 2, 3 ] } 再来看一例: linux创建用户,需要获取的变量有用户名,用户密码,用户组,有时候需要创建多个用户,那么传递给ansible的用户肯定是列表,但每一组又有用户名、密码、组这些变量值。 做法如下: [root@ss-server ~] # cat /etc/ansible/test.yml --- - hosts: test_host gather_facts: no tasks: - name: create or update account user: name={ { item.user }} password={ { item.password }} groups ={ { item.group }} system=no with_items: - '{ { user_list }}' 执行结果: [root@ss-server ansible] # ansible-playbook /etc/ansible/test.yml -e '{"user_list":[{"user":"user1","password":"*******","group":"user1"}]}' ################################################################################################################# 2)循环列表 结合循环,这个特性就变得很有用;以参数传递列表给playbook,不用在playbook中固定循环的次数与内容。如下示例: [root@ss-server ansible] # vim test.yml --- - hosts: test_host gather_facts: no vars: - list: [1,2,3] tasks: - name: this is loop debug: msg= "{ { item }}" with_items: '{ {list}}' 执行结果: [root@ss-server ansible] # ansible-palybook test.yml TASK [this is loop] ******************************************************************** ok: [172.16.60.220] => (item=1) => { "item" : 1, "msg" : 1 } ok: [localhost] => (item=2) => { "item" : 2, "msg" : 2 } ok: [localhost] => (item=3) => { "item" : 3, "msg" : 3 } 接着看下面一例: loop 关键字表示循环,去读循环体里的变量固定使用{ {item}},item是个字典对象item.key=value。 需要注意:下面 test .yml文件中的 "loop" 关键字 改为 "with_items" 关键字,效果是一样的! ansible的循环用法:在ansible 2.5版本之前,大多数人习惯使用 "with_X" 风格的关键字操作循环, 从ansible 2.6版本开始,官方开始推荐使用 "loop" 关键字代替 "with_X" 风格关键字。 [root@ss-server ~] # cat /etc/ansible/test.yml --- - name: this is test hosts: test_host connection: local gather_facts: no tasks: - name: debug loop debug: msg: "{ {item.A1}}" loop: #这里将"loop"关键字 改为 "with_items"关键字,效果是一样的! - A: a A1: a1 A2: a2 - B: b A1: b1 A2: b2 - C: c A1: c1 A2: c2 - D: d A1: d1 A2: d2 执行结果:以上loop下的四个变量分别称为一块,即一个item,符号 "-" 为循环体块的标志,{ {item.A1}}的值,即分别为a1,b1,c1,d1 [root@ss-server ~] # ansible-playbook /etc/ansible/test.yml PLAY [this is test ] ********************************************************************************************************************************* TASK [debug loop] ******************************************************************************************************************************** ok: [172.16.60.20] => (item={u 'A' : u 'a' , u 'A1' : u 'a1' , u 'A2' : u 'a2' }) => { "msg" : "a1" } ok: [172.16.60.20] => (item={u 'A1' : u 'b1' , u 'B' : u 'b' , u 'A2' : u 'b2' }) => { "msg" : "b1" } ok: [172.16.60.20] => (item={u 'A1' : u 'c1' , u 'C' : u 'c' , u 'A2' : u 'c2' }) => { "msg" : "c1" } ok: [172.16.60.20] => (item={u 'A1' : u 'd1' , u 'A2' : u 'd2' , u 'D' : u 'd' }) => { "msg" : "d1" } PLAY RECAP *************************************************************************************************************************************** 172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 再来看一例: [root@ss-server ~] # cat /etc/ansible/test.yml --- - name: this is test hosts: test_host connection: local gather_facts: no vars: my_list: - a - b - c - 1 tasks: - name: debug loop output debug: msg: "The { {index}} one is { {item}}" loop: "{ {my_list}}" # 或者使用with_items: "{ {my_list}}" 或者 with_list: "{ {my_list}}" loop_control: index_var: index 执行结果: [root@ss-server ~] # ansible-playbook /etc/ansible/test.yml PLAY [this is test ] ****************************************************************************************************************************** TASK [debug loop output] ************************************************************************************************************************* ok: [172.16.60.20] => (item=a) => { "msg" : "The 0 one is a" } ok: [172.16.60.20] => (item=b) => { "msg" : "The 1 one is b" } ok: [172.16.60.20] => (item=c) => { "msg" : "The 2 one is c" } ok: [172.16.60.20] => (item=1) => { "msg" : "The 3 one is 1" } PLAY RECAP *************************************************************************************************************************************** 172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ################################################################################################################# 3)字典变量 变量也可以为字典。如下示例, 用到了ansible的循环,循环关键字是 "with_X" , 改为loop关键字,效果是一样的! 注意:在变量中使用循环时,vars下必须要有 "-" ,符号 "-" 为循环体块的标志!! [root@ss-server ansible] # vim test.yml --- - hosts: test_host gather_facts: no vars: - lists: list1: [1,2,3] list2: [4,5] tasks: - name: loop debug: msg= "{ { item }}" with_items: '{ {lists["list1"]}}' #替换为 loop: '{ {lists["list1"]}}', 或者 with_list: '{ {lists["list1"]}}' 效果是一样的 执行结果: [root@ss-server ansible] # ansible-palybook test.yml TASK [loop] ******************************************************************** ok: [172.16.60.220] => (item=1) => { "item" : 1, "msg" : 1 } ok: [localhost] => (item=2) => { "item" : 2, "msg" : 2 } ok: [localhost] => (item=3) => { "item" : 3, "msg" : 3 } 接着看下面一例: [root@ss-server ~] # cat /etc/ansible/test.yml --- - hosts: test_host gather_facts: no vars: - users : name: [xiaoming] address: [anhui] age: [28] tasks: - name: this is loop debug: msg= "{ { item }}" loop: '{ {users["name"]}}' [root@ss-server ~] # ansible-playbook /etc/ansible/test.yml PLAY [test_host] ************************************************************************************************************************************* TASK [this is loop] ************************************************************************************************************************************** ok: [172.16.60.20] => (item=xiaoming) => { "msg" : "xiaoming" } PLAY RECAP *************************************************************************************************************************************** 172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 上面的示例,如果要想打印多个字典变量,修改如下: [root@ss-server ~] # cat /etc/ansible/test.yml --- - hosts: test_host gather_facts: no vars: - users : name: [xiaoming] address: [anhui] age: [28] tasks: - name: this is loop debug: msg= "{ { item }}" loop: #这里用loop,with_items,with_list都可以 - '{ {users["name"]}}' - '{ {users["address"]}}' - '{ {users["age"]}}' 执行结果: [root@ss-server ~] # ansible-playbook /etc/ansible/test.yml PLAY [test_host] ********************************************************************************************************************************* TASK [this is loop] ****************************************************************************************************************************** ok: [172.16.60.20] => (item=[u 'xiaoming' ]) => { "msg" : [ "xiaoming" ] } ok: [172.16.60.20] => (item=[u 'anhui' ]) => { "msg" : [ "anhui" ] } ok: [172.16.60.20] => (item=[28]) => { "msg" : [ 28 ] } PLAY RECAP *************************************************************************************************************************************** 172.16.60.20 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 |
###### 可以在一个yaml文件里放置多个hosts,将多个tasks任务一起执行 ########
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
[root@ss-server ansible] # cat /etc/ansible/hosts 172.16.60.237 [kevin] 172.16.60.238 [grace] 172.16.60.236 [root@ss-server ansible] # cat bo.yml --- - hosts: kevin remote_user: root gather_facts: False tasks: - name: this is test1 shell: hostname -I - hosts: 172.16.60.237 remote_user: root tasks: - name: this is test2 debug: msg= "this server is { { info }} " - hosts: 172.16.60.238 remote_user: root tasks: - name: this is test3 debug: msg= "this key is { { haha }} " 执行结果: [root@ss-server ansible] # ansible-playbook bo.yml -e "{'info':'my server','haha':'123123213123'}" PLAY [kevin] ************************************************************************************************************************************* TASK [this is test1] ***************************************************************************************************************************** changed: [172.16.60.238] PLAY [172.16.60.237] ***************************************************************************************************************************** TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.237] TASK [this is test2] ***************************************************************************************************************************** ok: [172.16.60.237] => { "msg" : "this server is my server " } PLAY [172.16.60.238] ***************************************************************************************************************************** TASK [Gathering Facts] *************************************************************************************************************************** ok: [172.16.60.238] TASK [this is test3] ***************************************************************************************************************************** ok: [172.16.60.238] => { "msg" : "this key is 123123213123 " } PLAY RECAP *************************************************************************************************************************************** 172.16.60.237 : ok=2 changed=0 unreachable=0 failed=0 172.16.60.238 : ok=3 changed=1 unreachable=0 failed=0 ############# 再来看下面一个测试环境用过的部署脚本配置 ######################### [root@localhost ansible] # cat /opt/ansible_cfg/deploy.yml --- - hosts: webservers tasks: - name: Installed Httpd Server yum: name=httpd state=present - name: Start Httpd Server systemd: name=httpd state=started enabled= yes - name: Start Firewalld Server systemd: name=firewalld state=started enabled= yes - name: Configure Firewalld Server firewalld: service=http immediate= yes permanent= yes state=enabled - hosts: web01 tasks: - name: Configure web01 Website copy: content= 'This is Web01' dest= /var/www/html/index .html - hosts: web02 tasks: - name: Cofnigure webi-2 weisite copy: content= 'This is Web02' dest= /var/www/html/index .html - hosts: nfs01 tasks: - name: Install NFS-utils Server yum: name=nfs-utils state=present - name: Configure Nfs-utils Server copy: src=. /exports .j2 dest= /etc/exports owner=root group=root mode=0644 - name: Create NFS Group group: name=www gid=666 - name: Create NFS User user: name=www uid=666 group=www create_home=no shell= /sbin/nologin - name: Create Data Directory file : path= /data state=directory owner=www group=www mode=0755 recurse= yes - name: Start NFS Server systemd: name=nfs state=started enabled= yes - hosts: nfs01 tasks: - name: Mount NFS Server mount : path= /opt src=172.16.60.23: /data fstype=nfs opts=defaults state=mounted |
###### Ansible-playbook如何正确获取ip ######
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
[root@ansible ~] # vim /etc/ansible/hosts_all.yaml --- - hosts: all tasks: - name: 将原有的hosts文件备份 shell: mv /etc/hosts /etc/hosts_bak - name: 将ansible端的hosts复制到各自机器上 copy: src= /root/hosts dest= /etc/ owner=root group=root mode=0644 - name: 在新的hosts文件后面追加各自机器内网ip和 hostname lineinfile: dest= /etc/hosts line= "`ansible_all_ipv4_addresses` `ansible_hostname`" 但是执行完ansible-playbook之后,ansible客户机器上的 /etc/hosts 文件里ip和主机名对应关系如下( cat /etc/hosts ): 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 [u '172.16.60.210' ] redis-fun01.kevin.cn 实际想要的结果是( cat /etc/hosts ): 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 172.16.60.210 redis-fun01.kevin.cn 解决办法: 调整ansible服务端的hosts_all.yaml文件中获取ip的变量 变量用 IP: "{ { ansible_eth0['ipv4']['address'] }}" ,而不是`ansible_all_ipv4_addresses` 修改后的yaml文件配置如下: [root@ansible ~] # vim /etc/ansible/hosts_all.yaml --- - hosts: all vars: IP: "{ { ansible_eth0['ipv4']['address'] }}" tasks: - name: 将原有的hosts文件备份 shell: mv /etc/hosts /etc/hosts_bak - name: 将ansible端的hosts复制到各自机器上 copy: src= /root/hosts dest= /etc/ owner=root group=root mode=0644 - name: 在新的hosts文件后面追加各自机器内网ip和 hostname lineinfile: dest= /etc/hosts line= "`IP` `ansible_hostname`" |
######## 获取ansible清单列表里对应组的ip、指定机器执行操作 [ --list-hosts、--limit ] ########
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
注意下面两个参数的使用: 1)获取hosts清单文件里指定组内的ip,使用 "--list-hosts" 2)指定hosts清单文件里的ip执行操作,使用 "--limit" 如下是ansible的一个清单文件: [root@localhost ~] # cat /opt/kevin-bo.cfg [kevin-bo_F] 172.16.60.65 [kevin-bo_A] 172.16.60.140 172.16.60.141 [kevin-bo:children] kevin-bo_D kevin-bo_F kevin-bo_A [kevin-bo:vars] deploy_path= /opt/web/kevin-bo/ start_time_out=90 stop_time_out=60 module=kevin-bo [kevin-bo_D] 172.16.60.195 现在需要获取 "kevin-bo_A" 和 "kevin-bo_D" 组下的ip,--list-hosts后面加不加引号都可以。 [root@localhost ~] # ansible -i /opt/kevin-bo.cfg --list-hosts kevin-bo_A hosts (2): 172.16.60.140 172.16.60.141 [root@localhost ~] # ansible -i /opt/kevin-bo.cfg --list-hosts "kevin-bo_A"|grep -v "hosts" 172.16.60.140 172.16.60.141 [root@localhost ~] # ansible -i /opt/kevin-bo.cfg --list-hosts kevin-bo_D|grep -v "hosts" 172.16.60.195 获取多个机器组内的ip,中间使用逗号隔开 [root@localhost ~] # ansible -i /opt/kevin-bo.cfg --list-hosts "kevin-bo_A,kevin-bo_D" hosts (3): 172.16.60.140 172.16.60.141 172.16.60.195 [root@localhost ~] # ansible -i /opt/kevin-bo.cfg --list-hosts "kevin-bo_A,kevin-bo_D"|grep -v "hosts" 172.16.60.140 172.16.60.141 172.16.60.195 在执行ansible-playbook时,可以指定单个ip或group,也可以指定多个ip或group,也可以一起指定ip和group,多个中间使用逗号隔开! --limit后面加不加引号都可以。 # ansible-playbook -i 清单文件 yml文件 --limit ip # ansible-playbook -i 清单文件 yml文件 --limit ip1,iP2,ipn # ansible-playbook -i 清单文件 yml文件 --limit group # ansible-playbook -i 清单文件 yml文件 --limit group1,group2,groupn # ansible-playbook -i 清单文件 yml文件 --limit ip1,ip2,group1,groupn 还可以将制定的ip或group放在一个文件里,如ip.txt,然后--limit后面跟 "@ip.txt" # ansible-playbook -i 列表文件 yml文件 --limit @ip.txt # cat ip.txt 172.16.60.140 172.16.60.141 kevin-bo_F 如上,需要对清单文件 "kevin-bo_A" 组下的机器进行发布操作,由于发布过程中涉及服务启停,为了保证发布中整体服务不中断,需要一台一台的执行,不能所有机器一起执行。如下: [root@localhost ~] # vim deploy.sh #!/bin/bash #设置变量,传参等 BRANCH=$1 MODULE_NAME=$2 PRODUCT_PATH=$3 USER=$4 APP_NANE=${Deploy_App} ..... for Next_Deploy_IP in $(ansible -i /opt/kevin-bo .cfg --list-hosts kevin-bo_A| grep - v "hosts" ) do ansible-playbook -i /opt/kevin-bo .cfg /root/ansible/web_deploy .yml --limit "${Next_Deploy_IP}" -e "user=${USER} app_name=${APP_NANE} package_name=... ..." if [ $? - eq 0 ]; then echo "[`date +%Y%m%d-%H%M%S`]++++++++++++++++++++++++${Next_Deploy_IP}部署成功!++++++++++++++++++++++++++++++" sleep 10 else echo "[`date +%Y%m%d-%H%M%S`]++++++++++++++++++++++++${Next_Deploy_IP}部署失败!++++++++++++++++++++++++++++++" exit 1 fi done |