Using Bacula to archive data.

I hate tape!

Ever since those early days working as an operator, my least favourite job was changing the daily backup tape. Oh sure, if your company has loads of cash you could shell out for an autochanger, but none of the organisations I worked for were ever prepared to pony up the dosh for one of those when they had a tape-monkey to do it for them!

With the cost per GB of storage dropping, backup up to disk was more of a reality and in looking around for something that could (a) Backup to disk (b) across the network and (c) was automated, I discovered “Bacula”.

So out comes the Ubuntu 10.04 server install disk and away we go…

Assumptions

The neat thing with bacula is that there are a many different scenarios you can cater for. I’m not a Bacula master, nor do I profess to know all the ins-and-outs of its configuration files. No. I started this with a plan, a plan to meet a requirement… so the configuration detailed below is what worked for me, to meet my needs. Its online more as a journal (so I can refer back to it if I ever need to rebuild it) than a claimed definitive HOWTO!!

The “so called” plan is build a Bacula server than will backup remote clients, initially to storage local to the server, but later pointing it to my FreeNAS server.

In this “journal” I’ll follow the following steps/stages:

  1. Stage One– Configuring the Bacula server
    • Install and configure Bacula without using “localhost”.
  2. Stage Two– Backup local files to localstorage.
  3. Stage Three– Backup remote files to local storage.
    • Install and configure Bacula file daemon on remote machine.
    • Backup remote files to local storage.
  4. Stage Four– Backup remote files to remote storage.
    • TBA: Install and configure Bacula storage daemon on remote machine.
    • TBA: Backup remote files to remote storage.

The Bacula Home Page contains the latest Bacula news and developments, or for more Bacula configuration options refer to the Bacula User’s Manual

Base Install

The base install of Ubuntu server 10.04 took about 30mins, with standard configuration and stopping “network auto-discovery” to declare a static IP. Once the install was completed I checked for any updates…

sudo aptitude update
sudo aptitude safe-upgrade

First we install MySQL which will be the database engine used to store the catalog information.

sudo aptitude install mysql-client mysql-server

During the install you will be prompted for a password for the MySQL “root” user.

Add SSH server to allow telnet remote access.

sudo aptitude install openssh-server

Next we install Apache web server which will be used by “BWeb” for remote administration (see later sections).

sudo aptitude install apache2

Last but not least… Bacula.

sudo aptitude install bacula

“Postfix” will be installed as one of the packages and you will be asked to select a configuration type. I just selected “Local only” and left the server name as the hostname of the machine. Next you will be asked to “Configure database for bacula-director-mysql with dbconfig-common” – select “Yes” then supply credentials for the database administrator followed by a new password for the bacula database user. The “database administrator” will need to have the appropriate rights to create a database.

Stage One: Configuring the Bacula server

Bacula configuration files are formatted based on resources comprising of directives surrounded by “{}” braces. Each Bacula component has an individual file in the /etc/bacula directory.

The various Bacula components must authorize themselves to each other. This is accomplished using the password directive. For example, the Storage resource password in the /etc/bacula/bacula-dir.conf file must match the Director resource password in /etc/bacula/bacula-sd.conf.

The Console can be used to query the Director about jobs, but to use the Console with a non-root user, the user needs to be in the bacula group. To add a user to the bacula group enter the following from a terminal:

sudo adduser username bacula

Log out then back in to make the change effective. As mentioned in the previous section, I’m going to backup my data to local storage so I need to create a location for the “file media“. While we’re at it I’ll also create a directory where files can be restored to.

sudo mkdir -m 770 /var/lib/archive
sudo mkdir -m 770 /var/lib/archive/backup
sudo mkdir -m 770 /var/lib/archive/restore sudo chown -R  bacula:bacula /var/lib/archive

Everything in Bacula is about using IP addresses or resolvable addresses. But despite my hostnames being resolvable by DNS I still had to make sure there was an entry in the hosts file for this machine before it would work. If you’ve defined a static IP address, this will have been done automatically during the OS install.

Configuring the File Daemon (bacula-fd.conf)

sudo vi /etc/bacula/bacula-fd.conf

In the FileDaemon {} block change

FileDaemon {

    FDAddress = hostname.example.com

}

Restart the bacula file daemon for the changes to take effect

sudo /etc/init.d/bacula-fd restart

