systemd 托管的进程热升级中,systemd的一些坑
发布日期:2021-09-16 04:36:45 浏览次数:22 分类:技术文章

本文共 2987 字,大约阅读时间需要 9 分钟。

问题

在我们对某些进程做升级时,并且这些进程是被systemd托管的,比如ovs热升级后发现有新ovs进程起来后又退出了,并且退出后,并没有被systemd重新拉起,这个比较严重,热升级后进程退出,还没有重新拉起,会导致虚拟机网络中断时间过长。

分析

分析退出原因:

1、首先分析什么原因退出的,分析 ovs 日志,并没有发现有报错。

2、手动起ovs进程,然后进行热升级,发现进程状态正常,但是用systemd起的ovs进程,只要热升级就退出。

3、查看日志,systemctl status ovs-vswitchd 能看到有报错 "Refusing to accept PID outside of service control group, acquired through unsafe symlink chain: /var/run/openvswitch/ovs-vswitchd-current.pid "

从以上可以粗略的得出是systemd杀掉 新  ovs 进程的

 

分析systemd代码流程

找到报错的函数  service_load_pid_file:

1、会发现先进行了个 symlink 的检查,如果symlink检查失败会给 questionable_pid_file 变量赋值为 true, 表示这是个有问题的pid 文件。

2、然后会调用 service_is_suitable_main_pid 去判断是否是一个正确的service服务,如果不是正常的service服务,就会去判断 questionable_pid_file ,如果pid file  也是有问题的,就会 return -EPERM 报错

service_load_pid_file

static int service_load_pid_file(Service *s, bool may_warn) {

 

        r = chase_symlinks(s->pid_file, NULL, CHASE_SAFE, NULL, &fd);

        if (r == -ENOLINK) {

                questionable_pid_file = true;

        }

 

        r = service_is_suitable_main_pid(s, pid, prio);

        if (r < 0)

                return r;

        if (r == 0) {

                struct stat st;

                if (questionable_pid_file) {

                        log_unit_error(UNIT(s), "Refusing to accept PID outside of service control group, acquired through unsafe symlink chain: %s", s->pid_file);

                        return -EPERM;

                }

}

 

分析 chase_symlinks 函数,只贴了部分代码,目前的FLAG只有CHASE_SAFE, 所以我们只分析部分和目前场景相关的代码, 函数大意就是会遍历整个 pid 文件的路径,比如 /var/run/openvswitch/openvswitch-current.pid,会遍历/var、/var/run、/var/run/openvswitch、/var/run/openvswitch/openvswitch-current.pid 整个路径。

1、只要以上其中路径一个有软链都会返回错误。

2、同时会调用 unsafe_transition 检查上下文件的所有者,如果文件的所有者不相同也会返回错误。

chase_symlinks 和 unsafe_transition 函数

int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) {

                if (fstat(child, &st) < 0)

                        return -errno;

                previous_stat = st;

                fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH);     //O_NOFOLLOW表示如果是软链,打开失败

                if (fd < 0)

                    return -errno;

 

 

                if (flags & CHASE_SAFE) {

                    if (fstat(fd, &st) < 0)

                        return -errno;

 

 

                if (unsafe_transition(&previous_stat, &st))

                    return log_unsafe_transition(child, fd, path, flags);

                    previous_stat = st;

                }

}

 

static bool unsafe_transition(const struct stat *a, const struct stat *b) {

        /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to

         * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files

         * making us believe we read something safe even though it isn't safe in the specific context we open it in. */

 

        if (a->st_uid == 0/* Transitioning from privileged to unprivileged is always fine */

                return false;

 

        return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */

}

 

线上环境现状

从下面可以看出 目前这个 /var/run/openvswitch/ovs-vswitchd-current.pid 路径上的问题比较多,和systemd冲突比较大,所以需要按systemd的方式来修改。

 

解决办法

根据以上代码分析,只要pid文件上没有软链,并且整条pid文件的路径上用户都是同一个,即可解决问题。

平时启动服务都不会有这问题,因为我们热升级的特殊性,在新进程创建后,需要把新的pid写入pid文件,在老进程退出时,systemd会重新加载这个 pidfile,由于有老进程的退出,新进程的创建,所以会比较麻烦。

 

其实这里还会有其它问题,比如pid文件要做到写的原子性,不然 systemd 读的时候会出现问题,导致systemd又把新进程杀掉了,还有就是对应 systemd service的配置要准备,这些在这里不细讲了。

转载地址:https://blog.csdn.net/wllabs/article/details/110195639 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:基于diagnose-tools 学习字符设备
下一篇:ARM64的内核栈、用户栈、寄存器上下文【转】

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月14日 10时51分41秒