Deploy your docker images to Amazon ECS container.

#!/bin/bash function debug() { COLOR='\033[01;32m' # bold yellow RESET='\033[00;00m' # normal white MESSAGE=${@:-"${RESET}Error: No message passed"} echo -e "${COLOR}${MESSAGE}${RESET}" } function warning() { COLOR='\033[01;34m' # bold yellow RESET='\033[00;00m' # normal white MESSAGE=${@:-"${RESET}Error: No message passed"} echo -e "${COLOR}${MESSAGE}${RESET}" } function error() { COLOR='\033[01;31m' # bold red RESET='\033[00;00m' # normal white MESSAGE=${@:-"${RESET}Error: No message passed"} echo -e "${COLOR}${MESSAGE}${RESET}" } function usage() { set -e cat <<EOM ##### ecs-deploy ##### Required arguments: -P | --ecs-compose-project-name Name of project to deploy -k | --aws-access-key AWS Access Key ID. May also be set as environment variable AWS_ACCESS_KEY_ID -s | --aws-secret-key AWS Secret Access Key. May also be set as environment variable AWS_SECRET_ACCESS_KEY -r | --region AWS Region Name. May also be set as environment variable AWS_DEFAULT_REGION -c | --cluster Name of ECS cluster -f | --docker-compose-filepath docker-compose.yaml file path. Optional arguments: -l | --admin-files-location Admin files for environment variables -D | --desired-count The number of instantiations of the task to place and keep running in your service. -m | --min minumumHealthyPercent: The lower limit on the number of running tasks during a deployment. -M | --max maximumPercent: The upper limit on the number of running tasks during a deployment. -v | --verbose Verbose output -e | --env Environment variable Requirements: aws: AWS Command Line Interface jq: Command-line JSON processor ecs-cli: ECS Command Line Interface Examples: ecs-deploy --aws-access-key XXXXXXXXXXXXXXXXXX --aws-secret-key SDSDFDFDFRXCCXCXCXCXCXCCDrereye --region us-west-2 --docker-compose-filepath docker-compose.yml --cluster default --ecs-compose-project-name hz-claims-apigee ./ecs-deploy.sh $(cat argumentsfile.txt) Add all required arguments in datafile.txt. If you are going to create a new task and service / update task and service you must provide argument -P {project_name}. EOM exit 2 } if [ $# == 0 ]; then error "Please see usage of arguments" usage; fi # Check requirements function require { command -v "$1" > /dev/null 2>&1 || { warning "One of the required software is not installed...." if [ "$1" == "jq" ]; then debug " Please install $1" >&2; debug '[INFO] To Install jq...' debug "sudo curl -o /usr/local/bin/jq https://github.com/stedolan/jq/releases/download/jq-1.4/jq-linux-x86_64" debug "sudo chmod +x /usr/local/bin/jq" fi if [ "$1" == "ecs-cli" ]; then debug "[INFO] Installing AWS ECS CLI..." sudo curl -o /usr/local/bin/ecs-cli https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-linux-amd64-latest debug "Making $1 executable..." sudo chmod +x /usr/local/bin/ecs-cli fi if [ "$1" == "aws" ]; then debug "[INFO] Installing AWS CLI..." sudo rm -rf /var/jenkins_home/tmp/ HOME=$HOME TMP=${HOME}/tmp TMP_AWSCLI=${TMP}/aws-cli-bundle mkdir -p $TMP_AWSCLI curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "${TMP_AWSCLI}/awscli-bundle.zip" unzip ${TMP_AWSCLI}/awscli-bundle.zip -d /${TMP_AWSCLI} sudo ${TMP_AWSCLI}/awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws fi } } # Setup default values for variables CLUSTER=false SERVICE_NAME=false TASK_DEFINITION=false AWS_DEFAULT_REGION=false MIN=false MAX=false VERBOSE=false AWS_CLI=/usr/local/bin/aws AWS_ECS="$AWS_CLI ecs --output json" ECS_COMPOSE_PROJECT_NAME=false DOCKER_COMPOSE_FILE_PATH=false ENV_VAR=false ADM_FILE_LOCATION=false ECS_ENV_VAR_NAME=false ENV_FILE=".env" DICT_FILE="final-env-vars.txt" DICT_FILE_PATH=false DICT_FILE_TEMP="final-env-vars-temp.txt" # Loop through arguments, two at a time for key and value while [[ $# -gt 0 ]] do key="$1" case $key in -k|--aws-access-key) AWS_ACCESS_KEY_ID="$2" shift # past argument ;; -s|--aws-secret-key) AWS_SECRET_ACCESS_KEY="$2" shift # past argument ;; -r|--region) AWS_DEFAULT_REGION="$2" shift # past argument ;; -l|--admin-files-location) ADM_FILE_LOCATION="$2" shift # past argument ;; -c|--cluster) CLUSTER="$2" shift # past argument ;; -e|--environment) ENV_VAR="$2" shift # past argument ;; -m|--min) MIN="$2" shift ;; -M|--max) MAX="$2" shift ;; -D|--desired-count) DESIRED="$2" shift ;; -f|--docker-compose-filepath) DOCKER_COMPOSE_FILE_PATH="$2" shift ;; -P|--ecs-compose-project-name) ECS_COMPOSE_PROJECT_NAME="$2" shift ;; -E|--ecs-environment-name) ECS_ENV_VAR_NAME="$2" shift ;; -v|--verbose) VERBOSE=true ;; *) usage exit 2 ;; esac shift # past argument or value done function configureEnvironment { debug "Started envirinment configuration method" if [ $VERBOSE == true ]; then set -x fi if [ $ECS_COMPOSE_PROJECT_NAME == false ]; then error "Please provide project name" usage exit 1 else TASK_DEFINITION="ecscompose-$ECS_COMPOSE_PROJECT_NAME" SERVICE_NAME="ecscompose-service-$ECS_COMPOSE_PROJECT_NAME" fi if [ $CLUSTER == false ]; then error "CLUSTER is required. You can pass the value using -c or --cluster" exit 1 fi if [ $DOCKER_COMPOSE_FILE_PATH == false ]; then error "DOCKER-COMPOSE file is required. You can pass the value using -f for docker compose file" exit 1 fi #DICT_FILE_PATH=`$(dirname "${DOCKER_COMPOSE_FILE_PATH}")` DICT_FILE_PATH=$(dirname $(readlink -f "$DOCKER_COMPOSE_FILE_PATH")) rm -rf $DICT_FILE_PATH/$DICT_FILE rm -rf $DICT_FILE_PATH/$DICT_FILE_TEMP echo -n "" > $DICT_FILE_PATH/$DICT_FILE #cat > $DICT_FILE_PATH/$DICT_FILE <<- "EOF" #EOF echo -n "" > $DICT_FILE_PATH/$DICT_FILE_TEMP #cat > $DICT_FILE_PATH/$DICT_FILE_TEMP <<- "EOF" #EOF debug "Successfully configured envirinment Variables" } function configureDeploymentArguments { debug "Configuring deployment Variables" DEPLOYMENT_CONFIG="" if [ $MAX != false ]; then DEPLOYMENT_CONFIG=",maximumPercent=$MAX" fi if [ $MIN != false ]; then DEPLOYMENT_CONFIG="$DEPLOYMENT_CONFIG,minimumHealthyPercent=$MIN" fi if [ ! -z "$DEPLOYMENT_CONFIG" ]; then DEPLOYMENT_CONFIG="--deployment-configuration ${DEPLOYMENT_CONFIG:1}" fi DESIRED_COUNT="" if [ ! -z ${DESIRED+undefined-guard} ]; then DESIRED_COUNT="--desired-count $DESIRED" else DESIRED_COUNT="--desired-count 1" fi debug "Desired count is : $DESIRED_COUNT" debug "Deployment configure is completed." } function registerTaskDefinition(){ `ecs-cli compose --file $DOCKER_COMPOSE_FILE_PATH --project-name $ECS_COMPOSE_PROJECT_NAME create` [ $? != 0 ] && \ error "Registered task definition failed !" && exit 100 debug "Registered task definition successfully" } function createOrUpdateService(){ configureDeploymentArguments SERVICE_DETAILS=`$AWS_ECS describe-services --cluster $CLUSTER --service $SERVICE_NAME | jq '.services[]|.serviceName'` SERVICE_STATUS=`$AWS_ECS describe-services --cluster $CLUSTER --service $SERVICE_NAME | jq '.services[]|.status'` SERVICE_DETAILS=`sed -e 's/^"//' -e 's/"$//' <<<"$SERVICE_DETAILS"` if [ -z "$SERVICE_DETAILS" ] || [ $SERVICE_DETAILS != $SERVICE_NAME ] || [ $SERVICE_STATUS == '"INACTIVE"' ]; then if [ ! -z "$SERVICE_STATUS" ] && [ $SERVICE_STATUS == '"INACTIVE"' ]; then debug "Deleting service $SERVICE_DETAILS which status is : $SERVICE_STATUS" deleteService debug "Wating for service $SERVICE_DETAILS to update" sleep 60 fi createService elif [ $SERVICE_DETAILS == $SERVICE_NAME ] && [ $SERVICE_STATUS == '"ACTIVE"' ]; then debug "Service already exists: $SERVICE_DETAILS, Updating the service" updateService fi } function deleteService(){ DELETE_SERVICE=`$AWS_ECS delete-service --service "$SERVICE_NAME" --cluster "$CLUSTER"` [ $? != 0 ] && \ error "Creating service failed !" && exit 100 debug "Service deleted successfully $DELETE_SERVICE" } function createService(){ # Create the service debug "Creating service" CREATE_SERVICE=`$AWS_ECS create-service --service-name "$SERVICE_NAME" $DESIRED_COUNT \ --cluster "$CLUSTER" \ --task-definition $TASK_DEFINITION` [ $? != 0 ] && \ error "Creating service failed !" && exit 100 debug "Service created successfully" } function updateService(){ # Update the service debug "Updating Service" UPDATE_SERVICE=`$AWS_ECS --region $AWS_DEFAULT_REGION update-service --service "$SERVICE_NAME" $DESIRED_COUNT --cluster "$CLUSTER" --task-definition $TASK_DEFINITION $DEPLOYMENT_CONFIG` [ $? != 0 ] && \ error "Updating service failed !" && exit 100 debug "Service Updated successfully" } function prepareDockerComposeFIle() { echo $ENV_VAR | sed -n 1'p' | tr ',' '\n' | while read envVar; do debug $envVar printf '%s\n' /environment:/a ' - '$envVar . w q | ex -s $DOCKER_COMPOSE_FILE_PATH done } function verifyAndUpdateEnvFiles() { fileName=$1 if [ -f "$fileName" ]; then result=$(grep -i "env_file:" "$DOCKER_COMPOSE_FILE_PATH") if [ -z $result ]; then debug "label env_file is not found in docker-compose file, creating label env_file: " sed -i -e '$a\ env_file:\' $DOCKER_COMPOSE_FILE_PATH fi printf '%s\n' /env_file:/a ' - '$fileName . w q | ex -s $DOCKER_COMPOSE_FILE_PATH else debug "File $fileName not found." fi } function updateEnvValues(){ fileName=$1 START='${' END='}' if [ -f "$fileName" ]; then cat $fileName >> $ADM_FILE_LOCATION/tempVal.txt while IFS= read -r line || [[ -n "$line" ]]; do echo "$line" >> $ADM_FILE_LOCATION/tempVal.txt done < "$fileName" while IFS="=" read -r key value do keyResult=$(grep -i ${key} $DICT_FILE_PATH/$DICT_FILE) if [ ! -z $keyResult ]; then grep -v "${keyResult}" $DICT_FILE_PATH/$DICT_FILE > $DICT_FILE_PATH/$DICT_FILE_TEMP; mv $DICT_FILE_PATH/$DICT_FILE_TEMP $DICT_FILE_PATH/$DICT_FILE #echo "$(grep -v "${keyResult}" dict.txt)" >dict.txt echo "$START${key}$END=${value}">>$DICT_FILE_PATH/$DICT_FILE else echo "$START${key}$END=${value}">>$DICT_FILE_PATH/$DICT_FILE fi done < $ADM_FILE_LOCATION/tempVal.txt rm -f $ADM_FILE_LOCATION/tempVal.txt fi } function prepareEnvFile() { if [ $ADM_FILE_LOCATION != false ] && [ -d "$ADM_FILE_LOCATION" ]; then fileNames=( "$ADM_FILE_LOCATION/${ECS_COMPOSE_PROJECT_NAME}-${ECS_ENV_VAR_NAME}.env" "$ADM_FILE_LOCATION/default-${ECS_ENV_VAR_NAME}.env" "$ADM_FILE_LOCATION/default.env" "${ECS_ENV_VAR_NAME}.env" "default.env" ) fileNamesForEnV=( "default.env" "${ECS_ENV_VAR_NAME}.env" "$ADM_FILE_LOCATION/default.env" "$ADM_FILE_LOCATION/default-${ECS_ENV_VAR_NAME}.env" "$ADM_FILE_LOCATION/${ECS_COMPOSE_PROJECT_NAME}-${ECS_ENV_VAR_NAME}.env") for t in "${fileNames[@]}" do # debug "Getting environment file, and the file is: $t" verifyAndUpdateEnvFiles $t done for tmpFile in "${fileNamesForEnV[@]}" do #debug "Getting file,to update environment values: $tmpFile" updateEnvValues $tmpFile done sed -i -e 's/=/ /g' $DICT_FILE_PATH/$DICT_FILE else if [ -f "$DOCKER_COMPOSE_FILE_PATH" ]; then result=$(grep -i "[[:alnum:]].env" "$DOCKER_COMPOSE_FILE_PATH") if [ -z $result ] ; then debug "label env_file: with out any values will throw YAML parse error. Deleting label env_file: " sed -i '/env_file:/d' $DOCKER_COMPOSE_FILE_PATH fi fi debug "No admin directory for env files exists at $ADM_FILE_LOCATION" fi } function createEnvironmentSection() { debug "Checking for environment argument, if provided then the values will be added to docker compose file, environment variables take precedence over env files." if [ $ENV_VAR != false ]; then result=$(grep -i "environment:" "$DOCKER_COMPOSE_FILE_PATH") if [ -z $result ]; then debug "label environment: does not exists, creating label environment: " sed -i -e '$a\ environment:\' $DOCKER_COMPOSE_FILE_PATH fi prepareDockerComposeFIle fi } function substituteEnvVarsInDockerCompose() { debug "Substitute EnvVars In Docker Compose" sed '/./!d;s/\([^ ]*\) *\(.*\)/\\|\1|s||\2|g/' $DICT_FILE_PATH/$DICT_FILE | sed -i -f - $DOCKER_COMPOSE_FILE_PATH # awk -F "[ )]" 'NR == FNR {a[$1] = $2; next} {sub($(NF-1), a[$(NF-1)]); print}' dict.txt $DOCKER_COMPOSE_FILE_PATH } function removeCommentedLines() { #grep -v "^#" $DOCKER_COMPOSE_FILE_PATH > $DOCKER_COMPOSE_FILE_PATH echo "$(grep -v "^#" $DOCKER_COMPOSE_FILE_PATH)" >$DOCKER_COMPOSE_FILE_PATH } function deployToECS(){ debug "Checking for required tools (AWS CLI, ECS CLI and JQ)" require aws # Check for jq, Command-line JSON processor require jq # Check for ecs-cli, Command-line ecs-cli require ecs-cli debug "Checked for required tools. Done" debug "Configuring Environment variables" configureEnvironment debug "Checking for available environment files to be used." removeCommentedLines prepareEnvFile createEnvironmentSection substituteEnvVarsInDockerCompose debug "Registering task definition" registerTaskDefinition debug "Calling create or update service" createOrUpdateService exit 0 } deployToECS
This will take of existing services or it will create a new services. This script built in Shell. Also it will take care any environment values that need to be substituted at run time in docker-compose.yml.

It also have lot of features. Due to time i am not describing each and every function.

Hope this this will help those people who are working in CI ,CD jobs and you want your Jenkins should deploy to AWS after every success build then this tool can do that.

I used https://github.com/silinternational/ecs-deploy for some part. Here you find detailed documentation. But most of my script is different from the original.

Sample docker-compose file as follows.

version: '2'
services:
config-server:
image: registry.myworks.com/config-server:1.2.0-SNAPSHOT
container_name: "config-server"
mem_limit: 1048576000
cpu_shares: 100
environment:
- SERVER_PORT=8080
- SERVICE_8080_NAME=config-server
- SERVICE_8080_TAGS=api
labels:
api-id: "config-server"
api-version: "1.2.0-SNAPSHOT"
restart: always
logging:
driver: awslogs
options:
awslogs-region: us-east-1
awslogs-group: development-horizon-01
awslogs-stream-prefix: "config-server"
env_file:
- /var/jenkins_home/workspace/lh-spring-cloud-config-DevDeployer/ecs-admin-files/config-server-dev.env

Be the first to comment

You can use [html][/html], [css][/css], [php][/php] and more to embed the code. Urls are automatically hyperlinked. Line breaks and paragraphs are automatically generated.