logrotate успешно работает при ручном запуске от имени root, но не работает с "Файловой системой только для чтения" при запуске от logrotate.service

У меня есть (ранее работал) настройка логротата для OpenResty, который я взял из предыдущей установки Ubuntu 18.04. Тем не менее, сервис logrotate.service сейчас не работает с этой ошибкой ...

error: error renaming /usr/local/openresty/nginx/logs/access.log.60.zst
to /usr/local/openresty/nginx/logs/access.log.61.zst: Read-only file system

... и я с трудом понимаю почему. Новые машины работают под управлением Ubuntu 20.04, но я не понимаю, почему это должно изменить ситуацию.

Во-первых, вот конфигурация:

$ cat /etc/logrotate.d/custom-openresty

/usr/local/openresty/nginx/logs/access.log
/usr/local/openresty/nginx/logs/error.log
{
  daily
  rotate 60
  maxsize 1G
  missingok
  notifempty
  compress
  compresscmd /usr/bin/zstd
  uncompresscmd /usr/bin/unzstd
  compressoptions -9 --long -T1
  compressext .zst
  delaycompress
  sharedscripts
  postrotate
    test ! -f /usr/local/openresty/nginx/logs/nginx.pid || kill -USR1 `cat /usr/local/openresty/nginx/logs/nginx.pid`
 endscript
}

/etc/logrotate.conf не изменилась и выглядит так:


# see "man logrotate" for details
# rotate log files weekly
weekly

# use the adm group by default, since this is the owning group
# of /var/log/syslog.
su root adm

# keep 4 weeks worth of backlogs
rotate 4

# create new (empty) log files after rotating old ones
create

# use date as a suffix of the rotated file
#dateext

# uncomment this if you want your log files compressed
#compress

# packages drop log rotation information into this directory
include /etc/logrotate.d

# system-specific logs may be also be configured here.

Вот состояние моих файлов (я ожидал получить access.log.1. после запуска logrotate):

$ ls -alhg /usr/local/openresty/nginx/logs/
total 10G
drwxr-xr-x  2 root 4.0K Sep 16 20:04 .
drwxr-xr-x 18 root 4.0K Sep 16 19:42 ..
-rw-r--r--  1 root  10G Sep 16 19:56 access.log
-rw-r--r--  1 root 5.5K Sep 16 19:56 error.log

Однако, сервис logrotate.log.service терпит неудачу с этой ошибкой:

~$ systemctl status logrotate.service 
● logrotate.service - Rotate log files
     Loaded: loaded (/lib/systemd/system/logrotate.service; static; vendor preset: enabled)
     Active: failed (Result: exit-code) since Wed 2020-09-16 20:11:32 UTC; 4min 32s ago
TriggeredBy: ● logrotate.timer
       Docs: man:logrotate(8)
             man:logrotate.conf(5)
    Process: 27403 ExecStart=/usr/sbin/logrotate /etc/logrotate.conf (code=exited, status=1/FAILURE)
   Main PID: 27403 (code=exited, status=1/FAILURE)

$ sudo journalctl --unit logrotate.service

Sep 16 20:11:32 fetcher-scheduler systemd[1]: Starting Rotate log files...
Sep 16 20:11:32 fetcher-scheduler logrotate[27403]: error: error renaming /usr/local/openresty/nginx/logs/access.log.60.zst to /usr/local/openresty/nginx/logs/access.log.61.zst: Read-only file system
Sep 16 20:11:32 fetcher-scheduler systemd[1]: logrotate.service: Main process exited, code=exited, status=1/FAILURE
Sep 16 20:11:32 fetcher-scheduler systemd[1]: logrotate.service: Failed with result 'exit-code'.
Sep 16 20:11:32 fetcher-scheduler systemd[1]: Failed to start Rotate log files.

Когда я запускаю его (без systemd) в отладочном режиме от имени root, я получаю следующий вывод:

# logrotate -v -d /etc/logrotate.d/custom-openresty

reading config file /etc/logrotate.d/custom-openresty
compress_prog is now /usr/bin/zstd
uncompress_prog is now /usr/bin/unzstd
compress_options is now  -9 --long -T1
compress_ext is now .zst
Reading state from file: /var/lib/logrotate/status
Allocating hash table for state file, size 64 entries
Creating new state
Creating new state
...
Creating new state

Handling 1 logs

rotating pattern: /usr/local/openresty/nginx/logs/access.log
/usr/local/openresty/nginx/logs/error.log
 after 1 days (60 rotations)
