ansible使用笔记(三)Jinja2模板及playbook简介
ansible playbook编写
一、Jinja2模板简介
Jinja2是什么
Jinja2是基于Python的模板引擎,包含变量和表达式两部分,两者在模板求值时会被替换为值,模板中还有标签,控制模板的逻辑
为什么要学习Jinja2模板
因为playbook的模板使用Python的Jinja2模块来处理
Jinja2模版的基本语法
- 模板的表达式都是包含在他要分隔符"{{}}"内的
- 控制的语句都是包含在分隔符"{% %}"内的
- 模板支持注释,都是包含在分隔符'{# #}'内,支持块注释
- 调用变量
{{varname}} - 计算
{{2+2}} - 判断
{{1 in {{1,2,3}} - Jinja2模版控制语句
{% if name == '诗仙' %}
李白
{% elif name == '诗圣' %}
杜甫
{% elif name == '诗魔' %}
白居易
{% else %}
李贺
{% endif %}
Jinja2过滤器
变量可以通过过滤器修改,过滤器与变量用管道符号( | )分割,也可以用圆括号传递可选参数,多个过滤器可以链式调用,前一个过滤器的输出会被作业后一个过滤器的输入
例如:
加密一个字符串:{{'astr'|password_hash('sha512')}}
二、playbook简介 playbook是什么
playbook是ansible用于配置,部署和管理托管主机剧本,通过playbook的详细描述,执行其中一系列tasks,可以让远端主机达到预期的状态
也可以说 playbook字面意思即剧本,现实中由演员按剧本表演,在ansible中由计算机进行安装,部署应用,提供对外服务,以及组织计算机处理各种各样的事情
为什么要使用palybook
- 执行一些简单的任务,使用ad-hoc命令可以方便的解决问题,但有时一个设施过于复杂时,执行ad-hoc命令是不合适的,最好使用playbook
- playbook可以反复使用编写的代码,可以放到不同机器上面,像函数一样,最大化的利用代码,在使用ansible的过程中,处理的大部分操作都是在编写playbook
- playbook由YAML语文编写,遵循YAML标准
- 在同一行中,#之后的内容表示注释
- 同一个列表中元素应该保持相同缩进
- playbook由一个或多个play组成
- play中host,variables,roles,tasks等对角表示方法都是键值中间以": "分隔表示
- YAML还有一个小的怪癖,它的文件开始行都是以 ---,这是YAML格式的一部分,表明一个文件的开始
- hosts: 定义交要执行playbook的远程主机级
- vars: 定义playbook运行时需要使用的变量
- tasks: 定义将要远程主机上执行的任务列表
- handlers: 定义task执行完成以后需要调用的任务
使用ansible-playbook运行playbook文件,输出内容为JSON格式,由不同颜色组成便于识别
- 绿色代表执行成功
- 黄色 代表系统状态发生改变
- 红色代表执行失败
"幂等性"是什么意思呢?举个例子,你想把一个文件拷贝到目标主机的某个目录上,但是你不确定此目录中是否已经存在此文件,当你使用ansible完成这项任务时,就非常简单了,因为如果目标主机的对应目录中已经存在此文件,那么ansible则不会进行任何操作,如果目标主机的对应目录中并不存在此文件,ansible就会将文件拷贝到对应目录中,说白了,ansible是"以结果为导向的",我们指定了一个"目标状态",ansible会自动判断,"当前状态"是否与"目标状态"一致,如果一致,则不进行任何操作,如果不一致,那么就将"当前状态"变成"目标状态",这就是"幂等性","幂等性"可以保证我们重复的执行同一项操作时,得到的结果是一样的。
有了幂等性的概念上面显示颜色代表的不同结果就比较好理解了
- 绿色 代表执行成功 结果已经和指定"目标状态"一致不需要修改
- 黄色 执行成功,系统状态发生改变,修改过"目标状态"
- 红色代表执行失败
[root@ansible default]# cat ping.yml
---#第一行,表示开始
- hosts: all#远程主机,多个可以用逗号孙隔开
remote_user: root#远程执行的用户名
tasks:
- name: ping test#任务名称,你也可以理解为备注信息,会在执行中显示,方便阅读
ping:#模块名ping[root@ansible default]# ansible-playbook ping.yml -f 5#-f 并发进程数量,默认是5PLAY [all] ******************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [cache]
ok: [web1]
ok: [web2]
ok: [db2]
ok: [db1]TASK [ping test] ************************************************************************************
ok: [cache]
ok: [web1]
ok: [web2]
ok: [db1]
ok: [db2]PLAY RECAP ******************************************************************************************
cache: ok=2changed=0unreachable=0failed=0skipped=0rescued=0ignored=0
db1: ok=2changed=0unreachable=0failed=0skipped=0rescued=0ignored=0
db2: ok=2changed=0unreachable=0failed=0skipped=0rescued=0ignored=0
web1: ok=2changed=0unreachable=0failed=0skipped=0rescued=0ignored=0
web2: ok=2changed=0unreachable=0failed=0skipped=0rescued=0ignored=0
playbook语法基础(2)
- tasks
命令的集合
每一个play包含一个task列表(任务列表)
一个task在其所对应的所有主机上(通过hosts pattern匹配的所有主机) 执行完毕之后,下一个task才执行 - hosts
主机的集合
定义要执行的主机
实现 给web主机添加用户james
设置默认密码123456,并把james用户添加到users组
[root@ansible default]# cat user.yml
---
- hosts: web
remote_user: root
tasks:
- name: Add the user 'james'
user:
name: james
groups: users
- name: set passwd
shell: echo 123456|passwd --stdin james[root@ansible default]# ansible-playbook user.yml PLAY [web] ******************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [web1]
ok: [web2]TASK [Add the user 'james'] *************************************************************************
changed: [web1]
changed: [web2]TASK [set passwd] ***********************************************************************************
changed: [web2]
changed: [web1]PLAY RECAP ******************************************************************************************
web1: ok=3changed=2unreachable=0failed=0skipped=0rescued=0ignored=0
web2: ok=3changed=2unreachable=0failed=0skipped=0rescued=0
案例2:
要求:
1.所以web主机安装Apache
2.修改配置文件的监听端口为8080
3.设置默认页面hello world
4.启动服务
5.设置开机自启
1) 先在本面准备默认访问
[root@ansible default]# cat /root/index.htm
hello world
2)编号playbook
[root@ansible default]# cat http.yml
---
- hosts: web
remote_user: root
tasks:
- name: install one specific version of Apache//tasks01.安装
yum:
name: httpd
state: installed
- lineinfile://tasks02.修改监听端口
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen'
line: 'Listen 8080'
- copy://tasks03替换主页index.html
src: /root/index.html
dest: /var/www/html/index.html
- service://tasks04.开启服务
name: httpd
enabled: yes
state: restarted
[root@ansible default]# ansible-playbook http.yml PLAY [web] ***********************************************************************************************************************TASK [Gathering Facts] ***********************************************************************************************************
ok: [web1]
ok: [web2]TASK [install one specific version of Apache] ************************************************************************************changed: [web2]
changed: [web1]TASK [lineinfile] ****************************************************************************************************************
changed: [web1]
changed: [web2]TASK [copy] **********************************************************************************************************************
changed: [web2]
changed: [web1]TASK [service] *******************************************************************************************************************
changed: [web2]
changed: [web1]PLAY RECAP ***********************************************************************************************************************
web1: ok=5changed=4unreachable=0failed=0skipped=0rescued=0ignored=0
web2: ok=5changed=4unreachable=0failed=0skipped=0rescued=0ignored=0[root@ansible default]# curl web1:8080//测试
holle world
四、playbook进阶 4.1 变量
案例3:**
要求:添加用户 使用变量username代替
给web主机添加用户james2,设置默认密码123456
[root@ansible default]# cat user2.yml
---
- hosts: web
remote_user: root
vars:
username: james2
tasks:
-
name: Add the user"{{username}}"
user:
name: "{{username}}"
-
shell: echo 123456|passwd --stdin "{{username}}"[root@ansible default]# ansible-playbook user2.yml PLAY [web] ************************......
用户设密码
作完以上的实验你肯定已经发现为什么不用user模块直接设置密码,而且用shell模块单独设置,这是因为user模块是把Password字符串直接写入shadow,并没有改变,而Linux的shadow密码是经过加密的,所以明文密码不能直接使用
解决方案
-使用变量过滤器password_hash
{{'urpassword'|password_hash('sha512')}}
最后的playbook如下
[root@ansible default]# cat user3.yml
---
- hosts: web
remote_user: root
vars:
username: james2
tasks:
- name: Add the user "{{username}}"
user:
name: "{{username}}"
group: root
password: "{{'123qqq...A'|password_hash('sha512')}}"
变量参数
-e 传递变量或文件参数
-e @文件
文件的格式必须是YAML或JSON格式
[root@ansible default]# ansible-playbook user3.yml -e username=james4PLAY [web] *******************************************************************************************TASK [Gathering Facts] *******************************************************************************
ok: [web2]
ok: [web1]TASK [Add the user "james4"] *************************************************************************
changed: [web2]
changed: [web1]PLAY RECAP *******************************************************************************************
web1: ok=2changed=1unreachable=0failed=0skipped=0rescued=0
web2: ok=2changed=1unreachable=0failed=0skipped=0rescued=0 [root@ansible default]# ssh web1 id james4
uid=1007(james4) gid=0(root) 组=0(root)[root@ansible default]# cat username.yml
---
username:
james5[root@ansible default]# ansible-playbook user3.yml -e @username.ymlPLAY [web] ******************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [web1]
ok: [web2]TASK [Add the user "james5"] ************************************************************************
changed: [web2]
changed: [web1]PLAY RECAP ******************************************************************************************
web1: ok=2changed=1unreachable=0failed=0skipped=0rescued=0ignored=0
web2: ok=2changed=1unreachable=0failed=0skipped=0rescued=0ignored=0[root@ansible default]# cat username.yml
---
username:
james5
[root@ansible default]# ssh web1 id james5
uid=1008(james5) gid=0(root) 组=0(root)
4.2 error 处理 ansible-playbook对错误的处理
因为playbook是顺序执行,当遇到错误就会停止往下执行,默认情况判断上一个任务$?,如果值不为0就停止执行
但某些情况我们需要忽略错误继续执行
案例4:
要求:
创建缓存目录,然后重启 apache
1.因为 /tmp/web 之前已经存在 shell模块 mkdir报错playbook停止继续执行,但其实这个错误是不影响下面的任务执行
[root@ansible default]# cat Restart.yml
---
- hosts: web
remote_user: root
tasks:
- shell: mkdir /tmp/web
- name: ReStart service httpd
service:
name: httpd
state: restarted[root@ansible default]# ansible-playbook Restart.yml PLAY [web] ******************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [web2]
ok: [web1]TASK [shell] ****************************************************************************************
[WARNING]: Consider using the file module with state=directory rather than running 'mkdir'.If you
need to use command because file is insufficient you can add 'warn: false' to this command task or
set 'command_warnings=False' in ansible.cfg to get rid of this message.
fatal: [web2]: FAILED! => {"changed": true, "cmd": "mkdir /tmp/web", "delta": "0:00:00.008765", "end": "2020-11-19 16:55:04.967697", "msg": "non-zero return code", "rc": 1, "start": "2020-11-19 16:55:04.958932", "stderr": "mkdir: 无法创建目录\"/tmp/web\": 文件已存在", "stderr_lines": ["mkdir: 无法创建目录\"/tmp/web\": 文件已存在"], "stdout": "", "stdout_lines": []}
fatal: [web1]: FAILED! => {"changed": true, "cmd": "mkdir /tmp/web", "delta": "0:00:00.007319", "end": "2020-11-19 16:55:05.751981", "msg": "non-zero return code", "rc": 1, "start": "2020-11-19 16:55:05.744662", "stderr": "mkdir: 无法创建目录\"/tmp/web\": 文件已存在", "stderr_lines": ["mkdir: 无法创建目录\"/tmp/web\": 文件已存在"], "stdout": "", "stdout_lines": []}PLAY RECAP ******************************************************************************************
web1: ok=1changed=0unreachable=0failed=1skipped=0rescued=0ignored=0
web2: ok=1changed=0unreachable=0failed=1skipped=0rescued=0ignored=0
错误处理方法:
添加错误选项:ignore_errors
True 表示忽略错误继续执行
False 表示遇到错误就停止执行(默认)
[root@ansible default]# cat Restart.yml
---
- hosts: web
remote_user: root
tasks:
- shell: mkdir /tmp/web
ignore_errors: True
- name: ReStart service httpd
service:
name: httpd
state: restarted[root@ansible default]# ansible-playbook Restart.yml PLAY [web] ******************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [web1]
ok: [web2]TASK [shell] ****************************************************************************************
[WARNING]: Consider using the file module with state=directory rather than running 'mkdir'.If you
need to use command because file is insufficient you can add 'warn: false' to this command task or
set 'command_warnings=False' in ansible.cfg to get rid of this message.
fatal: [web2]: FAILED! => {"changed": true, "cmd": "mkdir /tmp/web", "delta": "0:00:00.007426", "end": "2020-11-19 17:01:57.077906", "msg": "non-zero return code", "rc": 1, "start": "2020-11-19 17:01:57.070480", "stderr": "mkdir: 无法创建目录\"/tmp/web\": 文件已存在", "stderr_lines": ["mkdir: 无法创建目录\"/tmp/web\": 文件已存在"], "stdout": "", "stdout_lines": []}
...ignoring
fatal: [web1]: FAILED! => {"changed": true, "cmd": "mkdir /tmp/web", "delta": "0:00:00.014932", "end": "2020-11-19 17:01:57.853702", "msg": "non-zero return code", "rc": 1, "start": "2020-11-19 17:01:57.838770", "stderr": "mkdir: 无法创建目录\"/tmp/web\": 文件已存在", "stderr_lines": ["mkdir: 无法创建目录\"/tmp/web\": 文件已存在"], "stdout": "", "stdout_lines": []}
...ignoringTASK [ReStart service httpd] ************************************************************************
changed: [web2]
changed: [web1]PLAY RECAP ******************************************************************************************
web1: ok=3changed=2unreachable=0failed=0skipped=0rescued=0ignored=1
web2: ok=3changed=2unreachable=0failed=0skipped=0rescued=0ignored=1
4.3 tags标签
tags:给指定的任务定义一个调用标识 有时把多个模块写在一个playbook中.但只想用到其中一个或其中几个模块
-使用方法在模块里添加tags标签
-playbook调用方式
-t TAGS, --tags=TAGS
案例5:
要求:调用任务 task03 创建文件
[root@ansible default]# cat tags2.yml
---
- hosts: web
remote_user: root
tasks:
- name: task01
file:
path: /tmp/web/task01
state: touch
tags: t1
- name: task02
file:
path: /tmp/web/task02
state: touch
tags: t2
- name: task03
file:
path: /tmp/web/task03
state: touch
tags: t3[root@ansible default]# ansible-playbook tags2.yml -t t3PLAY [web] ******************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [web2]
ok: [web1]TASK [task03] ***************************************************************************************
changed: [web2]
changed: [web1]PLAY RECAP ******************************************************************************************
web1: ok=2changed=1unreachable=0failed=0skipped=0rescued=0ignored=0
web2: ok=2changed=1unreachable=0failed=0skipped=0rescued=0ignored=0[root@ansible default]# ssh web1 ls /tmp/web/
task03
4.4 handlers
当关注的资源发生变化时采取的操作
notify这个action可用于每个play的最后触发,这样就可以避免有多次改变发生时每次都执行指定的操作,取而代之仅在所有的变化发生完成后一次性执行指定操作
在notify中列出的操作称为handler,即notify调用handler中定义的操作
我们还是拿上面的httpd服务举例
案例6:
要求:
修改httpd 默认index.html 然后重启httpd服务
[root@ansible default]# cat RestartHttp.yml
---
- hosts: web
remote_user: root
tasks:
- copy:
src: /root/index.html
dest: /var/www/html/index.html
ignore_errors: True
- name: ReStart service httpd
service:
name: httpd
state: restarted[root@ansible default]# ansible-playbook RestartHttp.yml PLAY [web] *******************************************************************************************************************************************************************************************************TASK [Gathering Facts] *******************************************************************************************************************************************************************************************
ok: [web1]
ok: [web2]TASK [copy] ******************************************************************************************************************************************************************************************************
changed: [web2]
changed: [web1]TASK [ReStart service httpd] *************************************************************************************************************************************************************************************
changed: [web2]
changed: [web1]PLAY RECAP *******************************************************************************************************************************************************************************************************
web1: ok=3changed=2unreachable=0failed=0skipped=0rescued=0ignored=0
web2: ok=3changed=2unreachable=0failed=0skipped=0rescued=0ignored=0
第二次执行相同操作
[root@ansible default]# ansible-playbook RestartHttp.yml
【ansible使用笔记(三)Jinja2模板及playbook简介】第二次和第一次执行的区别是没有在拷贝文件,因为第一次已经拷贝过了,但服务还是重启过两次,看上去好像没问题,但细想第二次文件没有改动过,和我们修改配置文件后重启服务的实际需求是有区别的,或者说第二次服务重次是完全没有必要的
这时就要用的notify调用handler中定义的操作
简单说就是当文件发生过改动后,才重启服务
修改配置后
[root@ansible default]# cat RestartHttp.yml
---
- hosts: web
remote_user: root
tasks:
- copy:
src: /root/index.html
dest: /var/www/html/index.html
ignore_errors: True
notify:
- restart "ReStart service httpd"handlers:
- name: ReStart service httpd
service:
name: httpd
state: restarted[root@ansible default]# ansible-playbook RestartHttp.yml PLAY [web] ******************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [web2]
ok: [web1]TASK [copy] *****************************************************************************************
ok: [web1]
ok: [web2]PLAY RECAP ******************************************************************************************
web1: ok=2changed=0unreachable=0failed=0skipped=0rescued=0ignored=0
web2: ok=2changed=0unreachable=0failed=0skipped=0rescued=0ignored=0
从执行结果来看 并没有重启httpd服务
4.5 when条件判断
有些时候需要在满足特定条件后再触发某一项操作,或在特定的条件下终止某个行为,这时候需要进行条件判断,when正是解决这个问题的最佳选择,行程中的系统变量facts作为when的条件,可以通过setup模块查看
案例7:
我们用debug模块做个测试 debug模块我们后面会讲到
通过两个when判断条件如果成立,则输出"System release is centos7"
[root@ansible default]# cat when.yml
---
- hosts: cache
remote_user: root
tasks:
- debug:
msg: "System release is centos7"
when:
- ansible_distribution == "CentOS"
- ansible_distribution_major_version == "7"[root@ansible default]# ansible-playbook when.yml PLAY [cache] ****************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [cache]TASK [debug] ****************************************************************************************
ok: [cache] => {
"msg": "System release is centos7"
}PLAY RECAP ******************************************************************************************
cache: ok=2changed=0unreachable=0failed=0skipped=0rescued=0ignored=0
4.6 register返回值
有时候我们还需要更复杂的例子,如判断前一个命令的执行结果去处理后面的操作,这时候就需要register模块来保存前一个命令的返回状态,在后面进行调用
案例8:
要求:把系统负载太高的Apache服务停止
当系统负载超过0.7时,则关掉httpd
[root@web2 ~]# awk 'BEGIN{while(1){}}'//先用awk对web2提高系统负载,为测试创建条件[root@ansible default]# ssh web2 uptime//已经满足测试条件
11:10:11 up 24 days, 17:47,2 users,load average: 0.88, 0.30, 0.14root@all (5)[f:5]$ sys
[root@ansible default]# ansible-console web//通过ansible-console 查看httpd状态
Welcome to the ansible console.
Type help or ? to list commands.root@web (2)[f:5]$ systemctl status httpd
web1 | CHANGED | rc=0 >>
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service;
enabled;
vendor preset: disabled)
Active: active (running) since 四 2020-11-19 17:58:04 CST;
17h ago
......web2 | CHANGED | rc=0 >>
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service;
enabled;
vendor preset: disabled)
Active: active (running) since 四 2020-11-19 17:58:03 CST;
17h ago
....root@web (2)[f:5]$ exit[root@ansible default]# cat when.yml
---
- hosts: web
remote_user: root
tasks:
- shell: uptime|awk '{printf("%.2f\n",$(NF-2))}'
register: result//返回shell执行结果到变量result
- name: stopservice
service:
name: httpd
state: stopped
when: result.stdout|float > 0.7//对result 结果进行判断[root@ansible default]# ansible-playbook when.yml PLAY [web] ******************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [web1]
ok: [web2]TASK [shell] ****************************************************************************************
changed: [web1]
changed: [web2]TASK [stopservice] ********************************************************************************
skipping: [web1]
changed: [web2]PLAY RECAP ******************************************************************************************
web1: ok=2changed=1unreachable=0failed=0skipped=1rescued=0ignored=0
web2: ok=3changed=2unreachable=0failed=0skipped=0rescued=0ignored=0[root@ansible default]# ssh web2 systemctl status httpd
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service;
enabled;
vendor preset: disabled)
Active: inactive (dead) since 五 2020-11-20 11:24:23 CST;
41s ago//httpd服务已关闭
......
4.7 with_items 循环
with_items是playbook标准循环,可以用于迭代一个列表或字典,通过{{item}}获取每次迭代的值
案例9:
要求:一次性创建多个用户nb、aa、plj、lx
[root@ansible default]# cat adduser.yml
---
- hosts: cache
remote_user: root
tasks:
- name: add users
with_items: ["nb","aa","plj","lx"]
user: group=wheel password={{'123456'|password_hash('sha512')}} name={{item}}
[root@ansible default]# ansible-playbook adduser.yml PLAY [cache] ****************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [cache]TASK [add users] ************************************************************************************
changed: [cache] => (item=nb)
changed: [cache] => (item=aa)
changed: [cache] => (item=plj)
changed: [cache] => (item=lx)PLAY RECAP ******************************************************************************************
cache: ok=2changed=1unreachable=0failed=0skipped=0rescued=0ignored=0
4.8 include
在编写playbook的时候随随着项目越来越大,playbook越来越复杂,修改也很麻烦,这时可以把一些paly、task或handler放到其他文件中,通过include指令包含进来是一个不错的选择
包含项包括
- vars:变量层
- tasks:任务层
- handlers:触发条件
- files:文件
- template:模板
- default:默认,优先级最低
把任务层独立出来
[root@ansible default]# cat include1.yml
---
- hosts: cache
remote_user: root
tasks:
- include: in3.yml
when: 2 > 1[root@ansible default]# cat in3.yml
- debug:
msg: "task1 in in3.yml"
- debug:
msg: "task2 in in3.yml"[root@ansible default]# ansible-playbook include1.yml
4.9 debug调试
如果对于Python语法不熟悉的,playbook书写起来容易出错,且排错困难,这里介绍几种简单的排错调试方式
检测语法
- ansible-playbook --syntax-check playbook.ymal
- --list-hosts
显示工作的task - --list-tasks
显示将要运行的tag - --list-tags
模拟运行结果,但如果在任务列表中,任务2 需要任务1执行的结果,那么任务2可能会报错,因为任务1没有实际执行
- ansible-playbook -C playbook.yaml
案例11:
以之前when.yml为例,查看result变量中的内容
[root@ansible default]# catwhen.yml
---
- hosts: web
remote_user: root
tasks:
- shell: uptime|awk '{printf("%.2f\n",$(NF-2))}'
register: result
- name: stopservice
service:
name: httpd
state: stopped
when: result.stdout|float > 0.7
- name: Show debug info
debug:
var: result[root@ansible default]# ansible-playbook when.yml PLAY [web] ******************************************************************************************TASK [Gathering Facts] ******************************************************************************
ok: [web1]
ok: [web2]TASK [shell] ****************************************************************************************
changed: [web1]
changed: [web2]TASK [stopservice] ********************************************************************************
skipping: [web1]
ok: [web2]TASK [Show debug info] ******************************************************************************
ok: [web1] => {
"result": {
"changed": true,
"cmd": "uptime|awk '{printf(\"%.2f\\n\",$(NF-2))}'",
"delta": "0:00:00.010118",
"end": "2020-11-20 15:12:05.959884",
"failed": false,
"rc": 0,
"start": "2020-11-20 15:12:05.949766",
"stderr": "",
"stderr_lines": [],
"stdout": "0.04",
"stdout_lines": [
"0.04"
]
}
}
ok: [web2] => {
"result": {
"changed": true,
"cmd": "uptime|awk '{printf(\"%.2f\\n\",$(NF-2))}'",
"delta": "0:00:00.010133",
"end": "2020-11-20 15:12:05.484865",
"failed": false,
"rc": 0,
"start": "2020-11-20 15:12:05.474732",
"stderr": "",
"stderr_lines": [],
"stdout": "2.04",
"stdout_lines": [
"2.04"
]
}
}PLAY RECAP ******************************************************************************************
web1: ok=3changed=1unreachable=0failed=0skipped=1rescued=0ignored=0
web2: ok=4changed=1unreachable=0failed=0skipped=0rescued=
推荐阅读
- EffectiveObjective-C2.0|EffectiveObjective-C2.0 笔记 - 第二部分
- 由浅入深理解AOP
- 【译】20个更有效地使用谷歌搜索的技巧
- mybatisplus如何在xml的连表查询中使用queryWrapper
- MybatisPlus|MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决
- MybatisPlus使用queryWrapper如何实现复杂查询
- Android中的AES加密-下
- iOS中的Block
- Linux下面如何查看tomcat已经使用多少线程
- 使用composer自动加载类文件