100% ЦП для контейнера Docker при запуске через точку входа

Мы написали небольшое приложение на C++, которое в основном контролирует другие процессы через ZeroMQ. Поэтому большую часть времени приложение бездействует и периодически отправляет и получает какие-то запросы.

Мы создали образ докера на основе ubuntu, который содержит только это приложение, некоторые зависимости и файл entrypoint.sh. Точка входа в основном работает как /bin/bash, манипулирует некоторыми файлами конфигурации на основе переменных среды, а затем запускает приложение через exec.

Теперь вот странная часть. Когда мы запускаем приложение вручную без докера, мы получаем загрузку ЦП почти 0%. Когда мы запускаем то же приложение, что и образ докера, загрузка ЦП достигает 100% и блокирует ровно одно ядро ​​ЦП.

Чтобы выяснить, что происходит, мы установили точку входа нашего образа в /bin/yes (просто чтобы убедиться, что контейнер продолжает работать), а затем запустили bash внутри работающего контейнера. Оттуда мы запустили entrypoint.sh вручную, и ЦП снова был на 0%.

Поэтому нам интересно, что могло вызвать эту ситуацию. Есть ли что-нибудь, что нам нужно добавить в наш Dockerfile, чтобы предотвратить это?


Вот некоторые выходные данные, созданные с помощью strace. Я использовал strace -p <pid> -f -c и подождал пять минут, чтобы собрать информацию.

1. Запуск с docker run (100% ЦП)

strace: Process 12621 attached with 9 threads
strace: [ Process PID=12621 runs in x32 mode. ]
[...]

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 71.26   17.866443         144    124127           nanosleep
 14.40    3.610578       55547        65        31 futex
 14.07    3.528224        1209      2918           epoll_wait
  0.10    0.024760        4127         6         1 restart_syscall
  0.10    0.024700           0     66479           poll
  0.05    0.011339           4      2902         3 recvfrom
  0.02    0.005517           2      2919           write
  0.01    0.001685           1      2909           read
  0.00    0.000070          70         1         1 connect
  0.00    0.000020          20         1           socket
  0.00    0.000010           1        18           epoll_ctl
  0.00    0.000004           1         6           sendto
  0.00    0.000004           1         4           fcntl
  0.00    0.000000           0         1           close
  0.00    0.000000           0         1           getpeername
  0.00    0.000000           0         1           setsockopt
  0.00    0.000000           0         1           getsockopt
------ ----------- ----------- --------- --------- ----------------
100.00   25.073354                202359        36 total

2. Запуск с фиктивной точкой входа и docker exec (0% ЦП)

strace: Process 31394 attached with 9 threads
[...]
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 67.32   12.544007         102    123355           nanosleep
 14.94    2.784310       39216        71        33 futex
 14.01    2.611210         869      3005           epoll_wait
  2.01    0.373797           6     66234           poll
  1.15    0.213487          71      2999           recvfrom
  0.41    0.076113       15223         5         1 restart_syscall
  0.09    0.016295           5      3004           write
  0.08    0.014458           5      3004           read
------ ----------- ----------- --------- --------- ----------------
100.00   18.633677                201677        34 total

Обратите внимание, что в первом случае я начал strace немного раньше, поэтому есть несколько разных вызовов, которые можно проследить до кода инициализации.

Единственное отличие, которое я смог найти, это строка Process PID=12621 runs in x32 mode. при использовании docker run. Может ли это быть проблемой?

Также обратите внимание, что в обоих измерениях общее время выполнения составляет около 20 секунд, в то время как процесс выполнялся в течение пяти минут.


Некоторые дальнейшие исследования в случае 100% CPU. Я проверил процесс с помощью top -H -p <pid>, и только родительский процесс использовал 100% ЦП, в то время как все дочерние потоки в основном простаивали. Но при вызове strace -p <pid> в родительском процессе я мог убедиться, что процесс ничего не сделал (вывод не был сгенерирован).

Итак, у меня есть процесс, который использует все ядро ​​​​моего процессора и ничего не делает.


person Markus K    schedule 01.08.2019    source источник
comment
Прикрепите strace к запущенной команде и посмотрите, какие системные вызовы она выполняет. Сравните с «родным» запуском. Возможно, где-то небольшое несоответствие.   -  person Botje    schedule 01.08.2019
comment
Спасибо за ваше предложение, @Botje. Сегодня я узнал, что вы можете strace входить в процессы docker. Я добавил свои результаты strace к исходному вопросу.   -  person Markus K    schedule 01.08.2019
comment
В случае с докером я вижу ошибку connect. Это звучит как что-то для расследования. В качестве альтернативы вы можете попробовать подключить GDB к процессу внутри Docker, чтобы увидеть, где он тратит все процессорное время.   -  person Botje    schedule 01.08.2019
comment
Отказ от ответственности: коллега ОП здесь. Как только я подключаю gdb к процессу, загрузка процессора снижается до 0%. После отключения снова 100%.   -  person Sebastian    schedule 05.08.2019


Ответы (1)


Как оказалось, некоторая устаревшая часть программного обеспечения ожидала ввода с консоли в цикле while:

while (!finished) {
  std::cin >> command;
  processCommand(command)
}

Так что это отлично работало при локальном запуске и с docker exec. Но поскольку исполняемый файл был запущен как служба докеров, консоли не было. Поэтому std::cin был неблокирующим и возвращался немедленно. Таким образом мы создали бесконечный цикл без каких-либо засыпаний, что, естественно, привело к 100% загрузке ЦП.

Спасибо @Botje за помощь в процессе отладки.

person Markus K    schedule 05.08.2019