Thursday, September 1, 2016

Creating a Backup Server With Deduplication On Linux

The other day I needed to create a server that would be responsible for the backing up of data on a Windows share.

This was a critical chore since this share contained every department's data of said company.

Deduplication was an obvious choice for the disk space savings. Also, a method of syncing that would allow a differential backup had to be employed as well.

The obvious choice for deduplication would be ZFS, which is the most popular one. Alas, it is a memory hog and therefore Lessfs and OpenDedup were considered instead. In the end, I chose OpenDedup, as in the time of writing this, it was a project that was more recently updated than its competitor.

The obvious choice for the actual backing up of the files was rsync. No other comments are required, I think.

CentOS 6.8 was chosen as the O/S of choice for this one.

Our IP is 192.168.1.36 and the Windows share is at \\192.168.0.29\WORK. Our domain is MYNETWORK and the domain administrator's password is p@$$W0r(!

So let's move on to the deployment. Updating our packages and installing our favorite repos for one:

[root@BAK ~]# yum -y update
[root@BAK ~]# rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
[root@BAK ~]# rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
[root@BAK ~]# vi /etc/yum.repos.d/epel.repo
[epel]
name=Extra Packages for Enterprise Linux 6 - $basearch
#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=$basearch
failovermethod=priority
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6

[epel-debuginfo]
name=Extra Packages for Enterprise Linux 6 - $basearch - Debug
#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch/debug
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-debug-6&arch=$basearch
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
gpgcheck=1

[epel-source]
name=Extra Packages for Enterprise Linux 6 - $basearch - Source
#baseurl=http://download.fedoraproject.org/pub/epel/6/SRPMS
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-source-6&arch=$basearch
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
gpgcheck=1

Now, installing necessary packages:

[root@BAK ~]# yum -y install autofs cifs-utils smbclient samba-client samba samba-common samba-doc rsync ntp ntpdate

Configure ntp:

[root@BAK ~]# chkconfig ntpd on
[root@BAK ~]# ntpdate -d -u 3.europe.pool.ntp.org
[root@BAK ~]# service ntpd start

Disable SELinux:

[root@BAK ~]# echo 0 > /selinux/enforce
[root@BAK ~]# vi /etc/sysconfig/selinux
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=disabled
# SELINUXTYPE= can take one of these two values:
#     targeted - Targeted processes are protected,
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted

We'll be taking the backup on an external USB drive. Let's see what's already mounted:

[root@BAK ~]# df -kh
Filesystem            Size  Used Avail Use% Mounted on
/dev/sdd3             909G  3.5G  859G   1% /
tmpfs                 3.9G     0  3.9G   0% /dev/shm
/dev/sdd1             512M  280K  512M   1% /boot/efi
/dev/sde1             147G   60M  140G   1% /export

And the disks available are:

[root@BAK ~]# blkid -c /dev/null
/dev/sde1: UUID="0a9cb7c1-ee2c-439b-a73e-c55441f9743a" TYPE="ext4" 
/dev/sdd1: SEC_TYPE="msdos" UUID="B7C5-062C" TYPE="vfat" 
/dev/sdd2: UUID="98591ed9-25a1-404c-be7f-7740a53f69c7" TYPE="swap" 
/dev/sdd3: UUID="b1724545-924d-4ec1-814b-73301637713a" TYPE="ext4" 
/dev/sdf1: UUID="5dd578a1-b77c-4d3f-b21a-8f330474667a" TYPE="ext4"

There were a multitude of ways we could find this out, but I find the one in particular the easiest and fastest one.

Let's format our disk:

[root@BAK ~]# parted /dev/sdf
GNU Parted 2.1
Using /dev/sdf
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) mklabel gpt
Warning: The existing disk label on /dev/sdf will be destroyed and all data on this disk will be lost. Do you want to continue?
Yes/No? yes
(parted) unit TB                                                          
(parted) mkpart primary 0.00TB 4.00TB                                     
(parted) quit
Information: You may need to update /etc/fstab.
[root@BAK ~]# mkfs.ext4 /dev/sdf1
mke2fs 1.41.12 (17-May-2010)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=1 blocks, Stripe width=0 blocks
244187136 inodes, 976745728 blocks
48837286 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
29808 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks: 
 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
 4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968, 
 102400000, 214990848, 512000000, 550731776, 644972544

