Назад | Перейти на главную страницу

Проверка pidof на хосте завершается ошибкой, когда контейнер докеров запускает то же приложение (например, apache2)

Сценарий инициализации apache2 выполняет pidof проверьте, чтобы определить, запущен ли уже apache.

    if pidof $DAEMON > /dev/null 2>&1 ; then
            if [ -e $PIDFILE ] && pidof $DAEMON | tr ' ' '\n' | grep -w $(cat $PIDFILE) > /dev/null 2>&1 ; then
                    AP_RET=2
            else
                    AP_RET=1
            fi
...
            elif [ $AP_RET = 1 ] ; then
                    APACHE2_INIT_MESSAGE="There are processes named 'apache2' running which do not match your pid file which are left untouched in the name of safety, Please review the situation by hand".

(Файл: / etc / init / apache2 в Ubuntu 16.04.3 LTS - для краткости усечен)

Однако на хосте докеров в контейнерах виртуальных машин уже может быть apache. В таком случае pidof возвращает непустое значение, даже если на хосте не запущен apache.

$ sudo service apache2 stop
$ pidof apache2
32742 32480 32379 32365 31295 31294 31293 31292 31291 31274 31270

Это означает, что сценарий инициализации завершается успешно только тогда, когда все Docker-контейнеры с apache остановлены (или еще не запущены). Следовательно, apache на хосте не может быть restartизд.

Как исправить эту ситуацию, чтобы apache хоста можно было перезапустить независимо от виртуальных машин? Есть ли версия pidof который будет обнаруживать только pid, принадлежащие непосредственно init?

Жаль, что есть # can't use pidofproc from LSB here в сценарии инициализации без реального объяснения. Я бы по-прежнему считал этот скрипт apache2 ошибкой, о которой следует сообщить.

TL; DR: решение: заменить pidof apache2 с участием pgrep --ns 1 ^apache2$ (или, если это не сработает, pgrep --ns 1 --nslist uts ^apache2$)

Длинное объяснение пространств имен с примером, который я написал перед тем, как найти pgrep мог сделать это следующим образом:

Когда у вас есть «кандидаты», использующие pidof, вот способ их разделения: проверьте их пространства имен и сравните их с pid 1 (init / systemd) пространства имен. Пример использования lxc и inetd процесс, но это не зависит от технологии контейнера и названия процесса:

# lxc-start stretch-amd64
# pidof inetd
10285 3372
# ls -l /proc/1/ns/
total 0
lrwxrwxrwx. 1 root root 0 nov.   9 19:49 cgroup -> cgroup:[4026531835]
lrwxrwxrwx. 1 root root 0 nov.   9 19:49 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0 nov.   9 19:49 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 nov.   9 19:49 net -> net:[4026531993]
lrwxrwxrwx. 1 root root 0 nov.   9 19:49 pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 nov.   9 19:49 pid_for_children -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 nov.   9 19:49 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 nov.   9 19:49 uts -> uts:[4026531838]
# ls -l /proc/3372/ns/
total 0
lrwxrwxrwx. 1 root root 0 nov.   9 19:51 cgroup -> cgroup:[4026531835]
lrwxrwxrwx. 1 root root 0 nov.   9 19:51 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0 nov.   9 19:51 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 nov.   9 19:51 net -> net:[4026531993]
lrwxrwxrwx. 1 root root 0 nov.   9 19:51 pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 nov.   9 19:51 pid_for_children -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 nov.   9 19:51 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 nov.   9 19:51 uts -> uts:[4026531838]
# ls -l /proc/10285/ns/
total 0
lrwxrwxrwx. 1 root root 0 nov.   9 19:50 cgroup -> cgroup:[4026532516]
lrwxrwxrwx. 1 root root 0 nov.   9 19:50 ipc -> ipc:[4026532415]
lrwxrwxrwx. 1 root root 0 nov.   9 19:50 mnt -> mnt:[4026532410]
lrwxrwxrwx. 1 root root 0 nov.   9 19:50 net -> net:[4026532418]
lrwxrwxrwx. 1 root root 0 nov.   9 19:50 pid -> pid:[4026532416]
lrwxrwxrwx. 1 root root 0 nov.   9 19:50 pid_for_children -> pid:[4026532416]
lrwxrwxrwx. 1 root root 0 nov.   9 19:50 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 nov.   9 19:50 uts -> uts:[4026532414]

Здесь хорошо видно, что pid 3372 акции pid 1пространства имен. 3372 работает на хосте. 10285 не разделяет какое-либо пространство имен (нормально, пользователь тот же: контейнер запущен как root), поэтому он находится в контейнере. Возможно, что иногда какая-то программа, запущенная на хосте, по какой-то причине меняет некоторые из них (обычно связанные с безопасностью), но чего не должно быть, так это пространство имен uts (hostname). Итак, вот сценарий, использующий stat и это с учетом имени процесса в аргументе "$ 1" (например: set -- inetd или аргумент сценария) предоставит только процесс в том же пространстве имен uts, обычно означающий (тот же) хост.

pid1uts="$(stat -c %N /proc/1/ns/uts|cut -d' ' -f3)"
for i in $(pidof "$1"); do
    if [ "$pid1uts" = "$(stat -c %N /proc/$i/ns/uts|cut -d' ' -f3)" ]; then
        echo $i
    fi
done | xargs -r

который в моем примере возвращает 3372.

Я объяснил, как это сделать, но зачем изобретать велосипед, когда pgrep есть варианты, чтобы справиться с этим:

# pgrep ^inetd$
3372
10285
# pgrep --ns 1 --nslist uts ^inetd$
3372

Или в большинстве случаев просто:

# pgrep --ns 1 ^inetd$
3372

если ваша служба на хосте прослушивает порт 80, вы можете определить идентификатор процесса с помощью netstats

#netstat -plan | grep :80

Процессы контейнера должны быть связаны с другим номером порта на хосте и внутри контейнера с портом 80. так что вы можете легко определить хост-процесс и убить его.