Configuring the Storage Daemon (bacula-sd.conf)

sudo vi /etc/bacula/bacula-sd.conf

In the Storage {} block change

Storage {

    SDAddress = hostname.example.com

}

In the Device {} block for “FileStoragechange

Device {
    Name = FileStorage

    Archive Device = /var/lib/archive/backup

}

Restart the bacula storage daemon for the changes to take effect

sudo /etc/init.d/bacula-sd restart

Configuring the Director (bacula-dir.conf)

sudo vi /etc/bacula/bacula-dir.conf

In the Director {} block change

Director {

    DirAddress = hostname.example.com

}

In the Client {} block change

Client {
    Name = hostname-fd
    Address = hostname.example.com

}

In the Storage {} block change

Storage {

    Address = hostname.example.com

}

In the RestoreFiles » Job {} block change

Job {
    Name = "RestoreFiles"

    Where = /var/lib/archive/restore
}

In the Catalog {} block add

Catalog {
    Name = MyCatalog
    dbname = bacula; DB Address = "localhost"; dbuser = "bacula"; dbpassword = "baculaPASSWORD"
}

I don’t think this is a “requirement” but I changed it for the sake of “consistancy” and “completeness”.

Restart the bacula director for the changes to take effect

sudo /etc/init.d/bacula-director restart

Using Bconsole

I have to create the media for the backup jobs to use now. For that we use bconsole

bconsole

Connecting to Director hostname.example.com:9101
1000 OK: hostname-dir Version: 5.0.1 (24 February 2010)
Enter a period to cancel a command.
*

Automatically create the media and label it with the following command

* label
Automatically selected Catalog: MyCatalog
Using Catalog "MyCatalog"
Automatically selected Storage: File
Enter new Volume name:

Enter a meaningfull name for this “File” based media. I call mine “File-Repositoryxxx” where xxx increments 001,002,003… Select a relevant pool to make this media available in (I select “File“). If the permissions on our /var/lib/archive/ directories and the storage daemon are configured correctly, you should see…

Connecting to Storage daemon File at hostname.example.com:9103 ...
Sending label command for Volume "File-Repository001" Slot 0 ...
3000 OK label. VolBytes=202 DVD=0 Volume="File-Repository001" Device="FileStorage" (/var/lib/archive/backup)
Catalog record for Volume "File-Repository001", Slot 0 successfully created.
Requesting to mount FileStorage ...
3906 File device "FileStorage" (/var/lib/archive/backup) is always mounted.

This has now created a volume that will store a maximum of 50GB, as defined in the Pool {} section for File in the bacula-dir.conf

You can check the status of the various components with the status command. To check the status of the director and defined jobs type

* status director

If you’re unsure of a command, you can list them all by typing

* help

For help on a specific command, type help followed by the command name i.e.

* help status
* help list

Stage Two: Backup local files to local storage

In Bacula, the backup jobs are defined in the bacula-dir.conf file in the Job {} blocks. Normally (and for flexability) each job is defined with reference to a Fileset {}, Schedule {}, and Storage {} subset. Common elements can be included in the JobDefs {} subset and can be referenced in each job to avoid repitition and to save on scripting.

We’ll create a simple job to backup the /etc directory (including sub-directories) but ultimately it’s up to you how you’ll configure your jobs.

sudo vi /etc/bacula/bacula-dir.conf

Check what is already defined for JobDefs {}. It shoudl look something like this:

JobDefs {
  Name = "DefaultJob"
  Type = Backup
  Level = Incremental
  Client = hostname-fd
  FileSet = "Full Set"
  Schedule = "WeeklyCycle"
  Storage = File
  Messages = Standard
  Pool = File
  Priority = 10
  Write Bootstrap = "/var/lib/bacula/%c.bsr"
}

NOTE:

Any entries duplicated in the Job { } section, take precedence over ones already defined in the JobDefs { } section.

Next step is to create a new backup job entry.

Job {
  Name = "LocalConfigBackup"
  JobDefs = "DefaultJob"
  Type = Backup
  Level = Full
  Storage = File
  Client = hostname-fd
  FileSet = "LocalConfigFiles"
  Schedule = "DailyBackup"
}