Writing inode tables: done                            
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 34 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.

Mount it and make it a permanent mount:

[root@BAK ~]# mkdir /work_backup
[root@BAK ~]# mount /dev/sdf1 /work_backup
[root@BAK ~]# df -kh
Filesystem            Size  Used Avail Use% Mounted on
/dev/sdd3             909G  3.5G  859G   1% /
tmpfs                 3.9G     0  3.9G   0% /dev/shm
/dev/sdd1             512M  280K  512M   1% /boot/efi
/dev/sde1             147G   60M  140G   1% /export
/dev/sdc1             3.6T   68M  3.4T   1% /work_backup
[root@BAK ~]# vi /etc/fstab
#
# /etc/fstab
# Created by anaconda on Mon Aug 29 15:39:38 2016
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=b1724545-924d-4ec1-814b-73301637713a /                       ext4    defaults                   1 1
UUID=B7C5-062C                            /boot/efi               vfat    umask=0077,shortname=winnt 0 0
UUID=0a9cb7c1-ee2c-439b-a73e-c55441f9743a /export                 ext4    defaults                   1 2
UUID=98591ed9-25a1-404c-be7f-7740a53f69c7 swap                    swap    defaults                   0 0
UUID=583274ec-05f3-49c5-ace7-af9ad144540d /work_backup            ext4    defaults                   0 0
tmpfs                                     /dev/shm                tmpfs   defaults                   0 0
devpts                                    /dev/pts                devpts  gid=5,mode=620             0 0
sysfs                                     /sys                    sysfs   defaults                   0 0
proc                                      /proc                   proc    defaults                   0 0

Configure iptables:

[root@BAK ~]# vi /etc/sysconfig/iptables
# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -s 192.168.1.36/32 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
[root@BAK ~]# iptables-restore < /etc/sysconfig/iptables

Change our ulimits:

[root@BAK ~]# vi /etc/security/limits.conf
....
* soft nofile 65535
* hard nofile 65535
* - nofile 65535

Edit our hosts file (host resolution is required by OpenDedup, otherwise a generic Java error will occur). Remember, our IP is 192.168.1.36. Our FQDN is BAK.mynetwork.com:

[root@BAK ~]# vi /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.1.36 BAK.mynetwork.com

Install OpenDedup and mount our new, deduplicated volume:

[root@BAK ~]# wget http://www.opendedup.org/downloads/sdfs-latest.rpm
[root@BAK ~]# yum -y install jsvc libxml2 java-1.8.0-openjdk fuse
[root@BAK ~]# cd /work_backup/
[root@BAK ~]# mkdir -p /media/pool0
[root@BAK work_backup]# mkfs.sdfs --volume-name=pool0 --volume-capacity=6TB --hash-type=murmur3_128 --io-chunk-size=64 --base-path=/work_backup
[root@BAK work_backup]# mount -t sdfs pool0 /media/pool0/

  • --volume-name: The name of the volume. THIS IS A REQUIRED OPTION.
  • --volume-capacity: Capacity of the volume in [MB|GB|TB].  THIS IS A REQUIRED OPTION   
  • --hash-type <tiger16|tiger24|murmur3_128>: This is the type of hash engine used to calculate a unique hash. The valid options for hash-type are tiger16 tiger24  murmur3_128 This Defaults to murmur3_128
  • --io-chunk-size <SIZE in kB>: The unit size, in kB, of chunks stored. Set this to 4 if you would like to dedup VMDK files inline.  Defaults to 128 
  • --base-path <PATH>: The folder path for all volume data and meta data. Defaults to: /opt/sdfs/<volume name>. Here we wanted our volume to be stored on our external disk so we set that as the base path. 
SDFS will work within most networks but speed will suffer if network speed is low or the network latency is high. 1Gb/s or above dedicated network is recommeded. The developers of OpenDedup recommend to update your /etc/sysctl.conf to improve network IO performance as follows:

[root@BAK work_backup]# vi /etc/sysctl.conf
...
vm.swappiness=1
net.core.rmem_max=254800000
net.core.wmem_max=254800000
net.core.rmem_default=254800000
net.core.wmem_default=254800000
net.core.optmem_max=25480000
net.ipv4.tcp_timestamps=0
net.ipv4.tcp_sack=0
net.core.netdev_max_backlog=250000
net.ipv4.tcp_mem=25480000 25480000 25480000
net.ipv4.tcp_rmem=4096 87380 25480000
net.ipv4.tcp_wmem=4096 65536 25480000
net.ipv4.tcp_low_latency=1
[root@BAK work_backup]# sysctl -p

We will not put this volume in fstab, as we need it as an extremely delayed mount, after pretty much every service is finished. So what we can do is either create a service for it, or put it in our crontab. We'll save this one for later. In the meantime, let's check out that Windows share:

[root@BAK work_backup]# smbclient //192.168.0.29/WORK -U "MYNETWORK\Administrator" 
Enter MYNETWORK\Administrator's password: p@$$W0r(!
Domain=[MYNETWORK] OS=[Windows Server 2003 R2 3790 Service Pack 2] Server=[Windows Server 2003 R2 5.2]
smb: \> quit

Yup. Windows Server 2003 SP2. Gotta love this job. Let's automount that share:

