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