jekyll: проблеми та їх розв’язки

отже, онлайновий щоденник tivasyk@home перенесено з гуглівського blogger’а на власний сервер, і переведено на текстові файли markdown, які зберігаються у власному сховищі owncloud і з яких автоматично генерується статичний веб-сайт за допомогою двигунця jekyll, а traefik забезпечує шифрування ssl/tls з сертифікатом let’s encrypt. лишається одна проблема, яка мене бентежить: високе завантаження процесора контейнером jekyll.

проблема

поки я лінуюся переписати до ума свій скрипт для моніторингу, накидав на колінці ось таке для спостереження за контейнерами, завантаженням процесора і журналом контейнера:

 #!/usr/bin/env bash
 
 # CONSOLE WIDTH?
 WIDTH=$(tput cols)
 
 # UPTIME
 printf "UPTIME\n"
 uptime
 
 # PROCESSES
 printf "\nCPU USAGE\n"
 ps -e -o pid= -o %cpu= -o %mem= -o command= | sort -n -r -k 2 | head -n 3 | cut -c -$WIDTH
 
 # CONTAINERS
 printf "\n"
 docker ps --format="table {{.Names}}\t{{.Status}}\t{{.Command}}\t{{.Ports}}" | cut -c -$WIDTH
 
 # LOGS
 if [[ "$1" != "" ]]; then
     printf "\nLOGS\n"
     docker logs $1 --tail 10 | cut -c -$WIDTH
 fi

і що ж я бачу? ruby в контейнері jekyll постійно завантажує одне ядро процесора на 80-100% (див. ілюстрацію). решта системи й контейнери поводяться добре, і якщо я «приб’ю» jekyll, завантаження процесора падає до 2-3%!

jekyll завантажує процесор на 80-100%

перша гіпотеза: веб-сервер?

спершу я гадав, що проблему створює веб-сервер jekyll, написаний на ruby, — тому я додав контейнер з легким веб-сервером lighttpd і відключив веб-сервер jekyll: запускаю в режимі build замість serve. виявилося, що це зменшує навантаження в кращому випадку на 5%, але не вирішує проблеми.

друга гіпотеза: відслідковування змін?

наступна гіпотеза стосувалася відслідковування змін в теці з дописами: стандартно контейнер jekyll використовує опцію --force-polling, яка змушує двигунець перезапускати генерацію щоразу, коли вміст теки з дописами змінюється. потенційно це джерело підвищеного навантаження на процесор, тож я спробував поміняти режим на --wait, а також додав опцію --incremental, котра змушує jekyll обробляти лише змінені файли замість щоразу перебирати всі дописи.

перша зміна (--wait) ніяк не вплинула на завантаження; друга (--incremental) — сильно скоротила час генерації сайту, з 200 секунд до 30 секунд з хвостиком. оскільки я копіюю змінені файли за допомогою rsync раз на п’ять хвилин — це мало би вирішити проблему завантаження, адже jekyll «крутив» би процесор до 80% упродовж 30 секунд, а тоді «засинав» би до наступного поновлення.

але мій «наколінний» скрипт для моніторингу показує, що, по-перше, jekyll закінчує генерувати сайт — і майже одразу починає наступний цикл, попри те, що я не додаю нових дописів на owncloud! а по-друге, насправді генерація сайту триває значно більше за 30 секунд…

третя гіпотеза: owncloud та rsync?

чи можливо, що owncloud періодично дописує якісь атрибути до тек чи окремих файлів блогу, rsync бачить це як зміни й що п’ять хвилин переписує вміст теки з owncloud до jekyll, а jekyll реагує на ці «зміни» й кидається переписувати сайт? для перевірки я встановив пакунок inotify-tools заради утиліти inotifywatch, котра вміє відслідковувати зміни файлів:

inotifywatch -m --format "%T %e %w%f" --timeftm "%Y-%m-%d, %H:%M" ~/jekyll/blog/_posts

дивно, але inotifywatch не детектує жодних змін у файлах, окрім створення тимчасових копій кожного допису (імена починаються з крапки) — але ls -lt чітко показує, що всі файли (принаймні їхні атрибути) перезаписано що п’ять хвилин! вочевидячки, це таки робота rsync…

час зміни файлів у теці блогу

детальний опис алгоритму роботи rsync натякає, що файли дрежела та цілі порівнюються за часом та розміром; отже, якщо rsync не пересилає час створення в переліку файлів джерела (дякую disfinder за підказку в коментарях)… тоді навіть якщо дельта порожня (змінена частина файлу) — в теці цілі rsync все-одно збирає тимчасовий файл (він буде ідентичний тому, що там вже є) і (див. алгоритм) заміняє ним поточний файл?! посібник (man rsync) прямо радить уникати цього, вживаючи опцію -t, про яку я й забув, налаштовуючи копіювання.

отже, перевірка: додаю опцію -t до свого скрипта copy2blog і споглядаю вивід мого монітора контейнерів…

rtfm, як завжди!

…і бачу, що ruby так само крутить процесор до 70-80%, майже як і раніше? але рядок uptime і вивід top показують, що все нормалізувалося! як так?!

реальне завантаження процесора

відповідь: rtfm, ламеряко! ps має проблеми особливості з визначенням використання процесора, і про це білим по чорному написано в посібнику: він обраховує середнє використання за увесь час роботи програми!

в підсумку: однієї опції rsync бракувало, через що jekyll щоразу кидався перебудовувати весь сайт попри інструкцію робити лише інкрементальні поновлення. проблему вирішено.