Notes on the Job {} definition:

  • Name – defines the name of this particular job.
  • JobDefs – tells the job to load a particular set of default definitions called “DefaultJob“.
  • Type – tells the director it is a backup job.
  • Level – tells the director it should back *everything* up, not just files that have changed.
  • Storage – tells the director to use the storage defined in the Storage {} section as “File” (this in turn is referenced in the bacula-sd.conf file – in our case the local storage daemon)
  • Client – tells the director to use the client defined as “hostname-fd”, which in our case is the local machine (or more specifically, the local file daemon).
  • FileSet – tells the director to backup the files defined in the FileSet {} section named “LocalConfigFiles“.
  • Schedule – tells the director when to backup the files using values defined in the Schedule {} section named “DailyBackup“.

Now we can create a FileSet {} (referenced in the Job {} above). Filesets are handy and can be used by multiple jobs. An example might be backing up the same files, but off multiple different clients.

FileSet {
  Name = "LocalConfigFiles"
  Include {
    Options {
      signature = MD5
      compression=GZIP
    }
    File = /etc
  }
}

This FileSet will backup the /etc directory. The Options resource directives configure the FileSet to create a MD5 signature for each file backed up, and to compress the files using GZIP.

Next, create a new Schedule for the backup job:

Schedule {
  Name = "DailyBackup"
  Run = Full daily at 06:00
}

The job will run every day at 06:00 or 6:00 am. There are many other scheduling options available. Now lets run bconsole

bconsole

* run

The select the number that corresponds to the job you just created. When prompted with “OK to run?” answer Y and press enter. You should then see…

Job queued. JobId=2
*

It shouldn’t take very long for this job to complete (a few seconds depending on the speed of your machine). If you check the status of the director you should then see your job, with the JobId in the Terminated jobs: section

* status director
hostname-dir Version: 5.0.1 (24 February 2010) x86_64-pc-linux-gnu ubuntu 10.04

Terminated Jobs:
JobId Level Files Bytes Status Finished Name
====================================================================
2 Full 1,284 590.0 K OK 04-Nov-10 13:40 LocalConfigBackup

Congratulations! You have successfully used Bacula to backup files locally!! Now move onto the next section to backup files across the network.

Stage Three: Backup remote files to local storage.

Probably one of the most helpfull images in the Bacula documentation was this one, included below. It was really handy in helping to understand the relationship between the various components, particularly the remote ones!Bacula Relationship

The File-Daemon on the remote machine

Because of the various “distros” I’ll assume at this point that you’ve installed the bacula-fd daemon on the client you intend to backup. Start by opening up the bacula-fd.conf file on the remote machine and make a few changes.

sudo vi /etc/bacula/bacula-fd.conf

Director {
  Name = hostname-dir   # this is the name of the director
  Password = "aUniquePASSWORDofYourChoiceforFD"
}

 
FileDaemon {                          
  Name = FDName-fd
  FDport = 9102    
  WorkingDirectory = /var/lib/bacula
  Pid Directory = /var/run/bacula
  Maximum Concurrent Jobs = 20
  FDAddress = FDName.example.com
}

Restart the daemon for the changes to become effective. In some cases it may not be started already and you may even need to edit init scripts to ensure it starts on boot (CentOS and FreeBSD I’m looking at you!)

sudo /etc/init.d/bacula-fd restart

Now head back over to the machine running the director and edit…

sudo vi /etc/bacula/bacula-dir.conf

 and add an entry for the client you’ve just created.

Client {
  Name = FDName-fd
  Address = FDName.example.com
  FDPort = 9102
  Catalog = MyCatalog
  Password = "aUniquePASSWORDofYourChoiceforFD"
  File Retention = 30 days
  Job Retention = 6 months
  AutoPrune = yes
}

You can also create a Job, Fileset and Schedule as detailed in the previous section and then restart the director for the changes to take effect.

sudo /etc/init.d/bacula-director restart

NOTE:

Be aware of firewalls running on any machines that run the client, as they may block traffic and stop communication between the Director and FileDaemon.

You can check communication between the Director and FileDaemon using bconsole

bconsole

* status client

then select the appropriate client from the list. If you’ve created a Job, Fileset etc… you can perform an estimate on the backup Job…

* estimate

then select the appropriate remote job from the list. You should get a response similar to…

2000 OK estimate files=2678 bytes=109,647,535

Notes on installing bacula-fd on CentOS 5

My VoIP server uses CentOS as its Linux distro, and it would be handy to back the config files and voicemail messages. By default, none of the YUM repositories (package manager for CentOS) have a copy of bacula, so you have to add one that does… EPEL. There was reference online to RPMForge, but once installed I couldn’t find a copy of bacula on it. This site has a great post on how to enable the EPEL repository, otherwise…

rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm

will create the relevant repository entries in /etc/yum.repos.d/. Then download and install the bacula-client…

yum install bacula-client

All you have to do is configure the FileDaemon as detailed above. I used chkconfig to get it to start at boot.

chkconfig --level 345 bacula-fd on

Stage Four: Backup remote (or local) files to remote storage

Notes on installing bacula-sd on FreeBSD 7.1

To install the storage daemon add the repository…

setenv PACKAGESITE ftp://ftp.freebsd.org/pub/FreeBSD/ports/i386/packages-7-stable/Latest/ pkg_add -r bacula-server

cd /usr/local/lib
ln -s libintl.so.8 libintl.so.9
cp bacula-sd.conf.sample bacula-sd.conf
vi bacula-sd.conf

Start the storage daemon service

/usr/local/sbin/bacula-sd -c /usr/local/etc/bacula-sd.conf

Because this server is actually running FreeNAS server, use the web interface to add the above command into the System|Advanced|Command scripts section.

 Installing Bweb

Bweb is a perl based web program that provides a tool to perform basic operations and to get statistics. It obtains its information from your catalog database and the bconsole program. In this section the assumption is that you already have a working Bacula server, Apache server and MySQL.

First, we install some pre-requisite libraries.

sudo apt-get install -y libgdchart-gd2-xpm* make php-pear preload
sudo apt-get install -y libgd-graph-perl libhtml-template-perl libexpect-perl
sudo apt-get install -y libdbd-mysql-perl libdbd-pg-perl libdbi-perl libtime-modules-perl
sudo apt-get install -y libdate-calc-perl
sudo pear install db

You may get a warning at this stage that “DB” is depreciated in favour of “MDB2“. Since MDB2 is newer it’s probably better, but DB works fine for me – as part of the cleanup, if I find there is a major difference, I’ll note it here and alter my notes.

Now we download the bweb which is in the latest Bacula package. At the time of writing this, the latest version was 5.0.3, but you can check here and alter the instructions as required.

wget http://sourceforge.net/projects/bacula/files/bacula/5.0.3/bacula-gui-5.0.3.tar.gz

Now extract the files, compile the source and install some of the files to thier relevant locations