[root@BAK work_backup]# cd ~ 
[root@BAK ~]# vi /etc/auto.master
....
/mnt/work    /etc/auto.work    --timeout=1800 --ghost
[root@BAK ~]# mkdir /mnt/work
[root@BAK ~]# vi /etc/auto.work
work_backup     -fstype=cifs,rw,noperm,username=MYNETWORK\\Administrator,password=p@$$W0r(!,_netdev,iocharset=utf8 ://192.168.0.29/WORK
[root@BAK ~]# chkconfig autofs on
[root@BAK ~]# service autofs start
Loading autofs4:                                           [  OK  ]
Starting automount:                                        [  OK  ]
[root@BAK ~]# cd /mnt/work/
[root@BAK work]# ls -la
total 4
drwxr-xr-x. 3 root root    0 Aug 29 14:56 .
drwxr-xr-x. 3 root root 4096 Aug 29 14:44 ..
dr-xr-xr-x. 2 root root    0 Aug 29 14:56 work_backup
[root@BAK work]# ls -l work_backup/
total 1242973
....

It's crucial to:
  • Add _netdev as a mount option so as for the system to wait for the network to come up first and then perform the actual filesystem mount
  • Add iocharset as a mount option in order to be able to read any special characters our users use, otherwise we'll get a rsync: recv_generator: failed to stat "<filename>" : Invalid or incomplete multibyte or wide character (84) error
  • Use a double slash (MYNETWORK\\Administrator) instead of a single one (MYNETWORK\Administrator); since we are in a Linux environment, we need an escape character for this
 All right, let's create an initial backup directory:

[root@BAK work]# cd ~ 
[root@BAK ~]# rsync -avzr /mnt/work/ /media/pool0/ORIGINAL

Here, we just synchronize our share with the directory /media/pool0/ORIGINAL so that in the future, we compare the differences and send them to a third directory (i.e. we'll be creating a sparse backup of just files that have changed from our original backup).

Let's see our savings:

[root@BAK ~]# sdfscli --volume-info
Volume Capacity : 6 TB
Volume Current Logical Size : 567.57 GB
Volume Max Percentage Full : 95.0%
Volume Duplicate Data Written : 395.33 GB
Unique Blocks Stored: 227.61 GB
Unique Blocks Stored after Compression : 227.7 GB
Cluster Block Copies : 2
Volume Virtual Dedup Rate (Unique Blocks Stored/Current Size) : -4.56%
Volume Actual Storage Savings (Compressed Unique Blocks Stored/Current Size) : 59.88%
Compression Rate: -0.04%

Almost 60%. Yeah, that's pretty decent.
And once that's finished, we add our deduplicated volume mount command and our rsync command and we're all done:

[root@BAK work]# cd ~ 
[root@BAK ~]# crontab -e
@reboot /bin/sleep 60;/bin/mount -t sdfs pool0 /media/pool0/ #Mount our deduplicated volume a minute after booting
#15 17 * * * LANG=en_US.UTF-8;append=`/bin/date +"\%d\%m\%Y"`;if /usr/bin/test `/usr/bin/pgrep rsync | wc -l` -lt 1 ; then /bin/mkdir /media/pool0/$append;/usr/bin/rsync -avzr --log-file=/root/work_backup_$append.log --compare-dest=/media/pool0/ORIGINAL /mnt/work/ /media/pool0/$append; fi #We can scan if other rsync processes are still running and not start, it can apply in some environments
15 17 * * * LANG=en_US.UTF-8;append=`/bin/date +"\%d\%m\%Y"`;/bin/mkdir /media/pool0/$append;/usr/bin/rsync -avzr --log-file=/root/work_backup_$append.log --compare-dest=/media/pool0/ORIGINAL /mnt/work/ /media/pool0/$append #Check the differences between our Windows Share and our initial backup directory and put any differing files in /media/pool0/DayMonthYear
00 05 * * * LANG=en_US.UTF-8;append=`/bin/date +"\%d\%m\%Y" --date="yesterday"`;/usr/bin/rsync -av --ignore-existing /media/pool0/$append/work_backup/ /media/pool0/ORIGINAL/work_backup/ #If we want, we can actually put any new files in the "ORIGINAL" directory so that they don't get copied every time in order to save bandwidth and time. Any pre-existing files will not be overwritten.

As you can see, we specified the LANG=en_US.UTF-8 environment variable here in order for our environment to be in check with our volume's charset and be safe from the aforementioned rsync: recv_generator: failed to stat "<filename>" : Invalid or incomplete multibyte or wide character (84) error.

If you ever need to delete a volume, you need to unmount it first and then remove the data, log and configuration files. My deduplicated volume is mounted on /media/pool0, the volume name is pool0 and my actual disk is mounted on /work_backup:

[root@BAK ~]# umount /media/pool0/
[root@BAK ~]# rm -rf /work_backup/*
[root@BAK ~]# rm -f /var/log/sdfs/pool0-volume-cfg.xml*
[root@BAK ~]# rm -f /etc/sdfs/pool0-volume-cfg.xml

Wednesday, September 2, 2015

Java Error: Failed to validate certificate. The application will not be executed

I was trying to log in to a Brocade Fiber Channel Switch earlier on, and as always the ugly Java monster reared its ugly head.


Well, I usually get warnings and errors that have to do with the security level of the site... But I've already added this IP to the Java trusted sites and I've set Java security level as low as I can. What now? Let's investigate a bit further.



Let's view the certificate details:



Well. That didn't help me that much. A bunch of "unknown source" java errors and a certificate. Well, let's go with that. The certificate. So, what I did next was disable certificate checks and accept SSLv2 (yes, yes I know).


I thought that this was going to do the trick. Surprise, surprise it didn't. So what next? Well, mess around with the java.security file. To do that, we have to find out which java version we're using:


OK, so 8 U51 it is. So now, I need to go to C:\Program Files (x86)\Java\jre\1.8.0_51\lib\security and edit the java.security file so that anything SSL-related is commented out.

....
#jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 1024
....
#jdk.tls.disabledAlgorithms=SSLv3, DH keySize < 768
....
#jdk.tls.legacyAlgorithms= \
#        K_NULL, C_NULL, M_NULL, \
#        DHE_DSS_EXPORT, DHE_RSA_EXPORT, DH_anon_EXPORT, DH_DSS_EXPORT, \
#        DH_RSA_EXPORT, RSA_EXPORT, \
#        DH_anon, ECDH_anon, \
#        RC4_128, RC4_40, DES_CBC, DES40_CBC

And that was all it took. Obviously after finishing you should revert these changes as it makes any SSL connections with java completely insecure.

Java, I hate you more than I hate Windows. 10. With all the botnet "features" enabled.

Wednesday, August 19, 2015

How to backup MSSQL express databases

OK, so I had to backup an MSSQL express database the other day. In any other scenario, there's no problem: all you have to do is create a job for it. But MSSQL express? Hmmm...

Well, this is the route I'm going to go for: Create a Samba Share and use expressmaint for the actual backup.

Since creating a Samba share is out of the scope of this document, I'll just run through the basics:

Install Samba and create the share:

[root@dbbak ~]# yum -y install samba samba-client samba-common samba-doc
[root@dbbak ~]# cp /etc/samba/smb.conf /etc/samba/smb.conf.bak
[root@dbbak ~]# vi /etc/samba/smb.conf
....
#============================ Share Definitions ==============================

[backups]
        comment = Database Backups
        path = /backups
        valid users = dbbak
        public = no
        browseable = yes
        guest ok = no
        writable = yes
        printable = no
        create mask = 0765

Create the user:

[root@dbbak ~]# useradd dbbak -g users -m -s /bin/bash
[root@dbbak ~]# smbpasswd -a dbbak
[root@dbbak ~]# mkdir /backups
[root@dbbak ~]# chown -R dbbak:users /backups/
[root@dbbak ~]# passwd dbbak
[root@dbbak ~]# smbpasswd -a dbbak

Let's assume that I'll be using 12345678 as my password.

The ports that we'll need to add in iptables for SMB to work are:

-A INPUT -m state --state NEW -m udp -p udp --dport 137 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 137 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 138 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 138 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 139 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 139 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 445 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 445 -j ACCEPT

And finally:

[root@dbbak ~]# service smb restart
[root@dbbak ~]# chkconfig smb on

OK, now that we have our storage let's move on to the actual MSSQL backup.

First of all, we'll need to fire up the SQL Configuration Manager and make sure TCP/IP and Named Pipes are enabled:


And then, go to "SQL Server Network Configuration", expand "Protocols for ..." and double-click on "TCP-IP" (or right-click on "TCP-IP" and select "Properties").

Make sure that the "Dynamic Ports" field is empty everywhere and the "TCP Port" field is 1433.

Now, after restarting the MSSQL service, you need to go ahead and download expressmaint from http://expressmaint.codeplex.com and put it in a directory of your choice.

Create a batch job for it:

@ECHO OFF
net use Z: \\192.168.1.48\backups /USER:dbbak /PERSISTENT:NO 12345678
mkdir "C:\MMSQL_BK-%DATE:~4,2%%DATE:~7,2%%DATE:~10,4%"
"C:\Users\administrator\Downloads\expressmaint" -S (local) -D ALL -T DB -B "C:\MMSQL_BK-%DATE:~4,2%%DATE:~7,2%%DATE:~10,4%" -BU DAYS -BV 1 -R "C:\MMSQL_BK-%DATE:~4,2%%DATE:~7,2%%DATE:~10,4%" -RU DAYS -RV 1 -V -C
xcopy /E /Y /Q "C:\MMSQL_BK-%DATE:~4,2%%DATE:~7,2%%DATE:~10,4%" "Z:\MMSQL_BK-%DATE:~4,2%%DATE:~7,2%%DATE:~10,4%\"
net use Z: /DELETE
rmdir /S /Q "C:\MMSQL_BK-%DATE:~4,2%%DATE:~7,2%%DATE:~10,4%"
exit

This script assumes that my Samba share's IP is 192.168.1.48, the user is dbbak and the password 12345678. You should also check expressmaint's options to suit your needs.

Finally, all that's left is to create a scheduled task for it.

Saturday, June 27, 2015

syntax-error-in-debian-changelog while building openssl from source

I was building Openssl the other day as described in Zen Load Balancer 3.0.3 Perfomance and Security Customization Part 2, but instead of everything going fine as usual, I received this error message:

W: openssl: syntax-error-in-debian-changelog line 49 "couldn't parse date Thy, 8 Jan 2015 22:10:07 +0100"
W: openssl: non-standard-dir-perm etc/ssl/private/ 0700 != 0755
W: libssl-doc: syntax-error-in-debian-changelog line 49 "couldn't parse date Thy, 8 Jan 2015 22:10:07 +0100"
W: libssl-dev: syntax-error-in-debian-changelog line 49 "couldn't parse date Thy, 8 Jan 2015 22:10:07 +0100"
W: libssl1.0.0: hardening-no-fortify-functions usr/lib/i386-linux-gnu/openssl-1.0.0/engines/libatalla.so
W: libssl1.0.0: syntax-error-in-debian-changelog line 49 "couldn't parse date Thy, 8 Jan 2015 22:10:07 +0100"
E: libssl1.0.0: no-debconf-config
W: libssl1.0.0: postinst-uses-db-input
W: libssl1.0.0-dbg: syntax-error-in-debian-changelog line 49 "couldn't parse date Thy, 8 Jan 2015 22:10:07 +0100"
Finished running lintian.

It's obvious that someone made an error when entering the changelog's date ("couldn't parse date Thy, 8 Jan 2015 22:10:07 +0100" should obviously be Thu, 8 Jan 2015 22:10:07 +0100).

What you need to do is start the process all over again (remove any files/folders etc you worked on and do apt-get source openssl). Then, just go to the offending line of the debian/changelog file (49 in our case), manually change the error (Thy to Thu) and do the rest of the installation as you would do normally.

[root@hadoop1 ~]# vi debian/changelog
....
 -- Kurt Roeckx   Thu, 8 Jan 2015 22:10:07 +0100
....

That's it!

Saturday, March 21, 2015

Introduction to Parallel Computing Part 1f - Creating a Hadoop cluster (the easy way -- Cloudera)

Supported Operating Systems

Cloudera Manager supports the following operating systems:
  • RHEL-compatible
    • Red Hat Enterprise Linux and CentOS
      • 5.7, 64-bit
      • 6.4, 64-bit
      • 6.4 in SE Linux mode
      • 6.5, 64-bit
    • Oracle Enterprise Linux with default kernel and Unbreakable Enterprise Kernel, 64-bit
      • 5.6 (UEK R2)
      • 6.4 (UEK R2)
      • 6.5 (UEK R2, UEK R3)
  • SLES - SUSE Linux Enterprise Server 11, 64-bit. Service Pack 2 or later is required.
  • Debian - Wheezy (7.0 and 7.1), Squeeze (6.0) (deprecated), 64-bit
  • Ubuntu - Trusty (14.04), Precise (12.04), Lucid (10.04) (deprecated), 64-bit
I'm going to use RHEL 6.6 for this.

Unfortunately, the menial tasks that involve system configuration cannot be avoided, so let's press on:
First of all, let's update everything (This should be issued on every server in our cluster):

[root@hadoop1 ~]# yum -y update
[root@hadoop1 ~]# yum -y install wget

We'll need this (This should be issued on every server in our cluster.):

[root@hadoop1 ~]# yum -y install openssh-clients.x86_64

Note: It's always best to actually have your node FQDNs on your DNS server and skip the next two steps (editing the /etc/hosts and the /etc/host.conf files). 

Now, let's edit our /etc/hosts to reflect our cluster (This should be issued on every server in our cluster):

[root@hadoop1 ~]# vi /etc/hosts
192.168.0.101   hadoop1
192.168.0.102   hadoop2
192.168.0.103   hadoop3
192.168.0.104   hadoop4
192.168.0.105   hadoop5
192.168.0.106   hadoop6
192.168.0.107   hadoop7
192.168.0.108   hadoop8

We should also check our /etc/host.conf and our /etc/nsswitch.conf, unless we want to have resolvable hostnames:

[hadoop@hadoop1 ~]$ vi /etc/host.conf
multi on
order hosts bind
[hadoop@hadoop1 ~]$ vi /etc/nsswitch.conf
....
#hosts:     db files nisplus nis dns
hosts:      files dns
....

We'll need a large number of file descriptors (This should be issued on every server in our cluster):

[root@hadoop1 ~]# vi /etc/security/limits.conf
....
* soft nofile 65536
* hard nofile 65536
....

We should make sure that our network interface comes up automatically:

[root@hadoop1 ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0
....
ONBOOT="yes"
....

And of course make sure our other networking functions, such as our hostname are correct:

[root@hadoop1 ~]# vi /etc/sysconfig/network
NETWORKING=yes
HOSTNAME=hadoop1
GATEWAY=192.168.0.1

We'll need to log in using SSH as root, so for the time being let's allow root logins. We might want to turn that off after we're done, as this is as insecure as they come:

[root@hadoop1 ~]# vi /etc/ssh/sshd_config
....
PermitRootLogin yes
....
[root@hadoop1 ~]# service sshd restart

NTP should be installed on every server in our cluster. Now that we've edited our hosts file things are much easier though:

[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}'); do ssh "$host" "exec yum -y install ntp ntpdate"; done 
[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}'); do ssh "$host" chkconfig ntpd on; done
[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}'); do ssh "$host" ntpdate pool.ntp.org; done
[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}'); do ssh "$host" service ntpd start; done

Set up passwordless ssh authentication (note that this will be configured automatically during the actual installation so this in not necessary; it is useful though, since it saves us from a lot of typing):

[root@hadoop1 ~]# ssh-keygen -t rsa 
[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}' | grep -v hadoop1); do ssh "$host" mkdir -p .ssh; done
[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}'); do ssh-copy-id -i ~/.ssh/id_rsa.pub "$host"; done
[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}'); do ssh "$host" chmod 700 .ssh; done
[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}'); do ssh "$host" chmod 640 .ssh/authorized_keys; done

Time to tone down our security a bit so that our cluster runs without problems. My PC's IP is 192.168.0.55 so I will allow that as well:

[root@hadoop1 ~]# iptables -F
[root@hadoop1 ~]# iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
[root@hadoop1 ~]# iptables -A INPUT -i lo -j ACCEPT
[root@hadoop1 ~]# iptables -A INPUT -s 192.168.0.101,192.168.0.102,192.168.0.103,192.168.0.104,192.168.0.105,192.168.0.106,192.168.0.107,192.168.0.108 -j ACCEPT
[root@hadoop1 ~]# iptables -A INPUT -s 192.168.0.55 -j ACCEPT
[root@hadoop1 ~]# iptables -A INPUT -j DROP
[root@hadoop1 ~]# iptables -A FORWARD -j DROP
[root@hadoop1 ~]# iptables -A OUTPUT -j ACCEPT
[root@hadoop1 ~]# iptables-save > /etc/sysconfig/iptables
[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}' | grep -v hadoop1); do scp /etc/sysconfig/iptables "$host":/etc/sysconfig/iptables; done
[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}'); do ssh "$host" "iptables-restore < /etc/sysconfig/iptables"; done

Let's disable SELinux:

[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}'); do ssh "$host" setenforce 0; done
[root@hadoop1 ~]# vi /etc/sysconfig/selinux
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=disabled
# SELINUXTYPE= can take one of these two values:
#     targeted - Targeted processes are protected,
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted
[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}' | grep -v hadoop1); do scp /etc/sysconfig/selinux "$host":/etc/sysconfig/selinux; done

Turn down swappiness. Cloudera actually recommend turning down swappiness to 0, I prefer 1:

[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}'); do ssh "$host" "echo vm.swappiness = 1 >> /etc/sysctl.conf"; done
[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}'); do ssh "$host" sysctl -p; done

We've made quite a few changes, including kernel updates. Let's reboot and pick this up later.

[root@hadoop1 ~]# for host in $(grep hadoop /etc/hosts | awk '{print $2}' | grep -v hadoop1); do ssh "$host" reboot; done      
[root@hadoop1 ~]# reboot

Now, let's start installing Hadoop by downloading and running the Cloudera manager and installation script.

[root@hadoop1 ~]# wget http://archive.cloudera.com/cm5/installer/latest/cloudera-manager-installer.bin     
[root@hadoop1 ~]# chmod u+x cloudera-manager-installer.bin
[root@hadoop1 ~]# ./cloudera-manager-installer.bin

The installation process is very straight-forward to say the least. You only have to read a few licence agreements and select a few "Next" options.



After a while, you'll need to point your web browser to the system whose IP you installed cloudera manager on, port 7180. In my case therefore it's 192.168.0.101:7180.

Just in case, take a look at the logs before actually logging in. If there doesn't seem to be a cloudera manager service available listening at that address and port, wait for a bit until you see the relevant message:

[root@hadoop1 ~]# netstat -ntpl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
tcp        0      0 0.0.0.0:7432                0.0.0.0:*                   LISTEN      1807/postgres       
tcp        0      0 0.0.0.0:7182                0.0.0.0:*                   LISTEN      2419/java           
tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      920/sshd            
tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN      1045/master         
tcp        0      0 :::7432                     :::*                        LISTEN      1807/postgres       
tcp        0      0 :::22                       :::*                        LISTEN      920/sshd            
tcp        0      0 ::1:25                      :::*                        LISTEN      1045/master         
[root@hadoop1 ~]# tail -f /var/log/cloudera-scm-server/cloudera-scm-server.log 
....
2015-03-20 05:54:54,092 INFO WebServerImpl:org.mortbay.log: jetty-6.1.26.cloudera.4
2015-03-20 05:54:54,140 INFO WebServerImpl:org.mortbay.log: Started SelectChannelConnector@0.0.0.0:7180
2015-03-20 05:54:54,140 INFO WebServerImpl:com.cloudera.server.cmf.WebServerImpl: Started Jetty server.
2015-03-20 05:54:54,844 INFO SearchRepositoryManager-0:com.cloudera.server.web.cmf.search.components.SearchRepositoryManager: Finished constructing repo:2015-03-20T03:54:54.844Z


The default username and password are admin and admin.


As before, it is an extremely straight-forward process. The only point where you might be uncertain in regards as to what you should choose is when Cloudera asks if it should install using traditional installation methods such as .rpm or .deb packages or Cloudera's parcels method.


According to Cloudera, among other benefits, parcels provide a mechanism for upgrading the packages installed on a cluster from within the Cloudera Manager Admin Console with minimal disruption. So let's proceed using parcels.

Once it starts the cluster installation, it needs a fair bit of time to complete, so sit back and relax. Note that if the installation on a particular node appears to get stuck at "Acquiring Installation lock", just log on there, remove the lock:

[root@hadoop1 ~]# rm -f /tmp/.scm_prepare_node.lock

and abort and retry.


Once it's finished it will give you a warning that Cloudera recommend turning down swappiness to 0, but other than that, should be fine.



After that, you need to pick which Hadoop services should run on which server, create your databases (at which point you should also note the usernames, passwords and database names for future reference), and review base directory locations. We're going to do a pretty basic vanilla installation here so we choose custom and:



 
After that, we'll need to wait for a tad for the manager to start all the services and after that we'll be good to go.

And that's what you get for installing a Hadoop cluster on tiny VMs!
All right, time to do something with our new cluster. Let's go to our hue server.

To do that, go to your cloudera manager UI, select "Hue" and click on "Hue Web UI".


Just select your username and password that you will use for hue. As soon as you're in, it will do a few automatic checks and ask you if you need to create new users.

Which means that we have everything up and running and we can actually use our Hadoop cluster using a Web browser instead of going through everything manually!

References: http://www.cloudera.com/content/cloudera/en/documentation/cloudera-manager/v4-6-2/Cloudera-Manager-Managing-Clusters/cmmc_parcel_upgrade.html