WSL Ubuntu 20.04下Ansible service模块启动Apache2失败的排查与解决
使用sudo service命令启动Apache2服务可正常运行,但使用Ansible的service模块启动apache2时收到“Service is in unknown state”的错误提示。已将Ansible升级至最新版本,运行环境为WSL Ubuntu 20.04.3 LTS,SSH配置正常,apt、command、shell等其他Ansible ad-hoc命令均可正常运行。
问题源于Ansible的service_mgr.py文件中is_systemd_managed_offline(module)方法存在逻辑bug,会让Ansible误判当前服务管理器为systemd,但实际环境使用的是sysv init服务管理器。
原有bug逻辑如下:
if module.get_bin_path('systemctl'): # check if /sbin/init is a symlink to systemd # on SUSE, /sbin/init may be missing if systemd-sysvinit package is not installed. if os.path.islink('/sbin/init') and os.path.basename(os.readlink('/sbin/init')) == 'systemd': return True return False
WSL Ubuntu 20.04 LTS中默认存在/sbin/init指向systemd的软链接:
/sbin/init -> /lib/systemd/systemd*
正常情况下service_mgr.py的服务管理器检测逻辑如下:
elif collected_facts.get('ansible_system') == 'Linux': # FIXME: mv is_systemd_managed if self.is_systemd_managed(module=module): service_mgr_name = 'systemd' elif module.get_bin_path('initctl') and os.path.exists("/etc/init/"): service_mgr_name = 'upstart' elif os.path.exists('/sbin/openrc'): service_mgr_name = 'openrc' elif self.is_systemd_managed_offline(module=module): service_mgr_name = 'systemd' elif os.path.exists('/etc/init.d/'): service_mgr_name = 'sysvinit' if not service_mgr_name: # if we cannot detect, fallback to generic 'service' service_mgr_name = 'service'
由于上述软链接存在,Ansible执行到is_systemd_managed_offline对应的elif分支时就会停止判断,不会继续执行后续的sysvinit检测逻辑。可通过命令确认实际服务管理器为sysv init:
$ ps -p 1 -o comm= init
临时规避方案
调用Ansible service模块时添加use参数,强制Ansible使用service而非systemd作为服务管理器即可解决问题。注:该bug仍会导致Ansible setup模块误判当前WSL Ubuntu 20.04.3 LTS的服务管理器为systemd。
源码修复方案
可调整service_mgr.py中elif的判断顺序,将is_systemd_managed_offline分支移到elif列表末尾,将/etc/init.d存在性的判断提前,调整后代码如下:
elif collected_facts.get('ansible_system') == 'Linux': # FIXME: mv is_systemd_managed if self.is_systemd_managed(module=module): service_mgr_name = 'systemd' elif module.get_bin_path('initctl') and os.path.exists("/etc/init/"): service_mgr_name = 'upstart' elif os.path.exists('/sbin/openrc'): service_mgr_name = 'openrc' elif os.path.exists('/etc/init.d/'): service_mgr_name = 'sysvinit' elif self.is_systemd_managed_offline(module=module): service_mgr_name = 'systemd'
建议后续Ansible版本修复该Linux服务管理器检测的逻辑bug,针对WSL Ubuntu等WSL发行版做适配。
内容的提问来源于stack exchange,提问作者fbinliu

