Monday, February 25, 2013

MySQL Replication


Configuring Master/Master replication

The following procedure can be used to setup MySQL in a master/master replication configuration.   We'll call the two MySQL servers Master1 and Master2. In a dual master setup each server functions as both a master and a slave to the other server. The following assumptions are being made:
·         You have two MySQL servers running the same MySQL version.
·         Neither server is currently active (i.e. they are not currently accepting inserts or deletions or modifications of any type.)
·         Master1 refers to the first server that has data that you wish to replicate.
·         Master2 refers to the second server.
·         Master/Slave or Master/Master replication has not already been implemented.

NOTE: Change IP addreses and Server-ID’s in the following steps to match your environment.

1.    Configure Master1 by adding the following to /etc/my.cnf
#Replication Setup
#set server id to the last octet of the servers ip address
server-id = 150
replicate-same-server-id = 0
auto-increment-increment = 2
auto-increment-offset = 1

# change this to a path/name appropriate to your system
log-bin       = /usr/local/mysql/data/mysql-bin
log-bin-index = /usr/local/mysql/data/mysqld-bin.index
relay-log     = /usr/local/mysql/data/mysqld-relay-bin
relay-log-index = /usr/local/mysql/data/mysqld-relay-bin.index
binlog-format        = MIXED
binlog-cache-size    = 1M
expire-logs-days     = 7
sync-binlog          = 1

skip-slave-start
log-slave-updates

2.    Configure Master2 by adding the following to /etc/my.cnf.
#Replication Setup
#set server id to the last octet of the servers ip address
server-id = 148
replicate-same-server-id = 0
auto-increment-increment = 2
auto-increment-offset = 2

# change this to a path/name appropriate to your system
log-bin       = /usr/local/mysql/data/mysql-bin
log-bin-index = /usr/local/mysql/data/mysqld-bin.index
relay-log     = /usr/local/mysql/data/mysqld-relay-bin
relay-log-index = /usr/local/mysql/data/mysqld-relay-bin.index
binlog-format        = MIXED
binlog-cache-size    = 1M
expire-logs-days     = 7
sync-binlog          = 1

skip-slave-start
log-slave-updates


3.    Start MySQL on both servers.
Shell> cd /usr/local/mysql
Shell> ./bin/mysqld_safe --user=mysql &

4.  Backup the Database on Master 1
        Shell> mysqldump -u root -ppassword –-log-error=dump.log --all-databases -–master_data > master1_snapshot_master_data.sql


5.    Capture the master_log_file name, and position from the newly created backup

   Shell>grep MASTER_LOG_FILE master1_snapshot_master_data.sql | head -1

           CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=390;

6.    Install the Database on Master2
Shell> mysql –uroot –ppassword < master1_snapshot_master_data.sql

7.    Create slave user on Master 1:
      Adjust the IP address to reflect your systems.

mysql> use mysql;
mysql> CREATE USER 'slave_user'@'10.48.92.%' IDENTIFIED BY 'password';
mysql> GRANT REPLICATION SLAVE ON *.* to 'slave_user'@'10.48.92.%' identified by 'password';                         
mysql> FLUSH PRIVILEGES;


8.    Create slave user on Master 2:
      Adjust the IP address to reflect your systems.
mysql> use mysql;
mysql> CREATE USER 'slave_user'@'10.48.92.%' IDENTIFIED BY 'password';
mysql> GRANT REPLICATION SLAVE ON *.* to 'slave_user'@'10.48.92.%' identified by 'password';                 
mysql> FLUSH PRIVILEGES;


9.    Synchronize the Servers,

Master1:
mysql> STOP SLAVE;


Master2:  Set Master 2 to be a slave of Master 1
Set the value of “MASTER_LOG_FILE “ and “MASTER_LOG_POS”  with the values discovered in step 5 above.

