Initial version
This commit is contained in:
commit
6de43a4ca4
111
README
Normal file
111
README
Normal file
|
@ -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 <SVN revision>
|
||||
|
||||
Deploy your static files:
|
||||
% evolistrano.sh -S
|
||||
|
||||
Deploy for production:
|
||||
% evolistrano.sh -P <SVN revision>
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
0
conf-preprod/.empty
Normal file
0
conf-preprod/.empty
Normal file
0
conf-prod/.empty
Normal file
0
conf-prod/.empty
Normal file
51
evolistrano.conf
Normal file
51
evolistrano.conf
Normal file
|
@ -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=""
|
173
evolistrano.sh
Executable file
173
evolistrano.sh
Executable file
|
@ -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 <<EOT
|
||||
Usage: $0 [OPTION] REVNUM
|
||||
|
||||
Sans option : Mise en preproduction
|
||||
-P : Mise en production
|
||||
EOT
|
||||
exit 1
|
||||
}
|
||||
|
||||
read_confirm() {
|
||||
read ok
|
||||
if [ "$ok" != "y" ]; then
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
[ $# -lt 1 ] && usage
|
||||
if [ $1 == "-P" ]; then
|
||||
prod=1
|
||||
shift
|
||||
else
|
||||
prod=0
|
||||
fi
|
||||
|
||||
[ $# -lt 1 ] && usage
|
||||
revnum=$1
|
||||
|
||||
if [ $prod -eq 1 ]; then
|
||||
log_file=$logdir/prod.log
|
||||
opname="PRODUCTION"
|
||||
destdir="prod"
|
||||
sshuser=$deployproduser
|
||||
staticdestdir="prod/static"
|
||||
confdir=$confproddir
|
||||
else
|
||||
log_file=$logdir/preprod.log
|
||||
opname="preprod"
|
||||
destdir="preprod"
|
||||
sshuser=$deploypreproduser
|
||||
staticdestdir="preprod/static"
|
||||
confdir=$confpreproddir
|
||||
fi
|
||||
|
||||
# Display infos about deployement
|
||||
echo
|
||||
svn info $svnpath -r $revnum
|
||||
|
||||
echo -n "Confirmer la mise en $opname de la révision $revnum ? [y/N] "
|
||||
read_confirm
|
||||
|
||||
|
||||
# Warning if it's not the last revision
|
||||
last_commited_rev=`svn info $svnpath | grep ^Revision | sed 's/.*: \([0-9]\+\)$/\1/'`
|
||||
if [ $revnum -ne $last_commited_rev ]; then
|
||||
echo -n "Attention, la révision $revnum n'est pas la plus récente. Continuer ? [y/N] "
|
||||
read_confirm
|
||||
fi
|
||||
|
||||
tmpfile=`mktemp -p $tmpdir`
|
||||
cat <<EOT >$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
|
||||
|
0
log/.empty
Normal file
0
log/.empty
Normal file
Reference in a new issue