Transferring a Live Site to a New Server Using cPanel/WHM and SSH
Using cPanel/WHM it is very easy to transfer an existing cPanel account between servers. Coordinating this with a client who manages DNS on their own can make things a bit tricky, especially when their MX records point to the same server for webmail service.
In order to guarantee no downtime on the site, typically I copy a cPanel account over to the new server including all site files and email in advance of notifying a client it is okay to change the DNS. I also tell the site at the newly located cPanel account to continue to read and write to the original database on the old server using the server’s IP address as the host. This gives the client the flexibility to change the DNS at a time convenient for them.
After the DNS Has Propagated to the New Server
Once the DNS has propagated to the new server, a bit of cleanup is required. Following the example above,
- The database from the original server (which now contains newer information than the copy existing on the new server’s copy of the database) must be copied, and the new cPanel account’s database references must once again be set to
localhost. - We must ensure that all deleted/added email on the original server which occured between the time the cPanel copy was performed and when the client changed their MX records must be reflected on the new server to ensure no loss of email or reappearance of previously deleted email.
To accomplish #1 above is simple. Via SSH on the new server run the following commands
mysqladmin drop your_database_name
# Now update your site to point to localhost instead of the original server's IPThen, from the original server run the following commands
mysqldump -u your_cpanel_user -p your_database_name | ssh your_cpanel_user@the_new_server_ip mysql your_database_name
This will minimize downtime for your users1 while updating the site to read/write from the local copy of the database now containing the newest information.
Item #2 above is equally simple via rsync from the old server
rsync -av --progress /home/your_cpanel_user/mail/ root@the_new_server_ip:/home/your_cpanel_user/mail
This command properly updates the new mail server’s mail folder for all users with the latest changes to their accounts prior to the MX record change.
-
It goes without saying that this work should be done very late at night. ↩
Bash Shortcuts Using the Exclamation Point
The shell provides multiple shortcuts via the usage of the exclamation point when trying to execute recently performed commands. Err the Blog’s Bash Cheat Sheet provides the following tips and more.
If you need to run the most recently executed command again
!!If the last command run requires super user privileges
sudo !!
Run the most recently executed command beginning with foo
!fooRetrieve the last ‘word’ of the most recently executed command. In the example below, !$ would have the value my_database.
mysqldump -u root -p my_database !$ # Will contain the value 'my_database'
Similar to the example above, this shortcut contains all ‘words’ except the first in the most recently run command. Using the same mysqldump command above, !* would have the value -u root -p my_database.
mysqldump -u root -p my_database !* # Will contain the value '-u root -p my_database'
Note that the above commands can have :p appended, printing the commands or ‘words’ that the exclamation point replacement would use instead of running them as part of a new command in shell. The example below would print the most recently executed command beginning with foo
!foo:pMaking PROMPT_COMMAND More Informative
On Mac/Linux the shell’s command prompt is typically fairly uninformative, displaying only the top level of the current working directory path. If you are currently at /home/me/sites/mysite, the prompt might look something like
[mysite]$ ...
This isn’t as useful as it could be. LinDesk details multiple ways to make the command prompt more informative. Based on the information there I now have the following PROMPT_COMMAND set in my ~/.bash_profile.
PROMPT_COMMAND='DIR=`pwd|sed -e "s!$HOME!~!"`; if [ ${#DIR} -gt 30 ]; then CurDir=${DIR:0:12}...${DIR:${#DIR}-15}; else CurDir=$DIR; fi' PS1="[\$CurDir] \$ "
This now makes the same directory path above for /home/me/sites/mysite look like this
[/home/me/s...ite/public_html] $
MooTools SuperSleight Implementation for IE .png Transparency Issue
Recently I had to slice converted a site mockup to XHMTL and CSS. Part of the implementation required that the width of the site be what I like to call “static-variable width”.
The width of the site is not dependent on the width of the browser as a fluid layout is, but a slider control is available to the user to grow or shrink the width of the site as they desire.
Due to the need for this flexibility and the heavy image work used for the background of many of the DOM elements, .png files with transparency needed to be used in many places.
Things really looked great, except of course in IE6.
There are many different “solutions” to this problem, all which revolve around the Internet Explorer AlphaImageLoader Filter. After a lot of research and testing of different implementations, I settled on the SuperSleight. I like this technique for a variety of reasons
- It’s very simple to implement
- It degrades gracefully
- It doesn’t invalidate a site’s XHTML or CSS
I found that allinthehead has implemented a jQuery plugin. Since I’m personally more a fan of MooTools, I ported the plugin to MooTools, extending the native Element class.
Element.implement({ superSleight: function(settings) { var settings = $H(settings); settings.extend({ imgs: true, backgrounds: true, shim: '/_img/png_fix_blank.gif', apply_positioning: true }); if (Browser.Engine.trident && Browser.Engine.version < 7 && Browser.Engine.version >= 4) { $(this).getElements('*').each(function(elem) { // background pngs if (settings.backgrounds && elem.getStyle('background-image').match(/\.png/i) !== null) { var bg = elem.getStyle('background-image'); var src = bg.substring(5, bg.length - 2); var mode = (elem.getStyle('background-repeat') == 'no-repeat' ? 'crop': 'scale'); var styles = { 'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='" + mode + "')", 'background-image': 'url(' + settings.shim + ')' }; elem.setStyles(styles); } // image elements if (settings.imgs && elem.match('img[src$=png]')) { var elem_size = elem.getSize(); var styles = { 'width': elem_size.x + 'px', 'height': elem_size.y + 'px', 'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + elem.getProperty('src') + "', sizingMethod='scale')" }; elem.setStyles(styles).setProperty('src', settings.shim); } // apply position to 'active' elements if (settings.applyPositioning && ['a', 'input'].contains(elem.get('tag')) && elem.getStyle('position') === '') { elem.setStyle('position', 'relative'); } }); } } });
Some Notes
- Make sure to change the path to your
.giffile. This.giffile should be a transparent1x1 pximage. This is what the Alpha ImageLoader uses to fix the transparency issues with the.pngfiles - I’ll call this code stable since I haven’t personally had any issues with it yet. If you have any changes you’d like to recommend, post a comment or send me a message
- With the Alpha ImageLoader Filter for IE, there is no support for repeating backgrounds or background positioning. As a tip, on the site I used this code for, whenever I needed to mimic the
top rightCSSbackground-positionproperty, I found increasing the width of these.pngimages by putting transparent pixels as padding in the left side of the image itself can correctly force the image into position for IE6 without breaking the layout in good browsers.
Fixing Crontab Permissions for Jailshell cPanel Users on CentOS 5 & cPanel/WHM 11.24 RELEASE
Using CentOS 5 on my VPS, I recently found that newly created cPanel accounts running cPanel/WHM 11.24 RELEASE seemed to have some trouble properly setting permissions to allow the jailshell cPanel users to access/manage their crontab.
Setting up cron jobs via cPanel’s web interface itself worked just fine, but when trying to manage a jailshell users crontab via shell, the permissions were not set up properly to allow for this.
As d4ly you can see I don’t have permission to view/edit the crontab for d4ly
d4ly@d4ly.com [~]# crontab -e cron/d4ly: Permission denied
So as root I did the following
root@server [~]# chmod 4775 /usr/bin/crontab root@server [~]# cd /var/spool/cron root@server [/var/spool/cron]# chmod 0755 . root@server [/var/spool/cron]# chown d4ly.d4ly. d4ly root@server [/var/spool/cron]# ls -l total 24 drwxr-xr-x 2 root root 4096 Feb 4 17:03 ./ drwxr-xr-x 13 root root 4096 Sep 9 09:51 ../ -rw------- 1 d4ly d4ly 37 Feb 4 14:54 d4ly -rw------- 1 root root 1555 Jan 19 20:52 mailman -rw------- 1 root root 32 Jan 11 16:42 munin -rw------- 1 root root 485 Jan 26 13:17 root
Then I tested things out once more as d4ly
d4ly@d4ly.com [~]# crontab -e MAILTO="d4ly" 0 0 * * * ping d4ly.com # This cron is for testing jailshell crontab editing
Aiding the SVN Commit Process
The source code for D4core is maintained in and SVN repository. I have found it helpful to create a .cleanup and .commit Bash script in the root directory of our framework’s repository.
.cleanup has the task of removing cached files, temporary files, and log files which are no longer relevant to the current development cycle. We also use it currently to reformat our XML configuration file with pretty whitespace using xmllint.
#!/bin/bash echo "Cleaning D4core..." rm -f D4core/Content/Cache/Smarty/* rm -f D4core/Content/Compile/Smarty/* rm -f D4core/Content/Cache/Minify/* rm -f D4core/Config/Backup_* rm -f D4core/Log/Error/* rm -f D4core/Log/Logs/* xmllint -format D4core/Config/Config.xml -output D4core/Config/Config.xml echo 'Running `svn status`...' svn status echo "Cleaning D4core completed."
.commit (which calls .cleanup initially) is responsible for managing the commit process. This file is run only after all project files have been added/removed/moved within the working copy and we are ready to commit to SVN.
#!/bin/bash ./.cleanup svn commit echo "D4core SVN commit complete."
By running ./.cleanup before committing code to the repository it becomes a lot easier to ensure the status and location of all files within the working copy are correct. These simply scripts easily save me 5 minutes a day in shell.
Dumping the Number of Open Connection to Port 80 by IP Address
This displays the number of open connections to port 80 on a server, dumping the ip address/connection count pairs in ascending order.
netstat -anlp | grep :80 | awk '{print $5}' | awk -F: '{print $1}' | sort | uniq -c | sort -n
This can be useful in combination with grep1 searches performed on
httpd fullstatus
to determine if a DOS attack is being performed on a server.
Sed One-Liners Reference
An incredibly useful quick-reference for one-line sed1 commands.
http://sed.sourceforge.net/sed1line.txt
Trac - SubversionException: Can’t set position pointer in file…
When recompiling SVN 1.5.5 from source, making sure to include the swig-py bindings to allow Trac to read from local SVN repositories, I used the following ./configure command
./configure --prefix=/usr/local/svn-1.5.5/ --with-apr=/usr/local/apr/bin --with-apr-util=/usr/local/apr/bin --with-neon=/usr/bin --with-apxs=/usr/local/apache/bin/apxs
When trying to view my repository I was presented with the following error in my trac.log file
SubversionException: ("Can't set position pointer in file '/var/svn/db/revs/135': Invalid argument", 22)
After some research1 I found the most common cause of this error is that Subversion was compiled against a different version of apr and apr-util than apache was. I found apache compiled with cPanel’s EA3 uses
/usr/local/apache/bin
as it’s path for apr and apr-util. Changing the flags to reflect this when compiling Subversion I was left with
./configure --prefix=/usr/local/svn-1.5.5 --with-apr=/usr/local/apache/bin --with-apr-util=/usr/local/apache/bin --with-neon=/usr/bin --with-apxs=/usr/local/apache/bin/apxs make swig-py make install make install-swig-py cd /usr/local/svn-1.5.5/lib/svn-python/ cp -r libsvn/* /usr/lib/python2.4/site-packages/libsvn cp -r svn/* /usr/lib/python2.4/site-packages/svn service httpd restart
I also ran the following out of habit after recompiling SVN before restarting httpd.
trac-admin TRAC_ENV_PATH resync
Clear Hanging Apache Semaphores
Restarting httpd while trying to alleviate high load on one of the servers I manage, I was recently presented with the following error
(28)No space left on device: Couldn't create accept lock
After some research I found running the following command1
ipcs -sshowed there were a bunch of semaphores hanging around even though apache had been stopped normally via
service httpd restart
Similar to the code snippet mentioned by Major over at RackerHacker, the following command clears out those semaphores, letting httpd start up without a problem
ipcs -s | grep nobody | perl -e 'while (<STDIN>) { @a=split(/\s+/); print `ipcrm sem $a[1]`}'