Last active
October 23, 2020 15:56
-
-
Save Himura2la/21d6054eb9af69438946928387b76360 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
usage_msg="Usage: $0 ssh_address local_image_tag" | |
ssh_address=${1?$usage_msg} | |
image_name=${2?$usage_msg} | |
echo "Sending gzipped image '$image_name' to '$ssh_address' via ssh..." | |
docker image save $image_name | gzip | ssh $ssh_address 'zcat | docker image load' | |
echo "Connecting to '$ssh_address' via ssh to seamlessly deploy '$image_name'..." | |
( sed "\$a deploy $image_name" | ssh -T $ssh_address ) << 'END_OF_SCRIPT' | |
if [ "$SHELL" != "/bin/bash" ] | |
then | |
echo "The '$SHELL' shell is not supported by 'deploy.sh'. Set a '/bin/bash' shell for '$USER@$HOSTNAME'." | |
exit 1 | |
fi | |
deploy() { | |
local image_name=${1?"Usage: ${FUNCNAME[0]} image_name"} | |
ensure-reverse-proxy || return 2 | |
if get-active-slot $image_name | |
then | |
local OLD=${image_name}_BLUE | |
local new_slot=GREEN | |
else | |
local OLD=${image_name}_GREEN | |
local new_slot=BLUE | |
fi | |
local NEW=${image_name}_${new_slot} | |
echo "Ensuring the '$NEW' container from previous deploy is removed..." | |
docker rm -f $NEW || : | |
echo "Deploying '$NEW' in place of '$OLD'..." | |
docker run \ | |
--detach \ | |
--restart always \ | |
--log-driver journald \ | |
--name $NEW \ | |
--network web-gateway \ | |
$image_name || return 3 | |
echo "Container started. Checking health..." | |
for i in {1..20} | |
do | |
sleep 1 | |
if get-service-status $image_name $new_slot | |
then | |
echo "New '$NEW' service seems OK. Switching heads..." | |
sleep 2 # Ensure service is ready | |
set-active-slot $image_name $new_slot || return 4 | |
echo "The '$NEW' service is live!" | |
sleep 2 # Ensure all requests were processed | |
echo "Stopping '$OLD'..." | |
docker stop $OLD | |
docker image prune -f | |
echo "Deployment successful!" | |
return 0 | |
fi | |
echo "New '$NEW' service is not ready yet. Waiting ($i)..." | |
done | |
echo "New '$NEW' service did not raise, killing it. Failed to deploy T_T" | |
docker rm -f $NEW | |
return 5 | |
} | |
ensure-reverse-proxy() { | |
is-container-up reverse-proxy && return 0 | |
echo "Deploying reverse-proxy..." | |
docker network create web-gateway | |
docker run \ | |
--detach \ | |
--restart always \ | |
--log-driver journald \ | |
--name reverse-proxy \ | |
--network web-gateway \ | |
--publish 80:80 \ | |
nginx:alpine || return 1 | |
docker exec reverse-proxy sh -c "> /etc/nginx/conf.d/default.conf" | |
docker exec reverse-proxy nginx -s reload | |
} | |
is-container-up() { | |
local container=${1?"Usage: ${FUNCNAME[0]} container_name"} | |
[ -n "$(docker ps -f name=${container} -q)" ] | |
return $? | |
} | |
get-active-slot() { | |
local image=${1?"Usage: ${FUNCNAME[0]} image_name"} | |
if is-container-up ${image}_BLUE && is-container-up ${image}_GREEN; then | |
echo "Collision detected! Stopping ${image}_GREEN..." | |
docker rm -f ${image}_GREEN | |
return 0 # BLUE | |
fi | |
if is-container-up ${image}_BLUE && ! is-container-up ${image}_GREEN; then | |
return 0 # BLUE | |
fi | |
if ! is-container-up ${image}_BLUE; then | |
return 1 # GREEN | |
fi | |
} | |
get-service-status() { | |
local usage_msg="Usage: ${FUNCNAME[0]} image_name deployment_slot" | |
local image=${1?usage_msg} | |
local slot=${2?$usage_msg} | |
case $image in | |
# Add specific healthcheck paths for your services here | |
*) local health_check_port_path=":8080/" ;; | |
esac | |
local health_check_address="http://${image}_${slot}${health_check_port_path}" | |
echo "Requesting '$health_check_address' within the 'web-gateway' docker network:" | |
docker run --rm --network web-gateway alpine \ | |
wget --timeout=1 --quiet --output-document=/dev/null --server-response $health_check_address | |
return $? | |
} | |
set-active-slot() { | |
local usage_msg="Usage: ${FUNCNAME[0]} service_name deployment_slot" | |
local service=${1?$usage_msg} | |
local slot=${2?$usage_msg} | |
[ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1 | |
get-nginx-config $service $slot | docker exec --interactive reverse-proxy sh -c "cat > /etc/nginx/conf.d/$service.conf" | |
docker exec reverse-proxy nginx -t || return 2 | |
docker exec reverse-proxy nginx -s reload | |
} | |
get-nginx-config() { | |
local usage_msg="Usage: ${FUNCNAME[0]} image_name deployment_slot" | |
local image=${1?$usage_msg} | |
local slot=${2?$usage_msg} | |
[ "$slot" == BLUE ] || [ "$slot" == GREEN ] || return 1 | |
local container_name=${image}_${slot} | |
case $image in | |
# Add specific nginx configs for your services here | |
*) nginx-config-simple-service $container_name:8080 ;; | |
esac | |
} | |
nginx-config-simple-service() { | |
local usage_msg="Usage: ${FUNCNAME[0]} proxy_pass" | |
local proxy_pass=${1?$usage_msg} | |
# WARNING: Mixed tabs and spaces below | |
cat <<- EOF | |
server { | |
listen 80; | |
location / { | |
proxy_pass http://$proxy_pass; | |
} | |
} | |
EOF | |
} | |
rollback() { | |
local image_name=${1?"Usage: ${FUNCNAME[0]} image_name"} | |
if get-active-slot $image_name | |
then | |
local previous_slot=GREEN | |
local current_slot=BLUE | |
else | |
local previous_slot=BLUE | |
local current_slot=GREEN | |
fi | |
docker start ${image_name}_${previous_slot} | |
set-active-slot $image_name $previous_slot | |
docker stop ${image_name}_${current_slot} | |
} | |
END_OF_SCRIPT |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment