Linux Shell脚本之远程自动化部署java maven项目
#!/bin/bash# Name: doDeploy.sh
#Execute this shell script to deploy Java projects built by Maven automatically on remote hosts.
# debug option
#_XTRACE_FUNCTIONS=$(set +o | grep xtrace)
#set -o xtrace
# define user friendly messages
header="
Function: Execute this shell script to deploy Java projects built by Maven automatically on remote hosts.
License: Open source software
"
# define variables
# Where to get source code
project_clone_depends_1=""
project_clone="ssh://git@xxx/xxx.git"
deploy_target_host_ip="xxx.xxx.xxx.xxx"
project_top_directory_to_target_host="/path/to/deploy/project"
docker_container_name=""
# Setting how many days do you want save old releases, default is 10 days
save_old_releases_for_days=10
# end define variables
# pretreatment
test -z ${project_clone_depends_1} || project_clone_target_depends_1="`echo ${project_clone_depends_1} | awk -F '[/.]+' '{ print $(NF-1)}'`"
project_clone_target="`echo ${project_clone} | awk -F '[/.]+' '{ print $(NF-1)}'`"
project_clone_repository_name=${project_clone_target}
# end pretreatment
# Public header
# =============================================================================================================================
# resolve links - $0 may be a symbolic link
# learn from apache-tomcat-6.x.xx/bin/catalina.sh
PRG="$0"
while [ -h "$PRG" ]; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`/"$link"
fi
done
# Get standard environment variables
PRGDIR=`dirname "$PRG"`
# echo color function, smarter, learn from lnmp.org lnmp install.sh
function echo_r (){
# Color red: Error, Failed
[ $# -ne 1 ] && return 1
echo -e "\033[31m$1\033[0m"
}
function echo_g (){
# Color green: Success
[ $# -ne 1 ] && return 1
echo -e "\033[32m$1\033[0m"
}
function echo_y (){
# Color yellow: Warning
[ $# -ne 1 ] && return 1
echo -e "\033[33m$1\033[0m"
}
function echo_b (){
# Color blue: Debug, friendly prompt
[ $# -ne 1 ] && return 1
echo -e "\033[34m$1\033[0m"
}
# end echo color function, smarter
#WORKDIR="`realpath ${WORKDIR}`"
WORKDIR="`readlink -f ${PRGDIR}`"
# end public header
# =============================================================================================================================
USER="`id -un`"
LOGNAME="$USER"
if [ $UID -ne 0 ]; then
echo "WARNING: Running as a non-root user, \"$LOGNAME\". Functionality may be unavailable. Only root can use some commands or options"
fi
command_exists() {
# which "$@" >/dev/null 2>&1
command -v "$@" >/dev/null 2>&1
}
check_command_can_be_execute(){
[ $# -ne 1 ] && return 1
command_exists $1
}
check_network_connectivity(){
echo_b "checking network connectivity ... "
network_address_to_check=8.8.4.4
stable_network_address_to_check=114.114.114.114
ping_count=2
ping -c ${ping_count} ${network_address_to_check} >/dev/null
retval=$?
if [ ${retval} -ne 0 ] ; then
if ping -c ${ping_count} ${stable_network_address_to_check} >/dev/null;then
echo_g "Network to $stable_network_address_to_check succeed! "
echo_y "Note: network to $network_address_to_check failed once! maybe just some packages loss."
elif ! ip route | grep default >/dev/null; then
echo_r "Network is unreachable, gateway is not set."
exit 1
elif ! ping -c2 $(ip route | awk '/default/ {print $3}') >/dev/null; then
echo_r "Network is unreachable, gateway is unreachable."
exit 1
else
echo_r "Network is blocked! "
exit 1
fi
elif [ ${retval} -eq 0 ]; then
echo_g "Check network connectivity passed! "
fi
}
check_name_resolve(){
echo_b "checking DNS name resolve ... "
target_name_to_resolve="github.com"
stable_target_name_to_resolve="www.aliyun.com"
ping_count=1
if ! ping-c${ping_count} ${target_name_to_resolve} >/dev/null; then
echo_y "Name lookup failed for $target_name_to_resolve with $ping_count times "
if ping-c${ping_count} ${stable_target_name_to_resolve} >/dev/null; then
echo_g "Name lookup success for $stable_target_name_to_resolve with $ping_count times "
fi
eval_md5sum_of_nameserver_config="`md5sum /etc/resolv.conf | awk '{ print $1 }'`"
if test ${eval_md5sum_of_nameserver_config} = "674ea91675cdfac353bffbf49dc593c3"; then
echo_y "Nameserver config file is validated, but name lookup failed for $target_name_to_resolve with $ping_count times"
return 0
fi
[ -f /etc/resolv.conf ] && cp /etc/resolv.conf /etc/resolv.conf_$(date +%Y%m%d%H%M%S)~
cat >/etc/resolv.conf>${WORKDIR}/git_$(date +%Y%m%d)_$$.log 2>&1
# TODO(Guodong Ding) get branch names or revision numbers from VCS data
cd ${project_clone_directory}
git checkout ${branch} >>${WORKDIR}/git_$(date +%Y%m%d)_$$.log 2>&1
cd ..
echo_g "git clone from $project_clone_repository successfully! "
else
echo_b "git pull from $project_clone_repository"
cd ${project_clone_directory}
git pull >>${WORKDIR}/git_$(date +%Y%m%d)_$$.log 2>&1
git checkout ${branch} >>${WORKDIR}/git_$(date +%Y%m%d)_$$.log 2>&1
# TODO(Guodong Ding) get branch names or revision numbers from VCS data
cd ..
echo_g "git pull from $project_clone_repository successfully! "
fi
set +o errexit
}
function maven_build_project_deprecated(){
set -o errexit
echo_b "Do mvn build java project... "
check_command_can_be_execute mvn
[ $# -ge 1 ] && project_clone_repository="$1"
project_clone_repository_name="`echo ${project_clone_repository} | awk -F '[/.]+' '{ print $(NF-1)}'`"
project_clone_directory=${WORKDIR}/repository/${project_clone_repository_name}
cd ${project_clone_directory}
mvn install >>${WORKDIR}/mvn_build_$(date +%Y%m%d)_$$.log 2>&1
mvn clean package >>${WORKDIR}/mvn_build_$(date +%Y%m%d)_$$.log 2>&1
cd ..
echo_g "Do mvn build java project finished with exit code 0! "
set +o errexit
}
function maven_build_project(){
echo_b "Do mvn build java project for `echo $1 | awk -F '[/.]+' '{ print $(NF-1)}'`... "
check_command_can_be_execute mvn
[ $# -ge 1 ] && project_clone_repository="$1"
project_clone_repository_name="`echo ${project_clone_repository} | awk -F '[/.]+' '{ print $(NF-1)}'`"
project_clone_directory=${WORKDIR}/repository/${project_clone_repository_name}
cd ${project_clone_directory}
mvn install >>${WORKDIR}/mvn_build_$(date +%Y%m%d)_$$.log 2>&1
retval=$?
if [ ${retval} -ne 0 ] ; then
echo_r "mvn install failed! More details refer to ${WORKDIR}/mvn_build_$(date +%Y%m%d)_$$.log"
exit 1
else
echo_g "mvn install for ${project_clone_repository_name} successfully! "
fi
mvn clean package >>${WORKDIR}/mvn_build_$(date +%Y%m%d)_$$.log 2>&1
retval=$?
if [ ${retval} -ne 0 ] ; then
echo_r "mvn clean package for ${project_clone_repository_name} failed! More details refer to ${WORKDIR}/mvn_build_$(date +%Y%m%d)_$$.log"
exit 1
else
echo_g "mvn clean package for ${project_clone_repository_name} successfully! "
fi
cd ..
echo_g "Do mvn build java project finished for ${project_clone_repository_name} with exit code 0! "
}
function check_ssh_can_be_connect(){
[ $# -ne 1 ] && return 1
echo_b "Check if can ssh to remote host $1 ... "
check_command_can_be_execute ssh || return 1
ssh -i /etc/ssh/ssh_host_rsa_key -p 22 -oStrictHostKeyChecking=no root@$1 "uname -a >/dev/null 2>&1"
retval=$?
if [ ${retval} -ne 0 ] ; then
echo_r "Check ssh to remote host $1 failed! "
exit 1
else
echo_g "Check ssh to remote host $1 successfully! "
fi
}
# ssh_execute_command_on_remote_host hostname command
function ssh_execute_command_on_remote_host(){
[ $# -ne 2 ] && return 1
ssh -i /etc/ssh/ssh_host_rsa_key -p 22 -oStrictHostKeyChecking=no root@$1 "$2" >>${WORKDIR}/ssh_command_$(date +%Y%m%d)_$$.log
retval=$?
if [ ${retval} -ne 0 ] ; then
echo_r "ssh execute command on remote host $2 failed! More details refer to ${WORKDIR}/ssh_command_$(date +%Y%m%d)_$$.log"
return 1
else
echo_g "ssh execute command on remote host $2 successfully! "
return 0
fi
}
function restart_docker_container(){
echo_b "Restarting docker container..."
[ $# -ne 1 ] && return 1
# TODO(Guodong Ding) if we need restart more related docker container
local docker_container_name=""
test -n $1 && docker_container_name="$1"
ssh_execute_command_on_remote_host "docker restart $docker_container_name"
retval=$?
if [ ${retval} -ne 0 ] ; then
echo_r "restart docker container for$docker_container_name failed! "
exit 1
else
echo_g "restart docker container for $docker_container_name successfully! "
return 0
fi
}
# scp_local_files_to_remote_host local_path remote_hostname remote_path
function scp_local_files_to_remote_host(){
[ $# -ne 3 ] && return 1
[ ! -d $1 -a ! -f $1 ] && return 1
check_ssh_can_be_connect $2
scp -i /etc/ssh/ssh_host_rsa_key -P 22 -oStrictHostKeyChecking=no -rp $1 root@$2:$3 >/dev/null 2>&1
retval=$?
if [ ${retval} -ne 0 ] ; then
echo_r "scp local files to remote host failed! "
exit 1
else
echo_g "scp local files to remote host successfully! "
fi
}
# scp_remote_files_to_local_host remote_hostname remote_path local_path
function scp_remote_files_to_local_host(){
[ $# -ne 3 ] && return 1
check_ssh_can_be_connect $1
scp -i /etc/ssh/ssh_host_rsa_key -P 22 -oStrictHostKeyChecking=no -rp root@$1:$2 $3 >/dev/null 2>&1
retval=$?
if [ ${retval} -ne 0 ] ; then
echo_r "scp remote files to local host failed! "
exit 1
else
echo_g "scp remote files to local host successfully! "
fi
}
function backup_remote_host_config_files(){
echo_b "backup remote host config files..."
scp_remote_files_to_local_host ${deploy_target_host_ip} ${project_top_directory_to_target_host}/* ${WORKDIR}/backup
# get config files
[ "$(ls -A ${WORKDIR}/backup)" ] && find ${WORKDIR}/backup/. -type f ! -name . -a ! -name '*.xml*' -a ! -name '*.properties*' -exec rm -f -- '{}' \;
# remove empty directory
find ${WORKDIR}/backup/. -empty -type d -delete
# TODO(Guodong Ding) improvements here
echo_g "backup remote host config files finished."
}
function rollback_remote_host_config_files(){
echo_b "rollback remote host config files..."
#scp_local_files_to_remote_host ${WORKDIR}/backup ${deploy_target_host_ip} ${project_top_directory_to_target_host}
saved_IFS=$IFS
IFS=' '
cd ${WORKDIR}/current
for file in ${WORKDIR}/backup/*;do
scp_local_files_to_remote_host ${file} ${deploy_target_host_ip} ${project_top_directory_to_target_host}
done
cd ${WORKDIR}
IFS=${saved_IFS}
# TODO(Guodong Ding) if save remote host config files
# some ops
# TODO(Guodong Ding) improvements here
echo_g "rollback remote host config files finished."
}
function deploy() {
[ -n "$header" ] && echo "$header"
# check a directories lock, Note: this is redundant
if [[ ! -f ${WORKDIR}/.lock ]]; then
setDirectoryStructureOnLocalHost
fi
cleanOldReleases
# do dependencies checking
check_network_connectivity
check_name_resolve
checkOtherDependencies
check_ssh_can_be_connect ${deploy_target_host_ip}
# do core job
# TODO(Guodong Ding) if we need a git_project_clone "$project_clone_depends_1" here using auto judgment statement
test -z ${project_clone_depends_1} || git_project_clone "$project_clone_depends_1"
git_project_clone "$project_clone"
test -z ${project_clone_depends_1} || maven_build_project "$project_clone_depends_1"
maven_build_project "$project_clone"
cd ${WORKDIR}
# links_target_directory_to_current
# Make directory to release directory
if test ! -d ${WORKDIR}/release -o ! -d ${WORKDIR}/share; then
echo_r "capistrano directory structure is broken, make sure the file .capistrano_ds_lock is deleted before a new deploy! "
exit 1
# test -f ${WORKDIR}/.capistrano_ds_lock && \rm -rf${WORKDIR}/.capistrano_ds_lock
fi
new_release_just_created="$WORKDIR/release/$(date +%Y%m%d%H%M%S)"
[ ! -d ${new_release_just_created} ] && mkdir ${new_release_just_created}
[ -d ${WORKDIR}/repository/${project_clone_repository_name}/target/${project_clone_repository_name}/ ] && \
\cp -rf ${WORKDIR}/repository/${project_clone_repository_name}/target/${project_clone_repository_name}/* ${new_release_just_created}
# Make source code symbolic link to current
( [ -f ${WORKDIR}/current ] || [ -d ${WORKDIR}/current ] ) && rm -rf ${WORKDIR}/current
ln -s ${new_release_just_created} ${WORKDIR}/current
# backup remote host config files
backup_remote_host_config_files
# scp_local_files_to_remote_host ${WORKDIR}/current/ ${deploy_target_host_ip} ${project_top_directory_to_target_host}
saved_IFS=$IFS
IFS=' '
cd ${WORKDIR}/current
for file in ${WORKDIR}/current/*;do
scp_local_files_to_remote_host ${file} ${deploy_target_host_ip} ${project_top_directory_to_target_host}
done
cd ${WORKDIR}
IFS=${saved_IFS}
# rollback remote host config files
rollback_remote_host_config_files
# Move conf and logs directives from release to share
[ -d ${WORKDIR}/release/conf ] && mv ${WORKDIR}/release/conf ${WORKDIR}/share/conf
[ -d ${WORKDIR}/release/logs ] && mv ${WORKDIR}/release/logs ${WORKDIR}/share/logs
# Make conf and logs symbolic link to current
[ -d ${WORKDIR}/share/conf ] && ln -s ${WORKDIR}/share/conf ${WORKDIR}/current/conf
[ -d ${WORKDIR}/share/logs ] && ln -s ${WORKDIR}/share/logs ${WORKDIR}/current/logs
# Start service or validate status
if [[ -e ${WORKDIR}/current/bin/startup.sh ]]; then
${WORKDIR}/current/bin/startup.sh start
RETVAL=$?
else
# TODO(Guodong Ding) external health check
test -z ${docker_container_name} || restart_docker_container ${docker_container_name}
RETVAL=$?
fi
# if started ok, then create a workable program to a file
if [[ ${RETVAL} -eq 0 ]]; then
# Note cat with eof must start at row 0, and with eof end only, such as no blank spaces, etc
cat >${WORKDIR}/share/workable_program.log
页:
[1]