mysql> STOP SLAVE;
mysql> SHOW MASTER STATUS\G;
mysql> CHANGE MASTER TO MASTER_HOST=’<MASTER1-IP>’, MASTER_USER=’slave_user’, MASTER_PASSWORD=’password’, MASTER_LOG_FILE=’mysqld-bin.XXXXXX’, MASTER_LOG_POS=NNNN;

Master1:  Set Master 1 to be a slave of Master 2
We don’t need the Position: value because NOPS only writes to MASTER 1.
mysql> CHANGE MASTER TO MASTER_HOST =’<MASTER2-IP>’, MASTER_USER=’slave_user’, MASTER_PASSWORD=’password’;

        Master2:
mysql> START SLAVE;

               Master1:
mysql> START SLAVE;
       
10.  Verify Replication is running.
Execute the following on both Master1 and Master2.  For “SHOW SLAVE STATUS\G;” look for  “Seconds_Behind_Master: 0”.  For “SHOW PROCESSLIST;” look for “Master has sent all binlog to slave; waiting for binlog to be updated”

mysql> SHOW SLAVE STATUS\G;
…snip…
Seconds_Behind_Master: 0
…snip…

mysql> SHOW PROCESSLIST;
…snip…
Master has sent all binlog to slave; waiting for binlog to be updated
Slave has read all relay log; waiting for the slave I/O thread to update it
…snip…

Sunday, January 6, 2013

SSH authentication order

I recently created a script the purpose of which is to copy statistics charts from one server to another. I had created ssh private/public key trust so that the script would be able to run automatically. However the connection to the remote server was in my opinion taking much too long. This was due to ssh running through all of the possible connection protocols in the default order. I figured there must be a way to change the order. After reading the man page for ssh_config I discovered that the default order is hostbased, publickey, keyboard-interactive, password.  I also discovered that the order can be changed by changing the default order. SSH obtains configuration settings in the following order,
  1. command-line arguments
  2. user's configuration file ~/.ssh/config
  3. /etc/ssh/ssh_config
  Additionally ssh allows configuration for specific hosts, Thus I setup the following ~/.ssh/config file.

Host chartserver
HostName 12.34.56.78
PreferredAuthentications=publickey,hostbased,keyboard-interactive,password

Host setting can be an alias
Hostname can be DNS or IP address
PreferredAuthentications, change the default order to the order you prefer, in my case I wanted to use publickey as the first method as that would be the method I would be using.  Now whenever I connect to the server the publickey authentication method will be the first that SSH attempts.





Thursday, September 20, 2012

SVN upgrade errors and solutions


I ran into some Subversion issues while upgrading an old Subversion instance from 1.5.5 to 1.7. The process also involved moving the repositories from a 32bit 1CPU 2GB virtual server to a 64bit 2CPU 4GB virtual server.  I’m also running Subversion Edge, as the svn user and apache as the apache user.
  
Subversion (SVN): Can’t open file ‘db/txn-current-lock’: Permission denied
Discussion: Ownership on the repository db directory is incorrect
# ls -ld /<repo-path>/<repo>/db
drwxrwsr-x 6 svn svn 4096 Sep 20 12:45 db

Solution:  Change ownership
                # chown –R svn:apache db
 ==========================================================

Subversion (SVN): Can’t write activity db

Discussion: Ownership on the repository dav directory is incorrect
# ls -ld /<repo-path>/<repo>/dav
drwxrwxr-x 3 svn apache 4096 Sep 19 17:04 dav

Solution: Change ownership of the dav directory
          # cd /<repo-path>/<repo>/
    # chown –R apache:apache dav

 ==========================================================  

Subversion (SVN): “attempt to write a readonly database”

Discussion: The file “rep-cache.db” has the incorrect permissions
#ls -l rep-cache.db
-rw-r--r-- 1 svn apache 81920 Sep 20 13:09 rep-cache.db

Solution: Change ownership on rep-cache.db
    # cd /<repo-path>/<repo>/db
    # chmod 664 rep-cache.db

Tuesday, September 18, 2012

Port Forwarding with Putty

It has been a few years since I last updated my Subversion repository. I'm currently on version 1.5.5 and I need to update it to 1.7.x. My repository is on the other side of an internal company firewall, the only ports that are open are 80,8080,443 and 22.  Subversion Edge has an admin screen that is hosted on port 3343. 

Scenario:  Need to reach port 3343 (Subversion Edge) on a Linux server behind a firewall which only allows ports 80,8080,443 and 22 from windows system (Windows XP, Windows Vista, Windows 7)

  1. Open putty, enter the IP address of the server you wish to connect to. 
  2. Select SSH-> Tunnels
  3. Select an unused source port above 1024, in this example enter 3343. For Destination enter the IP address and the port you want to connect to on that server.
Source port: 3343
Destination: 10.48.92.159:3343

Select Add

Then Open your connection, You are now port forwarding port 3343 from your PC to port 3343 on your Linux server.

Open your browser, then enter the following in your browser, you should be forwarded to the Admin Screen

http://127.0.0.1:3343


Friday, August 31, 2012

Simple RPM for Shell Scripts

Some of the folks down stream from me think that tar archives and customized install scripts were too difficult to deal with. Thus they requested that I begin migrating my install packages to RPMs. I felt that this was not an unreasonable request, thus began a research project trying to learn everything I could. I decided to start with two small projects the first simply two files and the second about twelve.  I found many examples and some good write ups but even the simple examples were more complicated than what I needed. I eventually figured it out and have simplified the process to the following 5 steps, for the most basic install.


Step1: Create a linux user called "rpmbuild "
For various reasons it is best not to do this as the root user.

Step2: Create required directories under the rpmbuild user account

Because I have used this account to create multiple packages I've setup the following directory structure

Shell> mkdir -p /home/rpmbuild/packages/mygreatrpm
Shell> cd /home/rpmbuild/packages/mygreatrpm
Shell> mkdir BUILD RPMS SOURCES SPECS SRPMS

Step3: Copy your Shell scripts to the SOURCES directory
Step4: Create your spec file in the SPECS directory

Shell> vi SPECS/MyScript.spec

#
%define _topdir /home/rpmbuild/packages/mygreatrpm
Name            : MyScript
Summary         : Does something REALLY great
Version         : %{VERSION}
Release         : %{REV}
Group:          : Applications/Databases
License:        : (c) MyCompany
BuildArch       : noarch
BuildRoot       : %{_topdir}/%{name}-%{version}-root

# Use "Requires" for any dependencies, for example:
# Requires        : tomcat6

# Description gives information about the rpm package. This can be expanded up to multiple lines.
%description
This tool is designed to watch your kids, take out the trash, feed the cat and clean the refridgerator.

# Prep is used to set up the environment for building the rpm package
# Expansion of source tar balls are done in this section
%prep

# Used to compile and to build the source
%build

# The installation.
# We actually just put all our install files into a directory structure that
# mimics a server directory structure here
%install
rm -rf $RPM_BUILD_ROOT
install -d -m 755 $RPM_BUILD_ROOT/opt/MyTools
cp ../SOURCES/myscript.dat $RPM_BUILD_ROOT/opt/MyTools/.
cp ../SOURCES/myscript.sh $RPM_BUILD_ROOT/opt/MyTools/.

%post
echo "Installed %{name} scripts to /opt/MyTools"
# Contains a list of the files that are part of the package
# See useful directives such as attr here: http://www.rpm.org/max-rpm-snapshot/s1-rpm-specref-files-list-directives.html
%files
%defattr(755, root, root)
/opt/MyTools/myscript.dat
/opt/MyTools/myscript.sh
# Used to store any changes between versions
%changelog
* Fri Aug 31 2012 firstname lastname
- Initial release.