tar -zxvf bacula-gui-5.0.3.tar.gz
cd bacula-gui-5.0.3/bweb
sudo perl Makefile.PL
sudo make install
sudo pear channel-update pear.php.net
sudo mkdir -m 755 /usr/lib/cgi-bin/bweb
sudo install -m 755 -o root -g root cgi/*.pl /usr/lib/cgi-bin/bweb

Next, some authority changes, template copying and installing the HTML files

sudo chown root:bacula /etc/bacula
sudo vi /etc/bacula/bweb.conf

    $VAR1 = { template_dir => "/usr/share/bweb/tpl" };

sudo chown root:www-data /etc/bacula/bweb.conf
sudo mkdir -p /usr/share/bweb/tpl/en
sudo install -m 644 -o root -g root bweb/lang/en/tpl/*.tpl /usr/share/bweb/tpl/en

sudo mkdir /var/www/bweb
sudo install -m 644 -o root -g root bweb/html/*.{js,png,css,gif,ico,html} /var/www/bweb

sudo chmod 666 /etc/bacula/bconsole.conf

The Ext JS JavaScript library is needed for the “Web Restore” functionality to work properly. It’s probably writtern somewhere, but it didn’t leap out at me… it was Firebug that helped me to discover what was missing. The latest version at the time of publication was 3.3.0. First (in case you don’t have it already) install unzip.

sudo aptitide install -y unzip

Then we get the Ext JS package from the Ext JS CDN.

sudo wget http://extjs.cachefly.net/ext-3.3.0.zip

Then install the files in the bweb folder

sudo unzip ext-3.3.0.zip
sudo mkdir -m 755 /var/www/bweb/ext
sudo cp -r ext-3.3.0/. /var/www/bweb/ext

A few more changes to enable the web restore functionality of bweb…

sudo vi /etc/apache2/httpd.conf

    Alias /bweb/fv /var/spool/bweb
    <Directory "/var/spool/bweb">
        Options None
        AllowOverride AuthConfig
        Order allow,deny
        Allow from all
    </Directory>

sudo mkdir /var/spool/bweb
sudo chmod 700 /var/spool/bweb
sudo chown www-data /var/spool/bweb

then there are a number of changes to the database structure to support bweb that need to be made. You can import the commands from the bweb-mysql.sql file located in bacula-gui-5.0.3/bweb/script, but because I wasn’t sure of the results I just “cut-and-pasted” section by section using multiple SSH sessions, one running MySQL the other with the bacula-gui-5.0.3/bweb/script/bweb-mysql.sql file open, checking the results after each command.

To import it directly into the bacula database use

mysql -u root -p bacula

mysql> SOURCE path to files/bacula-gui-5.0.3/bweb/script/bweb-mysql.sql;

Next we must give the www-data (apache) user permission to execute the bconsole.

NOTE:

The locations highlighted in red below may differ on other Linux distributions… BE AWARE – and substitute as required!

cd /etc
sudo visudo

After the line root ALL=(ALL) ALL we add this…

www-data ALL= NOPASSWD: /usr/sbin/bacula-console
www-data ALL = (root) NOPASSWD: /usr/sbin/mtx -f /dev/changer transfer *
www-data ALL = (root) NOPASSWD: /usr/sbin/mtx -f /dev/changer status
www-data ALL = (root) NOPASSWD: /usr/sbin/mtx -f /dev/changer load *
www-data ALL = (root) NOPASSWD: /usr/sbin/mtx -f /dev/changer unload *

You then Ctrl-X to exit, then when it warns you that the buffers have changed, answer “Y” and then change the file name from sudoers.tmp to sudoers. Answer “Y” to overwrite.

Now we go back to the bweb.conf file to make some final changes

sudo vi /etc/bacula/bweb.conf

$VAR1 = bless( {
  'graph_font' => '/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf',
  'name' => undef,
  'config_file' => '/etc/bacula/bweb.conf',
  'bconsole' => '/usr/sbin/bacula-console -n -c /etc/bacula/bconsole.conf',
  'ach_list' => {
     'S1_L80' => bless( {
         'info' => {
                     'drive' => 0,
                     'io' => 0,
                     'slot' => 0
                   },
         'name' => 'S1_L80',
         'bweb' => undef,
         'device' => '/dev/changer',
         'drive' => [],
         'debug' => 0,
         'label' => {},
         'precmd' => 'sudo',
         'io' => [],
         'mtxcmd' => '/usr/sbin/mtx',
         'drive_name' => [
                           'S1_L80_SDLT0',
                           'S1_L80_SDLT1'
                         ],
         'slot' => []
       }, 'Bweb::Autochanger' )
   },
  'dbi' => 'DBI:mysql:database=bacula',
  'user' => 'bacula',
  'password' => 'baculaPASSWORD',
  'fv_write_path' => '/var/spool/bweb',
  'template_dir' => '/usr/share/bweb/tpl',
  'lang' => 'en',
  'error' => '',
  'debug' => 0,
  'email_media' => 'someone@hostname.example.com'
}, 'Bweb::Config' );

Last of all, restart the server for the changes to take effect…

sudo shutdown -r now

If everything has been configured properly, you should be able to start your web browser and go to http://hostname.example.com/bweb and see the bweb interface with information on your backup jobs, media, etc..

IMPORTANT NOTE:

While BWEB was tested – and worked – with both IE (8) and Firefox (3.6.13), the “Web Restore” component only seemed to function with Firefox. When selected from IE nothing seemed to happen, and no data displayed within the browser window. Since Firefox is my browser of choice I hadn’t put too much effort into investigating why this is, and did… well… nothing! If I do find out, or happen upon a reason I’ll note my findings here.

MILESTONE 24/12/2010:

I had a user delete all the messages from their “Sent” folder and I had to use BWEB to do a restore – It was SO COOL! I had some trepidation over how the restore would work thinking that the Bacula File Daemon would only allow one-way traffic and I would have to restore to a local destination and copy the files to their original location (and all the file permission changes that would ensue).

BUT… Bacula (specifically via BWEB’s “Web Restore” function) allowed a restore to the original location with prompting to overwrite if required!! HOORAY!! I love you Bacula, you’ve made my Christmas…