empty log files are not rotated, log files >= 1073741824 are rotated earlier, old logs are removed
considering log /usr/local/openresty/nginx/logs/access.log
  Now: 2020-09-16 20:24
  Last rotated at 2020-09-16 20:11
  log needs rotating
considering log /usr/local/openresty/nginx/logs/error.log
  Now: 2020-09-16 20:24
  Last rotated at 2020-09-16 19:00
  log does not need rotating (log has been rotated at 2020-9-16 19:0, that is not day ago yet)
rotating log /usr/local/openresty/nginx/logs/access.log, log->rotateCount is 60
dateext suffix '-20200916'
glob pattern '-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
previous log /usr/local/openresty/nginx/logs/access.log.1 does not exist
renaming /usr/local/openresty/nginx/logs/access.log.60.zst to /usr/local/openresty/nginx/logs/access.log.61.zst (rotatecount 60, logstart 1, i 60), 
renaming /usr/local/openresty/nginx/logs/access.log.59.zst to /usr/local/openresty/nginx/logs/access.log.60.zst (rotatecount 60, logstart 1, i 59), 
...
/logs/access.log.3.zst (rotatecount 60, logstart 1, i 2), 
renaming /usr/local/openresty/nginx/logs/access.log.1.zst to /usr/local/openresty/nginx/logs/access.log.2.zst (rotatecount 60, logstart 1, i 1), 
renaming /usr/local/openresty/nginx/logs/access.log.0.zst to /usr/local/openresty/nginx/logs/access.log.1.zst (rotatecount 60, logstart 1, i 0), 
log /usr/local/openresty/nginx/logs/access.log.61.zst doesn't exist -- won't try to dispose of it
renaming /usr/local/openresty/nginx/logs/access.log to /usr/local/openresty/nginx/logs/access.log.1
running postrotate script
running script with arg /usr/local/openresty/nginx/logs/access.log
/usr/local/openresty/nginx/logs/error.log
: "
    test ! -f /usr/local/openresty/nginx/logs/nginx.pid || kill -USR1 `cat /usr/local/openresty/nginx/logs/nginx.pid`
"

Для меня все выглядит нормально. Если я запускаю его, он также работает:

# logrotate -v /etc/logrotate.d/custom-openresty
reading config file /etc/logrotate.d/custom-openresty
compress_prog is now /usr/bin/zstd
uncompress_prog is now /usr/bin/unzstd
compress_options is now  -9 --long -T1
compress_ext is now .zst
Reading state from file: /var/lib/logrotate/status
Allocating hash table for state file, size 64 entries
Creating new state
...
Creating new state

Handling 1 logs

rotating pattern: /usr/local/openresty/nginx/logs/access.log
/usr/local/openresty/nginx/logs/error.log
 after 1 days (60 rotations)
empty log files are not rotated, log files >= 1073741824 are rotated earlier, old logs are removed
considering log /usr/local/openresty/nginx/logs/access.log
  Now: 2020-09-16 20:26
  Last rotated at 2020-09-16 20:11
  log needs rotating
considering log /usr/local/openresty/nginx/logs/error.log
  Now: 2020-09-16 20:26
  Last rotated at 2020-09-16 19:00
  log does not need rotating (log has been rotated at 2020-9-16 19:0, that is not day ago yet)
rotating log /usr/local/openresty/nginx/logs/access.log, log->rotateCount is 60
dateext suffix '-20200916'
glob pattern '-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
previous log /usr/local/openresty/nginx/logs/access.log.1 does not exist
renaming /usr/local/openresty/nginx/logs/access.log.60.zst to /usr/local/openresty/nginx/logs/access.log.61.zst (rotatecount 60, logstart 1, i 60), 
old log /usr/local/openresty/nginx/logs/access.log.60.zst does not exist
...
old log /usr/local/openresty/nginx/logs/access.log.2.zst does not exist
renaming /usr/local/openresty/nginx/logs/access.log.1.zst to /usr/local/openresty/nginx/logs/access.log.2.zst (rotatecount 60, logstart 1, i 1), 
old log /usr/local/openresty/nginx/logs/access.log.1.zst does not exist
renaming /usr/local/openresty/nginx/logs/access.log.0.zst to /usr/local/openresty/nginx/logs/access.log.1.zst (rotatecount 60, logstart 1, i 0), 
old log /usr/local/openresty/nginx/logs/access.log.0.zst does not exist
log /usr/local/openresty/nginx/logs/access.log.61.zst doesn't exist -- won't try to dispose of it
renaming /usr/local/openresty/nginx/logs/access.log to /usr/local/openresty/nginx/logs/access.log.1
running postrotate script

