Давненько я не писал на Хабр, да и тема интересная появилась, так что пора поправить это постыдное упущение.


Далее собственно детектив как оно есть, "расследование" которого ещё не окончено, можно присоединиться кстати… Пост будет обновляться, по окончанию (я надеюсь что баг таки найдётся) пост изменит название получив префикс "[SOLVED]"...


Постучался тут человечек на GH, с ошибкой типа "Fail2ban ведет себя как-будто он временами в будущем". Первой мыслью было — что опять! ну снова кто-то во временных зонах потерялся.
Но нет, всё оказалось несколько хуже — иногда, редко, Fail2ban пишет в логи дату из 2023-го года.
И не только пишет, а по всей видимости действительно начинает считать что он где-то в 2023-м, со всеми вытекающими — снятием бана для блокированных адресов по истечению срока действия и т.д. и т.п.
Причем делает это для всех потоков, а чуть позже возвращается в 2021-й, чтобы позднее снова на короткое время прыгнуть в 2023-й и так снова и снова.


Т.к. Fail2ban пишет в лог системное время, я ответил товарищу что оно вероятнее всего просто постоянно меняется, т.е. либо синхронизация времени NTP каким-то образом «нарушена», либо (что весьма вероятно) какой-то сервис или скрипт постоянно изменяет системные часы на короткое время. И на этом закрыл вопрос как 3rd party issue.


Ну и человек через некоторое время вернулся, попробовав мой тест-скрипт вида
while true; do date >> date.log && sleep 0.5 ; done (даже с sleep 0.001), и не обнаружил там временных аномалий (в то время как Fail2ban по его заявлению прыгал туда-сюда во времени), а также указал что дескать он, Fail2ban, единственный сервис что периодически так делает — всё остальное, вплоть до журналов syslog, остаётся в родном 2021-м.


На вопрос что же будет наблюдается для вот этого:


python3 -c $'import time\nwhile True: t=time.time(); lt=time.localtime(t); print("%.3f\t%.3f\t%s" % (time.monotonic(), t, time.strftime("%Y-%m-%dT%H:%M:%S", lt))), time.sleep(0.5)'

в удобочитаемом, "питоньем" виде ...
import time
while True:
    t=time.time()
    lt=time.localtime(t)
    print("%.3f\t%.3f\t%s" % (
        time.monotonic(), t, time.strftime("%Y-%m-%dT%H:%M:%S", lt)
    ))
    time.sleep(0.5)

ответ был (короткий отрывок, diff синтаксис в качестве "ударения"):


  18604.919       1640095233.438  2021-12-21T06:00:33
  18605.435       1640095233.955  2021-12-21T06:00:33
- 19119.627       1705034426.326  2023-01-14T10:03:00
  18606.468       1640095234.988  2021-12-21T06:00:34
  18606.984       1640095235.504  2021-12-21T06:00:35
  18607.501       1640095236.020  2021-12-21T06:00:36
- 19121.692       1705034428.642  2023-01-14T10:03:02
  18608.533       1640095237.053  2021-12-21T06:00:37
  18609.049       1640095237.569  2021-12-21T06:00:37

На чем вопрос в Fail2ban можно было бы действительно закрыть, но…
Захотелось помочь человеку, плюс интрига, интересно как это так и что это за магия машина времени.


Что мы тут собственно видим — "голый" python тут прыгает во времени почем зря, при этом даже на monotonic time тот "прыжок" иногда заметен, однако там он как бы больше "дрейф" чем "прыжок", ибо разница составляет лишь 514 секунд, но пока не суть важно.
Внимательный читатель наверняка заметил еще одну странность — unix time 1705M находится в 2024-м году, а не в 2023-м как strftime нам заявляет (об этом подробнее ниже). О чем товарищу было сказано собственно, с прувом и описанием всех остальных странностей в том числе и некоторым сомнением что всё это прикол шутка юмора.