STEP 5: Create the package
Shell> cd /home/rpmbuild/packages/mygreatrpm
Shell> rpmbuild -bb --define 'VERSION 1.0' --define 'REV 1234' SPECS/MyScript.spec
...snip...
/home/rpmbuild/packages/mygreatrpm/RPMS/noarch/MyScript-1.0-1234.noarch.rpm


Monday, August 27, 2012

Script runs as root but not as crontab? or
Why does my script run as the root user but won't run from cron.daily? or
What user does cron.daily run as?

I recently ran into a problem with a Subversion update script I wished to execute daily. Unfortunately the svn update failed due to a permissions error. This made little sense to me as I believed if I ran the script as root the script should work. It turns out that on some *nix systems (RH Linux and other variants) the root user has a home directory called /root. Within this directory is where the .subversion directory sits which contains your connection information.  Thus solution was to set the HOME variable in my script, as crontab doesn't necessarily pick up environment variables that you get when you log in.

Below you will find the simple fix.


#!/bin/sh
HOME=/root
export HOME=${HOME}
cd /usr/local/svn_workspace/trunk
/opt/CollabNet_Subversion/bin/svn update



Saturday, August 18, 2012

Shell Script Timestamps

Shell Script Trick No. 3 Timestamps
Over the years I've had the need to use time stamps in my scripts to name log files or directories. I created this function to make the task easier.
Usage is as follows:
timestamp "yyyymmdd"
sets the variable $timestamp to 20120818

timestamp "mm/dd/yy"
sets the varible $timestamp to 08/18/2012


##################
# timestamp: This function generates a timestamp
# Calling Profile: timestamp "format"
# Returns:  timestamp
##################
timestamp () {
  format=$1
    yyyy=`date +%Y`
    yy=`date +%y`
    month=`date +%m`
    day=`date +%d`
    hour=`date +%H`
    min=`date +%M`
    sec=`date +%S`

case $format in
     "yesterday yyyy.mm.dd" ) timestamp=`date --date yesterday "+%Y.%m.%d"` ;;
     "yesterday yyyy-mm-dd" ) timestamp=`date --date yesterday "+%Y-%m-%d"` ;;
     "yesterday yyyymmdd" )   timestamp=`date --date yesterday "+%Y%m%d"` ;;
     "yyyy" )                 timestamp="$yyyy" ;;
     "yy" )                   timestamp="$yy" ;;
     "dd" )                   timestamp="$day" ;;
     "mm" )                   timestamp="$month" ;;
     "hh:mm:ss" )             timestamp="$hour:$min:$sec" ;;
     "hhmmss" )               timestamp="$hour$min$sec" ;;
     "mm/dd/yy" )             timestamp="$month/$day/$yy" ;;
     "mm/dd/yyyy" )           timestamp="$month/$day/$yyyy" ;;
     "yyyymmdd"    )          timestamp="$yyyy$month$day" ;;
     "yyyymmddhhmm" )         timestamp="$yyyy$month$day$hour$min" ;;
     "yyyymmddhhmmss" )       timestamp="$yyyy$month$day$hour$min$sec" ;;
     "yyyy-mm-dd" )           timestamp="$yyyy-$month-$day" ;;
     "yyyy.mm.dd" )           timestamp="$yyyy.$month.$day" ;;
     "mmddyyyy"   )           timestamp="$month$day$yyyy" ;;
     "dd/mm/yy"   )           timestamp="$day/$month/$yy" ;;
     "dd-mm-yy"   )           timestamp="$day-$month-$yy" ;;
     "dd-mm-yyyy" )           timestamp="$day-$month-$yyyy" ;;
     "mm-dd-yy"   )           timestamp="$month-$day-$yy" ;;
     "mm-dd-yyyy" )           timestamp="$month-$day-$yyyy" ;;
     "mm dd, yyyy" )          timestamp="$month $day, $yyyy" ;;
             *   )            timestamp="$hour$min$sec";;
esac 
}