Ошибки нет и в конце концов access.log.1 создается, как и ожидалось:

# ls -algh /usr/local/openresty/nginx/logs/
total 10G
drwxr-xr-x  2 root 4.0K Sep 16 20:26 .
drwxr-xr-x 18 root 4.0K Sep 16 19:42 ..
-rw-r--r--  1 root  10G Sep 16 19:56 access.log.1
-rw-r--r--  1 root 5.5K Sep 16 19:56 error.log

Обратите внимание, что в нем сказано ...

log /usr/local/openresty/nginx/logs/access.log.61.zst
doesn't exist -- won't try to dispose of it

... вместо ошибки переименования ... Файловая система только для чтения (как это делает logrotate.service)

Почему она работает, когда запускается вручную как root, но дает сбой, когда запускается logrotate.service?

Я не вносил никаких изменений в logrotate.service. Для полноты, вот файл модуля:

$ systemctl cat logrotate.service
# /lib/systemd/system/logrotate.service
[Unit]
Description=Rotate log files
Documentation=man:logrotate(8) man:logrotate.conf(5)
ConditionACPower=true

[Service]
Type=oneshot
ExecStart=/usr/sbin/logrotate /etc/logrotate.conf

# performance options
Nice=19
IOSchedulingClass=best-effort
IOSchedulingPriority=7

# hardening options
#  details: https://www.freedesktop.org/software/systemd/man/systemd.exec.html
#  no ProtectHome for userdir logs
#  no PrivateNetwork for mail deliviery
#  no ProtectKernelTunables for working SELinux with systemd older than 235
#  no MemoryDenyWriteExecute for gzip on i686
PrivateDevices=true
PrivateTmp=true
ProtectControlGroups=true
ProtectKernelModules=true
ProtectSystem=full
RestrictRealtime=true

Сейчас у меня заканчиваются опции. Любая помощь в устранении неполадок очень ценится.

0
задан 17 September 2020 в 00:16

2 ответа

Думаю, я нашел его. Файл logrotate не является проблемой.

Вместо этого, это вызвано функциями защиты в файле модуля systemd. После того как я отключил параметры защиты ProtectSystem = full , все заработало. Причина в том, что logrotate в моем случае должен работать с каталогом / usr , который доступен только для чтения, если эта опция включена.

Из документации на ProtectSystem =

Принимает логический аргумент или специальные значения «полный» или «строгий». Если true, монтирует каталоги / usr и загрузчика (/ boot и / efi) только для чтения для процессов, запускаемых этим устройством. Если установлено значение «full», каталог / etc также монтируется только для чтения. Если установлено значение «strict», вся иерархия файловой системы монтируется только для чтения, за исключением поддеревьев файловой системы API / dev, / proc и / sys (защищайте эти каталоги с помощью PrivateDevices =, ProtectKernelTunables =, ProtectControlGroups =). Этот параметр гарантирует, что любая модификация поставляемой поставщиком операционной системы (и, возможно, ее конфигурации и локальных подключений) запрещена для службы. Рекомендуется включить этот параметр для всех долго работающих служб, если они не связаны с обновлениями системы или не нуждаются в других способах изменения операционной системы. Если используется этот параметр, ReadWritePaths = может использоваться для исключения определенных каталогов из режима только для чтения. Этот параметр подразумевается, если установлен DynamicUser =. Этот параметр не может гарантировать защиту во всех случаях. В целом он имеет те же ограничения, что и ReadOnlyPaths =, см. Ниже. По умолчанию выключено.

Чтобы исправить это правильно, я добавил следующую строку в /lib/systemd/system/logrotate.service :

ReadWritePaths=/usr/local/openresty/nginx/logs

Тогда ProtectSystem = full будет смонтировать все как только для чтения, кроме каталога, в котором находятся журналы. Теперь ошибка только для чтения исчезла:

# systemctl daemon-reload && systemctl start logrotate
(completed successfully without output)

Полагаю, я никогда не сталкивался с этой проблемой в Ubuntu 18.04, потому что параметры усиления еще не были значениями по умолчанию. Сейчас он есть в Ubuntu 20.04, но я не знаю, в каком выпуске Ubuntu они были впервые представлены.

0
ответ дан 4 January 2021 в 08:22

Хорошая находка для той же проблемы, которую я видел. Редактирование nginx.conf для входа в другой каталог (например, общий / var / log / nginx) является альтернативой, которая также работает.

0
ответ дан 4 January 2021 в 08:22

Другие вопросы по тегам:

Похожие вопросы: