| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 | 
							- #!/bin/bash
 
- #
 
- #   docker-deploy-elb - Deploy docker image to containers behind and ELB
 
- #
 
- #   usage: docker-deploy --aws-profile profile --deploy cmd --group group --proxy proxy --region region \
 
- #          --validate page --version 1.2.3 IMAGE
 
- REMOTE_SGROUP="remote-prod"
 
- DPORT=2375
 
- #
 
- #   Source configuration and define as env vars
 
- #
 
- #files=""
 
- #for f in ../CONFIG/keys.json CONFIG/keys.json product.json ../pak.json ; do
 
- #    [ -f ${f} ] && files="${files} ${f}"
 
- #done
 
- #eval $(../paks/assist/json2env ${files})
 
- if [ -f pak.json ] ; then
 
-     VERSION=$(pak --dir . edit version)
 
- else
 
-     VERSION=$(pak --dir .. edit version)
 
- fi
 
- while [[ $# -gt 0 ]] ; do
 
-     arg="$1"
 
-     case ${arg} in
 
-         --aws-profile)
 
-             AWS_PROFILE=${2}
 
-             shift ; shift
 
-             ;;
 
-         --deploy)
 
-             AWS_DEPLOY=${2}
 
-             shift ; shift
 
-             ;;
 
-         --group)
 
-             AWS_GROUP=${2}
 
-             shift ; shift
 
-             ;;
 
-         --proxy)
 
-             AWS_PROXY=${2}
 
-             shift ; shift
 
-             ;;
 
-         --region)
 
-             AWS_DEFAULT_REGION=${2}
 
-             shift ; shift
 
-             ;;
 
-         --validate)
 
-             AWS_VALIDATE=${2}
 
-             shift ; shift
 
-             ;;
 
-         --version)
 
-             VERSION=${2}
 
-             shift ; shift
 
-             ;;
 
-         *)
 
-             break
 
-             ;;
 
-     esac
 
- done
 
- IMAGE="${1}"
 
- NAME="${IMAGE}"
 
- if [ "${IMAGE}" = "" -o "${AWS_DEFAULT_REGION}" = "" ] ; then
 
-     echo "usage: docker-deploy [--aws-profile profile] [--region region] [--proxy proxy] [--group group] [--deploy cmd] [--validate cmd] image"
 
-     exit 255
 
- fi
 
- export AWS_PROFILE AWS_DEFAULT_REGION
 
- #
 
- #   Source functions
 
- #
 
- . $(dirname ${BASH_SOURCE[0]})/common
 
- . $(dirname ${BASH_SOURCE[0]})/docker-login
 
- . $(dirname ${BASH_SOURCE[0]})/remote-access
 
- echo -e "\nDeploy container \"${IMAGE}:${VERSION}\" to Load Balancer \"${AWS_PROXY}\" Group \"${AWS_GROUP}\"\n"
 
- getGroups() {
 
-     local groups arn
 
-     groups=""
 
-     for arn in $(aws elbv2 describe-target-groups --names ${AWS_GROUP} --output text --query 'TargetGroups[].TargetGroupArn')
 
-     do
 
-         if [ "${arn}" = "${arn/\/${AWS_GROUP}\//}" ] ; then
 
-             continue
 
-         fi
 
-         groups="${groups} ${arn}"
 
-     done
 
-     echo ${groups}
 
- }
 
- getTargets() {
 
-     local arn
 
-     arn=$1
 
-     aws elbv2 describe-target-health --target-group-arn ${arn} --output text --query 'TargetHealthDescriptions[].Target.Id'
 
-     if [ $? != 0 ] ; then
 
-         echo "Cannot get target health"
 
-         exit 255
 
-     fi
 
- }
 
- getHost() {
 
-     local target
 
-     target=$1
 
-     aws ec2 describe-instances --instance-ids ${target} --output text --query Reservations[0].Instances[0].PublicIpAddress
 
- }
 
- checkTargets() {
 
-     local arn targets tcount
 
-     allTargets=$(aws ec2 describe-instances \
 
-         --filter "Name=tag:aws:autoscaling:groupName,Values=${AWS_PROXY}" --output text \
 
-         --query 'Reservations[].Instances[].InstanceId')
 
-     if [ $? != 0 ] ; then
 
-         echo "Cannot describe instances"
 
-         exit 255
 
-     fi
 
-     if [ "${allTargets}" = "" ] ; then
 
-         echo "No targets found in autoscale group ${AWS_PROXY}"
 
-         exit 255
 
-     fi
 
-     echo "Check targets are registered with proxy"
 
-     for arn in $(getGroups)
 
-     do
 
-         targets=$(getTargets ${arn})
 
-         tcount=0
 
-         for target in ${allTargets}
 
-         do
 
-             if [ "${allTargets/${target}/}" = "${allTargets}" ] ; then
 
-                 echo "Repair target ${target} missing from proxy"
 
-                 aws elbv2 register-targets --target-group-arn ${arn} --targets Id=${target}
 
-                 if [ $? != 0 ] ; then
 
-                     echo "Cannot register ${target} with load balancer ${AWS_PROXY}"
 
-                     continue
 
-                 fi
 
-             else
 
-                 echo "Target ${target} is registered in proxy"
 
-                 tcount=$((tcount+1))
 
-             fi
 
-         done
 
-     done
 
-     echo "${NAME} has ${tcount} targets in target group"
 
- }
 
- pullImage() {
 
-     local i
 
-     i=0
 
-     while [ $i -lt 5 ]
 
-     do
 
-         echo Pull image ${IMAGE_PATH}:${VERSION}
 
-         output=$(docker pull ${IMAGE_PATH}:${VERSION})
 
-         if [ $? = 0 ] ; then
 
-             echo "Image pulled"
 
-             echo ${output} | egrep -v 'Already exists|Pulling|Waiting|Verifying|Download complete|Pull complete|Digest:'
 
-             return 0
 
-         fi
 
-         echo ${output}
 
-         echo "Cannot pull image, retry in 5 seconds. (${i})"
 
-         sleep 5
 
-         i=$((i+1))
 
-     done
 
-     return 1
 
- }
 
- validate() {
 
-     local host i
 
-     host=$1
 
-     echo "Validate application at http://${host}${AWS_VALIDATE}"
 
-     i=0
 
-     while [ $i -lt 5 ]
 
-     do
 
-         code=$(curl -s -o /dev/null --retry 10 --retry-delay 1 --retry-max-time 15 \
 
-             -I -w "%{http_code}" http://${host}${AWS_VALIDATE})
 
-         if [ "${code}" = 200 ] ; then
 
-             echo "PASSED: Health check successful"
 
-             return 0
 
-         fi
 
-         echo "Continue to wait for application, retry in 5 seconds. (${i})"
 
-         sleep 5
 
-         i=$((i+1))
 
-     done
 
-     echo "FAILED: Cannot validate application, status ${code}"
 
-     return 1
 
- }
 
- waitForDrain() {
 
-     local i state target
 
-     target=$1
 
-     echo "Wait for elb to drain target ${target}"
 
-     i=0
 
-     while [ $i -lt 30 ]
 
-     do
 
-         state=$(aws elbv2 describe-target-health --target-group-arn "${arn}" --output text --query "TargetHealthDescriptions[?Target.Id=='${target}'].TargetHealth.State")
 
-         if [ $? != 0 ] ; then
 
-             echo "Cannot get target health for ${target}"
 
-             exit 255
 
-         fi
 
-         if [ "${state}" = "" ] ; then
 
-             echo Target ${target} now removed from elb.
 
-             return 0
 
-         fi
 
-         echo Waiting for ${target} to drain. State: ${state}.
 
-         sleep 1
 
-         i=$((i+1))
 
-     done
 
-     echo "FAILED: Cannot drain instance ${target} state ${state}"
 
-     return 1
 
- }
 
- waitForReady() {
 
-     local i state target
 
-     target=$1
 
-     echo "Wait for elb to enable target ${target}"
 
-     i=0
 
-     while [ $i -lt 30 ]
 
-     do
 
-         state=$(aws elbv2 describe-target-health --target-group-arn "${arn}" --output text --query "TargetHealthDescriptions[?Target.Id=='${target}'].TargetHealth.State")
 
-         if [ $? != 0 ] ; then
 
-             echo "Cannot get target health for ${target}"
 
-             exit 255
 
-         fi
 
-         if [ "${state}" = "healthy" ] ; then
 
-             echo Target ${target} now ${state}.
 
-             return 0
 
-         fi
 
-         echo Waiting for ${target} to become healthy. State: ${state}.
 
-         sleep 1
 
-         i=$((i+1))
 
-     done
 
-     echo "FAILED: Instance not ready ${target} state ${state}"
 
-     return 1
 
- }
 
- #
 
- #   Currently 1-1 correspondence between target group and application
 
- #
 
- count=0
 
- passed=0
 
- checkTargets
 
- for arn in $(getGroups)
 
- do
 
-     for target in $(getTargets ${arn})
 
-     do
 
-         fail=
 
-         count=$((count+1))
 
-         URI=$(dockerLogin)
 
-         IMAGE_PATH=${URI}/${IMAGE}
 
-         grantAccess ${REMOTE_SGROUP} ${DPORT}
 
-         host=$(getHost ${target})
 
-         export DOCKER_HOST=tcp://${host}:${DPORT}
 
-         if ! pullImage ; then
 
-             echo "Cannot pull ${IMAGE}:${VERSION} on ${target}"
 
-             continue
 
-         fi
 
-         echo
 
-         echo "----------------------------------------------------------------------------------"
 
-         echo "Deploy to instance ${target} at ${host}"
 
-         echo "----------------------------------------------------------------------------------"
 
-         curret=$(docker ps --filter ancestor=${IMAGE}:${VERSION} --format '{{.ID}}')
 
-         if [ $? != 0 ] ; then
 
-             echo "Cannot talk to docker on ${target}"
 
-             fail=1
 
-             continue
 
-         fi
 
-         if [ "${current}" != "" ] ; then
 
-             echo "Target ${target} already running version ${IMAGE}:${VERSION}"
 
-             if [ "${FORCE}" = "" ] ; then
 
-                 passed=$((passed+1))
 
-                 continue
 
-             fi
 
-         fi
 
-         #
 
-         #   Deregister. After deregister, we must ALWAYS reregister below.
 
-         #
 
-         echo "Deregister instance ${target} from load balancer"
 
-         aws elbv2 deregister-targets --target-group-arn ${arn} --targets Id=${target}
 
-         if [ $? != 0 ] ; then
 
-             echo "Cannot deregister ${target} from target group ${AWS_GROUP}"
 
-             fail=1
 
-             # Keep going
 
-         fi
 
-         #
 
-         #   AWS seems to require at least a 10-15 second deregistration delay. It seems to deregister targets, but
 
-         #   continues to route requests to them for up to 15 seconds. Ugh!
 
-         #
 
-         delay=$(aws elbv2 describe-target-group-attributes --target-group-arn ${arn} \
 
-             --output text --query "Attributes[?Key=='deregistration_delay.timeout_seconds'].Value")
 
-         echo Waiting for the deregistration delay ${delay}
 
-         # This seems to need to be >= the deregistration delay for the target group
 
-         sleep ${delay}
 
-         #
 
-         #   Gracefully stop containers
 
-         #
 
-         containers=$(docker ps --filter "name=${NAME}" --format '{{.ID}}')
 
-         if [ "${containers}" != "" ] ; then
 
-             echo "Gracefully stop traffic on ${NAME}"
 
-             #
 
-             #   The ELB should have stopped sending requests by now.
 
-             #   The quit instructs the container to do what it can to gracefully clean up current requests.
 
-             #
 
-             startQuit=$(date +%s)
 
-             echo "docker kill -s SIGQUIT ${NAME}"
 
-             docker kill -s SIGQUIT ${NAME}
 
-             #
 
-             #   Wait for instance to be fully removed from the elb
 
-             #
 
-             if ! waitForDrain "${target}" ; then
 
-                 echo "Cannot drain ${target}, force kill"
 
-                 # keep going - should not happen - best to upgrade
 
-             fi
 
-             #
 
-             #   Wait for the app drain timeout
 
-             #
 
-             period=$((10 - $(date +%s) + ${startQuit}))
 
-             if [ "${period}" -gt 0 ] ; then
 
-                 echo sleep ${period}
 
-                 sleep ${period}
 
-             fi
 
-             echo "Stopping container ${NAME} ${containers}"
 
-             docker stop ${NAME}
 
-             if [ $? != 0 ] ; then
 
-                 echo "Cannot stop container ${container} on ${target}, continuing ..."
 
-                 # May not be running, continue
 
-             fi
 
-         fi
 
-         #
 
-         #   Remove existing containers
 
-         #
 
-         echo "Remove container ${NAME} ${containers}"
 
-         docker rm ${NAME} >/dev/null 2>&1
 
-         #
 
-         #   Start new container
 
-         #
 
-         COMMAND=$(echo ${AWS_DEPLOY} | sed "s/-d/-d -e HOST=${host}/" | sed "s^${IMAGE}:${VERSION}^${URI}/${IMAGE}:${VERSION}^")
 
-         echo "${COMMAND}"
 
-         ${COMMAND}
 
-         if [ $? != 0 ] ; then
 
-             echo "Cannot start container ${IMAGE}:${VERSION} on ${target}"
 
-             echo "WARNING: target ${target} is not registered with load balancer, skip further deployments."
 
-             fail=1
 
-         fi
 
-         #
 
-         #   Register with load balancer
 
-         #
 
-         echo "Register instance ${target} with load balancer"
 
-         aws elbv2 register-targets --target-group-arn ${arn} --targets Id=${target}
 
-         if [ $? != 0 ] ; then
 
-             echo "Cannot register ${target} with target group ${AWS_GROUP}"
 
-             continue
 
-         fi
 
-         #
 
-         #   Validate
 
-         #
 
-         if [ "${fail}" = "" ] ; then
 
-             echo -n "Started: "
 
-             docker ps --filter "ancestor=${IMAGE}:${VERSION}" --format '{{.ID}}, {{.Image}}, {{.Status}}'
 
-             if ! validate ${host} ; then
 
-                 echo "Could not validate target ${target}"
 
-                 break
 
-             fi
 
-         fi
 
-         #
 
-         #   Wait for instance to be recognized by elb
 
-         #
 
-         if ! waitForReady "${target}" ; then
 
-             echo "Target ${target} did not become ready. Halting deploy."
 
-             break
 
-         fi
 
-         dockerLogout
 
-         revokeAccess ${REMOTE_SGROUP} ${DPORT}
 
-         passed=$((passed+1))
 
-     done
 
- done
 
- if [ "${passed}" != "${count}" ] ; then
 
-     echo "FAILED, upgraded ${passed} instances of ${count} with ${IMAGE}:${VERSION}"
 
-     exit 1
 
- fi
 
- echo -e "\nPASSED, all ${count} instances running ${IMAGE}:${VERSION}"
 
- echo -e "\nRunning docker garbage collection"
 
- docker system prune -f >/dev/null
 
- DOCKER_HOST= docker system prune -f >/dev/null
 
- exit 0
 
 
  |