Автоматизация ( Автоматизация процедур сборки и разборки распределённой файловой системы. )
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
Скрипты, конфигурационные и журнальные файлы нашей самодельной сетевой файловой системы будут располагаться одинаково на всех серверах, как ведущих, так и ведомых:
Прежде всего создаём конфигурационный файл с параметрами для скрипта автоматизации:
Пишем скрипт автоматизации контроля подсистемы хранения:
Прописываем наш скрипт для нужных уровней исполнения в системе:
Для того, что бы не получать сильно разросшиеся файлы журналов, настроим их ротацию.
Устанавливаем приложение ротации текстовых файлов с одновременным их сжатием:
Создаем конфигурационный файл ротации журнальных файлов:
Проверяем корректность конфигурационного файла:
Переход к описанию вспомогательных функций.
Скрипты, конфигурационные и журнальные файлы нашей самодельной сетевой файловой системы будут располагаться одинаково на всех серверах, как ведущих, так и ведомых:
# mkdir -p /usr/local/etc/default/ /usr/local/etc/storage/fnc.d/ /usr/local/etc/storage/cnf.d/ /var/log/storage
Прежде всего создаём конфигурационный файл с параметрами для скрипта автоматизации:
# vi /usr/local/etc/default/storage-custom
# Default configuration file for /etc/init.d/storage-control
# Адрес директории динамически включаемых в тело скрипта управления описаний функций
FNCDIR="/usr/local/etc/storage/fnc.d"
# Адрес директории индивидуальных конфигураций сервисов хранилища
CNFDIR="/usr/local/etc/storage/cnf.d"
# Адрес директории файлов блокировок сервисов хранилища
LOCK="/tmp/storage/lck"
# Адрес файла журнала
LOG="/var/log/storage/default.log"
EMAIL=`cat /usr/local/etc/storage/email >/dev/null 2>&1`
# Адрес директории динамически включаемых в тело скрипта управления описаний функций
FNCDIR="/usr/local/etc/storage/fnc.d"
# Адрес директории индивидуальных конфигураций сервисов хранилища
CNFDIR="/usr/local/etc/storage/cnf.d"
# Адрес директории файлов блокировок сервисов хранилища
LOCK="/tmp/storage/lck"
# Адрес файла журнала
LOG="/var/log/storage/default.log"
EMAIL=`cat /usr/local/etc/storage/email >/dev/null 2>&1`
Пишем скрипт автоматизации контроля подсистемы хранения:
# vi /etc/init.d/storage-control && chmod ugo+rx /etc/init.d/storage-control
#!/bin/bash
### BEGIN INIT INFO
# Provides: storage-control
# Required-Start: $local_fs $syslog $network
# Required-Stop: $local_fs $syslog $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Script management of storage
# Description: Script management of storage
### END INIT INFO
# Подключаем первичный конфигурационный файл
source "/usr/local/etc/default/storage-custom"
[ ${?} -ne 0 ] && { echo "Fatal error: missing default custom storage configuration file. Operation aborted."; exit 1; }
# Принимаем в переменные с "говорящими" именами входящие аргументы
INSTANCE=${0}
OPERATION=${1}
TARGET=${2}
DATE=`date +"%Y-%m-%d %H:%M:%S"`
# Перебираем в цикле все объекты (по маске) в целевой директории динамически подключаемых описаний функций
cd "${FNCDIR}"
for OBJECT in *\.fnc ; do
# Включаем в тело скрипта текст обнаруженных фрагментов описаний функций
[ -f "${FNCDIR}/${OBJECT}" ] && source "${FNCDIR}/${OBJECT}"
done
# Создаём директорию для файлов блокировки
mkdir -p "${LOCK}"
# Определяем функцию предварительной инициализации конфигурационных файлов хранилища
function start-init() {
# Создаём временный файл журнала событий
LOGT=$(mktemp /tmp/storage-start.log.XXXXXXXX)
# Создаём временный файл глобальной конфигурации
CNF=$(mktemp /tmp/storage.cnf.XXXXXXXX)
cd "${CNFDIR}"
for OBJECT in *\.cnf ; do
# Включаем в тело временного глобального файла все обнаруженные фрагменты конфигураций
[ -f "${CNFDIR}/${OBJECT}" ] && cat "${CNFDIR}/${OBJECT}" >> "${CNF}"
done
DATE=`date +"%Y-%m-%d %H:%M:%S"`
start
# Если запуск системы хранилища завершился неудачно, то отсылаем отчёт и "разбираем" его
if [ "${?}" -ne "0" ] ; then
send-report "Storage assembling error!" "$(cat ${LOGT})"
return 1
fi
# Включаем временный журнал событий в основной и удаляем временный
cat "${LOGT}" >> "${LOG}"
rm --force "${LOGT}"
# Удаляем временный файл глобальной конфигурации
rm --force "${CNF}"
return ${?}
}
# Определяем функцию предварительной инициализации конфигурационных файлов хранилища
function stop-init() {
# Создаём временный файл журнала событий
LOGT=$(mktemp /tmp/storage-stop.log.XXXXXXXX)
# Создаём временный файл глобальной конфигурации
CNF=$(mktemp /tmp/storage.cnf.XXXXXXXX)
cd "${CNFDIR}"
for OBJECT in *\.cnf ; do
# Включаем в тело временного глобального файла все обнаруженные фрагменты конфигураций
[ -f "${CNFDIR}/${OBJECT}" ] && cat "${CNFDIR}/${OBJECT}" >> "${CNF}"
done
DATE=`date +"%Y-%m-%d %H:%M:%S"`
stop
# Если остановка системы хранилища завершилась неудачно, то отсылаем отчёт
if [ "${?}" -ne "0" ] ; then
send-report "Storage disassembling error!" "$(cat ${LOGT})"
fi
# Включаем временный журнал событий в основной и удаляем временный
cat "${LOGT}" >> "${LOG}"
rm --force "${LOGT}"
# Удаляем временный файл глобальной конфигурации
rm --force "${CNF}"
return ${?}
}
# Описываем функцию формирования опций хранилища и непосредственного её запуска
function start() {
echo | tee -a "${LOGT}"
echo "${DATE}: Procedure is running store is initialized." | tee -a "${LOGT}"
echo | tee -a "${LOGT}"
# Сборка массивов
declare -f start-mdadm >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция сборки RAID." | tee -a "${LOGT}"
else
start-mdadm
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Assembly arrays failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Активация групп томов LVM
declare -f start-lvm >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция сборки LVM." | tee -a "${LOGT}"
else
start-lvm
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Activating LVM volume groups failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Создание символической ссылки на заданный раздел целевого диска
declare -f make-symlink >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция создания символической ссылки на раздел целевого диска." | tee -a "${LOGT}"
else
make-symlink
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Create a symbolic link with error. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Монтирование локальных файловых систем хранилища
declare -f start-fs >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция монтирования локальных файловых систем хранилища." | tee -a "${LOGT}"
else
start-fs
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Mounting file systems store with error. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Публикация файловых систем хранилища посредством NFS
declare -f start-nfs-export >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция публикации файловых систем посредством NFS." | tee -a "${LOGT}"
else
start-nfs-export
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Publication of file systems via NFS storage failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Сборка файловых систем хранилища из ресурсов NFS
declare -f start-nfs-import >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция сборки файловых систем из ресурсов NFS." | tee -a "${LOGT}"
else
# Учитывая то, что нам неизвестно, когда станут доступными все импортируемые ресурсы, закручиваем бесконечный цикл в ожидании полностью безошибочного импорта всех ресурсов
STERR=""
while [ "${STERR}" != "0" ] ; do
# Ждём некоторое время, перед повторением операции (кроме первой итерации)
[ ! -z "${STERR}" ] && sleep 10
start-nfs-import
STERR="${?}"
done
# Проверяем успешность завершения функции импортирования
if [ "${STERR}" -ne "0" ] ; then
echo "${DATE}: The assembly of the storage file system NFS resource failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Сборка файловых систем в единую точку монтирования
declare -f start-mhddfs >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция сборки файловых систем в единой точке монтирования." | tee -a "${LOGT}"
else
start-mhddfs
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Assembly file systems into a single mount point failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
return ${?}
}
# Определяем функцию остановки хранилища
function stop() {
echo | tee -a "${LOGT}"
echo "${DATE}: Running stop procedure storage." | tee -a "${LOGT}"
echo | tee -a "${LOGT}"
# Разборка файловых систем, смонтированных в единую точку монтирования
declare -f stop-mhddfs >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция разборки файловых систем в единой точке монтирования." | tee -a "${LOGT}"
else
stop-mhddfs
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Disassembly of file systems mounted in a single mount point failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Разборка файловых систем хранилища из ресурсов NFS
declare -f stop-nfs-import >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция разборки файловых систем из ресурсов NFS." | tee -a "${LOGT}"
else
stop-nfs-import
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Disassembly of the storage file system NFS resource failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Отключение публикации файловых систем хранилища посредством NFS
declare -f stop-nfs-export >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция публикации файловых систем посредством NFS." | tee -a "${LOGT}"
else
stop-nfs-export
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Disabling Publishing file systems via NFS storage failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Демонтирование файловых систем хранилища
declare -f stop-fs >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция демонтирования локальных файловых систем хранилища." | tee -a "${LOGT}"
else
stop-fs
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Unmounting file systems store with error. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Деактивация групп томов LVM
declare -f stop-lvm >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция деактивации групп томов LVM хранилища." | tee -a "${LOGT}"
else
stop-lvm
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Deactivating LVM volume groups failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Остановка массивов
declare -f stop-mdadm >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция деактивации RAID." | tee -a "${LOGT}"
else
stop-mdadm
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Stop array failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
return ${?}
}
# Блок выбора варианта исполнения скрипта
case "$1" in
start)
start-init
# В случае ошибочного завершения сборки хранилища разбираем его
if [ "${?}" -ne "0" ] ; then
stop-init
fi
;;
stop)
stop-init
;;
check)
echo | tee -a "${LOGT}"
echo "${DATE}: Running the test procedure component." | tee -a "${LOGT}"
echo | tee -a "${LOGT}"
check-mdadm
check-lvm
check-fs
check-nfs-export
check-nfs-import
check-mhddfs
;;
*)
echo "Usage $0 {start|stop|check}" >&2
exit 1
;;
esac
exit $?
### BEGIN INIT INFO
# Provides: storage-control
# Required-Start: $local_fs $syslog $network
# Required-Stop: $local_fs $syslog $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Script management of storage
# Description: Script management of storage
### END INIT INFO
# Подключаем первичный конфигурационный файл
source "/usr/local/etc/default/storage-custom"
[ ${?} -ne 0 ] && { echo "Fatal error: missing default custom storage configuration file. Operation aborted."; exit 1; }
# Принимаем в переменные с "говорящими" именами входящие аргументы
INSTANCE=${0}
OPERATION=${1}
TARGET=${2}
DATE=`date +"%Y-%m-%d %H:%M:%S"`
# Перебираем в цикле все объекты (по маске) в целевой директории динамически подключаемых описаний функций
cd "${FNCDIR}"
for OBJECT in *\.fnc ; do
# Включаем в тело скрипта текст обнаруженных фрагментов описаний функций
[ -f "${FNCDIR}/${OBJECT}" ] && source "${FNCDIR}/${OBJECT}"
done
# Создаём директорию для файлов блокировки
mkdir -p "${LOCK}"
# Определяем функцию предварительной инициализации конфигурационных файлов хранилища
function start-init() {
# Создаём временный файл журнала событий
LOGT=$(mktemp /tmp/storage-start.log.XXXXXXXX)
# Создаём временный файл глобальной конфигурации
CNF=$(mktemp /tmp/storage.cnf.XXXXXXXX)
cd "${CNFDIR}"
for OBJECT in *\.cnf ; do
# Включаем в тело временного глобального файла все обнаруженные фрагменты конфигураций
[ -f "${CNFDIR}/${OBJECT}" ] && cat "${CNFDIR}/${OBJECT}" >> "${CNF}"
done
DATE=`date +"%Y-%m-%d %H:%M:%S"`
start
# Если запуск системы хранилища завершился неудачно, то отсылаем отчёт и "разбираем" его
if [ "${?}" -ne "0" ] ; then
send-report "Storage assembling error!" "$(cat ${LOGT})"
return 1
fi
# Включаем временный журнал событий в основной и удаляем временный
cat "${LOGT}" >> "${LOG}"
rm --force "${LOGT}"
# Удаляем временный файл глобальной конфигурации
rm --force "${CNF}"
return ${?}
}
# Определяем функцию предварительной инициализации конфигурационных файлов хранилища
function stop-init() {
# Создаём временный файл журнала событий
LOGT=$(mktemp /tmp/storage-stop.log.XXXXXXXX)
# Создаём временный файл глобальной конфигурации
CNF=$(mktemp /tmp/storage.cnf.XXXXXXXX)
cd "${CNFDIR}"
for OBJECT in *\.cnf ; do
# Включаем в тело временного глобального файла все обнаруженные фрагменты конфигураций
[ -f "${CNFDIR}/${OBJECT}" ] && cat "${CNFDIR}/${OBJECT}" >> "${CNF}"
done
DATE=`date +"%Y-%m-%d %H:%M:%S"`
stop
# Если остановка системы хранилища завершилась неудачно, то отсылаем отчёт
if [ "${?}" -ne "0" ] ; then
send-report "Storage disassembling error!" "$(cat ${LOGT})"
fi
# Включаем временный журнал событий в основной и удаляем временный
cat "${LOGT}" >> "${LOG}"
rm --force "${LOGT}"
# Удаляем временный файл глобальной конфигурации
rm --force "${CNF}"
return ${?}
}
# Описываем функцию формирования опций хранилища и непосредственного её запуска
function start() {
echo | tee -a "${LOGT}"
echo "${DATE}: Procedure is running store is initialized." | tee -a "${LOGT}"
echo | tee -a "${LOGT}"
# Сборка массивов
declare -f start-mdadm >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция сборки RAID." | tee -a "${LOGT}"
else
start-mdadm
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Assembly arrays failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Активация групп томов LVM
declare -f start-lvm >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция сборки LVM." | tee -a "${LOGT}"
else
start-lvm
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Activating LVM volume groups failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Создание символической ссылки на заданный раздел целевого диска
declare -f make-symlink >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция создания символической ссылки на раздел целевого диска." | tee -a "${LOGT}"
else
make-symlink
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Create a symbolic link with error. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Монтирование локальных файловых систем хранилища
declare -f start-fs >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция монтирования локальных файловых систем хранилища." | tee -a "${LOGT}"
else
start-fs
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Mounting file systems store with error. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Публикация файловых систем хранилища посредством NFS
declare -f start-nfs-export >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция публикации файловых систем посредством NFS." | tee -a "${LOGT}"
else
start-nfs-export
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Publication of file systems via NFS storage failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Сборка файловых систем хранилища из ресурсов NFS
declare -f start-nfs-import >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция сборки файловых систем из ресурсов NFS." | tee -a "${LOGT}"
else
# Учитывая то, что нам неизвестно, когда станут доступными все импортируемые ресурсы, закручиваем бесконечный цикл в ожидании полностью безошибочного импорта всех ресурсов
STERR=""
while [ "${STERR}" != "0" ] ; do
# Ждём некоторое время, перед повторением операции (кроме первой итерации)
[ ! -z "${STERR}" ] && sleep 10
start-nfs-import
STERR="${?}"
done
# Проверяем успешность завершения функции импортирования
if [ "${STERR}" -ne "0" ] ; then
echo "${DATE}: The assembly of the storage file system NFS resource failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Сборка файловых систем в единую точку монтирования
declare -f start-mhddfs >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция сборки файловых систем в единой точке монтирования." | tee -a "${LOGT}"
else
start-mhddfs
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Assembly file systems into a single mount point failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
return ${?}
}
# Определяем функцию остановки хранилища
function stop() {
echo | tee -a "${LOGT}"
echo "${DATE}: Running stop procedure storage." | tee -a "${LOGT}"
echo | tee -a "${LOGT}"
# Разборка файловых систем, смонтированных в единую точку монтирования
declare -f stop-mhddfs >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция разборки файловых систем в единой точке монтирования." | tee -a "${LOGT}"
else
stop-mhddfs
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Disassembly of file systems mounted in a single mount point failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Разборка файловых систем хранилища из ресурсов NFS
declare -f stop-nfs-import >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция разборки файловых систем из ресурсов NFS." | tee -a "${LOGT}"
else
stop-nfs-import
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Disassembly of the storage file system NFS resource failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Отключение публикации файловых систем хранилища посредством NFS
declare -f stop-nfs-export >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция публикации файловых систем посредством NFS." | tee -a "${LOGT}"
else
stop-nfs-export
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Disabling Publishing file systems via NFS storage failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Демонтирование файловых систем хранилища
declare -f stop-fs >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция демонтирования локальных файловых систем хранилища." | tee -a "${LOGT}"
else
stop-fs
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Unmounting file systems store with error. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Деактивация групп томов LVM
declare -f stop-lvm >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция деактивации групп томов LVM хранилища." | tee -a "${LOGT}"
else
stop-lvm
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Deactivating LVM volume groups failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Остановка массивов
declare -f stop-mdadm >/dev/null
if [ ${?} -ne 0 ]; then
echo "Notice. Не определена функция деактивации RAID." | tee -a "${LOGT}"
else
stop-mdadm
if [ "${?}" -ne "0" ] ; then
echo "${DATE}: Stop array failed. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
return ${?}
}
# Блок выбора варианта исполнения скрипта
case "$1" in
start)
start-init
# В случае ошибочного завершения сборки хранилища разбираем его
if [ "${?}" -ne "0" ] ; then
stop-init
fi
;;
stop)
stop-init
;;
check)
echo | tee -a "${LOGT}"
echo "${DATE}: Running the test procedure component." | tee -a "${LOGT}"
echo | tee -a "${LOGT}"
check-mdadm
check-lvm
check-fs
check-nfs-export
check-nfs-import
check-mhddfs
;;
*)
echo "Usage $0 {start|stop|check}" >&2
exit 1
;;
esac
exit $?
Прописываем наш скрипт для нужных уровней исполнения в системе:
# update-rc.d storage-control start 10 2 3 4 5 . stop 10 0 1 6 .
Для того, что бы не получать сильно разросшиеся файлы журналов, настроим их ротацию.
Устанавливаем приложение ротации текстовых файлов с одновременным их сжатием:
# aptitude install logrotate gzip
Создаем конфигурационный файл ротации журнальных файлов:
# mkdir -p /etc/logrotate.d
# vi /etc/logrotate.d/storage
# vi /etc/logrotate.d/storage
# шаблон указывающий объекты подлежащие "ротации"
/var/log/storage/*.log {
# размер журнального файла, после которого он обрабатывается утилитой
size 10M
# отсутствие файла не вызывает ошибку
missingok
# количество хранимых отработанных резервных копий
rotate 10
# указание сжимать отрабатываемые резервные копии
compress
# указание не сжимать первую резервную копию, делать это при повторном проходе
delaycompress
# указание не отрабатывать пустые файлы
notifempty
# добавлять к наименованию файла резервной копии даты в формате "-YYYYMMDD"
dateext
# задать права доступа, владельца и группу создаваемого журнального файла
create 640 root root
}
/var/log/storage/*.log {
# размер журнального файла, после которого он обрабатывается утилитой
size 10M
# отсутствие файла не вызывает ошибку
missingok
# количество хранимых отработанных резервных копий
rotate 10
# указание сжимать отрабатываемые резервные копии
compress
# указание не сжимать первую резервную копию, делать это при повторном проходе
delaycompress
# указание не отрабатывать пустые файлы
notifempty
# добавлять к наименованию файла резервной копии даты в формате "-YYYYMMDD"
dateext
# задать права доступа, владельца и группу создаваемого журнального файла
create 640 root root
}
Проверяем корректность конфигурационного файла:
# logrotate -d /etc/logrotate.d/storage
Переход к описанию вспомогательных функций.
Misc ( Вспомогательные функции. )
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
Пример опций конфигурационного файла хранилища:
Фрагмент кода со вспомогательными функциями:
Переход к настройке зеркалирования блочных устройств в рамках локальной системы.
Здесь размещено описание одного из функциональных блоков инструкций управления самодельным сетевым хранилищем на сочетании технологий "MDADM + LVM + XFS + NFS + MHDDFS" через простейшие BASH-скрипты. Отдельно неприменимо.
Пример опций конфигурационного файла хранилища:
# vi /usr/local/etc/storage/cnf.d/misc.cnf
....
# Перечень электронных почтовых адресов операторов подсистемы виртуализации
email=admin@example.com operator@example.net
....
# Перечень электронных почтовых адресов операторов подсистемы виртуализации
email=admin@example.com operator@example.net
....
Фрагмент кода со вспомогательными функциями:
# vi /usr/local/etc/storage/fnc.d/misc.fnc
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Определяем функцию уведомления администратора о нештатном развитии ситуации
# Example use: send-report "Subject" "Body"
function send-report() {
local MSUBJECT=$1
local MREPORT=$2
# Получаем перечень электронных почтовых адресов операторов подсистемы виртуализации
EMAIL=`grep --ignore-case "^email=" "${CNF}" | awk -F = '{print $2}'`
# Посылаем электронное письмо
echo -e "Content-Type: text/plain; charset="utf-8"\nSubject: Storage warning: ${HOSTNAME}: ${MSUBJECT}\n${DATE}.\nHost: ${HOSTNAME}.\nSubject: ${MSUBJECT}.\n${MREPORT}" | sendmail -F${HOSTNAME} ${EMAIL}
return $?
}
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Определяем функцию уведомления администратора о нештатном развитии ситуации
# Example use: send-report "Subject" "Body"
function send-report() {
local MSUBJECT=$1
local MREPORT=$2
# Получаем перечень электронных почтовых адресов операторов подсистемы виртуализации
EMAIL=`grep --ignore-case "^email=" "${CNF}" | awk -F = '{print $2}'`
# Посылаем электронное письмо
echo -e "Content-Type: text/plain; charset="utf-8"\nSubject: Storage warning: ${HOSTNAME}: ${MSUBJECT}\n${DATE}.\nHost: ${HOSTNAME}.\nSubject: ${MSUBJECT}.\n${MREPORT}" | sendmail -F${HOSTNAME} ${EMAIL}
return $?
}
Переход к настройке зеркалирования блочных устройств в рамках локальной системы.
MDADM ( Зеркалирование блочных устройств в рамках локальной системы. )
18 сентября 2012 (обновлено 17 июля 2019)
Эта публикация отнесена в архив. Она неактуальна.
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
Сохранность данных на диске можно повысить за счёт использования второго диска, работающего как зеркало первого. Этот режим называется RAID первого уровня (Redundant Array of Inexpensive Disks - избыточный массив недорогих дисков). Коротко - RAID-1.
Устанавливаем пакет приложений MDADM:
Первым делом отключим автоматический запуск утилит "mdadm" - в дальнейшем будем ими управлять самодельными скриптами, ориентируясь на UUID готовых к эксплуатации массивов.
Надо понимать, что есть модуль ядра "md", обеспечивающий работу с MD-устройствами, а есть пакет утилит "mdadm", который позволяет управлять и мониторить состояние массива, работающего на модуле "md". В частности, утилиты часто запускаются в фоновом режиме для контроля состояния массивов и уведомления по почте. Кстати, файл "mdadm.conf" предназначен именно для работы этого пакета и не нужен для работы модуля "md".
Ничего нам не мешает отдать процедуры сборки массивов, последующего активирования томов LVM, монтирования файловых систем и дальнейшей публикации каталогов соответствующим подсистемам, но мне хотелось бы свести управление хозяйством в единую точку, со своим набором конфигурационных файлов, что позволит упростить перенос и масштабирование схемы. Для этого отключаем всю автоматику.
Прежде всего останавливаем автоматически запущенные при инсталляции сервисы:
Следует иметь в виду, что простая правка параметров вышеприведённого конфигурационного файла не окажет ожидаемого влияния на работу всех компонентов подсистемы "mdadm"; у таковой имеется как набор утилит, которые могут читать информацию их этого конфигурационного файла, так и модули ядра, параметры к которым нужно сформировать отдельно. Кроме того, упомянутые модули ядра подсистемы mdadm загружаются ещё и в составе образа "initramfs" (специальным образом подготовленный "Initial RAM Disk" - мини операционная система, загружающая целевую) изменить конфигурацию которой можно только обновив её содержимое путём создания нового образа с учётом произведённых изменений. Применения изменений можно добиться перезапустив конфигурацию пакета и в процессе диалога подтвердить выбранные значения параметров; во время переконфигурирования пакета все необходимые изменения будут применены, так же и обновлением "initramfs":
В качестве последнего шага можно запретить загрузчику операционной системы (в нашем случае GRUB2) инициировать модуль "md" при обнаружении блочных устройств с мета-информацией RAID. Надо понимать, что это не позволит загрузить систему с устройств, объединённых в RAID-массив, но в нашем случае этого и не требуется. Зато, таким образом мы полностью исключим на этапе загрузке операционной системы возможность преждевременной автоматической инициализации наших массивов (и загрузка системы ускоряется за счёт пропуска операций сканирования всех блочных устройств):
После всех вышеприведённых манипуляций можно считать, что мы искоренили автоматическую инициализацию mdadm на всех уровнях загрузки: на уровне GRUB2, на уровне "Initial RAM Disk" и на уровне "init.rc".
При загрузке операционной системы модуль DM (Device Manager) по умолчанию автоматически сканирует все доступные блочные устройства на предмет наличия в них метаданных, свидетельствующих о том, что они части массива. В случае обнаружения - массив собирается автоматически.
Если модуль не загрузился, это можно сделать вручную:
Проверяем, загрузились ли нужные нам модули:
Попробуем подготовить HDD (или любые другие блочные устройства) для ввода их в RAID-1.
Проверяем, имеется ли на блочном устройстве "мета-данные":
Если на HDD имеются "мета-данные" от предыдущих экспериментов - затираем их (или делаем это из профилактических соображений):
Даём команду операционной системе перечитать обновлённую разметку HDD:
В команде "dd" выше, в особо злостных случаях, когда на диске предварительно порезвились и по натыкали массу вариантов разметки (вложенные контейнеры, многослойные загрузчики, сжатые файловые системы и тому подобное), параметр "count" можно установить в "1000", что бы перезаписать не один килобайт информации в начале дискового устройства, а мегабайт, например.
Для начала нужно подготовить разделы, которые хочется включить в RAID, присвоив им тип "fd (Linux RAID Autodetect)". Это не обязательно, но желательно. Для создания "программного массива" желательно использовать не весь аппаратный диск целиком, а лишь логические диски (желательно - одинакового объёма, в противном случае размер массива будет рассчитываться исходя из размера диска с минимальным объёмом), понятно, что желательно не растягивать раздел на всё доступное пространство, а оставить пару-пятёрку-десятку секторов свободными на возможность люфта объёма дисков:
Программный RAID в "Linux" ведёт журнал изменений блоков данных "bitmap". Он довольно активно используется и в случае медленного HDD ещё более замедляет его работу. Этот журнал по умолчанию сохраняется внутри самого блочного устройства RAID-а, но (режим "internal"), но можно вынести в файл на отдельный диск (системный в моём случае). Журнал вещь вспомогательная, массив восстанавливается и без "bitmap"-а, но медленнее.
Создаём директорию для выноса в неё журналов MDADM:
Первичную сборку массива осуществим ориентируясь на символические имена дисков, что автоматически создаются в каталоге устройств /dev. После будем ориентироваться на идентификаторы UUID.
Содаём массив из двух дисков:
Как вариант создаём вначале массив для двух дисков из одного диска, с резервированием места для подключения второго впоследствии:
Подключаем диск в массив, на место зарезервированное для него ранее:
Если что не так - массив можно деактивировать:
Наблюдаем за процессом зеркалирования:
Следующим набором команд можно выяснить состояние RAID-массивов и компонентов таковых:
После создания RAID-массивов можно ознакомиться с перечнем всех доступных в системе блочных устройств, как физических, так и виртуальных:
Иногда, например после некорректного завершения работы, синхронизация блочных устройств массива может быть отложена, переведена в неактивное состояние:
В таком случае синхронизацию нужно активизировать и она продолжиться с места остановки:
Переход к настройке автоматизации зеркалирования блочных устройств в рамках локальной системы.
Сохранность данных на диске можно повысить за счёт использования второго диска, работающего как зеркало первого. Этот режим называется RAID первого уровня (Redundant Array of Inexpensive Disks - избыточный массив недорогих дисков). Коротко - RAID-1.
Отмечу, что большую надёжность (но не производительность) даёт зеркалирование по сети посредством DRDB.
Устанавливаем пакет приложений MDADM:
# aptitude install mdadm
Первым делом отключим автоматический запуск утилит "mdadm" - в дальнейшем будем ими управлять самодельными скриптами, ориентируясь на UUID готовых к эксплуатации массивов.
Надо понимать, что есть модуль ядра "md", обеспечивающий работу с MD-устройствами, а есть пакет утилит "mdadm", который позволяет управлять и мониторить состояние массива, работающего на модуле "md". В частности, утилиты часто запускаются в фоновом режиме для контроля состояния массивов и уведомления по почте. Кстати, файл "mdadm.conf" предназначен именно для работы этого пакета и не нужен для работы модуля "md".
Ничего нам не мешает отдать процедуры сборки массивов, последующего активирования томов LVM, монтирования файловых систем и дальнейшей публикации каталогов соответствующим подсистемам, но мне хотелось бы свести управление хозяйством в единую точку, со своим набором конфигурационных файлов, что позволит упростить перенос и масштабирование схемы. Для этого отключаем всю автоматику.
Прежде всего останавливаем автоматически запущенные при инсталляции сервисы:
# /etc/init.d/mdadm stop
# /etc/init.d/mdadm-raid stop
# /etc/init.d/mdadm-raid stop
# vi /etc/default/mdadm
....
INITRDSTART='none'
AUTOSTART=false
AUTOCHECK=false
START_DAEMON=false
....
INITRDSTART='none'
AUTOSTART=false
AUTOCHECK=false
START_DAEMON=false
....
Следует иметь в виду, что простая правка параметров вышеприведённого конфигурационного файла не окажет ожидаемого влияния на работу всех компонентов подсистемы "mdadm"; у таковой имеется как набор утилит, которые могут читать информацию их этого конфигурационного файла, так и модули ядра, параметры к которым нужно сформировать отдельно. Кроме того, упомянутые модули ядра подсистемы mdadm загружаются ещё и в составе образа "initramfs" (специальным образом подготовленный "Initial RAM Disk" - мини операционная система, загружающая целевую) изменить конфигурацию которой можно только обновив её содержимое путём создания нового образа с учётом произведённых изменений. Применения изменений можно добиться перезапустив конфигурацию пакета и в процессе диалога подтвердить выбранные значения параметров; во время переконфигурирования пакета все необходимые изменения будут применены, так же и обновлением "initramfs":
# dpkg-reconfigure mdadm
....
Generating array device nodes... done.
update-initramfs: Generating /boot/initrd.img-2.6.32-5-amd64
W: mdadm: /etc/mdadm/mdadm.conf defines no arrays.
Generating array device nodes... done.
update-initramfs: Generating /boot/initrd.img-2.6.32-5-amd64
W: mdadm: /etc/mdadm/mdadm.conf defines no arrays.
В качестве последнего шага можно запретить загрузчику операционной системы (в нашем случае GRUB2) инициировать модуль "md" при обнаружении блочных устройств с мета-информацией RAID. Надо понимать, что это не позволит загрузить систему с устройств, объединённых в RAID-массив, но в нашем случае этого и не требуется. Зато, таким образом мы полностью исключим на этапе загрузке операционной системы возможность преждевременной автоматической инициализации наших массивов (и загрузка системы ускоряется за счёт пропуска операций сканирования всех блочных устройств):
# vi /etc/default
....
GRUB_CMDLINE_LINUX_DEFAULT="quiet raid=noautodetect"
....
GRUB_CMDLINE_LINUX_DEFAULT="quiet raid=noautodetect"
....
# update-grub
После всех вышеприведённых манипуляций можно считать, что мы искоренили автоматическую инициализацию mdadm на всех уровнях загрузки: на уровне GRUB2, на уровне "Initial RAM Disk" и на уровне "init.rc".
При загрузке операционной системы модуль DM (Device Manager) по умолчанию автоматически сканирует все доступные блочные устройства на предмет наличия в них метаданных, свидетельствующих о том, что они части массива. В случае обнаружения - массив собирается автоматически.
Если модуль не загрузился, это можно сделать вручную:
# modprobe md
# modprobe raid1
# modprobe raid1
Проверяем, загрузились ли нужные нам модули:
# lsmod | grep raid
Попробуем подготовить HDD (или любые другие блочные устройства) для ввода их в RAID-1.
Проверяем, имеется ли на блочном устройстве "мета-данные":
# mdadm --misc --query /dev/sdb
/dev/sdb: is not an md array
Если на HDD имеются "мета-данные" от предыдущих экспериментов - затираем их (или делаем это из профилактических соображений):
# dd if=/dev/zero of=/dev/sdb bs=1M count=10
Даём команду операционной системе перечитать обновлённую разметку HDD:
# blockdev --rereadpt /dev/sdb
В команде "dd" выше, в особо злостных случаях, когда на диске предварительно порезвились и по натыкали массу вариантов разметки (вложенные контейнеры, многослойные загрузчики, сжатые файловые системы и тому подобное), параметр "count" можно установить в "1000", что бы перезаписать не один килобайт информации в начале дискового устройства, а мегабайт, например.
Для начала нужно подготовить разделы, которые хочется включить в RAID, присвоив им тип "fd (Linux RAID Autodetect)". Это не обязательно, но желательно. Для создания "программного массива" желательно использовать не весь аппаратный диск целиком, а лишь логические диски (желательно - одинакового объёма, в противном случае размер массива будет рассчитываться исходя из размера диска с минимальным объёмом), понятно, что желательно не растягивать раздел на всё доступное пространство, а оставить пару-пятёрку-десятку секторов свободными на возможность люфта объёма дисков:
# fdisk -cu /dev/sdb
Программный RAID в "Linux" ведёт журнал изменений блоков данных "bitmap". Он довольно активно используется и в случае медленного HDD ещё более замедляет его работу. Этот журнал по умолчанию сохраняется внутри самого блочного устройства RAID-а, но (режим "internal"), но можно вынести в файл на отдельный диск (системный в моём случае). Журнал вещь вспомогательная, массив восстанавливается и без "bitmap"-а, но медленнее.
Создаём директорию для выноса в неё журналов MDADM:
# mkdir -p /mnt/journal/mdadm
Первичную сборку массива осуществим ориентируясь на символические имена дисков, что автоматически создаются в каталоге устройств /dev. После будем ориентироваться на идентификаторы UUID.
Содаём массив из двух дисков:
# mdadm --create /dev/md0 --level=raid1 --chunk=512 --bitmap=/mnt/journal/mdadm/md0-bitmap --raid-devices=2 /dev/sdb1 /dev/sdc1
Как вариант создаём вначале массив для двух дисков из одного диска, с резервированием места для подключения второго впоследствии:
# mdadm --create /dev/md1 --level=raid1 --chunk=512 --bitmap=/mnt/journal/mdadm/md1-bitmap --raid-devices=2 /dev/sdd1 missing
Подключаем диск в массив, на место зарезервированное для него ранее:
# mdadm /dev/md1 --add /dev/sde1
Если что не так - массив можно деактивировать:
# mdadm --misc --stop /dev/md0
Наблюдаем за процессом зеркалирования:
# cat /proc/mdstat
Personalities : [raid1]
md0 : active raid1 sdc1[1] sdb1[0]
976759343 blocks super 1.2 [2/2] [UU]
[>.....] resync = 0.9% (9579008/976759343) finish=177.6min speed=90732K/sec
bitmap: 462/466 pages [1848KB], 1024KB chunk, file: /var/spool/mdadm/md0-bitmap
unused devices: <none>
md0 : active raid1 sdc1[1] sdb1[0]
976759343 blocks super 1.2 [2/2] [UU]
[>.....] resync = 0.9% (9579008/976759343) finish=177.6min speed=90732K/sec
bitmap: 462/466 pages [1848KB], 1024KB chunk, file: /var/spool/mdadm/md0-bitmap
unused devices: <none>
Следующим набором команд можно выяснить состояние RAID-массивов и компонентов таковых:
# mdadm --detail --scan --verbose
# mdadm --misc --query --examine /dev/sdb1
# mdadm --misc --query --detail /dev/md0
# mdadm --misc --query --examine /dev/sdb1
# mdadm --misc --query --detail /dev/md0
После создания RAID-массивов можно ознакомиться с перечнем всех доступных в системе блочных устройств, как физических, так и виртуальных:
# blkid
Иногда, например после некорректного завершения работы, синхронизация блочных устройств массива может быть отложена, переведена в неактивное состояние:
# cat /proc/mdstat
Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
md0 : active (auto-read-only) raid1 sdb1[0] sdc1[1]
976759343 blocks super 1.2 [2/2] [UU]
resync=PENDING
md0 : active (auto-read-only) raid1 sdb1[0] sdc1[1]
976759343 blocks super 1.2 [2/2] [UU]
resync=PENDING
В таком случае синхронизацию нужно активизировать и она продолжиться с места остановки:
# echo idle > /sys/block/md0/md/sync_action
Важно иметь в виду, что при обнаружении любой (!) ошибки при работе с RAID-1, RAID-4, RAID-5, RAID-6 и RAID-10 драйвер "mdadm" отключает устройство (помечает его как сбойное флагом "faulty") и продолжает работу на оставшихся. Если есть запасное (spare) устройство, то оно вводится в эксплуатацию вместо отключённого.
Переход к настройке автоматизации зеркалирования блочных устройств в рамках локальной системы.
Автоматизация MDADM ( Автоматизация зеркалирования блочных устройств в рамках локальной системы. )
18 сентября 2012 (обновлено 17 июля 2019)
Эта публикация отнесена в архив. Она неактуальна.
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
Обновим конфигурационный файл с перечнем и описанием характеристик ресурсов, готовых к инициализации и использованию.
Содержимое конфигурационного файла сформируем из вывода команды сканирования текущего состояния подсистемы, сразу после первичной сборки или изменения таковой. Примерный вывод:
Также все необходимые данные можно вычленить из вывода утилиты "blkid":
Неудобство лишь в том, что неясно, какой массив должен быть создан из соответствующих дисков. Собственно, для того, что бы зафиксировать эти отношения мы и напишем нижеследующий конфигурационный файл. Обратите внимание на отличие в форматах отображения UUID, где то символ ":", а где то символ "-". Думаю, лучше остановится на символе "-".
Берём из вывода только необходимое и формируем строки, которыми удобно манипулировать в скрипте. Для работы с уже существующим массивом нам не нужно ничего, кроме его символического имени, UUID-а и адреса файла содержащего "битовую таблицу". Например:
Фрагмент кода автоматизации функционала MDADM подсистемы хранения:
Переход к настройке сведения блочных устройств в единое в рамках локальной системы.
Здесь размещено описание одного из функциональных блоков инструкций управления самодельным сетевым хранилищем на сочетании технологий "MDADM + LVM + XFS + NFS + MHDDFS" через простейшие BASH-скрипты. Отдельно неприменимо.
Обновим конфигурационный файл с перечнем и описанием характеристик ресурсов, готовых к инициализации и использованию.
Содержимое конфигурационного файла сформируем из вывода команды сканирования текущего состояния подсистемы, сразу после первичной сборки или изменения таковой. Примерный вывод:
# mdadm --detail --scan
ARRAY /dev/md0 metadata=1.2 bitmap=/mnt/journal/mdadm/md0-bitmap name=node1:0 UUID=0b03c852:5d850f6e:7635830d:1f8e0e8d
ARRAY /dev/md1 metadata=1.2 bitmap=/mnt/journal/mdadm/md1-bitmap name=node1:1 UUID=3786fb6e:b5746f9e:3bb90781:59bdd3a8
ARRAY /dev/md2 metadata=1.2 bitmap=/mnt/journal/mdadm/md2-bitmap name=node1:2 UUID=067b70c7:c9fa66b7:7fbb9927:39f4a423
ARRAY /dev/md1 metadata=1.2 bitmap=/mnt/journal/mdadm/md1-bitmap name=node1:1 UUID=3786fb6e:b5746f9e:3bb90781:59bdd3a8
ARRAY /dev/md2 metadata=1.2 bitmap=/mnt/journal/mdadm/md2-bitmap name=node1:2 UUID=067b70c7:c9fa66b7:7fbb9927:39f4a423
Также все необходимые данные можно вычленить из вывода утилиты "blkid":
# blkid
/dev/sdb1: UUID="ed0c7d76-5d03-a1c1-6bda-02e4ae6a6d0c" TYPE="linux_raid_member"
/dev/sdc1: UUID="ed0c7d76-5d03-a1c1-6bda-02e4ae6a6d0c" TYPE="linux_raid_member"
/dev/sdd1: UUID="29e32e2d-07b5-a4a0-f0dd-b4d123eb9099" TYPE="linux_raid_member"
/dev/sde1: UUID="29e32e2d-07b5-a4a0-f0dd-b4d123eb9099" TYPE="linux_raid_member"
/dev/sdf1: UUID="1d7b49bc-163f-96f5-c68d-f65279db4da0" TYPE="linux_raid_member"
/dev/sdg1: UUID="1d7b49bc-163f-96f5-c68d-f65279db4da0" TYPE="linux_raid_member"
/dev/sdc1: UUID="ed0c7d76-5d03-a1c1-6bda-02e4ae6a6d0c" TYPE="linux_raid_member"
/dev/sdd1: UUID="29e32e2d-07b5-a4a0-f0dd-b4d123eb9099" TYPE="linux_raid_member"
/dev/sde1: UUID="29e32e2d-07b5-a4a0-f0dd-b4d123eb9099" TYPE="linux_raid_member"
/dev/sdf1: UUID="1d7b49bc-163f-96f5-c68d-f65279db4da0" TYPE="linux_raid_member"
/dev/sdg1: UUID="1d7b49bc-163f-96f5-c68d-f65279db4da0" TYPE="linux_raid_member"
Неудобство лишь в том, что неясно, какой массив должен быть создан из соответствующих дисков. Собственно, для того, что бы зафиксировать эти отношения мы и напишем нижеследующий конфигурационный файл. Обратите внимание на отличие в форматах отображения UUID, где то символ ":", а где то символ "-". Думаю, лучше остановится на символе "-".
Берём из вывода только необходимое и формируем строки, которыми удобно манипулировать в скрипте. Для работы с уже существующим массивом нам не нужно ничего, кроме его символического имени, UUID-а и адреса файла содержащего "битовую таблицу". Например:
# vi /usr/local/etc/storage/cnf.d/mdadm.cnf
....
mdadm.module=raid1
mdadm.array=md0
mdadm.array.md0.uuid=ed0c7d76-5d03-a1c1-6bda-02e4ae6a6d0c
mdadm.array.md0.journal=/mnt/journal/mdadm/md0-bitmap
mdadm.array=md1
mdadm.array.md1.uuid=1d7b49bc-163f-96f5-c68d-f65279db4da0
mdadm.array.md1.journal=/mnt/journal/mdadm/md1-bitmap
mdadm.array=md2
mdadm.array.md2.uuid=29e32e2d-07b5-a4a0-f0dd-b4d123eb9099
mdadm.array.md2.journal=/mnt/journal/mdadm/md2-bitmap
....
mdadm.module=raid1
mdadm.array=md0
mdadm.array.md0.uuid=ed0c7d76-5d03-a1c1-6bda-02e4ae6a6d0c
mdadm.array.md0.journal=/mnt/journal/mdadm/md0-bitmap
mdadm.array=md1
mdadm.array.md1.uuid=1d7b49bc-163f-96f5-c68d-f65279db4da0
mdadm.array.md1.journal=/mnt/journal/mdadm/md1-bitmap
mdadm.array=md2
mdadm.array.md2.uuid=29e32e2d-07b5-a4a0-f0dd-b4d123eb9099
mdadm.array.md2.journal=/mnt/journal/mdadm/md2-bitmap
....
Фрагмент кода автоматизации функционала MDADM подсистемы хранения:
# vi /usr/local/etc/storage/fnc.d/mdmdm.fnc
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция запуска (сборки) вручную подготовленных массивов
function start-mdadm() {
# Перебираем все строки именований необходимых модулей
for MODULES in `grep --ignore-case "^mdadm.module=" "${CONF}" | uniq`; do
MODULE=`echo ${MODULES} | awk -F = '{print $2}'`
# Проверяем, загружен ли необходимый для работы с массивами модуль
if [ "`lsmod | grep --count --ignore-case ${MODULE}`" -eq "0" ] ; then
echo "${DATE}: Probe module ${MODULE} load..." >> ${LOG}
modprobe ${MODULE}
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
send-report "MDADM" "Error load module ${MODULE}. Aborting."
echo "${DATE}: Error load module ${MODULE}. Aborting." >> ${LOG}
return 1
fi
fi
done
# Перебираем все строки именований массивов
for ARRAYS in `grep --ignore-case "^mdadm.array=" "${CONF}" | uniq`; do
ARRAY=`echo ${ARRAYS} | awk -F = '{print $2}'`
# Проверяем, не собран ли уже целевой массив
STATE=`cat /proc/mdstat | grep --count --ignore-case "^${ARRAY}[ ]*:[ ]*"`
if [ "${STATE}" -eq "0" ] ; then
# Перебираем все строки параметров массивов
for PARAMS in `grep --ignore-case "^mdadm.array.${ARRAY}." "${CONF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".uuid=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && UUID="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".journal=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && JOURNAL="${BUFFER}"
done
if [ "${UUID}" == "" ] || [ "${JOURNAL}" == "" ] ; then
send-report "MDADM" "Parameters of the target array are not in full. Aborting."
echo "${DATE}: Parameters of the target array are not in full. Aborting." >> "${LOG}"
return 1
fi
# Собираем массив
mdadm --assemble --bitmap="${JOURNAL}" --uuid="${UUID}" "/dev/${ARRAY}" >/dev/null 2>&1
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
send-report "MDADM" "Error assemble array ${ARRAY}. Aborting."
echo "${DATE}: Error assemble array ${ARRAY}. Aborting." >> "${LOG}"
return 1
fi
# Даём системе пару секунд на осознание места нового устройства
sleep 2
# Проверяем успешность отрабатывания операции сборки массива
if [ -b "/dev/${ARRAY}" ] ; then
echo "${DATE}: Array ${ARRAY} successfully built." >> "${LOG}"
else
send-report "MDADM" "Assembling the array ${ARRAY} failed. Aborting."
echo "${DATE}: Assembling the array ${ARRAY} failed. Aborting." >> "${LOG}"
return 1
fi
else
# Проверяем состояние массива на базовом уровне
STATE=`cat /proc/mdstat | grep --count --ignore-case "^${ARRAY}[ ]*:[ ]*active[ ]*"`
if [ "${STATE}" -eq "0" ] ; then
send-report "MDADM" "Array ${ARRAY} collected in error and is not active. Aborting."
echo "${DATE}: Array ${ARRAY} collected in error and is not active. Aborting." >> "${LOG}"
return 1
else
echo "${DATE}: Array ${ARRAY} is already assembled." >> "${LOG}"
fi
fi
done
# Создаём файл блокировки, показывающий на состояние работы подсистемы
touch "${LOCK}/mdadm.lck"
return $?
}
# Функция остановки вручную подготовленных массивов
function stop-mdadm() {
# Перебираем все строки именований массивов
for ARRAYS in `grep --ignore-case "^mdadm.array=" "${CONF}" | uniq` ; do
ARRAY=`echo ${ARRAYS} | awk -F = '{print $2}'`
# Если массив доступен, то останавливаем его
[ -b "/dev/${ARRAY}" ] && mdadm --stop "/dev/${ARRAY}" >/dev/null 2>&1
done
# Удаляем файл блокировки, показывающий на состояние работы подсистемы
rm --force "${LOCK}/mdadm.lck"
return $?
}
# Функция проверки состояния вручную подготовленных массивов
function check-mdadm() {
# Удостоверимся в том, что сборка массивов прошла успешно (в противном случае проверку не запускаем - бессмысленно)
if [ -f "${LOCK}/mdadm.lck" ]; then
echo "check"
fi
return $?
}
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция запуска (сборки) вручную подготовленных массивов
function start-mdadm() {
# Перебираем все строки именований необходимых модулей
for MODULES in `grep --ignore-case "^mdadm.module=" "${CONF}" | uniq`; do
MODULE=`echo ${MODULES} | awk -F = '{print $2}'`
# Проверяем, загружен ли необходимый для работы с массивами модуль
if [ "`lsmod | grep --count --ignore-case ${MODULE}`" -eq "0" ] ; then
echo "${DATE}: Probe module ${MODULE} load..." >> ${LOG}
modprobe ${MODULE}
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
send-report "MDADM" "Error load module ${MODULE}. Aborting."
echo "${DATE}: Error load module ${MODULE}. Aborting." >> ${LOG}
return 1
fi
fi
done
# Перебираем все строки именований массивов
for ARRAYS in `grep --ignore-case "^mdadm.array=" "${CONF}" | uniq`; do
ARRAY=`echo ${ARRAYS} | awk -F = '{print $2}'`
# Проверяем, не собран ли уже целевой массив
STATE=`cat /proc/mdstat | grep --count --ignore-case "^${ARRAY}[ ]*:[ ]*"`
if [ "${STATE}" -eq "0" ] ; then
# Перебираем все строки параметров массивов
for PARAMS in `grep --ignore-case "^mdadm.array.${ARRAY}." "${CONF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".uuid=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && UUID="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".journal=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && JOURNAL="${BUFFER}"
done
if [ "${UUID}" == "" ] || [ "${JOURNAL}" == "" ] ; then
send-report "MDADM" "Parameters of the target array are not in full. Aborting."
echo "${DATE}: Parameters of the target array are not in full. Aborting." >> "${LOG}"
return 1
fi
# Собираем массив
mdadm --assemble --bitmap="${JOURNAL}" --uuid="${UUID}" "/dev/${ARRAY}" >/dev/null 2>&1
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
send-report "MDADM" "Error assemble array ${ARRAY}. Aborting."
echo "${DATE}: Error assemble array ${ARRAY}. Aborting." >> "${LOG}"
return 1
fi
# Даём системе пару секунд на осознание места нового устройства
sleep 2
# Проверяем успешность отрабатывания операции сборки массива
if [ -b "/dev/${ARRAY}" ] ; then
echo "${DATE}: Array ${ARRAY} successfully built." >> "${LOG}"
else
send-report "MDADM" "Assembling the array ${ARRAY} failed. Aborting."
echo "${DATE}: Assembling the array ${ARRAY} failed. Aborting." >> "${LOG}"
return 1
fi
else
# Проверяем состояние массива на базовом уровне
STATE=`cat /proc/mdstat | grep --count --ignore-case "^${ARRAY}[ ]*:[ ]*active[ ]*"`
if [ "${STATE}" -eq "0" ] ; then
send-report "MDADM" "Array ${ARRAY} collected in error and is not active. Aborting."
echo "${DATE}: Array ${ARRAY} collected in error and is not active. Aborting." >> "${LOG}"
return 1
else
echo "${DATE}: Array ${ARRAY} is already assembled." >> "${LOG}"
fi
fi
done
# Создаём файл блокировки, показывающий на состояние работы подсистемы
touch "${LOCK}/mdadm.lck"
return $?
}
# Функция остановки вручную подготовленных массивов
function stop-mdadm() {
# Перебираем все строки именований массивов
for ARRAYS in `grep --ignore-case "^mdadm.array=" "${CONF}" | uniq` ; do
ARRAY=`echo ${ARRAYS} | awk -F = '{print $2}'`
# Если массив доступен, то останавливаем его
[ -b "/dev/${ARRAY}" ] && mdadm --stop "/dev/${ARRAY}" >/dev/null 2>&1
done
# Удаляем файл блокировки, показывающий на состояние работы подсистемы
rm --force "${LOCK}/mdadm.lck"
return $?
}
# Функция проверки состояния вручную подготовленных массивов
function check-mdadm() {
# Удостоверимся в том, что сборка массивов прошла успешно (в противном случае проверку не запускаем - бессмысленно)
if [ -f "${LOCK}/mdadm.lck" ]; then
echo "check"
fi
return $?
}
Переход к настройке сведения блочных устройств в единое в рамках локальной системы.
LVM ( Сведение блочных устройств в единое в рамках локальной системы. )
18 сентября 2012 (обновлено 17 июля 2019)
Эта публикация отнесена в архив. Она неактуальна.
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
Устанавливаем пакет утилит для работы с LVM:
Сканируем систему на предмет обнаружения групп томов LVM и активируем всё обнаруженное:
Перед использование в LVM активируем в роли "физического тома" RAID-массив:
Просматриваем перечень имеющихся "физических томов" LVM:
Создаём группу томов LVM, включая в неё "физические тома" в качестве носителей:
Дополняем группу томов новым "физическим томом":
Явно активируем новую группу томов:
Просматриваем перечень доступных групп томов LVM:
Создаём в группе томов LVM "логический том":
Просматриваем перечень доступных "логических томов" LVM:
При желании расширяем "логический том" LVM на указанный объём:
Переход к настройке автоматизации сведения блочных устройств в единое в рамках локальной системы.
Устанавливаем пакет утилит для работы с LVM:
# aptitude install lvm2
Сканируем систему на предмет обнаружения групп томов LVM и активируем всё обнаруженное:
# vgscan && vgchange -ay
Перед использование в LVM активируем в роли "физического тома" RAID-массив:
# pvcreate /dev/md0
Просматриваем перечень имеющихся "физических томов" LVM:
# pvdisplay
Создаём группу томов LVM, включая в неё "физические тома" в качестве носителей:
# vgcreate vg0 /dev/md0
Дополняем группу томов новым "физическим томом":
# vgextend vg0 /dev/md1
Явно активируем новую группу томов:
# vgchange -a y vg0
Просматриваем перечень доступных групп томов LVM:
# vgdisplay
Создаём в группе томов LVM "логический том":
# lvcreate --size 930G --name lvstorage0 vg0
Просматриваем перечень доступных "логических томов" LVM:
# lvdisplay
При желании расширяем "логический том" LVM на указанный объём:
# lvextend --size +930G /dev/vg0/lvstorage0
Переход к настройке автоматизации сведения блочных устройств в единое в рамках локальной системы.
Автоматизация LVM ( Автоматизация сведения блочных устройств в единое в рамках локальной системы. )
18 сентября 2012 (обновлено 17 июля 2019)
Эта публикация отнесена в архив. Она неактуальна.
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
Создадим конфигурационный файл перечнем и описанием характеристик ресурсов, готовых к инициализации и использованию:
Фрагмент кода автоматизации функционала LVM подсистемы хранения:
Переход к подготовке символических ссылок, используемых при сборке XFS.
Здесь размещено описание одного из функциональных блоков инструкций управления самодельным сетевым хранилищем на сочетании технологий "MDADM + LVM + XFS + NFS + MHDDFS" через простейшие BASH-скрипты. Отдельно неприменимо.
Создадим конфигурационный файл перечнем и описанием характеристик ресурсов, готовых к инициализации и использованию:
# vi /usr/local/etc/storage/cnf.d/lvm.cnf
....
lvm.physical=md0
lvm.physical=md1
lvm.physical=md2
lvm.group=vg0
lvm.group.vg0.volume=lvstorage0
....
lvm.physical=md0
lvm.physical=md1
lvm.physical=md2
lvm.group=vg0
lvm.group.vg0.volume=lvstorage0
....
Фрагмент кода автоматизации функционала LVM подсистемы хранения:
# vi /usr/local/etc/storage/fnc.d/lvm.fnc
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция запуска (сборки) вручную подготовленных LVM-томов
function start-lvm() {
# Сканируем все доступные блочные устройства на предмет обнаружения изменений "мета"-данных
vgscan >/dev/null 2>&1
# Перебираем все строки именований физических томов LVM
for LPHYSICALS in `grep --ignore-case "^lvm.physical=" "${CONF}" | uniq` ; do
LPHYSICAL=`echo ${LPHYSICALS} | awk -F = '{print $2}'`
# Проверяем доступность физических томов LVM
STATE=`pvs --noheadings | grep --count --ignore-case "^[ ]*/dev/${LPHYSICAL}[ ]*"`
if [ ! -b "/dev/${LPHYSICAL}" ] || [ "${STATE}" -eq "0" ] ; then
send-report "LVM" "Physical volume /dev/${LPHYSICAL} is unavailable. Aborting."
echo "${DATE}: Physical volume /dev/${LPHYSICAL} is unavailable. Aborting." >> "${LOG}"
return 1
fi
done
# Перебираем все строки именований групп томов LVM
for LGROUPS in `grep --ignore-case "^lvm.group=" "${CONF}" | uniq` ; do
LGROUP=`echo ${LGROUPS} | awk -F = '{print $2}'`
# Проверяем доступность группы томов LVM
STATE=`vgs --noheadings | grep --count --ignore-case "^[ ]*${LGROUP}[ ]*"`
if [ "${STATE}" -eq "0" ] ; then
send-report "LVM" "Volume group LVM ${LGROUP} is unavailable. Aborting."
echo "${DATE}: Volume group LVM ${LGROUP} is unavailable. Aborting." >> "${LOG}"
return 1
else
# Проверяем, не инициирована ли уже целевая группа томов LVM
if [ ! -d "/dev/${LGROUP}" ] ; then
# Активируем целевую группу томов LVM
vgchange --available y "${LGROUP}" >/dev/null 2>&1
# Проверяем успешность завершения операции
if [ "${?}" -ne "0" ] ; then
send-report "LVM" "Error activating LVM group ${LGROUP}. Aborting."
echo "${DATE}: Error activating LVM group ${LGROUP}. Aborting." >> "${LOG}"
return 1
fi
# Даём системе пару секунд на осознание места нового устройства
sleep 2
# Проверяем успешность отрабатывания операции инициализации группы томов LVM
if [ -d "/dev/${LGROUP}" ] ; then
echo "${DATE}: Volume group LVM ${LGROUP} successfully activated." >> "${LOG}"
else
send-report "LVM" "Activating a volume group LVM ${LGROUP} failed. Aborting."
echo "${DATE}: Activating a volume group LVM ${LGROUP} failed. Aborting." >> "${LOG}"
return 1
fi
else
echo "${DATE}: Volume group LVM ${LGROUP} is already active." >> "${LOG}"
fi
# Перебираем все строки именований логических томов LVM в рамках группы
for LVOLUMES in `grep --ignore-case "^lvm.group.${LGROUP}.volume=" "${CONF}" | uniq` ; do
LVOLUME=`echo ${LVOLUMES} | awk -F = '{print $2}'`
# Проверяем доступность логических томов LVM
STATE=`lvs --noheadings | grep --count --ignore-case "^[ ]*${LVOLUME}[ ]*${LGROUP}[ ]*"`
if [ -b "/dev/${LGROUP}/${LVOLUME}" ] && [ "${STATE}" -ne "0" ] ; then
echo "${DATE}: Logical volume /dev/${LGROUP}/${LVOLUME} active." >> "${LOG}"
else
send-report "LVM" "Logical volume /dev/${LGROUP}/${LVOLUME} unavailable. Aborting."
echo "${DATE}: Logical volume /dev/${LGROUP}/${LVOLUME} unavailable. Aborting." >> "${LOG}"
return 1
fi
done
fi
done
# Создаём файл блокировки, показывающий на состояние работы подсистемы
touch "${LOCK}/lvm.lck"
return $?
}
# Функция остановки вручную подготовленных LVM-томов
function stop-lvm() {
# Перебираем все строки именований групп томов LVM
for LGROUPS in `grep --ignore-case "^lvm.group=" "${CONF}" | uniq` ; do
LGROUP=`echo ${LGROUPS} | awk -F = '{print $2}'`
# Если группа томов LVM доступна, то деактивируем её
[ -d "/dev/${LGROUP}" ] && vgchange --available n "${LGROUP}" >/dev/null 2>&1
done
# Удаляем файл блокировки, показывающий на состояние работы подсистемы
rm --force "${LOCK}/lvm.lck"
return $?
}
# Функция проверки состояния вручную подготовленных LVM-томов
function check-lvm() {
# Удостоверимся в том, что активация томов LVM прошла успешно (в противном случае проверку не запускаем - бессмысленно)
if [ -f "${LOCK}/lvm.lck" ]; then
echo "check"
fi
return $?
}
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция запуска (сборки) вручную подготовленных LVM-томов
function start-lvm() {
# Сканируем все доступные блочные устройства на предмет обнаружения изменений "мета"-данных
vgscan >/dev/null 2>&1
# Перебираем все строки именований физических томов LVM
for LPHYSICALS in `grep --ignore-case "^lvm.physical=" "${CONF}" | uniq` ; do
LPHYSICAL=`echo ${LPHYSICALS} | awk -F = '{print $2}'`
# Проверяем доступность физических томов LVM
STATE=`pvs --noheadings | grep --count --ignore-case "^[ ]*/dev/${LPHYSICAL}[ ]*"`
if [ ! -b "/dev/${LPHYSICAL}" ] || [ "${STATE}" -eq "0" ] ; then
send-report "LVM" "Physical volume /dev/${LPHYSICAL} is unavailable. Aborting."
echo "${DATE}: Physical volume /dev/${LPHYSICAL} is unavailable. Aborting." >> "${LOG}"
return 1
fi
done
# Перебираем все строки именований групп томов LVM
for LGROUPS in `grep --ignore-case "^lvm.group=" "${CONF}" | uniq` ; do
LGROUP=`echo ${LGROUPS} | awk -F = '{print $2}'`
# Проверяем доступность группы томов LVM
STATE=`vgs --noheadings | grep --count --ignore-case "^[ ]*${LGROUP}[ ]*"`
if [ "${STATE}" -eq "0" ] ; then
send-report "LVM" "Volume group LVM ${LGROUP} is unavailable. Aborting."
echo "${DATE}: Volume group LVM ${LGROUP} is unavailable. Aborting." >> "${LOG}"
return 1
else
# Проверяем, не инициирована ли уже целевая группа томов LVM
if [ ! -d "/dev/${LGROUP}" ] ; then
# Активируем целевую группу томов LVM
vgchange --available y "${LGROUP}" >/dev/null 2>&1
# Проверяем успешность завершения операции
if [ "${?}" -ne "0" ] ; then
send-report "LVM" "Error activating LVM group ${LGROUP}. Aborting."
echo "${DATE}: Error activating LVM group ${LGROUP}. Aborting." >> "${LOG}"
return 1
fi
# Даём системе пару секунд на осознание места нового устройства
sleep 2
# Проверяем успешность отрабатывания операции инициализации группы томов LVM
if [ -d "/dev/${LGROUP}" ] ; then
echo "${DATE}: Volume group LVM ${LGROUP} successfully activated." >> "${LOG}"
else
send-report "LVM" "Activating a volume group LVM ${LGROUP} failed. Aborting."
echo "${DATE}: Activating a volume group LVM ${LGROUP} failed. Aborting." >> "${LOG}"
return 1
fi
else
echo "${DATE}: Volume group LVM ${LGROUP} is already active." >> "${LOG}"
fi
# Перебираем все строки именований логических томов LVM в рамках группы
for LVOLUMES in `grep --ignore-case "^lvm.group.${LGROUP}.volume=" "${CONF}" | uniq` ; do
LVOLUME=`echo ${LVOLUMES} | awk -F = '{print $2}'`
# Проверяем доступность логических томов LVM
STATE=`lvs --noheadings | grep --count --ignore-case "^[ ]*${LVOLUME}[ ]*${LGROUP}[ ]*"`
if [ -b "/dev/${LGROUP}/${LVOLUME}" ] && [ "${STATE}" -ne "0" ] ; then
echo "${DATE}: Logical volume /dev/${LGROUP}/${LVOLUME} active." >> "${LOG}"
else
send-report "LVM" "Logical volume /dev/${LGROUP}/${LVOLUME} unavailable. Aborting."
echo "${DATE}: Logical volume /dev/${LGROUP}/${LVOLUME} unavailable. Aborting." >> "${LOG}"
return 1
fi
done
fi
done
# Создаём файл блокировки, показывающий на состояние работы подсистемы
touch "${LOCK}/lvm.lck"
return $?
}
# Функция остановки вручную подготовленных LVM-томов
function stop-lvm() {
# Перебираем все строки именований групп томов LVM
for LGROUPS in `grep --ignore-case "^lvm.group=" "${CONF}" | uniq` ; do
LGROUP=`echo ${LGROUPS} | awk -F = '{print $2}'`
# Если группа томов LVM доступна, то деактивируем её
[ -d "/dev/${LGROUP}" ] && vgchange --available n "${LGROUP}" >/dev/null 2>&1
done
# Удаляем файл блокировки, показывающий на состояние работы подсистемы
rm --force "${LOCK}/lvm.lck"
return $?
}
# Функция проверки состояния вручную подготовленных LVM-томов
function check-lvm() {
# Удостоверимся в том, что активация томов LVM прошла успешно (в противном случае проверку не запускаем - бессмысленно)
if [ -f "${LOCK}/lvm.lck" ]; then
echo "check"
fi
return $?
}
Переход к подготовке символических ссылок, используемых при сборке XFS.
О симлинках ( О символических ссылках, используемых при сборке XFS. )
18 сентября 2012 (обновлено 17 июля 2019)
Эта публикация отнесена в архив. Она неактуальна.
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
В зависимости от очерёдности инициализации дисковых устройств они могут получать разные символические имена. В один раз системный диск может припарковаться на имя "sda", а в другой - на что-нибудь вроде "sdf". А мы желаем выделить на одном из дисков раздел для журналирования файловой системы хранилища; в случае невозможности получения доступа к журналу таковая просто не смонтируется. К сожалению, ориентироваться по UUID не получится, так как это сущность файловой системы, а не физического устройства как такового, так что ничего не остаётся, как перед запуском системы хранения искать целевое устройство по серийному номеру и создавать на один из его разделов символическую ссылку, с которой в дальнейшем и работать.
Для этого нам понадобится создать некий список соответствий серийных номеров целевых дисков, идентификаторов разделов, на которые нам нужно сослаться, и наименований символических ссылок (или ссылки, если более одной не нужно). Нужную информацию можно легко выяснить просмотрев вывод утилит "dmesg", "ls -l /dev/sd*" и "hdparm -i /dev/sdX". Автоматизацию процесса создания ссылки проработаем в одной из следующих заметок, а пока установим требуемое программное обеспечение:
Для простоты договоримся о том, что мы будем использовать символическое имя "sd.sys.xfsjournal", в дальнейшем именно на него мы будем ориентировать драйвер XFS, чтобы тот размещал на указанном разделе журнал транзакций.
Замечу, к вышеприведённому, что обращаться с помощью "мягкой" ссылки в дальнейшем мы будем не к блочному устройству вроде "sda", например, а к разделу на указанном устройстве, например: "sda7". То есть, сопоставление будет вида: "sda7 => sd.sys.xfsjournal".
Учитывая то, что содержимое директории "/dev" с загрузкой операционной системы полностью обновляется, можно не опасаться загромождения её устаревшими ссылками; в связи с указанной особенностью директории очевидным является необходимость вызывать скрипт создания "жёстких" ссылок на устройства после каждой загрузки системы перед началом работы.
Переход к созданию на блочных устройствах LVM файловой системы XFS.
В зависимости от очерёдности инициализации дисковых устройств они могут получать разные символические имена. В один раз системный диск может припарковаться на имя "sda", а в другой - на что-нибудь вроде "sdf". А мы желаем выделить на одном из дисков раздел для журналирования файловой системы хранилища; в случае невозможности получения доступа к журналу таковая просто не смонтируется. К сожалению, ориентироваться по UUID не получится, так как это сущность файловой системы, а не физического устройства как такового, так что ничего не остаётся, как перед запуском системы хранения искать целевое устройство по серийному номеру и создавать на один из его разделов символическую ссылку, с которой в дальнейшем и работать.
Для этого нам понадобится создать некий список соответствий серийных номеров целевых дисков, идентификаторов разделов, на которые нам нужно сослаться, и наименований символических ссылок (или ссылки, если более одной не нужно). Нужную информацию можно легко выяснить просмотрев вывод утилит "dmesg", "ls -l /dev/sd*" и "hdparm -i /dev/sdX". Автоматизацию процесса создания ссылки проработаем в одной из следующих заметок, а пока установим требуемое программное обеспечение:
# aptitude install hdparm
Для простоты договоримся о том, что мы будем использовать символическое имя "sd.sys.xfsjournal", в дальнейшем именно на него мы будем ориентировать драйвер XFS, чтобы тот размещал на указанном разделе журнал транзакций.
Замечу, к вышеприведённому, что обращаться с помощью "мягкой" ссылки в дальнейшем мы будем не к блочному устройству вроде "sda", например, а к разделу на указанном устройстве, например: "sda7". То есть, сопоставление будет вида: "sda7 => sd.sys.xfsjournal".
Учитывая то, что содержимое директории "/dev" с загрузкой операционной системы полностью обновляется, можно не опасаться загромождения её устаревшими ссылками; в связи с указанной особенностью директории очевидным является необходимость вызывать скрипт создания "жёстких" ссылок на устройства после каждой загрузки системы перед началом работы.
Переход к созданию на блочных устройствах LVM файловой системы XFS.
XFS ( Создание на блочных устройствах LVM файловой системы XFS. )
18 сентября 2012 (обновлено 17 июля 2019)
Эта публикация отнесена в архив. Она неактуальна.
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
Инсталлируем пакет утилит поддержки XFS:
Создаем файловую систему XFS:
Где:
Монтируем новоприобретённую файловую систему:
Проверяем, смонтировалась ли таковая:
При желании легко расширить файловую систему на всё доступное пространство.
Демонтируем файловую систему для проверки целостности данных:
Проверяем файловую систему:
Монтируем файловую систему перед расширением таковой:
Расширяем файловую систему XFS на всё доступное пространство:
Переход к автоматизации создания на блочных устройствах LVM файловой системы XFS.
Инсталлируем пакет утилит поддержки XFS:
# aptitude install xfsprogs
Создаем файловую систему XFS:
# mkfs.xfs -b size=4096 -d agcount=256 -l logdev=/dev/sd.sys.xfsjournal,size=128m /dev/vg0/lvstorage0
Где:
"-d agcount" (allocation groups) - параметр, влияющий на эффективность параллельной записи на устройство. В соответствии с рекомендациями разработчиков на каждые 4 (четыре) Гигабайта на блочном устройстве желательно иметь одну "allocation groups". Если пропустить назначение этого параметра, драйвер сам будет его определять и, все хором говорят, это не лучшим образом скажется на производительности;
"-l size" - параметр, определяющий размер журнала для "метаданных". Внятных рекомендаций я не нашёл, но подумал, что для "терабайтного" диска вполне должно хватить и 128 (ста двадцати восьми) Мегабайт:;
"-l logdev" - указатель на блочное устройство, в которое вынесены журналы транзакций XFS.
"-l size" - параметр, определяющий размер журнала для "метаданных". Внятных рекомендаций я не нашёл, но подумал, что для "терабайтного" диска вполне должно хватить и 128 (ста двадцати восьми) Мегабайт:;
"-l logdev" - указатель на блочное устройство, в которое вынесены журналы транзакций XFS.
# mkdir -p /mnt/storage0
Монтируем новоприобретённую файловую систему:
# mount -t xfs -o noatime,nodiratime,logdev=/dev/sd.sys.xfsjournal /dev/vg0/lvstorage0 /mnt/storage0
Проверяем, смонтировалась ли таковая:
# mount
/dev/mapper/vg0-lvstorage0 on /mnt/storage0 type xfs (rw,noatime,nodiratime,logdev=/dev/sd.sys.xfsjournal)
При желании легко расширить файловую систему на всё доступное пространство.
Демонтируем файловую систему для проверки целостности данных:
# umount /dev/vg0/lvstorage0
Проверяем файловую систему:
# xfs_check -l /dev/sd.sys.xfsjournal -s /dev/vg0/lvstorage0
Монтируем файловую систему перед расширением таковой:
# mount -t xfs -o noatime,nodiratime,logdev=/dev/sd.sys.xfsjournal /dev/vg0/lvstorage0 /mnt/storage0
Расширяем файловую систему XFS на всё доступное пространство:
# xfs_growfs /dev/vg0/lvstorage0
Переход к автоматизации создания на блочных устройствах LVM файловой системы XFS.
Автоматизация XFS ( Автоматизация создания на блочных устройствах LVM файловой системы XFS. )
18 сентября 2012 (обновлено 17 июля 2019)
Эта публикация отнесена в архив. Она неактуальна.
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
Создадим конфигурационный файл перечнем и описанием характеристик ресурсов, готовых к инициализации и использованию:
Пишем часть функциональности сканирования имеющихся дисковых устройств, поиска среди них наших, проверяющий корректность их применения и создающий для них ссылки, если в этом есть необходимость:
Переход к настройкам публикации и монтирования удалённых NFS-ресурсов.
Здесь размещено описание одного из функциональных блоков инструкций управления самодельным сетевым хранилищем на сочетании технологий "MDADM + LVM + XFS + NFS + MHDDFS" через простейшие BASH-скрипты. Отдельно неприменимо.
Создадим конфигурационный файл перечнем и описанием характеристик ресурсов, готовых к инициализации и использованию:
# vi /usr/local/etc/storage/cnf.d/fs.cnf
....
hdd.target=hdd0
hdd.target.hdd0.serial=5LJ1J3HB
hdd.target.hdd0.partition=7
hdd.target.hdd0.symlink=sd.sys.xfsjournal
fs.target=storage0
fs.target.storage0.type=xfs
fs.target.storage0.device=/dev/vg0/lvstorage0
fs.target.storage0.mount.journal=/dev/sd.sys.xfsjournal
fs.target.storage0.mount.point=/mnt/storage0
....
hdd.target=hdd0
hdd.target.hdd0.serial=5LJ1J3HB
hdd.target.hdd0.partition=7
hdd.target.hdd0.symlink=sd.sys.xfsjournal
fs.target=storage0
fs.target.storage0.type=xfs
fs.target.storage0.device=/dev/vg0/lvstorage0
fs.target.storage0.mount.journal=/dev/sd.sys.xfsjournal
fs.target.storage0.mount.point=/mnt/storage0
....
Пишем часть функциональности сканирования имеющихся дисковых устройств, поиска среди них наших, проверяющий корректность их применения и создающий для них ссылки, если в этом есть необходимость:
# vi /usr/local/etc/storage/fnc.d/fs.fnc
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция создания символической ссылки на заданный раздел целевого диска
function make-symlink() {
# Перебираем все строки именований целевых дисков
for HDDS in `grep --ignore-case "^hdd.target=" "${CONF}" | uniq` ; do
HDD=`echo ${HDDS} | awk -F = '{print $2}'`
# Перебираем все строки параметров HDD
for PARAMS in `grep --ignore-case "^hdd.target.${HDD}." "${CONF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".serial=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && SERIAL="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".partition=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && PARTITION="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".symlink=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && SYMLINK="${BUFFER}"
done
if [ "${SERIAL}" == "" ] || [ "${PARTITION}" == "" ] || [ "${SYMLINK}" == "" ] ; then
send-report "Symlink" "Parameters of the target device are not in full. Aborting."
echo "${DATE}: Parameters of the target device are not in full. Aborting." >> "${LOG}"
return 1
fi
# Перебираем все строки именований HDD
for OBJECT in `ls /dev | grep -i "^sd[a-z]$"` ; do
# Ищем совпадения в выводе информации об устройстве с заданным серийным номером
STATE=`hdparm -i /dev/${OBJECT} | grep --ignore-case "serial" | grep --count --ignore-case "${SERIAL}"`
if [ "${STATE}" -ne "0" ] ; then
# Ищем целевой раздел
if [ "`ls /dev | grep --ignore-case ${OBJECT}${PARTITION}`" != "" ] ; then
# Проверяем наличие корректной ссылки
STATE=`ls -l /dev | grep --ignore-case "lrwx" | grep --ignore-case "${SYMLINK}" | grep --count --ignore-case "${OBJECTS}${PART}"`
if [ "${STATE}" -eq "0" ] ; then
ln --force --symbolic /dev/${OBJECT}${PARTITION} /dev/${SYMLINK}
# Проверяем успешность завершения операции
if [ "${?}" -eq "0" ] ; then
echo "${DATE}: A symbolic link ${SYMLINK} successfully created for /dev/${OBJECT}${PARTITION}" >> "${LOG}"
break
else
send-report "Symlink" "Error creating symlink ${SYMLINK}. Aborting."
echo "${DATE}: Error creating symlink ${SYMLINK}. Aborting." >> "${LOG}"
return 1
fi
else
echo "${DATE}: Section on ${PARTITION} of the target device ${OBJECT} is already a symbolic link ${SYMLINK}." >> "${LOG}"
break
fi
else
send-report "Symlink" "The target device ${OBJECT} not specified partition ${PARTITION}. Aborting."
echo "${DATE}: The target device ${OBJECT} not specified partition ${PARTITION}. Aborting" >> "${LOG}"
return 1
fi
fi
done
done
return $?
}
# Функция монтирования вручную подготовленных файловых систем хранилища
function start-fs() {
# Перебираем все строки именований целевых файловых систем
for TFSS in `grep --ignore-case "^fs.target=" "${CONF}" | uniq` ; do
TFS=`echo ${TFSS} | awk -F = '{print $2}'`
# Перебираем все строки параметров целевой файловой системы
for PARAMS in `grep --ignore-case "^fs.target.${TFS}." "${CONF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".type=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && TFSTYPE="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".device=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && TFSDEVICE="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".mount.journal=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && TFSJOURNAL="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".mount.point=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && TFSPOINT="${BUFFER}"
done
if [ "${TFSTYPE}" == "" ] || [ "${TFSDEVICE}" == "" ] || [ "${TFSJOURNAL}" == "" ] || [ "${TFSPOINT}" == "" ] ; then
send-report "FS" "Parameters of the target file system are not fully. Aborting."
echo "${DATE}: Parameters of the target file system are not fully. Aborting." >> "${LOG}"
return 1
fi
# Проверяем доступность целевых блочных устройств
if [ -b "${TFSDEVICE}" ] && [ -b "${TFSJOURNAL}" ] ; then
# Вычленяем из пути к монтируемому блочному устройству его имя
TFSDEVICENAME=`basename "${TFSDEVICE}"`
# Проверяем, не смонтирована ли уже целевая файловая система
STATE=`mount | grep --ignore-case "${TFSDEVICENAME}" | grep --count --ignore-case "${TFSPOINT}"`
if [ "${STATE}" -eq "0" ] ; then
mount -t "${TFSTYPE}" -o noatime,nodiratime,logdev=${TFSJOURNAL} "${TFSDEVICE}" "${TFSPOINT}"
# Проверяем успешность завершения операции
if [ "${?}" -ne "0" ] ; then
send-report "FS" "Error mount target FS ${TFSPOINT}. Aborting."
echo "${DATE}: Error mount target FS ${TFSPOINT}. Aborting." >> "${LOG}"
return 1
fi
# Даём системе пару секунд на осознание места новой файловой системы
sleep 2
# Проверяем успешность отрабатывания операции монтирования файловой системы
STATE=`mount | grep --ignore-case "${TFSDEVICENAME}" | grep --count --ignore-case "${TFSPOINT}"`
if [ "${STATE}" -ne "0" ] ; then
echo "${DATE}: The file system storage successfully installed." >> "${LOG}"
else
send-report "FS" "Mount the file system storage with error. Aborting."
echo "${DATE}: Mount the file system storage with error. Aborting." >> "${LOG}"
return 1
fi
else
echo "${DATE}: The target file system is already mounted to the point ${TFSPOINT}." >> "${LOG}"
fi
else
send-report "FS" "Target block device ${TFSDEVICE} and ${TFSJOURNAL} unavailable. Aborting."
echo "${DATE}: Target block device ${TFSDEVICE} and ${TFSJOURNAL} unavailable. Aborting." >> "${LOG}"
return 1
fi
done
# Создаём файл блокировки, показывающий на состояние работы подсистемы
touch "${LOCK}/fs.lck"
return $?
}
# Функция размонтирования вручную подготовленных файловых систем хранилища
function stop-fs() {
# Перебираем все строки именований целевых файловых систем
for TFSS in `grep --ignore-case "^fs.target=" "${CONF}" | uniq` ; do
TFS=`echo ${TFSS} | awk -F = '{print $2}'`
# Перебираем необходимые строки параметров целевой файловой системы
for PARAMS in `grep --ignore-case "^fs.target.${TFS}." "${CONF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".mount.point=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && TFSPOINT="${BUFFER}"
done
if [ "${TFSPOINT}" == "" ] ; then
send-report "FS" "Parameters of the target file system are not fully. Aborting."
echo "${DATE}: Parameters of the target file system are not fully. Aborting." >> "${LOG}"
return 1
fi
# Проверяем, смонтирована ли уже целевая файловая система
STATE=`mount | grep --count --ignore-case "${TFSPOINT}"`
if [ "${STATE}" -ne "0" ] ; then
umount -f "${TFSPOINT}"
fi
done
# Удаляем файл блокировки, показывающий на состояние работы подсистемы
rm --force "${LOCK}/fs.lck"
return $?
}
# Функция проверки состояния вручную подготовленных файловых систем хранилища
function check-fs() {
# Удостоверимся в том, что монтирование файловых систем хранилища прошло успешно (в противном случае проверку не запускаем - бессмысленно)
if [ -f "${LOCK}/fs.lck" ]; then
echo "check"
fi
return $?
}
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция создания символической ссылки на заданный раздел целевого диска
function make-symlink() {
# Перебираем все строки именований целевых дисков
for HDDS in `grep --ignore-case "^hdd.target=" "${CONF}" | uniq` ; do
HDD=`echo ${HDDS} | awk -F = '{print $2}'`
# Перебираем все строки параметров HDD
for PARAMS in `grep --ignore-case "^hdd.target.${HDD}." "${CONF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".serial=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && SERIAL="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".partition=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && PARTITION="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".symlink=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && SYMLINK="${BUFFER}"
done
if [ "${SERIAL}" == "" ] || [ "${PARTITION}" == "" ] || [ "${SYMLINK}" == "" ] ; then
send-report "Symlink" "Parameters of the target device are not in full. Aborting."
echo "${DATE}: Parameters of the target device are not in full. Aborting." >> "${LOG}"
return 1
fi
# Перебираем все строки именований HDD
for OBJECT in `ls /dev | grep -i "^sd[a-z]$"` ; do
# Ищем совпадения в выводе информации об устройстве с заданным серийным номером
STATE=`hdparm -i /dev/${OBJECT} | grep --ignore-case "serial" | grep --count --ignore-case "${SERIAL}"`
if [ "${STATE}" -ne "0" ] ; then
# Ищем целевой раздел
if [ "`ls /dev | grep --ignore-case ${OBJECT}${PARTITION}`" != "" ] ; then
# Проверяем наличие корректной ссылки
STATE=`ls -l /dev | grep --ignore-case "lrwx" | grep --ignore-case "${SYMLINK}" | grep --count --ignore-case "${OBJECTS}${PART}"`
if [ "${STATE}" -eq "0" ] ; then
ln --force --symbolic /dev/${OBJECT}${PARTITION} /dev/${SYMLINK}
# Проверяем успешность завершения операции
if [ "${?}" -eq "0" ] ; then
echo "${DATE}: A symbolic link ${SYMLINK} successfully created for /dev/${OBJECT}${PARTITION}" >> "${LOG}"
break
else
send-report "Symlink" "Error creating symlink ${SYMLINK}. Aborting."
echo "${DATE}: Error creating symlink ${SYMLINK}. Aborting." >> "${LOG}"
return 1
fi
else
echo "${DATE}: Section on ${PARTITION} of the target device ${OBJECT} is already a symbolic link ${SYMLINK}." >> "${LOG}"
break
fi
else
send-report "Symlink" "The target device ${OBJECT} not specified partition ${PARTITION}. Aborting."
echo "${DATE}: The target device ${OBJECT} not specified partition ${PARTITION}. Aborting" >> "${LOG}"
return 1
fi
fi
done
done
return $?
}
# Функция монтирования вручную подготовленных файловых систем хранилища
function start-fs() {
# Перебираем все строки именований целевых файловых систем
for TFSS in `grep --ignore-case "^fs.target=" "${CONF}" | uniq` ; do
TFS=`echo ${TFSS} | awk -F = '{print $2}'`
# Перебираем все строки параметров целевой файловой системы
for PARAMS in `grep --ignore-case "^fs.target.${TFS}." "${CONF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".type=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && TFSTYPE="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".device=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && TFSDEVICE="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".mount.journal=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && TFSJOURNAL="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".mount.point=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && TFSPOINT="${BUFFER}"
done
if [ "${TFSTYPE}" == "" ] || [ "${TFSDEVICE}" == "" ] || [ "${TFSJOURNAL}" == "" ] || [ "${TFSPOINT}" == "" ] ; then
send-report "FS" "Parameters of the target file system are not fully. Aborting."
echo "${DATE}: Parameters of the target file system are not fully. Aborting." >> "${LOG}"
return 1
fi
# Проверяем доступность целевых блочных устройств
if [ -b "${TFSDEVICE}" ] && [ -b "${TFSJOURNAL}" ] ; then
# Вычленяем из пути к монтируемому блочному устройству его имя
TFSDEVICENAME=`basename "${TFSDEVICE}"`
# Проверяем, не смонтирована ли уже целевая файловая система
STATE=`mount | grep --ignore-case "${TFSDEVICENAME}" | grep --count --ignore-case "${TFSPOINT}"`
if [ "${STATE}" -eq "0" ] ; then
mount -t "${TFSTYPE}" -o noatime,nodiratime,logdev=${TFSJOURNAL} "${TFSDEVICE}" "${TFSPOINT}"
# Проверяем успешность завершения операции
if [ "${?}" -ne "0" ] ; then
send-report "FS" "Error mount target FS ${TFSPOINT}. Aborting."
echo "${DATE}: Error mount target FS ${TFSPOINT}. Aborting." >> "${LOG}"
return 1
fi
# Даём системе пару секунд на осознание места новой файловой системы
sleep 2
# Проверяем успешность отрабатывания операции монтирования файловой системы
STATE=`mount | grep --ignore-case "${TFSDEVICENAME}" | grep --count --ignore-case "${TFSPOINT}"`
if [ "${STATE}" -ne "0" ] ; then
echo "${DATE}: The file system storage successfully installed." >> "${LOG}"
else
send-report "FS" "Mount the file system storage with error. Aborting."
echo "${DATE}: Mount the file system storage with error. Aborting." >> "${LOG}"
return 1
fi
else
echo "${DATE}: The target file system is already mounted to the point ${TFSPOINT}." >> "${LOG}"
fi
else
send-report "FS" "Target block device ${TFSDEVICE} and ${TFSJOURNAL} unavailable. Aborting."
echo "${DATE}: Target block device ${TFSDEVICE} and ${TFSJOURNAL} unavailable. Aborting." >> "${LOG}"
return 1
fi
done
# Создаём файл блокировки, показывающий на состояние работы подсистемы
touch "${LOCK}/fs.lck"
return $?
}
# Функция размонтирования вручную подготовленных файловых систем хранилища
function stop-fs() {
# Перебираем все строки именований целевых файловых систем
for TFSS in `grep --ignore-case "^fs.target=" "${CONF}" | uniq` ; do
TFS=`echo ${TFSS} | awk -F = '{print $2}'`
# Перебираем необходимые строки параметров целевой файловой системы
for PARAMS in `grep --ignore-case "^fs.target.${TFS}." "${CONF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".mount.point=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && TFSPOINT="${BUFFER}"
done
if [ "${TFSPOINT}" == "" ] ; then
send-report "FS" "Parameters of the target file system are not fully. Aborting."
echo "${DATE}: Parameters of the target file system are not fully. Aborting." >> "${LOG}"
return 1
fi
# Проверяем, смонтирована ли уже целевая файловая система
STATE=`mount | grep --count --ignore-case "${TFSPOINT}"`
if [ "${STATE}" -ne "0" ] ; then
umount -f "${TFSPOINT}"
fi
done
# Удаляем файл блокировки, показывающий на состояние работы подсистемы
rm --force "${LOCK}/fs.lck"
return $?
}
# Функция проверки состояния вручную подготовленных файловых систем хранилища
function check-fs() {
# Удостоверимся в том, что монтирование файловых систем хранилища прошло успешно (в противном случае проверку не запускаем - бессмысленно)
if [ -f "${LOCK}/fs.lck" ]; then
echo "check"
fi
return $?
}
Переход к настройкам публикации и монтирования удалённых NFS-ресурсов.
NFS ( Публикация и монтирование удалённых NFS-ресурсов. )
18 сентября 2012 (обновлено 17 июля 2019)
Эта публикация отнесена в архив. Она неактуальна.
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
NFS подсистема древняя (в смысле богатая историей). Исторически сложилась ситуация, когда она считается базовым функционалом UNIX (а по наследству и Linux, и BSD). Несмотря на то, что NFS, по сути, является отдельной подсистемой вроде HTTP-сервера или иного сервера приложений, конфигурируемого отдельно от несущей операционной системы, ситуация с настройкой NFS несколько иная - для версий "2" и "3" запустить "просто NFS", без корректировки параметров смежных уровней абстракции нереально. С версией NFSv4 ситуация сильно улучшилась (теперь NFS практически автономный сервис), но разработчики излишне, на мой взгляд, озабочены поддержкой обратной совместимости и обеспечением работы подсистемы в условиях эксплуатации оной "дураком", запуская сразу все её составляющие, даже никому не нужные уже лет десять; очень неприятно, знаете ли, знать, что кроме нужного сервиса принимающего подключения по протоколу NFSv4, на сервере запущено ещё с десяток сервисов, обеспечивающих работу с протоколами NFSv1, NFSv2 и NFSv3. Мало того, что ненужные сервисы элементарно занимают неиспользуемыми ими ресурсы, так их наличие ещё и вносит путаницу в процесс первичной настройки и локализации неисправностей в последствии; мало где я читал столько чепухи, сколько в ветках обсуждения процедур настройки NFS разного рода интернет форумов - местами просто феерия непонимания связи параметров конфигурирования с целями такового.
Заинтересовавшимся рекомендую сразу начать читать здесь: http://wiki.linux-nfs.org/
Я буду использовать в работе только NFSv4 и только на транспорте TCP (главное преимущество TCP - более эффективный механизм повторной передачи, что компенсирует возможные сбои в работе, позволяя продолжать её даже на приходящем в неисправность оборудовании). Отключить поддержку NFSv2 и NFSv3 в Debian Linux Lenny/Squeeze/Wheezy исправлением значений пары-тройки переменных нельзя (так, что бы изменения сохранились бы при обновлении конфигурируемых пакетов) и здесь я даже не буду этого делать, оставив конфигурацию сервисов "по умолчанию"; далее мы будем управлять таковой "на лету", путём воздействия на неё из "командной строки". Для энтузиастов позже напишу отдельную заметку о запуске NFS с поддержкой только одной, нужной нам, версии "4".
Устанавливаем необходимые пакеты:
APT-пакет "nfs-server" - виртуальный и объединяет собой как минимум "nfs-common", "nfs-kernel-server" и "portmap". Любителям точности можно устанавливать непосредственно целевые пакеты.
Надо заметить, что сборщик пакетов NFS в Debian совсем не заинтересован в детальной проработке их конфигурации; что-то можно настроить, а что-то нельзя, несмотря на то, что препятствий к этому с точки зрения воздействия на целевое программное обеспечение нет никаких. Отключить всё не получится, но на некоторые составляющие мы можем повоздействовать через предусмотренные конфигурационный файлы. Корректируем два файла, применяя значения указанным переменным:
В итоге мы чуть уменьшили количество задействуемых компонентов, в целом оставив конфигурацию "по умолчанию".
Перезапустим сервисы NFS с обновлёнными настройками:
Возможно попытка запуска ненастроенного NFS-сервера не будет успешной и сопровождаться следующим сообщением об ошибке:
В "Linux Debian Lenny/Squeeze" можно было запустить NFS-сервер с пустым перечнем экспортируемых ресурсов - вхолостую, так сказать. Это удобно, когда сразу после старта системы в объявлении ресурсов нет необходимости или возможности. В "Debian Wheezy" эту возможность прикрыли, прерывая запуск NFS-сервера в случае пустых файлов "/etc/exports" и "/etc/exports.d/*exports". Глупо, надо заметить, и придётся с этим побороться.
Переделывать стартовый скрип не вижу смысла - мало ли что ещё поменяется в следующих реализациях, о чём я своевременно не узнаю? Потому просто подставим на публикацию в файле "/etc/exports" корень NFS, доступ к которому разрешим только на чтение:
Сразу можно будет посмотреть, опубликовался ли наш ресурс:
Нашему серверу достаточно иметь запущенными всего пару сервисов, удостоверимся в их наличии:
Опять же, в плане сетевых подключений для поддержания NFSv4 поверх TCP нам требуется прослушивание лишь одного порта "TCP:2049":
Как я уже упоминал выше, мы будем использовать только NFSv4 поверх TCP. По умолчанию запускается масса сервисов, обеспечивающих поддержку всей линейки протоколов NFS как на транспорте TCP, так и на UDP. Можете полюбоваться на них, запросив с помощью утилиты "rpcinfo" перечень зарегистрированных на сервере RPC-процессов:
На этом предварительную настройку подсистемы NFS будем считать завершённой.
Следующим, по сути первым, этапом будет оформление публикуемых (экспортируемых) ресурсов в должном виде.
Реализация NFSv4 не поддерживает публикацию произвольных веток файловых систем; теперь требуется предварительно создать некую отправную точку, которая объявляется условным корнем файловой системы, содержимое которой уже доступно для публикации (экспортирования). Получается своего рода "chroot"; надо полагать, это сделано в целях повышения уровня безопасности "от дурака", что бы случайно не раскрыть всем окружающим важные ресурсы.
Заведём себе такую точку сбора:
Непосредственно в директории выделенной для публикации можно разместить соответствующие ресурсы, а можно смонтировать их сюда с помощью функционала "--bind" (создавая синоним ресурса, а не ссылку на таковой, как некоторые неверно считают; преимуществом этого способа над символьными ссылками является возможность обходить ограничения доступа к файловой системе, возникающие перед процессами, запущенными в среде "chroot"):
Удостоверимся, что ресурс успешно смонтирован:
Следующим шагом станет определение локальных прав собственности публикуемых (экспортируемых) ресурсов и правил доступа к таковым удалёнными клиентами.
Специфика NFS в том, что (до последнего времени, когда стала возможной аутентификация подключающихся пользователей с помощью Kerberos) сервер доверяет клиенту и принимает "на веру" заявленные им при доступе к ресурсам UID и GID, на основании которых определяется, имеет клиент право доступа к ресурсам или нет. Предполагается, что в пределах схемы существует общее пространство имен пользователей, то есть пользователь "storage" на любом клиентском (с точки зрения сервера NFS) компьютере имеет то же имя и тот же идентификатор, что и пользователь "storage" на сервере NFS; по умолчанию, если GID и UID клиента не обнаруживаются на сервере, их значения транслируются в значения пользователя "nobody:nogroup" сервера. В NFSv4 трансляцией имён и идентификаторов занимается сервис "idmapd".
Соответственно, самое простое, что мы можем сделать для обеспечения возможности операций чтения и записи с ресурсам, так это сделать их собственником пользователя "nobody:nogroup", разрешив ему всё необходимое. Однако, я считаю, что лучше завести специального пользователя как на сервере так и на клиенте, задав им при создании идентичные UID и GID, хотя бы и для того, что бы в последствии наладить полноценное удостоверение подлинности клиента при обращении к серверу, попутно избежав выдачи слишком высокого уровня доступа к ресурсам пользователю "nobody:nogroup", который по идее (в рамках которой он создавался) вообще не должен иметь возможности изменять что либо.
Добавляем пользователя, собственника ресурсов хранилища публикуемых с помощью NFS:
Естественно, что цифровые значения UID и GID необходимо подобрать несовпадающие с уже имеющимися во всех задействованных в схеме системах.
Обращаю внимание на то, что я лишил пользователя "storage" возможности работать в так называемой "оболочке", иначе говоря, от имени пользователя можно запустить приложение или обратится к ресурсам, но нельзя будет работать в "командной строке".
Делаем пользователя и группу "storage:storage" собственниками содержимого хранилища:
Пока не заведена система удостоверения подлинности подключающихся клиентов проще всего указать ключами "all_squash, anonuid=1500, anongid=1500" серверу NFS считать всех анонимными пользователями и преобразовывать их UID и GID в значения локального пользователя "storage:storage", собственника ресурсов хранилища. Тогда мы сможем обратится к ресурсам от имени любого пользователя; в данном упрощённом варианте пользователи с идентичными именами, UID и GID на всех серверах и клиентах схемы нужны более для того, что бы корректно отображать параметры доступа на удалённых узлах, явно показывая в выводе утилит вроде "ls" собственника ресурсов и права доступа к таковым.
Отменить экспортирование:
Где:
Проверяем, успешно ли опубликованы ресурсы:
На стороне сборки опубликованных NFS-ресурсов потребуется провести несколько иные работы.
Установим утилиты подключения к NFS-ресурсам и монтирования их в локальную файловую систему:
Чуть подправим конфигурацию NFS-клиента:
Добавляем пользователя, собственника ресурсов хранилища, публикуемых с помощью NFS:
Создаём директорию, в которую будем монтировать NFS-ресурсы:
Запрашиваем перечень доступных для монтирования удалённых NFS-ресурсов:
Ранее, в настройках NFSv4-сервера мы обозначили директорию "/mnt/export" корневой для клиентов с помощью опции "fsid=0". Теперь при монтировании с NFSv4-клиента следует указывать целевые ресурсы отталкиваясь не от корневой директории NFS-сервера, а от своеобразного "chroot" по адресу "/mnt/export". То есть, если требуется смонтировать ресурс "/mnt/export/resource", нужно монтировать "/resource":
Иногда удобнее позволить NFS-клиенту уведомлять приложение использующее его ресурсы о проблемах со смонтированной файловой системой. Для этого монтируем файловую систему с опцией "soft", указываю количество попыток перемонтирования до вывода сообщения об ошибке опцией "retrans=5" (пять раз) и указываем таймаут для запроса опцией "timeo=600" (минута, в десятых долях секунды):
Важный аспект оптимизации производительности - заранее определяемый размер передаваемого пакета данных. Если пакет меньше оптимально - их будет передаваться избыточно много (затраты ресурсов на формирование пакетов), а если больше оптимально - на каком-то из уровней будут затрачиваться ресурсы на из дробление, инкапсуляцию и последующую сборку. Полагаю отталкиваться при выборе оптимального размера пакетов следует исходя из неизменяемых значений - например размера блока файловой системы хранения (как правило он уже установлен исходя из параметров аппаратуры). Узнать его можно следующим образом:
В современных блочных устройствах это будет "4096" байт, скорее всего. Тогда подгоним размер пакета под это значение, установив соответствующие опции "rsize=4096,wsize=4096".
Переход к настройкам автоматизации публикации NFS-ресурсов.
NFS подсистема древняя (в смысле богатая историей). Исторически сложилась ситуация, когда она считается базовым функционалом UNIX (а по наследству и Linux, и BSD). Несмотря на то, что NFS, по сути, является отдельной подсистемой вроде HTTP-сервера или иного сервера приложений, конфигурируемого отдельно от несущей операционной системы, ситуация с настройкой NFS несколько иная - для версий "2" и "3" запустить "просто NFS", без корректировки параметров смежных уровней абстракции нереально. С версией NFSv4 ситуация сильно улучшилась (теперь NFS практически автономный сервис), но разработчики излишне, на мой взгляд, озабочены поддержкой обратной совместимости и обеспечением работы подсистемы в условиях эксплуатации оной "дураком", запуская сразу все её составляющие, даже никому не нужные уже лет десять; очень неприятно, знаете ли, знать, что кроме нужного сервиса принимающего подключения по протоколу NFSv4, на сервере запущено ещё с десяток сервисов, обеспечивающих работу с протоколами NFSv1, NFSv2 и NFSv3. Мало того, что ненужные сервисы элементарно занимают неиспользуемыми ими ресурсы, так их наличие ещё и вносит путаницу в процесс первичной настройки и локализации неисправностей в последствии; мало где я читал столько чепухи, сколько в ветках обсуждения процедур настройки NFS разного рода интернет форумов - местами просто феерия непонимания связи параметров конфигурирования с целями такового.
Заинтересовавшимся рекомендую сразу начать читать здесь: http://wiki.linux-nfs.org/
Я буду использовать в работе только NFSv4 и только на транспорте TCP (главное преимущество TCP - более эффективный механизм повторной передачи, что компенсирует возможные сбои в работе, позволяя продолжать её даже на приходящем в неисправность оборудовании). Отключить поддержку NFSv2 и NFSv3 в Debian Linux Lenny/Squeeze/Wheezy исправлением значений пары-тройки переменных нельзя (так, что бы изменения сохранились бы при обновлении конфигурируемых пакетов) и здесь я даже не буду этого делать, оставив конфигурацию сервисов "по умолчанию"; далее мы будем управлять таковой "на лету", путём воздействия на неё из "командной строки". Для энтузиастов позже напишу отдельную заметку о запуске NFS с поддержкой только одной, нужной нам, версии "4".
Устанавливаем необходимые пакеты:
# aptitude install --without-recommends nfs-common nfs-server
APT-пакет "nfs-server" - виртуальный и объединяет собой как минимум "nfs-common", "nfs-kernel-server" и "portmap". Любителям точности можно устанавливать непосредственно целевые пакеты.
Надо заметить, что сборщик пакетов NFS в Debian совсем не заинтересован в детальной проработке их конфигурации; что-то можно настроить, а что-то нельзя, несмотря на то, что препятствий к этому с точки зрения воздействия на целевое программное обеспечение нет никаких. Отключить всё не получится, но на некоторые составляющие мы можем повоздействовать через предусмотренные конфигурационный файлы. Корректируем два файла, применяя значения указанным переменным:
# vi /etc/default/nfs-common
....
# Отключаем выделенный сервис "statd" (он уже включен в сервис nfsd NFSv4 и отдельно нужен только для поддержки NFSv2 и NFSv3)
NEED_STATD=no
# Влючаем выделенный сервис (предусмотренный для поддержки NFSv4) трансляции имён пользователей в идентификаторы (он полезен в системе с полноценной аутентификацией клиентов с помощью протоколов PAM или Kerberos)
NEED_IDMAPD=yes
# Отключаем выделенный сервис, авторизирующий подключающихся пользователей с помощью протокола Kerberos (сейчас нам это не нужно, так как серверы выделенные и подключения защищены на транспортном уровне)
NEED_GSSD=no
....
# Отключаем выделенный сервис "statd" (он уже включен в сервис nfsd NFSv4 и отдельно нужен только для поддержки NFSv2 и NFSv3)
NEED_STATD=no
# Влючаем выделенный сервис (предусмотренный для поддержки NFSv4) трансляции имён пользователей в идентификаторы (он полезен в системе с полноценной аутентификацией клиентов с помощью протоколов PAM или Kerberos)
NEED_IDMAPD=yes
# Отключаем выделенный сервис, авторизирующий подключающихся пользователей с помощью протокола Kerberos (сейчас нам это не нужно, так как серверы выделенные и подключения защищены на транспортном уровне)
NEED_GSSD=no
....
# vi /etc/default/nfs-kernel-server
....
# Зададим количество серверов принимающих запросы. Наш сервер будет обслуживать всего одного клиента, так что укажем запускать пару экземпляров (по умолчанию: 8)
RPCNFSDCOUNT=2
# Отключаем выделенный сервис, нужный только для публикации ресурсов, требующих авторизии подключающихся пользователей с помощью протокола Kerberos
NEED_SVCGSSD=no
....
# Зададим количество серверов принимающих запросы. Наш сервер будет обслуживать всего одного клиента, так что укажем запускать пару экземпляров (по умолчанию: 8)
RPCNFSDCOUNT=2
# Отключаем выделенный сервис, нужный только для публикации ресурсов, требующих авторизии подключающихся пользователей с помощью протокола Kerberos
NEED_SVCGSSD=no
....
В итоге мы чуть уменьшили количество задействуемых компонентов, в целом оставив конфигурацию "по умолчанию".
Перезапустим сервисы NFS с обновлёнными настройками:
# /etc/init.d/nfs-common restart
# /etc/init.d/nfs-kernel-server restart
# /etc/init.d/nfs-kernel-server restart
Возможно попытка запуска ненастроенного NFS-сервера не будет успешной и сопровождаться следующим сообщением об ошибке:
[warn] Not starting NFS kernel daemon: no exports. ... (warning).
В "Linux Debian Lenny/Squeeze" можно было запустить NFS-сервер с пустым перечнем экспортируемых ресурсов - вхолостую, так сказать. Это удобно, когда сразу после старта системы в объявлении ресурсов нет необходимости или возможности. В "Debian Wheezy" эту возможность прикрыли, прерывая запуск NFS-сервера в случае пустых файлов "/etc/exports" и "/etc/exports.d/*exports". Глупо, надо заметить, и придётся с этим побороться.
Переделывать стартовый скрип не вижу смысла - мало ли что ещё поменяется в следующих реализациях, о чём я своевременно не узнаю? Потому просто подставим на публикацию в файле "/etc/exports" корень NFS, доступ к которому разрешим только на чтение:
# vi /etc/exports
....
/mnt/export 10.10.12.192/28(fsid=0,ro,nohide,root_squash,no_subtree_check,sync)
/mnt/export 10.10.12.192/28(fsid=0,ro,nohide,root_squash,no_subtree_check,sync)
# /etc/init.d/nfs-kernel-server restart
[ ok ] Stopping NFS kernel daemon: mountd nfsd.
[ ok ] Unexporting directories for NFS kernel daemon....
[ ok ] Exporting directories for NFS kernel daemon....
[ ok ] Starting NFS kernel daemon: nfsd mountd.
[ ok ] Unexporting directories for NFS kernel daemon....
[ ok ] Exporting directories for NFS kernel daemon....
[ ok ] Starting NFS kernel daemon: nfsd mountd.
Сразу можно будет посмотреть, опубликовался ли наш ресурс:
# showmount --exports
Export list:
/mnt/export 10.10.12.192/28
/mnt/export 10.10.12.192/28
Нашему серверу достаточно иметь запущенными всего пару сервисов, удостоверимся в их наличии:
# ps wax | grep -v grep | grep nfs
....
2831 ... [nfsd4]
2832 ... [nfsd]
....
2831 ... [nfsd4]
2832 ... [nfsd]
....
Опять же, в плане сетевых подключений для поддержания NFSv4 поверх TCP нам требуется прослушивание лишь одного порта "TCP:2049":
# netstat -apn | grep 2049
tcp ... 0 0.0.0.0:2049 0.0.0.0:* LISTEN
udp ... 0 0.0.0.0:2049 0.0.0.0:*
udp ... 0 0.0.0.0:2049 0.0.0.0:*
Как я уже упоминал выше, мы будем использовать только NFSv4 поверх TCP. По умолчанию запускается масса сервисов, обеспечивающих поддержку всей линейки протоколов NFS как на транспорте TCP, так и на UDP. Можете полюбоваться на них, запросив с помощью утилиты "rpcinfo" перечень зарегистрированных на сервере RPC-процессов:
# rpcinfo -p 127.0.0.1
На этом предварительную настройку подсистемы NFS будем считать завершённой.
Следующим, по сути первым, этапом будет оформление публикуемых (экспортируемых) ресурсов в должном виде.
Реализация NFSv4 не поддерживает публикацию произвольных веток файловых систем; теперь требуется предварительно создать некую отправную точку, которая объявляется условным корнем файловой системы, содержимое которой уже доступно для публикации (экспортирования). Получается своего рода "chroot"; надо полагать, это сделано в целях повышения уровня безопасности "от дурака", что бы случайно не раскрыть всем окружающим важные ресурсы.
Заведём себе такую точку сбора:
# mkdir -p /mnt/export
Непосредственно в директории выделенной для публикации можно разместить соответствующие ресурсы, а можно смонтировать их сюда с помощью функционала "--bind" (создавая синоним ресурса, а не ссылку на таковой, как некоторые неверно считают; преимуществом этого способа над символьными ссылками является возможность обходить ограничения доступа к файловой системе, возникающие перед процессами, запущенными в среде "chroot"):
# mkdir -p /mnt/export/storage0
# mount --bind /mnt/storage0 /mnt/export/storage0
# mount --bind /mnt/storage0 /mnt/export/storage0
Удостоверимся, что ресурс успешно смонтирован:
# mount
....
/mnt/storage0 on /mnt/export/storage0 type none (rw,bind)
/mnt/storage0 on /mnt/export/storage0 type none (rw,bind)
Следующим шагом станет определение локальных прав собственности публикуемых (экспортируемых) ресурсов и правил доступа к таковым удалёнными клиентами.
Специфика NFS в том, что (до последнего времени, когда стала возможной аутентификация подключающихся пользователей с помощью Kerberos) сервер доверяет клиенту и принимает "на веру" заявленные им при доступе к ресурсам UID и GID, на основании которых определяется, имеет клиент право доступа к ресурсам или нет. Предполагается, что в пределах схемы существует общее пространство имен пользователей, то есть пользователь "storage" на любом клиентском (с точки зрения сервера NFS) компьютере имеет то же имя и тот же идентификатор, что и пользователь "storage" на сервере NFS; по умолчанию, если GID и UID клиента не обнаруживаются на сервере, их значения транслируются в значения пользователя "nobody:nogroup" сервера. В NFSv4 трансляцией имён и идентификаторов занимается сервис "idmapd".
Соответственно, самое простое, что мы можем сделать для обеспечения возможности операций чтения и записи с ресурсам, так это сделать их собственником пользователя "nobody:nogroup", разрешив ему всё необходимое. Однако, я считаю, что лучше завести специального пользователя как на сервере так и на клиенте, задав им при создании идентичные UID и GID, хотя бы и для того, что бы в последствии наладить полноценное удостоверение подлинности клиента при обращении к серверу, попутно избежав выдачи слишком высокого уровня доступа к ресурсам пользователю "nobody:nogroup", который по идее (в рамках которой он создавался) вообще не должен иметь возможности изменять что либо.
Добавляем пользователя, собственника ресурсов хранилища публикуемых с помощью NFS:
# groupadd --gid 1500 storage
# useradd --shell /bin/false --home-dir /var/lib/nfs --uid 1500 --gid storage storage
# useradd --shell /bin/false --home-dir /var/lib/nfs --uid 1500 --gid storage storage
Естественно, что цифровые значения UID и GID необходимо подобрать несовпадающие с уже имеющимися во всех задействованных в схеме системах.
Обращаю внимание на то, что я лишил пользователя "storage" возможности работать в так называемой "оболочке", иначе говоря, от имени пользователя можно запустить приложение или обратится к ресурсам, но нельзя будет работать в "командной строке".
Делаем пользователя и группу "storage:storage" собственниками содержимого хранилища:
# chown -R storage:storage /mnt/storage0
# chmod -R ug+rw /mnt/storage0
# chmod -R o-rw /mnt/storage0
# chmod -R ug+rw /mnt/storage0
# chmod -R o-rw /mnt/storage0
Пока не заведена система удостоверения подлинности подключающихся клиентов проще всего указать ключами "all_squash, anonuid=1500, anongid=1500" серверу NFS считать всех анонимными пользователями и преобразовывать их UID и GID в значения локального пользователя "storage:storage", собственника ресурсов хранилища. Тогда мы сможем обратится к ресурсам от имени любого пользователя; в данном упрощённом варианте пользователи с идентичными именами, UID и GID на всех серверах и клиентах схемы нужны более для того, что бы корректно отображать параметры доступа на удалённых узлах, явно показывая в выводе утилит вроде "ls" собственника ресурсов и права доступа к таковым.
# exportfs -i -o fsid=0,rw,nohide,all_squash,anonuid=1500,anongid=1500,no_subtree_check,sync 10.10.12.192/28:/mnt/export/storage0
Отменить экспортирование:
# exportfs -u 10.10.12.192/28:/mnt/export/storage0
Где:
"-i" - игнорируем содержимое "/etc/exports" руководствуясь только параметрами данной команды;
"-o fsid=0" - обозначает, что публикуется корневой для всех остальных экспортируемых каталогов (соответственно, все остальные каталоги, расположенные внутри него, публикуются с параметром "-o fsid=1").
"-o fsid=0" - обозначает, что публикуется корневой для всех остальных экспортируемых каталогов (соответственно, все остальные каталоги, расположенные внутри него, публикуются с параметром "-o fsid=1").
Проверяем, успешно ли опубликованы ресурсы:
# showmount --exports
/mnt/export 10.10.12.192/28
/mnt/export/storage0 10.10.12.192/28
/mnt/export/storage0 10.10.12.192/28
На стороне сборки опубликованных NFS-ресурсов потребуется провести несколько иные работы.
Установим утилиты подключения к NFS-ресурсам и монтирования их в локальную файловую систему:
# aptitude install --without-recommends nfs-common nfs-client
Чуть подправим конфигурацию NFS-клиента:
# vi /etc/default/nfs-common
....
# Отключаем выделенный сервис "statd" (он уже включен в сервис nfsd NFSv4 и отдельно нужен только для поддержки NFSv2 и NFSv3)
NEED_STATD=no
# Включаем выделенный сервис (предусмотренный для поддержки NFSv4) трансляции имён пользователей в идентификаторы
NEED_IDMAPD=yes
# Отключаем выделенный сервис, авторизирующий подключающихся пользователей с помощью протокола Kerberos (сейчас нам это не нужно, так как серверы выделенные и подключения защищены на транспортном уровне)
NEED_GSSD=no
....
# Отключаем выделенный сервис "statd" (он уже включен в сервис nfsd NFSv4 и отдельно нужен только для поддержки NFSv2 и NFSv3)
NEED_STATD=no
# Включаем выделенный сервис (предусмотренный для поддержки NFSv4) трансляции имён пользователей в идентификаторы
NEED_IDMAPD=yes
# Отключаем выделенный сервис, авторизирующий подключающихся пользователей с помощью протокола Kerberos (сейчас нам это не нужно, так как серверы выделенные и подключения защищены на транспортном уровне)
NEED_GSSD=no
....
# /etc/init.d/nfs-common restart
Добавляем пользователя, собственника ресурсов хранилища, публикуемых с помощью NFS:
# groupadd --gid 1500 storage
# useradd --shell /bin/false --home-dir /var/lib/nfs --uid 1500 --gid storage storage
# useradd --shell /bin/false --home-dir /var/lib/nfs --uid 1500 --gid storage storage
Создаём директорию, в которую будем монтировать NFS-ресурсы:
# mkdir -p /mnt/import
Запрашиваем перечень доступных для монтирования удалённых NFS-ресурсов:
# showmount --exports 10.10.12.201
Export list for 10.10.12.201:
/mnt/export 127.0.0.1/32
/mnt/export/storage0 10.10.12.192/28
/mnt/export 127.0.0.1/32
/mnt/export/storage0 10.10.12.192/28
Ранее, в настройках NFSv4-сервера мы обозначили директорию "/mnt/export" корневой для клиентов с помощью опции "fsid=0". Теперь при монтировании с NFSv4-клиента следует указывать целевые ресурсы отталкиваясь не от корневой директории NFS-сервера, а от своеобразного "chroot" по адресу "/mnt/export". То есть, если требуется смонтировать ресурс "/mnt/export/resource", нужно монтировать "/resource":
# mount.nfs4 -o proto=tcp,port=2049,hard,intr,tcp,sync,noatime,nodiratime,nodev,noexec,nosuid 10.10.12.195:/storage0 /mnt/import
Иногда удобнее позволить NFS-клиенту уведомлять приложение использующее его ресурсы о проблемах со смонтированной файловой системой. Для этого монтируем файловую систему с опцией "soft", указываю количество попыток перемонтирования до вывода сообщения об ошибке опцией "retrans=5" (пять раз) и указываем таймаут для запроса опцией "timeo=600" (минута, в десятых долях секунды):
# mount.nfs4 -o soft,intr,retrans=5,timeo=600,tcp,sync,noatime,nodiratime,nodev,noexec,nosuid 10.10.12.195:/storage0 /mnt/import
За несколько лет эксплуатации я так и не пришёл к выводу, какой режим выгоднее - "мягкий", с некорректно отрабатывающими ответы NFS приложениями, или "жёсткий" - с зависающими в ожидании ответа приложениями. Пока остаюсь с "hard".
Иногда сбой в работе приложения обращающегося к файловой системе NFS и не получающего ответа приводит к его зависанию намертво (например MHDDFS сваливается до уровня "defunct" и "zombie" после длительного (десятки минут и более) ожидания ответа от файловой системы и множества дублирующих неудовлетворённых запросов к ресурсам) так, что его порой невозможно остановить без форсированной ("echo 1 > /proc/sys/kernel/sysrq; echo b > /proc/sysrq-trigger") перезагрузки системы (если зависшее приложение использует модуль ядра, драйвер файловой системы или что-то низкоуровневое в этом роде, то даже может не сработать программная перезагрузка, так как система будет ждать ответа от зависшего приложения или заблокированного им модуля бесконечно долго).
Важный аспект оптимизации производительности - заранее определяемый размер передаваемого пакета данных. Если пакет меньше оптимально - их будет передаваться избыточно много (затраты ресурсов на формирование пакетов), а если больше оптимально - на каком-то из уровней будут затрачиваться ресурсы на из дробление, инкапсуляцию и последующую сборку. Полагаю отталкиваться при выборе оптимального размера пакетов следует исходя из неизменяемых значений - например размера блока файловой системы хранения (как правило он уже установлен исходя из параметров аппаратуры). Узнать его можно следующим образом:
# blockdev --getbsz /dev/sda
В современных блочных устройствах это будет "4096" байт, скорее всего. Тогда подгоним размер пакета под это значение, установив соответствующие опции "rsize=4096,wsize=4096".
Переход к настройкам автоматизации публикации NFS-ресурсов.
Автоматизация экспорта NFS ( Автоматизация публикации NFS-ресурсов. )
18 сентября 2012 (обновлено 17 июля 2019)
Эта публикация отнесена в архив. Она неактуальна.
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
Заранее готовим корневую точку монтирования для публикуемых NFS-ресурсов:
Создадим конфигурационный файл перечнем и описанием характеристик ресурсов, готовых к инициализации и использованию:
Фрагмент кода автоматизации функционала экспорта NFS-ресурсов подсистемы хранения:
Переход к настройке автоматизации импорта и монтирования NFS-ресурсов.
Здесь размещено описание одного из функциональных блоков инструкций управления самодельным сетевым хранилищем на сочетании технологий "MDADM + LVM + XFS + NFS + MHDDFS" через простейшие BASH-скрипты. Отдельно неприменимо.
Заранее готовим корневую точку монтирования для публикуемых NFS-ресурсов:
# mkdir -p /mnt/export
# chown -R storage:storage /mnt/export
# chmod -R ug+rw /mnt/export
# chown -R storage:storage /mnt/export
# chmod -R ug+rw /mnt/export
Создадим конфигурационный файл перечнем и описанием характеристик ресурсов, готовых к инициализации и использованию:
# vi /usr/local/etc/storage/cnf.d/nfs.export.cnf
....
nfs.export.server.chroot=/mnt/export
nfs.export.server.network=10.10.12.192/28
nfs.export=storage0
nfs.export.storage0.fsid=1
nfs.export.storage0.source=/mnt/storage0
nfs.export.storage0.point=/mnt/export/storage0
nfs.export.storage0.anonuid=1500
nfs.export.storage0.anongid=1500
....
nfs.export.server.chroot=/mnt/export
nfs.export.server.network=10.10.12.192/28
nfs.export=storage0
nfs.export.storage0.fsid=1
nfs.export.storage0.source=/mnt/storage0
nfs.export.storage0.point=/mnt/export/storage0
nfs.export.storage0.anonuid=1500
nfs.export.storage0.anongid=1500
....
Фрагмент кода автоматизации функционала экспорта NFS-ресурсов подсистемы хранения:
# vi /usr/local/etc/storage/fnc.d/nfs.export.fnc
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция публикации файловых систем хранилища посредством NFS
function start-nfs-export() {
# Вычленяем необходимые глобальные параметры сервера NFS
GECHROOT=`grep --ignore-case "^nfs.export.server.chroot=" "${CNF}" | awk -F = '{print $2}'`
GENETWORK=`grep --ignore-case "^nfs.export.server.network=" "${CNF}" | awk -F = '{print $2}'`
# Проверяем наличие пути к корневой псевдофайловой системе NFS
if [ ! -d "${GECHROOT}" ] ; then
echo "${DATE}: There is no way to root pseudo file system NFS. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем корректность формата NETWORK
if [ "${GENETWORK}" == "" ] ; then
echo "${DATE}: Not all global NFS server settings are in full. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем, не опубликован ли уже корень псевдофайловой системы NFS
STATE=`showmount --exports --no-headers | grep --count --ignore-case "^${GECHROOT}[ ]*${GENETWORK}*"`
if [ "${STATE}" -eq "0" ] ; then
exportfs -i -o fsid=0,root_squash ${GENETWORK}:${GECHROOT}
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
echo "${DATE}: Error export NFS pseudo file system. Aborting." | tee -a "${LOGT}"
return 1
fi
# Даём системе пару секунд на завершение публикации
sleep 2
# Проверяем успешность публикации
STATE=`showmount --exports --no-headers | grep --count --ignore-case "^${GECHROOT}[ ]*${GENETWORK}*"`
if [ "${STATE}" -ne "0" ] ; then
echo "${DATE}: NFS resource ${GECHROOT} published successfully." | tee -a "${LOGT}"
else
echo "${DATE}: Publication NFS resource ${GECHROOT} failed. Aborting." | tee -a "${LOGT}"
return 1
fi
else
echo "${DATE}: NFS root pseudo file system has already been published." | tee -a "${LOGT}"
fi
# Перебираем все строки именований публикуемых ресурсов
for ENFSS in `grep --ignore-case "^nfs.export=" "${CNF}" | uniq`; do
ENFS=`echo ${ENFSS} | awk -F = '{print $2}'`
# Перебираем все строки параметров публикуемых ресурсов
for PARAMS in `grep --ignore-case "^nfs.export.${ENFS}." "${CNF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".fsid=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && EFSID="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".source=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && ESOURCE="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".point=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && EPOINT="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".anonuid=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && EANONUID="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".anongid=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && EANONGID="${BUFFER}"
done
# Проверяем наличие субьектов файловой системы
if [ ! -d "${ESOURCE}" ] || [ ! -d "${EPOINT}" ] ; then
echo "${DATE}: Not all mounted and exported resources are in full. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем корректность параметров
if [ "${EFSID}" == "" ] || [ "${EANONUID}" == "" ] || [ "${EANONGID}" == "" ] ; then
echo "${DATE}: Not all resource settings are in full. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем, не опубликованы ли уже ресурсы
STATE=`showmount --exports --no-headers | grep --count --ignore-case "^${EPOINT}[ ]*${GENETWORK}"`
if [ "${STATE}" -eq "0" ] ; then
# Проверяем, не смонтирован ли уже публикуемый ресурс в псевдофайловую систему NFS
STATE=`mount | grep --count --ignore-case "^${ESOURCE}[ ]*on[ ]*${EPOINT}"`
if [ "${STATE}" -eq "0" ] ; then
# Монтируем публикуемый ресурс
mount --bind "${ESOURCE}" "${EPOINT}"
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
echo "${DATE}: Error bind mount ${ESOURCE} on ${EPOINT}. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Публикуем целевой ресурс
EOPT="fsid=${EFSID},rw,nohide,all_squash,anonuid=${EANONUID},anongid=${EANONGID},no_subtree_check,sync"
exportfs -i -o ${EOPT} ${GENETWORK}:${EPOINT}
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
echo "${DATE}: Error export NFS resource ${EPOINT}. Aborting." | tee -a "${LOGT}"
return 1
fi
# Даём системе пару секунд на завершение публикации
sleep 2
# Проверяем успешность публикации целевого ресурса
STATE=`showmount --exports --no-headers | grep --count --ignore-case "^${EPOINT}[ ]*${GENETWORK}"`
if [ "${STATE}" -ne "0" ] ; then
echo "${DATE}: NFS resource ${EPOINT} published successfully." | tee -a "${LOGT}"
else
echo "${DATE}: Publication NFS resource ${EPOINT} failed. Aborting." | tee -a "${LOGT}"
return 1
fi
else
echo "${DATE}: Resource ${EPOINT} has already been published." | tee -a "${LOGT}"
fi
done
# Создаём файл блокировки, показывающий на состояние работы подсистемы
touch "${LOCK}/nfs-export.lck"
return $?
}
# Функция прекращения публикации файловых систем хранилища посредством NFS
function stop-nfs-export() {
# Вычленяем необходимые параметры
GECHROOT=`grep --ignore-case "^nfs.export.server.chroot=" "${CNF}" | awk -F = '{print $2}'`
GENETWORK=`grep --ignore-case "^nfs.export.server.network=" "${CNF}" | awk -F = '{print $2}'`
# Перебираем все строки именований публикуемых ресурсов
for ENFSS in `grep --ignore-case "^nfs.export=" "${CNF}" | uniq`; do
ENFS=`echo ${ENFSS} | awk -F = '{print $2}'`
# Вычленяем необходимые параметры публикуемых ресурсов
for PARAMS in `grep --ignore-case "^nfs.export.${ENFS}.point=" "${CNF}" | uniq` ; do
EPOINT=`echo ${PARAMS} | awk -F = '{print $2}'`
# Проверяем состояние публикации целевого ресурса
STATE=`showmount --exports --no-headers | grep --count --ignore-case "^${EPOINT}[ ]*${GENETWORK}"`
if [ "${STATE}" -ne "0" ] ; then
# Останавливаем публикацию ресурса
exportfs -u ${GENETWORK}:${EPOINT}
fi
done
done
# Останавливаем публикацию псевдофайловой системы NFS
exportfs -u ${GENETWORK}:${GECHROOT}
# Удаляем файл блокировки, показывающий на состояние работы подсистемы
rm --force "${LOCK}/nfs-export.lck"
return $?
}
# Функция проверки состояния публикации файловых систем хранилища посредством NFS
function check-nfs-export() {
# Удостоверимся в том, что публикация ресурсов прошла успешно (в противном случае проверку не запускаем - бессмысленно)
if [ -f "${LOCK}/nfs-export.lck" ]; then
echo "check"
fi
return $?
}
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция публикации файловых систем хранилища посредством NFS
function start-nfs-export() {
# Вычленяем необходимые глобальные параметры сервера NFS
GECHROOT=`grep --ignore-case "^nfs.export.server.chroot=" "${CNF}" | awk -F = '{print $2}'`
GENETWORK=`grep --ignore-case "^nfs.export.server.network=" "${CNF}" | awk -F = '{print $2}'`
# Проверяем наличие пути к корневой псевдофайловой системе NFS
if [ ! -d "${GECHROOT}" ] ; then
echo "${DATE}: There is no way to root pseudo file system NFS. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем корректность формата NETWORK
if [ "${GENETWORK}" == "" ] ; then
echo "${DATE}: Not all global NFS server settings are in full. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем, не опубликован ли уже корень псевдофайловой системы NFS
STATE=`showmount --exports --no-headers | grep --count --ignore-case "^${GECHROOT}[ ]*${GENETWORK}*"`
if [ "${STATE}" -eq "0" ] ; then
exportfs -i -o fsid=0,root_squash ${GENETWORK}:${GECHROOT}
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
echo "${DATE}: Error export NFS pseudo file system. Aborting." | tee -a "${LOGT}"
return 1
fi
# Даём системе пару секунд на завершение публикации
sleep 2
# Проверяем успешность публикации
STATE=`showmount --exports --no-headers | grep --count --ignore-case "^${GECHROOT}[ ]*${GENETWORK}*"`
if [ "${STATE}" -ne "0" ] ; then
echo "${DATE}: NFS resource ${GECHROOT} published successfully." | tee -a "${LOGT}"
else
echo "${DATE}: Publication NFS resource ${GECHROOT} failed. Aborting." | tee -a "${LOGT}"
return 1
fi
else
echo "${DATE}: NFS root pseudo file system has already been published." | tee -a "${LOGT}"
fi
# Перебираем все строки именований публикуемых ресурсов
for ENFSS in `grep --ignore-case "^nfs.export=" "${CNF}" | uniq`; do
ENFS=`echo ${ENFSS} | awk -F = '{print $2}'`
# Перебираем все строки параметров публикуемых ресурсов
for PARAMS in `grep --ignore-case "^nfs.export.${ENFS}." "${CNF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".fsid=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && EFSID="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".source=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && ESOURCE="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".point=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && EPOINT="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".anonuid=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && EANONUID="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".anongid=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && EANONGID="${BUFFER}"
done
# Проверяем наличие субьектов файловой системы
if [ ! -d "${ESOURCE}" ] || [ ! -d "${EPOINT}" ] ; then
echo "${DATE}: Not all mounted and exported resources are in full. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем корректность параметров
if [ "${EFSID}" == "" ] || [ "${EANONUID}" == "" ] || [ "${EANONGID}" == "" ] ; then
echo "${DATE}: Not all resource settings are in full. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем, не опубликованы ли уже ресурсы
STATE=`showmount --exports --no-headers | grep --count --ignore-case "^${EPOINT}[ ]*${GENETWORK}"`
if [ "${STATE}" -eq "0" ] ; then
# Проверяем, не смонтирован ли уже публикуемый ресурс в псевдофайловую систему NFS
STATE=`mount | grep --count --ignore-case "^${ESOURCE}[ ]*on[ ]*${EPOINT}"`
if [ "${STATE}" -eq "0" ] ; then
# Монтируем публикуемый ресурс
mount --bind "${ESOURCE}" "${EPOINT}"
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
echo "${DATE}: Error bind mount ${ESOURCE} on ${EPOINT}. Aborting." | tee -a "${LOGT}"
return 1
fi
fi
# Публикуем целевой ресурс
EOPT="fsid=${EFSID},rw,nohide,all_squash,anonuid=${EANONUID},anongid=${EANONGID},no_subtree_check,sync"
exportfs -i -o ${EOPT} ${GENETWORK}:${EPOINT}
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
echo "${DATE}: Error export NFS resource ${EPOINT}. Aborting." | tee -a "${LOGT}"
return 1
fi
# Даём системе пару секунд на завершение публикации
sleep 2
# Проверяем успешность публикации целевого ресурса
STATE=`showmount --exports --no-headers | grep --count --ignore-case "^${EPOINT}[ ]*${GENETWORK}"`
if [ "${STATE}" -ne "0" ] ; then
echo "${DATE}: NFS resource ${EPOINT} published successfully." | tee -a "${LOGT}"
else
echo "${DATE}: Publication NFS resource ${EPOINT} failed. Aborting." | tee -a "${LOGT}"
return 1
fi
else
echo "${DATE}: Resource ${EPOINT} has already been published." | tee -a "${LOGT}"
fi
done
# Создаём файл блокировки, показывающий на состояние работы подсистемы
touch "${LOCK}/nfs-export.lck"
return $?
}
# Функция прекращения публикации файловых систем хранилища посредством NFS
function stop-nfs-export() {
# Вычленяем необходимые параметры
GECHROOT=`grep --ignore-case "^nfs.export.server.chroot=" "${CNF}" | awk -F = '{print $2}'`
GENETWORK=`grep --ignore-case "^nfs.export.server.network=" "${CNF}" | awk -F = '{print $2}'`
# Перебираем все строки именований публикуемых ресурсов
for ENFSS in `grep --ignore-case "^nfs.export=" "${CNF}" | uniq`; do
ENFS=`echo ${ENFSS} | awk -F = '{print $2}'`
# Вычленяем необходимые параметры публикуемых ресурсов
for PARAMS in `grep --ignore-case "^nfs.export.${ENFS}.point=" "${CNF}" | uniq` ; do
EPOINT=`echo ${PARAMS} | awk -F = '{print $2}'`
# Проверяем состояние публикации целевого ресурса
STATE=`showmount --exports --no-headers | grep --count --ignore-case "^${EPOINT}[ ]*${GENETWORK}"`
if [ "${STATE}" -ne "0" ] ; then
# Останавливаем публикацию ресурса
exportfs -u ${GENETWORK}:${EPOINT}
fi
done
done
# Останавливаем публикацию псевдофайловой системы NFS
exportfs -u ${GENETWORK}:${GECHROOT}
# Удаляем файл блокировки, показывающий на состояние работы подсистемы
rm --force "${LOCK}/nfs-export.lck"
return $?
}
# Функция проверки состояния публикации файловых систем хранилища посредством NFS
function check-nfs-export() {
# Удостоверимся в том, что публикация ресурсов прошла успешно (в противном случае проверку не запускаем - бессмысленно)
if [ -f "${LOCK}/nfs-export.lck" ]; then
echo "check"
fi
return $?
}
Переход к настройке автоматизации импорта и монтирования NFS-ресурсов.
Автоматизация импорта NFS ( Автоматизация импорта и монтирования NFS-ресурсов. )
18 сентября 2012 (обновлено 17 июля 2019)
Эта публикация отнесена в архив. Она неактуальна.
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
Заранее готовим корневую точку монтирования для импортируемых NFS-ресурсов:
Создадим конфигурационный файл перечнем и описанием характеристик ресурсов, готовых к инициализации и использованию:
Фрагмент кода автоматизации функционала импорта NFS-ресурсов подсистемы хранения:
Переход к настройке автоматизации сборки файловых систем в единую точку посредством MHDDFS.
Здесь размещено описание одного из функциональных блоков инструкций управления самодельным сетевым хранилищем на сочетании технологий "MDADM + LVM + XFS + NFS + MHDDFS" через простейшие BASH-скрипты. Отдельно неприменимо.
Заранее готовим корневую точку монтирования для импортируемых NFS-ресурсов:
# mkdir -p /mnt/import
# chown -R storage:storage /mnt/import
# chmod -R ug+rw /mnt/import
# chown -R storage:storage /mnt/import
# chmod -R ug+rw /mnt/import
Создадим конфигурационный файл перечнем и описанием характеристик ресурсов, готовых к инициализации и использованию:
# vi /usr/local/etc/storage/cnf.d/nfs.import.cnf
....
nfs.import=node0
nfs.import.node0.address=10.10.12.195
nfs.import.node0.root=/mnt/export
nfs.import.node0.from=/storage0
nfs.import.node0.to=/mnt/import/node0
nfs.import=node1
nfs.import.node1.address=10.10.12.196
nfs.import.node1.root=/mnt/export
nfs.import.node1.from=/storage0
nfs.import.node1.to=/mnt/import/node1
nfs.import=node2
nfs.import.node2.address=10.10.12.197
nfs.import.node2.root=/mnt/export
nfs.import.node2.from=/storage0
nfs.import.node2.to=/mnt/import/node2
nfs.import=node3
nfs.import.node3.address=10.10.12.198
nfs.import.node3.root=/mnt/export
nfs.import.node3.from=/storage0
nfs.import.node3.to=/mnt/import/node3
....
nfs.import=node0
nfs.import.node0.address=10.10.12.195
nfs.import.node0.root=/mnt/export
nfs.import.node0.from=/storage0
nfs.import.node0.to=/mnt/import/node0
nfs.import=node1
nfs.import.node1.address=10.10.12.196
nfs.import.node1.root=/mnt/export
nfs.import.node1.from=/storage0
nfs.import.node1.to=/mnt/import/node1
nfs.import=node2
nfs.import.node2.address=10.10.12.197
nfs.import.node2.root=/mnt/export
nfs.import.node2.from=/storage0
nfs.import.node2.to=/mnt/import/node2
nfs.import=node3
nfs.import.node3.address=10.10.12.198
nfs.import.node3.root=/mnt/export
nfs.import.node3.from=/storage0
nfs.import.node3.to=/mnt/import/node3
....
Фрагмент кода автоматизации функционала импорта NFS-ресурсов подсистемы хранения:
# vi /usr/local/etc/storage/fnc.d/nfs.import.fnc
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция импорта файловых систем хранилища посредством NFS
function start-nfs-import() {
# Перебираем все строки именований импортируемых ресурсов
for INFSS in `grep --ignore-case "^nfs.import=" "${CNF}" | uniq`; do
INFS=`echo ${INFSS} | awk -F = '{print $2}'`
# Перебираем все строки параметров импортируемых ресурсов
for PARAMS in `grep --ignore-case "^nfs.import.${INFS}." "${CNF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".address=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && IADDRESS="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".root=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && IROOT="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".from=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && IFROM="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".to=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && ITO="${BUFFER}"
done
# Проверяем наличие субьектов файловой системы
if [ ! -d "${ITO}" ] ; then
echo "${DATE}: The mount point ${ITO} intended to import the resource is not available. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем корректность параметров
if [ "${IROOT}" == "" ] || [ "${IADDRESS}" == "" ] ; then
echo "${DATE}: Not all resource settings are in full. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем, не импортированы ли уже ресурсы
STATE=`mount | grep --count --ignore-case "^${IADDRESS}:${IFROM}[ ]*on[ ]*${ITO}"`
if [ "${STATE}" -eq "0" ] ; then
# Проверяем доступность импортируемого ресурса
STATE=`showmount --exports --no-headers ${IADDRESS} | grep --count --ignore-case "^${IROOT}${IFROM}"`
if [ "${STATE}" -ne "0" ] ; then
# # IOPT="hard,tcp,sync,noatime,nodiratime,nodev,noexec,nosuid"
IOPT="soft, intr, retrans=5, timeo=600, rsize=4096, wsize=4096, tcp, async, noatime, nodiratime, nodev, noexec, nosuid"
mount.nfs4 ${IADDRESS}:${IFROM} ${ITO} -o ${IOPT}
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
echo "${DATE}: Importing resource NFS ${IADDRESS}:${IFROM} completed with error. Aborting." | tee -a "${LOGT}"
return 1
fi
# Даём системе пару секунд на завершение импортирования
sleep 2
# Проверяем успешность импортирования
STATE=`mount | grep --count --ignore-case "^${IADDRESS}:${IFROM}[ ]*on[ ]*${ITO}"`
if [ "${STATE}" -ne "0" ] ; then
echo "${DATE}: Importing resource NFS ${IADDRESS}:${IFROM} completed successfully." | tee -a "${LOGT}"
else
echo "${DATE}: Importing resource NFS ${IADDRESS}:${IFROM} completed with error. Aborting." | tee -a "${LOGT}"
return 1
fi
else
echo "${DATE}: Imported resource NFS ${IADDRESS}:${IROOT}${IFROM} is not available. Aborting." | tee -a "${LOGT}"
return 1
fi
else
echo "${DATE}: Resource NFS ${IADDRESS}:${IFROM} has already been imported." | tee -a "${LOGT}"
fi
done
# Создаём файл блокировки, показывающий на состояние работы подсистемы
touch "${LOCK}/nfs-import.lck"
return $?
}
# Функция прекращения импорта файловых систем хранилища посредством NFS
function stop-nfs-import() {
# Перебираем все строки именований импортируемых ресурсов
for INFSS in `grep --ignore-case "^nfs.import=" "${CNF}" | uniq`; do
INFS=`echo ${INFSS} | awk -F = '{print $2}'`
# Перебираем все строки параметров импортируемых ресурсов
for PARAMS in `grep --ignore-case "^nfs.import.${INFS}." "${CNF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".address=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && IADDRESS="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".from=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && IFROM="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".to=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && ITO="${BUFFER}"
done
# Проверяем, импортированы ли ресурсы
STATE=`mount | grep --count --ignore-case "^${IADDRESS}:${IFROM}[ ]*on[ ]*${ITO}"`
if [ "${STATE}" -ne "0" ] ; then
# Демонтируем ресурс NFS (сочетание ключей "force" и "lazy" позволяет отцепить даже задействованный ресурс)
umount.nfs4 "${ITO}" -fl
fi
done
# Удаляем файл блокировки, показывающий на состояние работы подсистемы
rm --force "${LOCK}/nfs-import.lck"
return $?
}
# Функция проверки состояния импортированных файловых систем хранилища посредством NFS
function check-nfs-import() {
# Удостоверимся в том, что публикация ресурсов прошла успешно (в противном случае проверку не запускаем - бессмысленно)
if [ -f "${LOCK}/nfs-import.lck" ]; then
echo "check"
fi
return $?
}
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция импорта файловых систем хранилища посредством NFS
function start-nfs-import() {
# Перебираем все строки именований импортируемых ресурсов
for INFSS in `grep --ignore-case "^nfs.import=" "${CNF}" | uniq`; do
INFS=`echo ${INFSS} | awk -F = '{print $2}'`
# Перебираем все строки параметров импортируемых ресурсов
for PARAMS in `grep --ignore-case "^nfs.import.${INFS}." "${CNF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".address=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && IADDRESS="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".root=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && IROOT="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".from=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && IFROM="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".to=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && ITO="${BUFFER}"
done
# Проверяем наличие субьектов файловой системы
if [ ! -d "${ITO}" ] ; then
echo "${DATE}: The mount point ${ITO} intended to import the resource is not available. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем корректность параметров
if [ "${IROOT}" == "" ] || [ "${IADDRESS}" == "" ] ; then
echo "${DATE}: Not all resource settings are in full. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем, не импортированы ли уже ресурсы
STATE=`mount | grep --count --ignore-case "^${IADDRESS}:${IFROM}[ ]*on[ ]*${ITO}"`
if [ "${STATE}" -eq "0" ] ; then
# Проверяем доступность импортируемого ресурса
STATE=`showmount --exports --no-headers ${IADDRESS} | grep --count --ignore-case "^${IROOT}${IFROM}"`
if [ "${STATE}" -ne "0" ] ; then
# # IOPT="hard,tcp,sync,noatime,nodiratime,nodev,noexec,nosuid"
IOPT="soft, intr, retrans=5, timeo=600, rsize=4096, wsize=4096, tcp, async, noatime, nodiratime, nodev, noexec, nosuid"
mount.nfs4 ${IADDRESS}:${IFROM} ${ITO} -o ${IOPT}
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
echo "${DATE}: Importing resource NFS ${IADDRESS}:${IFROM} completed with error. Aborting." | tee -a "${LOGT}"
return 1
fi
# Даём системе пару секунд на завершение импортирования
sleep 2
# Проверяем успешность импортирования
STATE=`mount | grep --count --ignore-case "^${IADDRESS}:${IFROM}[ ]*on[ ]*${ITO}"`
if [ "${STATE}" -ne "0" ] ; then
echo "${DATE}: Importing resource NFS ${IADDRESS}:${IFROM} completed successfully." | tee -a "${LOGT}"
else
echo "${DATE}: Importing resource NFS ${IADDRESS}:${IFROM} completed with error. Aborting." | tee -a "${LOGT}"
return 1
fi
else
echo "${DATE}: Imported resource NFS ${IADDRESS}:${IROOT}${IFROM} is not available. Aborting." | tee -a "${LOGT}"
return 1
fi
else
echo "${DATE}: Resource NFS ${IADDRESS}:${IFROM} has already been imported." | tee -a "${LOGT}"
fi
done
# Создаём файл блокировки, показывающий на состояние работы подсистемы
touch "${LOCK}/nfs-import.lck"
return $?
}
# Функция прекращения импорта файловых систем хранилища посредством NFS
function stop-nfs-import() {
# Перебираем все строки именований импортируемых ресурсов
for INFSS in `grep --ignore-case "^nfs.import=" "${CNF}" | uniq`; do
INFS=`echo ${INFSS} | awk -F = '{print $2}'`
# Перебираем все строки параметров импортируемых ресурсов
for PARAMS in `grep --ignore-case "^nfs.import.${INFS}." "${CNF}" | uniq` ; do
BUFFER=`echo ${PARAMS} | grep --ignore-case ".address=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && IADDRESS="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".from=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && IFROM="${BUFFER}"
BUFFER=`echo ${PARAMS} | grep --ignore-case ".to=" | awk -F = '{print $2}'`
[ "${BUFFER}" != "" ] && ITO="${BUFFER}"
done
# Проверяем, импортированы ли ресурсы
STATE=`mount | grep --count --ignore-case "^${IADDRESS}:${IFROM}[ ]*on[ ]*${ITO}"`
if [ "${STATE}" -ne "0" ] ; then
# Демонтируем ресурс NFS (сочетание ключей "force" и "lazy" позволяет отцепить даже задействованный ресурс)
umount.nfs4 "${ITO}" -fl
fi
done
# Удаляем файл блокировки, показывающий на состояние работы подсистемы
rm --force "${LOCK}/nfs-import.lck"
return $?
}
# Функция проверки состояния импортированных файловых систем хранилища посредством NFS
function check-nfs-import() {
# Удостоверимся в том, что публикация ресурсов прошла успешно (в противном случае проверку не запускаем - бессмысленно)
if [ -f "${LOCK}/nfs-import.lck" ]; then
echo "check"
fi
return $?
}
Переход к настройке автоматизации сборки файловых систем в единую точку посредством MHDDFS.
Автоматизация MHDDFS ( Автоматизация сборки файловых систем в единую точку посредством MHDDFS. )
18 сентября 2012 (обновлено 17 июля 2019)
Эта публикация отнесена в архив. Она неактуальна.
OS: "Linux Debian 5/6/7 (Lenny/Squeeze/Wheezy)".
Устанавливаем пакет утилит файловой системы MHDDFS:
Заранее готовим точку монтирования:
Создадим конфигурационный файл с перечнем и описанием характеристик ресурсов, готовых к инициализации и использованию.
Фрагмент кода автоматизации функционала сборки в единую точку файловых систем подсистемы хранения:
Здесь размещено описание одного из функциональных блоков инструкций управления самодельным сетевым хранилищем на сочетании технологий "MDADM + LVM + XFS + NFS + MHDDFS" через простейшие BASH-скрипты. Отдельно неприменимо.
Устанавливаем пакет утилит файловой системы MHDDFS:
# aptitude install mhddfs
Заранее готовим точку монтирования:
# mkdir -p /mnt/storage-core0
Создадим конфигурационный файл с перечнем и описанием характеристик ресурсов, готовых к инициализации и использованию.
# vi /usr/local/etc/storage/cnf.d/mhddfs.cnf
....
mhddfs.target=/mnt/storage-core0
mhddfs.source=/mnt/import/node0
mhddfs.source=/mnt/import/node1
mhddfs.source=/mnt/import/node2
mhddfs.source=/mnt/import/node3
....
mhddfs.target=/mnt/storage-core0
mhddfs.source=/mnt/import/node0
mhddfs.source=/mnt/import/node1
mhddfs.source=/mnt/import/node2
mhddfs.source=/mnt/import/node3
....
Фрагмент кода автоматизации функционала сборки в единую точку файловых систем подсистемы хранения:
# vi /usr/local/etc/storage/fnc.d/mhddfs.fnc
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция сбора разрозненых файловых систем хранилища в одну точку монтирования
function start-mhddfs() {
# Вычленяем адрес точки сбора файловых систем
MTARGET=`grep --ignore-case "^mhddfs.target=" "${CNF}" | awk -F = '{print $2}'`
# Проверяем наличие пути к точку сбора файловых систем
if [ ! -d "${MTARGET}" ] ; then
echo "${DATE}: Collection point file system is not available. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем, не смонтирована ли уже сборная файловая система
STATE=`mount | grep --count --ignore-case "on[ ]*${MTARGET}[ ]*type[ ]*fuse.mhddfs"`
if [ "${STATE}" -eq "0" ] ; then
MHDDFS=""
# Перебираем все строки адресов собираемых файловых систем
for MSOURCES in `grep --ignore-case "^mhddfs.source=" "${CNF}" | uniq`; do
MSOURCE=`echo ${MSOURCES} | awk -F = '{print $2}'`
# Проверяем наличие пути к собираемым файловым системам
if [ ! -d "${MSOURCE}" ] ; then
echo "${DATE}: Mounted file system ${MSOURCE} is not available. Aborting." | tee -a "${LOGT}"
return 1
fi
# Добавляем точку монтирования в список
MHDDFS=${MHDDFS}",${MSOURCE}"
done
# Отрезаем лишнюю запятую впереди строки
MHDDFS=`echo "${MHDDFS}" | cut -c 2-`
# Собираем файловые системы в единой точке монтирования (-o logfile=...)
nice --adjustment=-1 mhddfs -o mlimit=100G,direct_io,async,noatime,allow_other "${MHDDFS}" "${MTARGET}" >/dev/null 2>&1
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
echo "${DATE}: Mounting file systems into a single point with error. Aborting." | tee -a "${LOGT}"
return 1
fi
# Даём системе пару секунд на завершение монтирования
sleep 2
# Проверяем успешность монтирования сборной файловой системы
STATE=`mount | grep --count --ignore-case "on[ ]*${MTARGET}[ ]*type[ ]*fuse.mhddfs"`
if [ "${STATE}" -ne "0" ] ; then
echo "${DATE}: Team of the file system is successfully installed." | tee -a "${LOGT}"
else
echo "${DATE}: Mounting the file system team with error. Aborting." | tee -a "${LOGT}"
return 1
fi
else
echo "${DATE}: Team of the file system is already installed at ${MTARGET}." | tee -a "${LOGT}"
fi
# Создаём файл блокировки, показывающий на состояние работы подсистемы
touch "${LOCK}/mhddfs.lck"
return $?
}
# Функция разбора разрозненых файловых систем хранилища из единой точки монтирования
function stop-mhddfs() {
# Вычленяем адрес точки сбора файловых систем
MTARGET=`grep --ignore-case "^mhddfs.target=" "${CNF}" | awk -F = '{print $2}'`
[ -d "${MTARGET}" ] && fusermount -uz "${MTARGET}"
# Удаляем файл блокировки, показывающий на состояние работы подсистемы
rm --force "${LOCK}/mhddfs.lck"
return $?
}
# Функция проверки состояния сборной файловой системы
function check-mhddfs() {
# Удостоверимся в том, что публикация ресурсов прошла успешно (в противном случае проверку не запускаем - бессмысленно)
if [ -f "${LOCK}/mhddfs.lck" ]; then
echo "check"
fi
return $?
}
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция сбора разрозненых файловых систем хранилища в одну точку монтирования
function start-mhddfs() {
# Вычленяем адрес точки сбора файловых систем
MTARGET=`grep --ignore-case "^mhddfs.target=" "${CNF}" | awk -F = '{print $2}'`
# Проверяем наличие пути к точку сбора файловых систем
if [ ! -d "${MTARGET}" ] ; then
echo "${DATE}: Collection point file system is not available. Aborting." | tee -a "${LOGT}"
return 1
fi
# Проверяем, не смонтирована ли уже сборная файловая система
STATE=`mount | grep --count --ignore-case "on[ ]*${MTARGET}[ ]*type[ ]*fuse.mhddfs"`
if [ "${STATE}" -eq "0" ] ; then
MHDDFS=""
# Перебираем все строки адресов собираемых файловых систем
for MSOURCES in `grep --ignore-case "^mhddfs.source=" "${CNF}" | uniq`; do
MSOURCE=`echo ${MSOURCES} | awk -F = '{print $2}'`
# Проверяем наличие пути к собираемым файловым системам
if [ ! -d "${MSOURCE}" ] ; then
echo "${DATE}: Mounted file system ${MSOURCE} is not available. Aborting." | tee -a "${LOGT}"
return 1
fi
# Добавляем точку монтирования в список
MHDDFS=${MHDDFS}",${MSOURCE}"
done
# Отрезаем лишнюю запятую впереди строки
MHDDFS=`echo "${MHDDFS}" | cut -c 2-`
# Собираем файловые системы в единой точке монтирования (-o logfile=...)
nice --adjustment=-1 mhddfs -o mlimit=100G,direct_io,async,noatime,allow_other "${MHDDFS}" "${MTARGET}" >/dev/null 2>&1
# Проверяем успешность завершения операции
if [ "${?}" != "0" ] ; then
echo "${DATE}: Mounting file systems into a single point with error. Aborting." | tee -a "${LOGT}"
return 1
fi
# Даём системе пару секунд на завершение монтирования
sleep 2
# Проверяем успешность монтирования сборной файловой системы
STATE=`mount | grep --count --ignore-case "on[ ]*${MTARGET}[ ]*type[ ]*fuse.mhddfs"`
if [ "${STATE}" -ne "0" ] ; then
echo "${DATE}: Team of the file system is successfully installed." | tee -a "${LOGT}"
else
echo "${DATE}: Mounting the file system team with error. Aborting." | tee -a "${LOGT}"
return 1
fi
else
echo "${DATE}: Team of the file system is already installed at ${MTARGET}." | tee -a "${LOGT}"
fi
# Создаём файл блокировки, показывающий на состояние работы подсистемы
touch "${LOCK}/mhddfs.lck"
return $?
}
# Функция разбора разрозненых файловых систем хранилища из единой точки монтирования
function stop-mhddfs() {
# Вычленяем адрес точки сбора файловых систем
MTARGET=`grep --ignore-case "^mhddfs.target=" "${CNF}" | awk -F = '{print $2}'`
[ -d "${MTARGET}" ] && fusermount -uz "${MTARGET}"
# Удаляем файл блокировки, показывающий на состояние работы подсистемы
rm --force "${LOCK}/mhddfs.lck"
return $?
}
# Функция проверки состояния сборной файловой системы
function check-mhddfs() {
# Удостоверимся в том, что публикация ресурсов прошла успешно (в противном случае проверку не запускаем - бессмысленно)
if [ -f "${LOCK}/mhddfs.lck" ]; then
echo "check"
fi
return $?
}
Комментарии
Отправить комментарий