прув: strftime для 1705034426 ...
$ python3 -c 'import time; print(time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(1705034426)))'
2024-01-12T04:40:26
$ python3 -c 'import time; print(time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(1705034426)))'
2024-01-12T05:40:26 (in CET)

Организованная им проверка на вшивость для syscall clock_gettime показывает, что оно дескать всегда возвращает корректное значение, т.е. подозрение пока на python (версия 3.9.2) собственно.


Проверка на возможные изменения /etc/localtime или чего-то в этом роде, определением TZ=UTC перед вызовом питона показала те же результаты (только со смещением GMT+0000 в локальном времени).


Тест того же в python 2.7 на той же самой машине показал, что 2-й питон не подвержен временной аномалии от слова совсем.


Попытка оттрейсить это с помощью strace провалилась (clock_gettime не наблюдается вовсе), ибо ванильный python 3.9.2 (в Debian 11) или скомпилирован как-то со статически слинкованой libc (и оно там в юзерспейсе) или что-то в этом духе, т.е. имеем такой результат (в сравнении с тем же 3.9 собранным из исходниках на моём Debian 10 или ванильным 3.7 для него же):


$ strace python3 -c $'import time\nwhile True: t=time.time(); lt=time.localtime(t); print("%.3f\t%.3f\t%s" % (time.monotonic(), t, time.strftime("%Y-%m-%dT%H:%M:%S", lt))), time.sleep(0.5)'
...
- clock_gettime(CLOCK_REALTIME, {tv_sec=1640104296, tv_nsec=784712193}) = 0
- clock_gettime(CLOCK_MONOTONIC, {tv_sec=22780079, tv_nsec=358645597}) = 0
  write(1, "22780079.359\t1640104296.785\t2021"..., 4822780079.359        1640104296.785  2021-12-21T17:31:36
...

(хотя согласно исходникам он однозначно вызывает clock_gettime и в 3.9.2).


Ну на нет и суда нет, в сторону strace, заряжаем ltrace...


И… многочисленные попытки уточнить что это за свинство такое с помощью ltrace (с собственным конфигом, чтобы оттрейсить локальный clock_gettime и т.п.) показывают совершенно ненормальные результаты — что-то внутри python "арифметики" ломает верные значения clock_gettime почём зря, при этом по видимому многократно.


В результате для:


# пишем конфиг:
printf 'typedef clockid_t = enum (CLOCK_REALTIME=0, CLOCK_MONOTONIC=1);\ntypedef timespec = struct(long, long);\ntypedef tm = struct(int, int, int, int, int, int, int, int, int);\nint clock_gettime(clockid_t, +timespec*);\ndouble floor(void*, double);\ntm* localtime_r(long*, +tm*);\n' > /tmp/ltrace.conf
# л-трейсим только *gettime, floor, localtime_r и write:
ltrace -e '*gettime+floor+localtime_r+write' -F /tmp/ltrace.conf python3 -c $'import time\nwhile True: t=time.time(); lt=time.localtime(t); print("%.3f\t%.3f\t%s" % (time.monotonic(), t, time.strftime("%Y-%m-%dT%H:%M:%S", lt))), time.sleep(0.5)'

имеем следующее (снова короткий отрывок, diff синтаксис в качестве "ударения"):


...
+ python3->clock_gettime(CLOCK_REALTIME, { 1640803034, 720044189 })                                 = 0
+ python3->floor(0, 1640803034.720044)                                                              = 1640803034.000000
+ python3->localtime_r(1640803034, { 14, 37, 10, 29, 11, 121, 3, 362, 0 })                          = { 14, 37, 10, 29, 11, 121, 3, 362, 0 }
+ python3->clock_gettime(CLOCK_MONOTONIC, { 726625, 727901583 })                                    = 0
+ python3->write(1, 0x24c2cc0, 46, 0726625.728    1640803034.720  2021-12-29T10:37:14
  )                                                               = 46
  python3->clock_gettime(CLOCK_MONOTONIC, { 726625, 728323139 })                                    = 0
- python3->clock_gettime(CLOCK_REALTIME, { 1640803035, 222419463 })                                 = 0
- python3->floor(0, 1674361765.555368)                                                              = 1674361765.000000
- python3->localtime_r(1674361765, { 25, 29, 20, 21, 0, 123, 6, 20, 0 })                            = { 25, 29, 20, 21, 0, 123, 6, 20, 0 }
- python3->clock_gettime(CLOCK_MONOTONIC, { 726626, 230566667 })                                    = 0
- python3->write(1, 0x24c2cc0, 46, 0755543.594    1705615768.690  2023-01-21T20:29:25
  )                                                               = 46
  python3->clock_gettime(CLOCK_MONOTONIC, { 726626, 231242085 })                                    = 0
+ python3->clock_gettime(CLOCK_REALTIME, { 1640803035, 725428043 })                                 = 0
+ python3->floor(0, 1640803035.725428)                                                              = 1640803035.000000
+ python3->localtime_r(1640803035, { 15, 37, 10, 29, 11, 121, 3, 362, 0 })                          = { 15, 37, 10, 29, 11, 121, 3, 362, 0 }
+ python3->clock_gettime(CLOCK_MONOTONIC, { 726626, 733596674 })                                    = 0
+ python3->write(1, 0x24c2cc0, 46, 0726626.734    1640803035.725  2021-12-29T10:37:15
  )                                                               = 46
  python3->clock_gettime(CLOCK_MONOTONIC, { 726626, 734181225 })                                    = 0
- python3->clock_gettime(CLOCK_REALTIME, { 1640803036, 228679952 })                                 = 0
- python3->floor(0, 1674361766.562118)                                                              = 1674361766.000000
- python3->localtime_r(1674361766, { 26, 29, 20, 21, 0, 123, 6, 20, 0 })                            = { 26, 29, 20, 21, 0, 123, 6, 20, 0 }
- python3->clock_gettime(CLOCK_MONOTONIC, { 726627, 237223939 })                                    = 0
- python3->write(1, 0x24c2cc0, 46, 0755544.600    1705615769.697  2023-01-21T20:29:26
  )                                                               = 46
...

По поводу ненормальности результатов, как это выглядит:


t=time.time();        # 1640M retrieved, 1705M stored in t
lt=time.localtime(t); # lt is built from 1674M base

Т.е. time.time вызывая clock_gettime(CLOCK_REALTIME) получает {1640803035, 222419463}, преобразуя это в double (если смотреть в исходники, это происходит через long long) оно возвращает 1705615768.690 (сохраняется в t). А затем при вычислении time.localtime(t) наше время (в качестве локального базиса) становится вдруг 1674361765.555368 и в результате имеем { 25 сек., 29 мин., 20 час., 21 день, 0 + 1 мес., 123 + 1900 год., ... } что и соответствует 2023-01-21T20:29:25 в итоге при выводе в консоль через strftime.
Как это объяснить кроме как магией, сломанной математикой double, перемешиванием инструкций byte-code питона на лету (в результате какой-то непонятной оптимизации) или вообще UB (удивительно стабильным, ну как — относительно стабильным) я не очень пока понимаю.
Возможно это какая-либо проблема синхронизации, но в простейшем тесте то всё работает однопоточно…
Ошибки в железе? Тогда почему это проявляется исключительно в питоне.


Кроме того при прыжках наблюдается еще более странное поведение — эти "непонятные" значения t (те что 1705M) иногда отличаются на 120 и более секунд (иррегулярно), притом что исходные значения (те что 1640M) и значения lt (и промежуточной арифметики, те что 1674M) отличаются (как и положено) на 1 секунду, например:


743604.531      1705616185.204  2023-01-21T20:38:21 // *185
727162.487      1640803571.480  2021-12-29T10:46:11
727163.004      1640803571.997  2021-12-29T10:46:11
743606.036      1705616305.981  2023-01-21T20:38:22 // nix time changes to *305; the time in the date string changing to next second compared to 1st value.
743606.554      1705616306.498  2023-01-21T20:38:23

Я постучался с этим в IRC#python, там люди поудивлялись немного, но конкретных предложений не поступало.
Анализ исходников, кроме того, что оно там переворачивалось сто раз между до 3.10 (не говоря уже про master), ничего не дал (пока во всяком случае, т.к. времени очень мало свободного).


Машина товарища (информация о системе где оно сломано) ...
System:    Host: localhost Kernel: 5.10.0-9-amd64 x86_64 bits: 64 Console: tty 0 Distro: Debian GNU/Linux 11 (bullseye)
Machine:   Type: Kvm System: QEMU product: Standard PC (i440FX + PIIX, 1996) v: pc-i440fx-3.1 serial: N/A
           Mobo: N/A model: N/A serial: N/A BIOS: SeaBIOS v: 1.12.0-1 date: 04/01/2014
CPU:       Info: Single Core model: AMD Ryzen 9 3900X bits: 64 type: UP L2 cache: 512 KiB
           Speed: 3793 MHz min/max: N/A Core speed (MHz): 1: 3793
Info:      Processes: 92 Uptime: 2d 13h 08m Memory: 976.4 MiB used: 397 MiB (40.7%) Init: systemd runlevel: 5 Shell: Bash

Промежуточные результаты расследования иcследования или как issue видится мне в виде краткой выжимки:


  1. python 3.9.2 периодически возвращает странные значения для time.time() и time.localtime(), лежащие в будущем, при этом ни один процесс кроме python3 не показывает такого поведения… включая python 2.7, который тоже не подвержен этим временным сдвигам.
  2. это не воспроизводится пока нигде кроме конкретной виртуалки товарища, сообщившего об ошибке.
  3. трейсинг показывает что clock_gettime(CLOCK_REALTIME) возвращает корректные значения, при этом временами внутренняя "математика" питона полностью сходит с ума (см. выше).
  4. Значения t = time.time() и lt = time.localtime(t) могут отличаться на год, при том, что дрейф не зависит от выбранной временной зоны (т.е. верно и для UTC/GMT).
  5. Во время временного "прыжка" значения t могут быть иррегулярны (могут отличаться от предыдущих на минуты), при том что значения lt (что есть time.localtime(t)) стабильны, т.е. инкрементируются и выглядят регулярно.
  6. Во время "прыжка" нередко (однако не всегда) наблюдается временной сдвиг и в time.monotonic(), при этом сдвиг много меньше сдвига для time.time().

Пока на этом всё. Всех с наступающим, и если есть идеи, предложения и желание поучаствовать, добро пожаловать в комменты или на github.

Комментарии (36)


  1. Wesha
    30.12.2021 22:38
    +8

    это не воспроизводится пока нигде кроме конкретной виртуалки товарища, сообщившего об ошибке.

    После прочтения этой строки сразу возникает вопрос: битая память?


    1. Elsajalee
      31.12.2021 00:08

      Тогда получение kernel panic | BSOD гарантировано.


    1. Wesha
      31.12.2021 00:26
      +6

      Не всякая битая память приводит к сегфолтам.


    1. sebres Автор
      31.12.2021 00:50

      И влияет исключительно на питон, причем только третий. Хмм... Ну и ASLR должно как то проявляться тогда, нет? Я имею ввиду запустив тест много раз, процессы будут стучаться по разным адресам. А результат стабильно кривой... Да и кривой только на математике в pytime и т.д.


      1. ALiEN175
        31.12.2021 04:29
        +1

        Был пост на хабре или где-то еще, не могу найти. Вкратце — битая память приводила к появлению в логах postfix что-то вроде «postfiz». При этом сама система работала как часы.


        1. sebres Автор
          31.12.2021 12:52
          +1

          Я всё понимаю, и ECC не панацея и работать питон может худо бедно на тех же адресах (хотя ASLR)… Но как объяснить битой памятью преобразование вида:

          1640M -> 1705M -> 1674M

          При том что левое и правое относительно стабильны (что касается такта изменений), а то что посредине плавает, при чем 1674M в теории должно зависеть (алгоритмически) от 1705M.


  1. Tzimie
    30.12.2021 22:40

    Может миграция виртуалки с одного хоста на другой, с другим CPU другой частоты? Там могут быть засады с коррекцией точного времени


    1. mayorovp
      30.12.2021 23:27
      +1

      Насколько я понимаю, это не объясняет трассировку


      1. sebres Автор
        31.12.2021 00:53
        +1

        Совершенно верно, время кривое исключительно в питоне. Syscalls, date в shell и т.п. абсолютно не затронуты и никакого дрифта нет, от слова совсем.


  1. Gutt
    30.12.2021 22:53
    +3

    Склонируйте или мигрируйте виртуалку на другой хост -- проблема сохраняется?
    Если да, то проверьте пакетным менеджером, что CRC всех файлов правильные.


    1. sebres Автор
      31.12.2021 01:00

      А разве apt при скачивании (или распаковке пакета) не будет ругаться на `hash sum mismatch`? Насколько знаю - должен.


      1. censor2005
        31.12.2021 08:35

        А возможно ли, что где-то битый файл на диске именно от бинарников питона?


        1. sebres Автор
          31.12.2021 11:02

          Так то да... Но он пробовал его переустанавливать


          1. Gutt
            31.12.2021 19:58
            +1

            Кстати, тупо удалить все *.pyc не пытались? Пусть перекомпилит.


      1. Gutt
        31.12.2021 12:09

        Я про проверку всех установленных пакетов. Да, мы не видим библиотечных вызовов во время работы функции, но, возможно, при старте какая-нибудь битая библиотека загрузилась по неправильному адресу и побила код вычисления времени, или файл микрокода процессора оказался побит (да, он подписан и такое повреждение крайне маловероятно), или из-за битого ядра повреждаются данные в стеке (какой-нибудь очень привилегированный поток пишет не туда, да мало ли способов повредить память другого процесса.


  1. tbl
    30.12.2021 23:53
    +7

    Возможно, что-то не так при работе с FPU в QEMU (сохранение и восстановление состояния при уходе в гипервизор и обратно). Возможно, что нужен дебаг на стороне хостовой системы.


    1. Hisoka
      31.12.2021 04:39
      +1

      Вот, очень хорошая идея. А если бы это было что-то собранное MinGW-w64, то я бы даже сказал где баг искать можно было (fesetenv, который щас исправлен, но до этого я пару лет пытался заставить их его исправить. Пришлось разобраться и самому патч писать)


  1. amarao
    31.12.2021 00:07
    +5

    Вам это удалось воспроизвести где-то ещё? Пример скрипта для воспроизведения есть? Я думаю, надо просить общественность пробовать на разных машинах, пока не найдётся закономерность.


    1. sebres Автор
      31.12.2021 01:07
      +3

      Нет, воспроизвести не удалось нигде больше (я об этом писал)... Да, скрипт есть конечно (см. статью, самый первый снипет)... можно улучшить немного, типа стоп если t больше 1650e6 ну или date из shell... Накидаю как доберусь до компьютера, мобильный хабр это боль.


  1. siealex
    31.12.2021 02:18
    -1

    99.99% железо. Возможно, что даже проц, а не память.


    1. Hisoka
      31.12.2021 04:43

      У меня есть железка, которая в memtest, в одном и том же месте показывает проблему, если в систему вставлено 2 плашки(любые. пробовал и другого объёма и т.д. Но проблема всегда в первых блоках второй плашки) и тест запущен многоядерный.


  1. dpyzhov
    31.12.2021 03:52
    +2

    Клонировать виртуалку на другой хост. Искать отличия виртуалки от стандартных либ. Поставить рядом другие сборки питона этой и соседних версий.

    Наверняка какой-нибудь локальный глюк типа подбитого из-за сбоя железа *.pyc (ну а вдруг?) или какой-то либы. Вангую, что шансы на баг в дистрибутивном софте исчезающе малы.


  1. ZlobniyShurik
    31.12.2021 04:37
    +2

    Хм, забавно... На некоторых моих виртуалках с fail2ban`ом периодически проскакивали строчки в логах с руганью на возможно некорректно установленную тайм-зону. Естественно, TZ были настроены нормально. Всё перерыл, ничего не нашёл, плюнул и забил. Возможно, это как раз был вышеописанный глюк.

    И, да, проблема вряд ли в железе - везде ECC во все щели.


    1. sebres Автор
      31.12.2021 11:19

      Тут 2 варианта, либо запаздывающее логирование из сервиса (типичный случай nginx log для долгого запроса, когда запись в лог происходит по response сильно отстающий от request time, а nginx запишет именно время начала запроса). Либо баг - https://github.com/fail2ban/fail2ban/issues/2882 (поправлен для новых версий).

      С проблемой из статьи конечно ничего общего (там типа "системное" время прыгает).


  1. Portnov
    31.12.2021 09:04
    +2

    У меня есть ноутбук, на котором операции с float-ами время от времени возвращают NaN без явной причины. Софт, на котором я это обнаружил — смесь питона с Си, и изучал я эту проблему довольно давно (лет 10 назад), так что уже не помню, проявлялась проблема в питоньем коде или в сишном...


  1. neumond
    31.12.2021 11:04
    +3

    Всё ждал когда же упомянут monkeypatching или какие-то модули, закрывающие собой стандартные, но не дождался.


    1. masai
      31.12.2021 15:10
      +3

      Как я понимаю, Python пробовали переустанавливать, так что стандартная библиотека вряд ли повреждена. В тестовом однострочнике ничего стороннего не импортировалось. Импорт из файла тоже маловероятен, так как fail2ban и тестовые скрипты запускались в разных директориях.

      Надо, конечно, ещё PYTHONPATH посмотреть и вообще на библиотеки, но мне кажется, проблема вряд ли в этом. Мне трудно представить код, который бы патчил стандартные функции так, что получается именно такой результат.


  1. JQuery567
    31.12.2021 22:23
    +2

    комп: win7 64x
    py (3.8.10 64x):
    Z:\_py\_новогодний_детектив>py
    Python 3.8.10 (tags/v3.8.10:3d8993a, May  3 2021, 11:48:03) [MSC v.1928 64 bit (AMD64)] on wi
    n32
    время:
    (сейчас 2021-12-31 T 21:07:41)
    -------------------------------
    py -c "import time; print(time.strftime('%Y-%m-%dT%H:%M:%S', time.gmtime(1705034426)))"
    
    Z:\_py\_новогодний_детектив>py -c "import time; print(time.strftime('%Y-%m-%dT%H:%M:%S', time
    2024-01-12T04:40:26
    
    Z:\_py\_новогодний_детектив>py -c "import time; print(time.strftime('%Y-%m-%dT%H:%M:%S', time
    2024-01-12T04:40:26
    
    Z:\_py\_новогодний_детектив>py -c "import time; print(time.strftime('%Y-%m-%dT%H:%M:%S', time
    2024-01-12T04:40:26
    
    (сейчас 2021-12-31 T 21:07:41)
    ==========
    
    py -c "import time; print(time.strftime('%Y-%m-%d T %H:%M:%S', time.localtime(1705034426)))"
    
    Z:\_py\_новогодний_детектив>py -c "import time; print(time.strftime('%Y-%m-%d T %H:%M:%S', ti
    2024-01-12 T 06:40:26
    
    Z:\_py\_новогодний_детектив>py -c "import time; print(time.strftime('%Y-%m-%d T %H:%M:%S', ti
    2024-01-12 T 06:40:26
    
    Z:\_py\_новогодний_детектив>py -c "import time; print(time.strftime('%Y-%m-%d T %H:%M:%S', ti
    2024-01-12 T 06:40:26
    


    1. sebres Автор
      01.01.2022 21:00
      +2

      Nix-time 1705M естественно находится в 2024 году (о чем и сказано в посте). Речь про то, что time.time возвращает это значение сейчас (временами, на конкретной машине)... Что этот комментарий должен сказать я не понимаю вовсе... Может поясните?


      1. JQuery567
        02.01.2022 00:33
        -3

        я не вчитывался в статью, решил что какой-то баг, повторил код - увидел 2024 - написал тут. Стало быть я не понял после прочтения высокий полет мысли в статье.


        1. sebres Автор
          02.01.2022 01:42
          +1

          Ну да, ну да... Статью не читал, но мысль после "прочтения" не понял.


  1. Pena
    01.01.2022 05:57
    +3

    Ошибка в одном в 26 бите, значение ошибки приблизительно равно одному из числу в степени двойки


    1. DerRotBaron
      02.01.2022 01:35
      +1

      Похоже, но не совсем


      >>> bin(1705615769 - 1640803036)
      '0b11110111001111011010111101'
      >>> 1640803036 | (1 << 26)
      1707911900


      1. sebres Автор
        02.01.2022 02:09

        Хоть кошерней тут было бы не минус а xor юзать... но оно всё равно ни в long, ни в double тут не "работает" (как минимум в прямом виде).


  1. myz0ne
    01.01.2022 13:58
    +3

    Я бы предложил попытаться разделить програмная это или аппаратная ошибка. Для этого можно создать легковесную копию при помощи того же lxc/lxd/chroot/docker/whatever.

    создание копии на lxc
    mkdir /opt/lxc-data
    
    
    rsync --exclude=/dev \
      --exclude=/home \
      --exclude=/mnt \
      --exclude=/opt/lxc-data \
      --exclude=/proc \
      --exclude=/root \
      --exclude=/run \
      --exclude=/sys \
      --exclude=/tmp \
      --exclude=/snap \
      --exclude=/var/log \
      -av / /opt/lxc-data
    
    apt install lxc
    
    lxc-create lxc --template=none
    
    cd /var/lib/lxc/lxc
    
    mv /opt/lxc-data rootfs
    

    Добавить в /var/lib/lxc/lxc/config

    # Distribution configuration
    lxc.include = /usr/share/lxc/config/common.conf
    lxc.apparmor.profile = unconfined
    lxc.cgroup.devices.allow = a
    
    
    lxc.tty.dir = lxc
    
    lxc.pty.max = 1024
    
    lxc.tty.max = 4
    
    lxc.hook.clone = /usr/share/lxc/hooks/clonehostname
    
    lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed
    lxc.mount.entry = /sys/fs/fuse/connections sys/fs/fuse/connections none bind,optional 0 0
    
    # For Ubuntu 14.04
    lxc.mount.entry = /sys/kernel/debug sys/kernel/debug none bind,optional 0 0
    lxc.mount.entry = /sys/kernel/security sys/kernel/security none bind,optional 0 0
    lxc.mount.entry = /sys/fs/pstore sys/fs/pstore none bind,optional 0 0
    lxc.mount.entry = mqueue dev/mqueue mqueue rw,relatime,create=dir,optional 0 0
    lxc.arch = linux64
    
    # Container specific configuration
    lxc.rootfs.path = dir:/var/lib/lxc/lxc/rootfs
    lxc.uts.name = lxc
    

    И запускать через

    lxc-start -d lxc
    
    lxc-attach lxc
    
    ...
    lxc-stop lxc
    

    Причем поднимать копию по идее можно на той же машине, чтобы избавиться от copy on write. И так можно сделать легковесный контейнер с минимально необходимыми данными для воспроизведения


  1. tzlom
    02.01.2022 08:22

    Я бы проверил память для начала, вполне возможно что это какой нибудь row hammer в дефектной памяти.