From 6de43a4ca49cbbabdcd167b242f779f0427056ed Mon Sep 17 00:00:00 2001 From: Gregory Colpart Date: Sun, 3 Oct 2010 02:43:54 +0200 Subject: [PATCH] Initial version --- README | 111 ++++++++++++++++++++++++++++ conf-preprod/.empty | 0 conf-prod/.empty | 0 evolistrano.conf | 51 +++++++++++++ evolistrano.sh | 173 ++++++++++++++++++++++++++++++++++++++++++++ log/.empty | 0 6 files changed, 335 insertions(+) create mode 100644 README create mode 100644 conf-preprod/.empty create mode 100644 conf-prod/.empty create mode 100644 evolistrano.conf create mode 100755 evolistrano.sh create mode 100644 log/.empty diff --git a/README b/README new file mode 100644 index 0000000..6507ab7 --- /dev/null +++ b/README @@ -0,0 +1,111 @@ +Evolistrano +~~~~~~~~~~~ + +Evolistrano is a small tool for source code deployment on several WWW servers. +It's more a "proof of concept" to show that an home-made shell script is often +better that generic tool like Capistrano (great tool) or Fredistrano. +Evolistrano has killer-features like deployment on several WWW servers +with *instant* deployment if you have a load-balancer like HAProxy, +incremental storage on WWW server to server multiples version of your code, +calculate available space before uploading, etc. + + + ____ WWW server 0 (preprod & prod) + / ___WWW server 1 (preprod & prod) + / / __WWW server 2 (preprod & prod) +Dev server / / / +(SVN, SSH, --> Evolistrano -->-------- + etc.) \ ___WWW static server 0 + \___WWW static server 1 + + +svn:// -> $subdocroot/{prod,preprod}/current +svn:// $staticfilesdir -> $subdocroot/static + + +INSTALLATION +------------ + +On dev server +~~~~~~~~~~~~~ + +* Download evolistrano on http://git.evolix.org + +* Copy evolistrano/ where you want and secure rights + (for example, add "deploy" group, and authorize + only this group to access it => to allow deployment, + we just had the user on this group!) + +* Generate an SSH key, for example: + ssh-keygen -f deploy.key + ... and ajust rights because SSH is very strict: + chown root:deploy deploy.key* + chmod 640 deploy.key.* + +* Read and configure evolistrano.conf file + +* You have probably specific files which are not in repository + (for example conf file)... then centralize them here and + edit section "Deploy conf files" in evolistrano.sh + +* You need also probably specific rights on you source code + (for example adding write permission)... then + edit section "UNIX rights" in evolistrano.sh + +* If you need specific actions on WWW servers or whatever + (SQL updates, etc.)... just edit sectionS "ADD SPECIFIC ACTIONS" + +On remote servers +~~~~~~~~~~~~~~~~~ + +* Add "deploy" et "deploy-preprod" users on your WWW servers + and "deploy" user on your static servers. Users should + be in www group for adding easily write permissions! + And add with authorization for new SSH key. + +* Point DocumentRoot to $subdocroot/prod/current + on your WWW servers for production and to + $subdocrootpre/prod/current for preproduction + +* Point DocumentRoot to $subdocroot/static + on your static servers + + + +USAGE +----- + +Deploy for preproduction: +% evolistrano.sh + +Deploy your static files: +% evolistrano.sh -S + +Deploy for production: +% evolistrano.sh -P + + + +FAQ +--- + +Q: Can I have remote SVN? +A: Probably, but we don't test it. If you do, let us know if it works. + +Q: Which language is used for Evolistrano? +A: Shell. + +Q: What are the depends for Capistrano? +A: Shell, SVN client, SSH client, rsync. + Written for Linux, but you can use it on *BSD with some changes. + +Q: Your tool is just 1% of Capistrano, why you do this crap? +A: Use Capistrano. + +Q: Is your tool production-ready? +A: We use it on production environment, but you can't use it without read source code + to be sure it will do the job. + +Q: Evolistrano lacks of foo feature. +A: Send us patch. + diff --git a/conf-preprod/.empty b/conf-preprod/.empty new file mode 100644 index 0000000..e69de29 diff --git a/conf-prod/.empty b/conf-prod/.empty new file mode 100644 index 0000000..e69de29 diff --git a/evolistrano.conf b/evolistrano.conf new file mode 100644 index 0000000..3d0ab09 --- /dev/null +++ b/evolistrano.conf @@ -0,0 +1,51 @@ +################################### +#### Evolistrano Configuration #### +################################### + +# WWW list +wwwlist="www00.example.com www01.example.com www02.example.com" + +# sub DocumentRoot (DocumentRoot will be $subdocroot/prod/current) +subdocroot="/srv/www" + +# Exclude list for deploying +excludelist="--exclude global.ini --exclude database.ini --exclude config.ini" + +# Static (CSS/JS/IMG/etc.) list +# TODO: Gerer Amazon S3/CloudFront +staticlist="static00.example.com static01.example.com" + +# SSH key +sshkey="deploy.key" + +# SSH port +sshport="22" + +# logs +logdir="log" + +# tmp dir +tmpdir="/var/tmp" + +# Deploy SSH/RSYNC users +deployproduser="deploy" +deploypreproduser="deploy-preprod" + +# Compute space before deply +calculspace="true" + +# Stop HTTP service waiting last deploy +veryhighcritical="true" + +# Use hardlinks ? +usehardlinks="true" + +# SVN branch path for WWW code +# TODO: Git support +svnpath="file:////svn/project/branches/stable" + +# Path for static files in repository +staticfilesdir="static" + +# email address for notification (empty if no notification) +mailnotif="" diff --git a/evolistrano.sh b/evolistrano.sh new file mode 100755 index 0000000..be884a5 --- /dev/null +++ b/evolistrano.sh @@ -0,0 +1,173 @@ +#!/bin/sh + +set -e +set -u + +export LC_ALL=C + +full_path=`echo $(dirname $(readlink -f $0))` +. $full_path/evolistrano.conf + +tmp_dir=`mktemp -p $tmpdir -d` +dir_export=$tmp_dir/export +time_stamp=`date +%s` + +usage() { + cat <$tmpfile +Date : `date` +User : $LOGNAME +Revision : $revnum + +EOT + +# Send email notification +[ $prod -eq 1 ] && [ "$mailnotif" != "" ] && ( cat $tmpfile | mail -s "[Evolistrano] Mise en prod" $mailnotif ) + +cat $tmpfile >>$log_file +rm $tmpfile + +# +umask 022 +svn export -r $revnum -q $svnpath $dir_export +echo +echo "SVN export to $dir_export" +echo + +#set +e + +# Deploy on WWW servers +for remote in $wwwlist; do + + # Be sure to have space for deploying + local_space=`du -sm $dir_export/ | sed 's/^\([0-9]\+\)\t.*$/\1/'` + remote_space=`ssh -p $sshport -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $sshuser@$remote df -lPm $subdocroot | grep ^/ | tr -s " " | cut -d" " -f4` + if [ $local_space -ge $remote_space ]; then echo "WARNING... $remote has only $remote_space Mo while you want upload $local_space Mo. Do you want stop ? [y/N]"; read_confirm; fi + + echo "sending code on $remote" + + if [ "$usehardlinks" = "true" ]; then + rsync -rlptq --link-dest=../current -e "ssh -p $sshport -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey" $dir_export/ --delete $excludelist $sshuser@$remote:$subdocroot/$destdir/$time_stamp + else + rsync -rlptq -e "ssh -p $sshport -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey" $dir_export/ --delete $excludelist $sshuser@$remote:$subdocroot/$destdir/$time_stamp + fi + + # Deploy conf files => UNCOMMENT AND ADJUST LINES + #scp -q -P $sshport -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $full_path/conf-$destdir/foo-global.ini $sshuser@$remote:$subdocroot/$destdir/$time_stamp/config/config/foo/global.ini + #scp -q -P $sshport -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $full_path/conf-$destdir/foo-database.ini $sshuser@$remote:$subdocroot/$destdir/$time_stamp/config/config/foo/database.ini + #scp -q -P $sshport -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $full_path/conf-$destdir/bar-config.ini $sshuser@$remote:$subdocroot/$destdir/$time_stamp/config/config/bar/config.ini + + # UNIX rights => ADJUST ALL RIGHTS, PARTICULARLY FOR ADDING WRITE PERMISSIONS + ssh -p $sshport -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $sshuser@$remote chmod -R g+rX,o+rX $subdocroot/$destdir/$time_stamp + #ssh -p $sshport -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $sshuser@$remote chmod -R g+w $subdocroot/$destdir/$time_stamp/cache + + # ADD SPECIFIC ACTIONS ON WWW SERVERS + +done + +for remote in $staticlist; do + + #TODO: calculer la taille de la destination (df $subdocroot) && pas de deploiement si cela depasse !! + # Be sure to have space for deploying + local_space=`du -sm $dir_export/www/static/ | sed 's/^\([0-9]\+\)\t.*$/\1/'` + remote_space=`ssh -p $sshport -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $sshuser@$remote df -lPm /var/www/$staticdestdir | grep ^/ | tr -s " " | cut -d" " -f4` + if [ $local_s -ge $remote_s ]; then echo "WARNING... $remote has only $remote_s Mo while you want upload $local_s Mo : stop deploy now with Ctrl+C"; read enter; fi + + echo "sending static on $remote" + + rsync -rlptq -e "ssh -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey" $dir_export/$staticfilesdir --delete $excludelist $sshuser@$remote:$subdocroot/static + ssh -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $sshuser@$remote chmod -R g+rX,o+rX $subdocroot/static +done + +# Enable new code +last_frontal=`echo -n $wwwlist | sed 's/.* \+\([^ ]\+ *\)$/\1/'` +for remote in `echo -n $wwwlist | sed "s/$last_frontal//"`; do + + if [ $prod -eq 1 ]; then + echo "stopping Apache on $remote" + ssh -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $sshuser@$remote sudo /etc/init.d/apache2 stop + fi + + # Change symlink current to new code + ssh -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $sshuser@$remote "rm $subdocroot/$destdir/current && cd $subdocroot/$destdir && ln -s $time_stamp current" + + if [ $prod -eq 1 ] && [ "$veryhighcritical" != "true" ]; then + echo "starting Apache on $remote" + sleep 3 && ssh -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $sshuser@$remote sudo /etc/init.d/apache2 start + fi + +done + +for remote in $last_frontal; do + ssh -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $sshuser@$remote "rm $subdocroot/$destdir/current && cd $subdocroot/$destdir && ln -s $time_stamp current" +done + +if [ "$veryhighcritical" = "true" ]; then + for remote in `echo -n $wwwlist | sed "s/$last_frontal//"`; do + echo "starting Apache on $remote" + ssh -o UserKnownHostsFile=$full_path/known_hosts -i $full_path/$sshkey $sshuser@$remote sudo /etc/init.d/apache2 start + done +fi + +# ADD SPECIFIC ACTIONS (SQL DEPLOYMENT, etc.) + +rm -rf $tmp_dir + diff --git a/log/.empty b/log/.empty new file mode 100644 index 0000000..e69de29