В статье описан редкий случай использования сервера mysql (далее mysqld) с параметром memlock: на сервере с AlmaLinux8 развернут контейнер с Centos7 через systemd-nspawn, в котором надо запустить mysqld (в случае с Centos7 это штатный пакет mariadb-server-5.5.68) с параметром memlock.

Одним из условий параметра memlock является то, что mysqld должен быть запущен от пользователя root. В случае с Centos7 это говорит о том, что нужно создавать новый сервисный файл для systemd, т.к. в штатном запуск осуществляется от пользователя mysql. Также в новом сервисном файле придется отказаться от mysqld_safe, т.к. в ней тоже жестко прописан пользователь mysql.

# cat /etc/systemd/system/mariadb-memlock.service
[Unit]
Description=MariaDB database server with the parameter memlock
After=syslog.target
After=network.target

[Service]
Type=simple
ExecStart=/usr/libexec/mysqld --basedir=/usr --plugin-dir=/usr/lib64/mysql/plugin --log-error=/var/log/mariadb/mariadb.log --user=mysql --memlock
# Give a reasonable amount of time for the server to start up/shut down
TimeoutSec=300
# Place temp files in a secure directory, not /tmp
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Ну вот вроде бы и все, создали сервисный файл. Осталось его запустить. Если бы mysqld запускался вне контейнера, то на этом можно было бы и закончить, т.к. параметр memlock применился бы без проблем (проверено). Но у нас же контейнер, и после запуска в логе mysqld пояляется вот такое сообщение.

[Warning] Failed to lock memory. Errno: 1

Хм... явно что-то нехорошее. И действительно, если посмотреть на переменную locked_in_memory, то она OFF.

# mysql -u root -e "SHOW VARIABLES LIKE 'locked_in_memory'"
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| locked_in_memory | OFF   |
+------------------+-------+

Начинается самое интересное (на мой взгляд) - расследование причин такого поведения. Гугление по строке "Failed to lock memory. Errno: 1" ничего не дает, как и топтание вокруг ulimit -l (LimitMEMLOCK=infinity). Затем пробую "прыгнуть" в контейнер через nsenter и запустить mysqld без сервисного файла.

# nsenter -m -u -i -n -p -t $(machinectl show centos7-1 | grep Leader | awk -F'=' '{print $2}')
[root@localhost /]# /usr/libexec/mysqld --basedir=/usr --plugin-dir=/usr/lib64/mysql/plugin --log-error=/var/log/mariadb/mariadb.log --user=mysql --memlock &

И о чудо, параметр memlock применился без проблем. Получается, что если запускать mysqld через сервисный файл, memlock не применяется, а если "прыгнуть" через nsenter и запустить mysqld в консоли, то применяется. Это наводит на мысль о том, что systemd-nspawn при запуске контейнера задает какие-то ограничения. Обычно в таких случаях, когда нагуглить ответ не удалось, а чтение документации непонятно откуда начинать, остается один вариант: смириться и начать плакать незаменимый инструмент - strace. И вот что удалось увидеть в проблемном запуске mysqld.

mlockall(MCL_CURRENT)                   = -1 EPERM (Operation not permitted)

А вот так это выглядит в непроблемном запуске mysqld.

mlockall(MCL_CURRENT)                   = 0

Строка "Operation not permitted" подтверждает догадку о том, что есть какие-то ограничения у контейнера. Внимательное чтение документации приводит к тому, что глаза цепляются за параметр Capability. В файл настроек запуска контейнера добавляем этот параметр со значением CAP_IPC_LOCK.

# cat /etc/systemd/nspawn/centos7-1.nspawn
[Exec]
PrivateUsers=no
Capability=CAP_IPC_LOCK

[Files]

[Network]
VirtualEthernet=no

Перезапускаем контейнер и смотрим.

# mysql -u root -e "SHOW VARIABLES LIKE 'locked_in_memory'"
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| locked_in_memory | ON    |
+------------------+-------+

Параметр memlock применён успешо!

PS:

После того как удалось решить проблему с контейнером запущенным через systemd-nspawn мне стало интересно как обстоят дела у docker. Буду краток - при запуске mysqld через docker run нужно выставить параметр --ulimit memlock=-1:-1, иначе strace покажет вот так

mlockall(MCL_CURRENT)                   = -1 ENOMEM (Cannot allocate memory)

а в логе mysqld будет такая строка

[Warning] Failed to lock memory. Errno: 12

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