Просыпаюсь, иду работать, ничего не предвещает беды.
Хрен.
Через пару часов падает часть подов. UI - таймауты. Бэкенд - 5xx.
Приложение пишет, что не может создать файл.
Иногда не может прочитать файл.
Иногда "
no such file or directory". Иногда "
permission denied". Иногда просто падает - "
error".Короче, классический Kubernetes с его прозрачностью в траблшутинге.😀
Ну ладно. Открываю Grafana/VMlogs - ошибок море, но… никакой, блть, логики.
Все поды работают вроде бы одинаково, но часть подов стабильно отваливается примерно через минуту после старта.
Куда копать - сходу непонятно.
Иду в самое очевидное - Storage: PV, PVC
Потому что ошибки про файлы > проблемы с томами > ну значит PV/PVC где-то хромает, как старая лошадь.
Проверяю:
- PVC смонтировались? Да.
- PV healthy? Да.
- Нода здорова? Да.
- Место есть? Да.
- Inodes есть? (снова пригодились знания 🕺) Да.
Да чтоб тебя, сука.
Пробую перезапустить под на другой ноде - та же история.
Смотрю в события пода - красиво, чисто, как будто всё хорошо.
А приложение продолжает ругаться.
Симптомы - про Storage, причина - явно что-то другое.
Чешу свой пельменоаннигилятор и иду смотреть
emptyDir/tmpfs и вот это вот всё.Разработчики говорят:
- Мы в PV почти ничего не пишем, у нас всё в /tmp, shared memory, и ещё парочка emptyDir
(я вообще не уверен, что они понимают разницу, наверняка спросили нейронку). Ладно.
Лезу в POD, смотрю
/tmp.И тут интересное - папка есть, но файлы внутри иногда пропадают, иногда имеют странные владельцы, иногда пустые.
Жмурюсь, словно алкоголик с утра, перезапускаю под - то же самое.
Щёлкают мысли:
- race condition?
- или tmpfs не монтируется как надо?
- коричневая магия?
Проверяю:
mount | grep tmpfs
Да, монтируется.
Проверяю параметры - тоже всё ок.
Чо ха херня.
Поехали к ноде. Захожу на ноду и смотрю логи kubelet
И тут - первый звоночек:
MountVolume.SetUp failed for volume "cache" : chown /var/lib/kubelet/pods/.../volumes/kubernetes.io~empty-dir/cache: permission denied
Вот оно.
Бл, но как так-то?!
Это странно:
emptyDir монтируется нормально, но kubelet не может сделать chown/chmod.* На самом деле этого уже достаточно, чтобы понять причину, но мне это станет ясно только позже.
Пока я этого не понимаю, иду дальше.
Пытаюсь понять, что у файла не так.
Смотрю
ls -la внутри контейнера:drwxr-xr-x 3 1001 root ...
Иду в документацию приложения и к разработчикам, выясняю, что приложение должно работать от:
runAsUser: 2000
runAsGroup: 2000
fsGroup: 2000
Но папки создаются с 1001, какие-то с root, какие-то с 2000.
Ошибка хаотичная.
При этом разработчики уверяют:
- Мы ничего такого не делали!
Ну да, конечно, лол.
Проверяю
git diff Helm-чарта.И вот оно:
securityContext:
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
fsGroupChangePolicy: "OnRootMismatch"
Ну, думаю, может фигня, мало ли. Но идём дальше.
По дороге читаю документацию к приложению, куберу (я чо, всё помнить должен что-ли? Конечно же я постоянно подсматриваю).
И тут я натыкаюсь на тот самый скрытый момент.
Как я понимаю, если в контейнере есть:
- tmpfs (например /tmp, shared memory, projected volumes)
- emptyDir
- securityContext с fsGroup
- и при этом runAsUser != fsGroup
- И ПРИ ЭТОМ, сука, приложение создаёт файлы при старте до того, как kubelet применил fsGroup, то...
...получаем race condition, когда kubelet пытается chown, но контейнер уже создал свои файлы - с другими UID/GID - и chown уже не проходит.
kubelet такой:
- Я меняю владельца папки!
контейнер такой:
- А я создаю файлы внутри неё!
девопс такой:
- А я в дурку еду от вас двоих!
И кто быстрее - тот и прав.
Результат:
- часть файлов принадлежит пользователю 1001
- часть - root
- часть - fsGroup (предполагаю)
- часть - остаётся как попало
- kubelet иногда пишет permission denied
- приложение иногда пишет permission denied
- иногда - no such file (потому что удаляется побочным процессом?)
- иногда - ломается tmpfs-шляпа полностью
Эти два идиота(кублет и контейнер) сражаются как два бомжа палками, измазанными в говне. Придурошные, лол.