上篇文章Ansible对接操作系统升级脚本(一)中介绍了项目的基本构成以及ansible系统升级角色中的检查任务。这篇文章将介绍操作系统升级、重启任务和vmtools安装任务。在重启任务中将解决第一篇文章Ansible对接操作系统升级脚本(一)中涉及的三个难点:
(1)如何判断服务器是否需要重启?
(2)如何判断服务器是否重启成功?
(3)如何避免服务器重启过程中ansible会话的断开?
名词解释
▂▂
(1)控制节点:部署ansible环境以及存放playbook的主机。
(2)受管主机:执行playbook中的任务的主机。
update.yml文件内容如下,文件内容可以分为三个部分。
-
name:
create
log
directory
delegate_to:
localhost
run_once:
true
file:
path:
"
{{ playbook_dir }}
/log"
state:
directory
mode:
0750
-
name:
start
update
os
shell:
"
{{ update_os_url }}
"
args:
warn:
no
changed_when:
false
register:
output
-
name:
Create
the
log
file
for
the
updated
packages.
delegate_to:
localhost
copy:
content:
"
{{ output.stdout_lines | join('n') }}
"
dest:
"
{{ playbook_dir }}
/log/
{{ ansible_hostname }}
_
{{ ansible_default_ipv4.address }}
_log"
owner:
ugrep
group:
ugrep
mode:
0640
-
name:
Judge
whether
the
upgrade
is
successful
delegate_to:
localhost
shell:
tail
-10
{{
playbook_dir
}}
/log/{{
ansible_hostname
}}_{{
ansible_default_ipv4.address
}}_log
|
grep
Complete
&&
echo
$?
ignore_errors:
true
changed_when:
false
register:
judge
-
name:
alert
if
host
has
not
been
updated
fail:
msg:
"
{{ ansible_default_ipv4.address }}
update os failed, manual check needed"
when:
judge.rc
!=
0
第一部分:在控制节点创建存放日志的目录
<
- name: create
log
directory
delegate_to: localhost
run_once:
true
file:
path:
"{{ playbook_dir }}/log"
state: directory
mode:
0750
这部分使用ansible的file模块来在控制节点创建日志目录。在ansible运行任务时,默认将任务在受管主机【配置文件ansible.cfg中inventory配置项指定的主机】上执行, delegate_to关键字可以确保日志目录创建在控制节点,而不是受管主机。delegate_to关键字可以将任务委派到指定的主机进行运行。run_once:true表示这个任务只能在控制节点上执行一次。playbook_dir表示当前正在执行playbook的目录路径, 是ansible内置的变量。
第二部分:执行系统升级脚本并记录日志于控制节点
<
- name: start update os
shell:
"{{ update_os_url }}"
args:
warn: no
changed_when:
false
register
: output
- name: Create the
log
file
for
the updated packages.
delegate_to: localhost
copy:
content:
"{{ output.stdout_lines | join('n') }}"
dest:
"{{ playbook_dir }}/log/{{ ansible_hostname }}_{{ ansible_default_ipv4.address }}_log"
owner: ugrep
group: ugrep
mode:
0640
这部分的第一个任务是执行操作系统升级脚本。这个任务有三种方式来实现。
第一种:使用ansible的shell模块。在这种方式中,我们需要先使用ansible的copy模块,将脚本从控制节点复制到受管主机,然后使用shell模块执行已经传到受管主机的脚本。
第二种:使用ansible的script模块。编写方式如下:
- name: start update os
script: update_os.sh
changed_when:
false
register
: output
这种方式不需要使用copy模块上传脚本,script模块会自动在受管主机执行playbook用户的家目录下创建一个.ansible/tmp目录,同时这个目录下也会产生一个临时目录,脚本会被上传到这个目录。脚本执行结束后这个临时目录会自动被删除。如下图:
上传升级脚本update_os.sh
update_os.sh脚本执行结束后删除临时目录。
第三种是使用shell模块:将脚本存放在一台web服务器上,然后shell模块使用curl --noporxy ‘*’ http:/IP/update_os.sh | bash的方式来执行脚本, 这里的IP是提供web服务的主机IP地址。
对比而言,第一种和第二种方式都会在受管主机上产生文件。第三种方式不会存在在受管主机上新建目录或者文件的问题。因此选择第三种方式。在实现过程中,我在update_os角色的vars目录中定义了一个变量update_os_url。
update_os_url:
curl
--noproxy
'*'
http://192.168.X.X/install/update_os.sh
|
bash
然后shell模块调用这个变量来执行升级脚本。如果web服务器地址变化,则只需要修改这个变量,不需要修改playbook。register记录升级的日志到output变量中。
第二个任务是将output变量中记录的日志写到文件中。生成的日志文件名为主机名_主机IP地址_log。
第三部分:判断主机升级系统是否成功
<
- name: Judge whether the upgrade is successful
delegate_to: localhost
shell: tail
-10
{{ playbook_dir }}/
log
/{{ ansible_hostname }}_{{ ansible_default_ipv4.address }}_log | grep Complete && echo $?
ignore_errors:
true
changed_when:
false
register
: judge
- name: alert
if
host has
not
been updated
fail:
msg:
"{{ ansible_default_ipv4.address }} update os failed, manual check needed"
when: judge.rc !=
0
在主机系统升级成功后,日志文件的最后一行会出现Complete关键字,因此我们通过查找日志中是否存在这个关键字来判断主机是否升级成功。第一个任务是查找日志文件中的Complete关键字。register将shell命令的执行结果赋给注册变量judge。系统升级成功时judge.rc 等于0。第二个任务是终端输出系统升级失败的主机IP地址。
重启任务和vmtools安装任务分别由reboot.yml和install_vmtools.yml文件实现。
reboot.yml文件的内容如下,文件内容由三部分组成:判断是否需要重启服务器、重启服务器以及判断重启服务器是否成功。
-
name:
Judge
whether
the
upgrade
is
successful
delegate_to:
localhost
shell:
tail
-10
{{
playbook_dir
}}
/log/{{
ansible_hostname
}}_{{
ansible_default_ipv4.address
}}_log
|
grep
Complete
&&
echo
$?
ignore_errors:
yes
changed_when:
false
register:
judge
-
name:
reboot
the
server
from
script
delegate_to:
localhost
command:
sh
api.sh
{{
ansible_default_ipv4.address
}}
ignore_errors:
yes
changed_when:
false
when:
judge.rc
==
0
-
name:
wait
for
to
finish
reboot
wait_for_connection:
delay:
180
-
name:
check
if
host
has
booted
within
last
5min
shell:
test
$(
cat
/proc/uptime
|
cut
-d.
-f1)
-lt
300
ignore_errors:
yes
changed_when:
false
register:
reboot_check
-
name:
alert
if
host
has
not
been
rebooted
fail:
msg:
"
{{ ansible_default_ipv4.address }}
has not booted, manual check needed"
when:
reboot_check.rc
!=
0
第一部分:判断是否需要重启服务器
<
-
name:
Judge
whether
the
upgrade
is
successful
delegate_to:
localhost
shell:
tail
-10
{{
playbook_dir
}}
/log/{{
ansible_hostname
}}_{{
ansible_default_ipv4.address
}}_log
|
grep
Complete
&&
echo
$?
ignore_errors:
yes
changed_when:
false
register:
judge
在前面内容介绍过,在主机系统升级成功后,日志文件的最后一行会出现Complete关键字,我们通过查找日志中是否存在这个关键字来判断主机是否升级成功。register将shell命令的执行结果赋给注册变量judge。系统升级成功时judge.rc 等于0,此时我们将执行重启服务器的任务。
第二部分:重启服务器
<
-
name:
reboot
the
server
from
script
delegate_to:
localhost
command:
sh
api.sh
{{
ansible_default_ipv4.address
}}
ignore_errors:
yes
changed_when:
false
when:
judge.rc
==
0
-
name:
wait
for
to
finish
reboot
wait_for_connection:
delay:
180
当judge.rc 等于0 成立时,ansible将执行reboot the server from script任务来重启服务器,这解决了文章开头的第一个难点。delegate_to声明这个任务的执行是在本机执行,api.sh脚本用来调云管平台的接口来重启服务器(实际使用中可以替换成自己重启服务器的方式)。ansible接着执行wait for to finish reboot任务,这个任务是保证服务器重启之后,ansible的会话还能连接,还能继续后续的任务,这就解决了文章开头的第三个难点。在这里,我设置的等待重新连接的时间是180秒,因为在生产环境中云管平台重启服务器的耗时大约为2分钟。
第三部分:判断服务器是否重启成功
<
- name: check
if
host has booted within last
5
min
shell: test $( cat /proc/uptime | cut -d. -f1) -lt
300
ignore_errors: yes
changed_when:
false
register
: reboot_check
- name: alert
if
host has
not
been rebooted
fail:
msg:
"{{ ansible_default_ipv4.address }} has not booted, manual check needed"
when: reboot_check.rc !=
0
在这一部分中,我使用cat /proc/uptime | cut -d. -f1命令来获得服务器从启动到现在的时间(单位是秒),如果这个数值小于300,则判断服务器重启成功,否则服务器重启失败(reboot_check.rc 不等于0)。至此,重启任务的介绍结束了。
vmtool安装任务由install_vmtools.yml文件来实现。文件的内容如下:
-
name:
Install
VMtools
block:
-
name:
start
install
vmtools
shell:
"
{{ install_vmtools_url }}
"
changed_when:
false
args:
warn:
no
register:
vmtools_output
-
name:
Create
the
log
file
delegate_to:
localhost
changed_when:
false
copy:
content:
"
{{ vmtools_output.stdout_lines | join('n') }}
"
dest:
"
{{ playbook_dir }}
/log/
{{ ansible_hostname }}
_
{{ ansible_default_ipv4.address }}
_vmtools_log"
owner:
ugrep
group:
ugrep
mode:
0640
-
name:
Judge
whether
install
vmtools
is
successful
delegate_to:
localhost
shell:
grep
-i
error
{{
playbook_dir
}}
/log/{{
ansible_hostname
}}_{{
ansible_default_ipv4.address
}}_vmtools_log
&&
echo
$?
ignore_errors:
true
changed_when:
false
register:
judge_vmtools
-
name:
alert
install
vmtools
fail:
msg:
"
{{ ansible_default_ipv4.address }}
install vmtools failed, manual check needed"
when:
judge_vmtools
==
0
install_vmtools.yml文件和update.yml文件的结构类似,由两部分组成。
第一部分:执行vmtools安装脚本并记录日志于控制节点
<
- name: start install vmtools
shell:
"{{ install_vmtools_url }}"
changed_when:
false
args:
warn: no
register
: vmtools_output
- name: Create the
log
file
delegate_to: localhost
changed_when:
false
copy:
content:
"{{ vmtools_output.stdout_lines | join('n') }}"
dest:
"{{ playbook_dir }}/log/{{ ansible_hostname }}_{{ ansible_default_ipv4.address }}_vmtools_log"
owner: ugrep
group: ugrep
mode:
0640
install_vmtools_url变量值为curl --noporxy ‘*’ http:/IP/install_vmtools.sh | bash, 这里的IP是提供web服务的主机IP地址。
第二部分:通过查找日志中是否存在error关键字来判断vmtools的安装是否成功
<
- name: Judge whether install vmtools is successful
delegate_to: localhost
shell: grep -i error {{ playbook_dir }}/
log
/{{ ansible_hostname }}_{{ ansible_default_ipv4.address }}_vmtools_log && echo $?
ignore_errors:
true
changed_when:
false
register
: judge_vmtools
- name: alert install vmtools
fail:
msg:
"{{ ansible_default_ipv4.address }} install vmtools failed, manual check needed"
when: judge_vmtools ==
0
这里需要注意一点,在update.yml文件中Complete关键字是系统升级成功后自动产生的,在install_vmtools.yml文件中error关键字是人为添加的。人为修改vmtools安装脚本,在程序可能出错的地方添加error关键字。
至此,Ansible对接操作系统升级脚本的系列介绍完毕。在实际生产环境测试中,存在以下几点问题待后续优化:
(1)目前ansible升级操作系统是同步模式进行,在一台主机升级出现问题卡住时,后续的ansible任务都将卡住不执行。
(2)由于操作系统的升级时间较长,因此会出现ssh会话断开现象,此时整个ansible任务都将退出,此时也不能确定每台主机升级步骤进行到了哪一步。
(3)日志拉取到ansible控制节点的问题。以update.yml文件为例,日志拉取是在操作系统升级结束之后进行,如果一台主机升级卡住,此时即使存在升级成功的主机,它的升级日志也不会被拉取到ansible控制节点。
【文献参考】
文章作者 | 王振杰
原文始发于微信公众号(EBCloud):ansible对接操作系统升级脚本(终篇)
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论