Чрезмерный размер инкрементной резервной копии в BareOS
У меня есть установка BareOS с очень небольшим изменением файлов конфигурации по умолчанию. Выполняется полное, инкрементное и дифференциальное резервное копирование. Похоже, что большинство клиентов получают резервные копии, как и ожидалось.
Тем не менее, один из моих клиентов, по-видимому, неоднократно резервировал более 10% всей файловой системы в каждом инкрементном цикле.
Как найти самые большие файлы и папки, для которых выполняется постоянное резервное копирование?
BAT здесь не очень полезен, так как в нем указан только размер самого файлового узла, а не весь размер папки. Я эффективно ищу du
команда, которая работает в рамках BareOS для конкретной попытки резервного копирования.
3 ответа
Хотя я ценю усилия @damiano-verzulli, обсуждение IRC-канала BareOS на FreeNode ускользнуло от этого ответа:
Оказывается, что Kjetil Torgrim Homme уже написал сценарий для этого, который называется bacula-du
, (Наряду с довольно многими другими полезными скриптами!)
Они все перечислены и доступны здесь:
http://heim.ifi.uio.no/kjetilho/hacks/
Особенно bacula-du
объясняется так:
Usage: bacula-du [OPTIONS] -j JOBID Summarize disk usage of directories included in the backup JOBID Main options are: -a, --all write counts for all files, not just directories -S, --separate-dirs do not include size of subdirectories -t, --threshold=SIZE skip output for files or directories with usage below SIZE. default is 1 octet. -L, --largest=NUM only print NUM largest directories/files There is also an alternate mode which can be useful as a faster alternative to a verify job. Usage: bacula-du --md5sum -j JOBID --md5sum output list of all files in job in md5sum format
bacula-du (версия 1.4)
Есть одна небольшая заметка, которую я должен добавить здесь. Чтобы это работало, он должен иметь доступ к базе данных (очевидно). В конфигурации по умолчанию он использует механизм безопасности, основанный на пользователях, поэтому для его работы необходимо выполнить команду от имени пользователя bareos, например
$ sudo -u bareos ./bacula-du -j 1429
done reading database.
807160 /log/
6372 /var/openldap-data/
6372 /var/
813532 /admin/
...
119983392 /
Обратите внимание, что важное обновление было добавлено в конце первой части
К сожалению, даже если очень просто точно проверить, что произошло с конкретной резервной копией, получить размер резервных копий файлов не так просто.
Давайте углубимся в некоторые примеры.
В моем случае из bconsole
Я могу получить список последних работ с:
*list jobs
Automatically selected Catalog: CatalogoBareos
Using Catalog "CatalogoBareos"
+-------+------------------------+---------------------+------+-------+------------+-------------------+-----------+
| JobId | Name | StartTime | Type | Level | JobFiles | JobBytes | JobStatus |
+-------+------------------------+---------------------+------+-------+------------+-------------------+-----------+
[...]
| 7,060 | backup-XXXXXXXX | 2016-01-02 16:00:50 | B | I | 2 | 74,225,116 | T |
| 7,062 | backup-YYY | 2016-01-02 16:04:47 | B | F | 890,482 | 181,800,839,481 | T |
[...]
+-------+------------------------+---------------------+------+-------+------------+-------------------+-----------+
Из приведенного выше вы можете увидеть две работы:
- Задание 7060: резервное копирование "I", интересные 2 файла, всего 74 МБ данных;
- Задание 7062: полная резервная копия, интересный файл 890492, всего 181 ГБ данных;
Давайте сосредоточимся на работе 7060, так как она является дополнительной. Давайте проверим, какие файлы были заархивированы:
*list files jobid=7060
c:/allXXX_Backup/PLONE/Backup_Plone.bkf
c:/allXXX_Backup/PLONE/
+-------+-----------------+---------------------+------+-------+----------+------------+-----------+
| JobId | Name | StartTime | Type | Level | JobFiles | JobBytes | JobStatus |
+-------+-----------------+---------------------+------+-------+----------+------------+-----------+
| 7,060 | backup-XXXXXXXX | 2016-01-02 16:00:50 | B | I | 2 | 74,225,116 | T |
+-------+-----------------+---------------------+------+-------+----------+------------+-----------+
*
Так что Job 7060 заинтересовал один файл (Backup_Plone.bkf) и один каталог (содержащий папку).
К сожалению, как вы можете видеть, вывод list files jobid=7060
НЕ предоставляет необходимый вам размер файла..... это полезно, надеюсь, но не решает вашу проблему.
Давайте шаг вперед.
Я путешествовал по всей официальной документации bareos, так как не смог найти правильный способ получить "размер файла" из bconsole. Поэтому я решил пойти тяжелым путем: прямой SQL-доступ к каталогу.
Примечание: пожалуйста, будьте предельно осторожны, когда имеете дело с прямым доступом к каталогу, так как каждое неправильное действие может привести к серьезному повреждению и потере данных!
После подключения к DB-движку (MySQL, в моем случае.... но это деталь, как и в PostgreSQL, то же самое), я увидел, что метаданные файла резервной копии хранятся (... среди прочих) в:
File
Таблица: он хранит в основном все метаданные, за исключением...Filename
таблица: в ней хранится имя файла резервной копииPath
таблица: хранит полный путь к резервному файлу
С большим удивлением я обнаружил, что File
таблица не включает size
поле. Таким образом, с помощью простого запроса невозможно получить то, что нам нужно. Во всяком случае, я нашел интересный LStat
поле (подробнее об этом позже).
Поэтому я запустил следующий запрос SQL:
select
f.JobId,f.LStat,f.MD5, fn.Name, p.Path
from
Filename fn,
File f,
Path p
where
f.JobId=7060 and
fn.FilenameId = f.FilenameId and
p.PathId = f.PathId
и получил следующие результаты:
mysql> select f.JobId,f.LStat,f.MD5, fn.Name, p.Path
-> from
-> Filename fn,
-> File f,
-> Path p
-> where
-> f.JobId=7060 and
-> fn.FilenameId = f.FilenameId and
-> p.PathId = f.PathId
-> ;
+-------+------------------------------------------------------+------------------------+------------------+-------------------------+
| JobId | LStat | MD5 | Name | Path |
+-------+------------------------------------------------------+------------------------+------------------+-------------------------+
| 7060 | A A IH/ B A A A EbJQA A A BWheFw BWheFw BTD/En A A L | 8ZuPGwdo9JYJileo+sVlfg | Backup_Plone.bkf | c:/all***_Backup/PLONE/ |
| 7060 | A A EH/ B A A A A A A BWhRjY BWgz4o BTD/En A A L | 0 | | c:/all***_Backup/PLONE/ |
+-------+------------------------------------------------------+------------------------+------------------+-------------------------+
2 rows in set (0.00 sec)
Для LStat
в Официальном руководстве разработчика BareOS я увидел:
> Column Name Data Type Remark
> [...]
> LStat tinyblob File attributes in base64 encoding
Итак, теперь проблема заключается в следующем:
- Включает ли LStat размер файла?
и, как я бы поставил на "ДА! Определенно!":
- Как можно получить FileSize из строки LStat?
Быстрый поиск "BareOS LStat" привел меня к нескольким результатам. Через несколько секунд я получил эту ветку, включающую несколько комментариев о поле LStat, в том числе небольшой скрипт PERL для правильного его декодирования. Вот оно (* любезно предоставлено Брайаном Макдональдом, 2005 *), слегка измененное, чтобы лучше соответствовать вашим потребностям:
#!/usr/bin/perl
my $base64_digits =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
my $base64_values = { };
my $val = 0;
foreach my $letter ( split(//, $base64_digits ) )
{ $base64_values->{ $letter } = $val++; }
print "Please, enter your LSTAT string:";
my $lstat = <STDIN>;
my $vals = decode_stats($lstat);
print "Here are the results:\n";
foreach my $key (keys %{$vals}) {
printf("%15s: %s\n",$key, $$vals{$key}) ;
}
exit;
sub decode_stats
{
my $stats = shift;
my $hash = { };
# The stats data is in base64 format. Yuck! - I mean Yay!
# Each value is base64 encoded incorrectly, a deficiency we have
# to correct here. In particular, some values are encoded with a single
# base64 character. This results in a 6 bit value, and you have to
# understand how bacula padded and manipulated those values before storing
# them in the DB.
# the fields are, in order:
my @fields = qw(
st_dev st_ino st_mode st_nlink st_uid st_gid st_rdev st_size
st_blksize st_blocks st_atime st_mtime st_ctime LinkFI st_flags data
);
# Decoding this mess is based on reading src/lib/base64.c in bacula, in
# particular the to_base64 function, which is how these got in the DB in
# the first place.
my $field_idx = 0;
foreach my $element ( split( /\s+/, $stats ) )
{
my $result = 0;
for ( my $i = 0; $i<length($element); $i++ )
{
if ($i) { $result <<= 6; }
my $r = substr($element, $i, 1 );
$result += $base64_values->{$r};
}
$hash->{ $fields[$field_idx] } = $result;
$field_idx++;
}
return $hash;
}
Когда он запускается и получает строку LSTAT, он сообщает много данных, включая размер файла (st_size, последнее поле вывода):
verzulli@iMac-Chiara:/tmp$ perl pp.pl
Please, enter your LSTAT string:A A IH/ B A A A EbJQA A A BWheFw BWheFw BTD/En A A L
Here are the results:
LinkFI: 0
st_atime: 1451614576
st_ino: 0
st_flags: 0
st_mtime: 1451614576
st_dev: 0
st_nlink: 1
st_blksize: 0
st_blocks: 0
data: 11
st_gid: 0
st_mode: 33279
st_uid: 0
st_rdev: 0
st_ctime: 1393553703
st_size: 74224640
Итак, теперь у нас есть размер файла, но, к сожалению, он не так легко доступен в одном запросе, чтобы найти самый большой файл за одно задание резервного копирования.
Существует несколько решений:
если вы используете MySQL 5.6.1 или более позднюю версию или механизм СУБД, поддерживающий кодирование / декодирование BASE_64, вы можете запросить SUBSTR в LSTAT, а затем попросить механизм БД декодировать его значение как Base64. Например, смотрите здесь
Вы могли бы написать ХРАНЕННУЮ ПРОЦЕДУРУ. На самом деле он должен уже присутствовать в PostgreSQL, как для этого (кто заявляет: " ... Добавил пример хранимых процедур postgresql для поля lstat.... ");
Вы могли бы написать небольшой скрипт на PERL, опрашивая каталог и проходя через процесс декодирования
...
Надеюсь, этого будет достаточно;-)
Обновление 1
Я только что обнаружил существование API BVFS, явно " ... предназначенного в основном для разработчиков, которые хотят разработать новый интерфейс GUI для Bareos... ".
Эти API предоставляют новый набор команд (так называемые " точечные команды "), в том числе интересный .bvfs_lsfiles
который показывает на консоли некоторые метаданные, включая поле LSTAT. Так:
- Определенно возможно получить поле LSTAT без прямого доступа к базовой БД / Каталогу.
Кроме того, в BareOS 15.2 был представлен новый "режим API 2", добавив поддержку вывода JSON. Я только что проверил это:
- с включенным API V.2 объекты JSON, возвращаемые
.bvfs_lsfiles
, содержит поле размера файла, правильно декодированное.
Вот следующий пример:
*.bvfs_update
Using Catalog "MyCatalog"
*.bvfs_lsfiles path=/var/log/2016/01/06/ jobid=79
107131 34080 3614785 79 P0A CCMR IGA B A A A H1V BAA BI BWjIkK BWjJAx BWjJAx A A C shorewall.log
107131 34081 3614786 79 P0A CCMQ IGA B A A A BT1 BAA Q BWjIkK BWjI7p BWjI7p A A C system.log
*.api 2
{
"jsonrpc": "2.0",
"id": null,
"result": {
"api": 2
}
}*
*.bvfs_lsfiles path=/var/log/2016/01/06/ jobid=79
{
"jsonrpc": "2.0",
"id": null,
"result": {
"files": [
{
"type": "F",
"stat": {
"dev": 64768,
"nlink": 1,
"mode": 33152,
"ino": 533265,
"rdev": 0,
"user": "root",
"group": "root",
"atime": 1452050698,
"size": 32085,
"mtime": 1452052529,
"ctime": 1452052529
},
"pathid": 107131,
"name": "shorewall.log",
"fileid": 3614785,
"filenameid": 34080,
"jobid": 79,
"lstat": "P0A CCMR IGA B A A A H1V BAA BI BWjIkK BWjJAx BWjJAx A A C",
"linkfileindex": 0
},
{
"type": "F",
"stat": {
"dev": 64768,
"nlink": 1,
"mode": 33152,
"ino": 533264,
"rdev": 0,
"user": "root",
"group": "root",
"atime": 1452050698,
"size": 5365,
"mtime": 1452052201,
"ctime": 1452052201
},
"pathid": 107131,
"name": "system.log",
"fileid": 3614786,
"filenameid": 34081,
"jobid": 79,
"lstat": "P0A CCMQ IGA B A A A BT1 BAA Q BWjIkK BWjI7p BWjI7p A A C",
"linkfileindex": 0
}
]
}
}*
Итак, в конце, с последней версией BareOS, первоначальная проблема, кажется, решаема без прямого доступа к каталогу.
В дополнение к полезному ответу Damiano Verzulli
Это не очень эффективно, поскольку каждый раз при запуске переопределяет переменные. Я ожидаю, что для больших объемов файлов он будет на порядок медленнее, чем собственный код.
USE bacula;
-- Function to add. (Only once)
-- OUTPUTS:
-- 1: Device
-- 2: Inode
-- 3: Mode (unix permissions)
-- 4: Hard link count
-- 5: UID
-- 6: GID
-- 7: DeviceType
-- 8: Size in bytes
-- 9: Block size (4k?)
-- 10: Last access
-- 11: Last modified
-- 12: Last changed
-- 13/14/15 Link/flags/misc.
DELIMITER $$
CREATE OR REPLACE FUNCTION base64_decode_lstat (lsIndex INT ,lstat VARCHAR(128)) RETURNS BIGINT
BEGIN
DECLARE i,j,partlen INT;
DECLARE alphabet BINARY(64);
DECLARE part VARCHAR(64);
DECLARE iOut BIGINT;
SET part = TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(lstat, ' ', lsIndex), ' ', -1));
SET alphabet = BINARY 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
SET i = 0;
SET iOut = 0;
SET partlen = LENGTH(part);
WHILE i < partlen DO
SET j = partlen - i - 1;
-- Note: SUBSTRING is not zero-indexed, so +1 as otherwise off-by-1.
-- Note 2: LOCATE is also not zero-indexed, so subtract 1 to fix off-by-1.
-- Note 3: j is made zero-indexed so it runs from n-1 to 0.
-- This does addition in base-64.
SET iOut = iOut + ((LOCATE(SUBSTRING(part, i + 1, 1), alphabet) - 1) << (6 * j));
SET i = i + 1;
END WHILE;
RETURN iOut;
END $$
DELIMITER ;
Пример использования (получается временная метка unix для времени изменения первого файла в базе данных);
SELECT base64_decode_lstat(11, (SELECT File.LStat FROM File LIMIT 1));