Skip to content

Instantly share code, notes, and snippets.

@yefim
Last active April 7, 2023 16:11
Show Gist options
  • Save yefim/93fb5aa3291b3843353794127804976f to your computer and use it in GitHub Desktop.
Save yefim/93fb5aa3291b3843353794127804976f to your computer and use it in GitHub Desktop.
Build a Docker image, push it to AWS EC2 Container Registry, then deploy it to AWS Elastic Beanstalk
#!/bin/bash
# usage: ./deploy.sh staging f0478bd7c2f584b41a49405c91a439ce9d944657
# license: public domain
BRANCH=$1
SHA1=$2
AWS_ACCOUNT_ID=12345678900
NAME=name-of-service-to-deploy
EB_BUCKET=aws-s3-bucket-to-hold-application-versions
VERSION=$BRANCH-$SHA1
ZIP=$VERSION.zip
aws configure set default.region us-east-1
# Authenticate against our Docker registry
eval $(aws ecr get-login)
# Build and push the image
docker build -t $NAME:$VERSION .
docker tag $NAME:$VERSION $AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/$NAME:$VERSION
docker push $AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/$NAME:$VERSION
# Replace the <AWS_ACCOUNT_ID> with the real ID
sed -i='' "s/<AWS_ACCOUNT_ID>/$AWS_ACCOUNT_ID/" Dockerrun.aws.json
# Replace the <NAME> with the real name
sed -i='' "s/<NAME>/$NAME/" Dockerrun.aws.json
# Replace the <TAG> with the real version number
sed -i='' "s/<TAG>/$VERSION/" Dockerrun.aws.json
# Zip up the Dockerrun file (feel free to zip up an .ebextensions directory with it)
zip -r $ZIP Dockerrun.aws.json
aws s3 cp $ZIP s3://$EB_BUCKET/$ZIP
# Create a new application version with the zipped up Dockerrun file
aws elasticbeanstalk create-application-version --application-name $NAME-application \
--version-label $VERSION --source-bundle S3Bucket=$EB_BUCKET,S3Key=$ZIP
# Update the environment to use the new application version
aws elasticbeanstalk update-environment --environment-name $NAME \
--version-label $VERSION
{
"AWSEBDockerrunVersion": "1",
"Image": {
"Name": "<AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/<NAME>:<TAG>",
"Update": "true"
},
"Ports": [
{
"ContainerPort": "443"
}
]
}
@philipithomas
Copy link

philipithomas commented May 12, 2016

Looks like our Staffjoy deploy code. I suggest polling to see if the deploy succeeded. I copied and sanitized some code to do this from our deploys below that should match your variables (I haven't tested the modifications though). It looks for the deployed tag to match the anticipated one, it looks for EB to return to a "Ready" state (where another deploy can be initiated), and it times out if these conditions are not met (returning exit code 1).

deploystart=$(date +%s)
timeout=3000 # Seconds to wait before error. If it's taking awhile - your boxes probably are too small.
threshhold=$((deploystart + timeout))
while true; do
    # Check for timeout
    timenow=$(date +%s)
    if [[ "$timenow" > "$threshhold" ]]; then
        echo "Timeout - $timeout seconds elapsed"
        exit 1
    fi

    # See what's deployed
    current_version=`aws elasticbeanstalk describe-environments --application-name "$NAME-application" --environment-name "$NAME" --query "Environments[*].VersionLabel" --output text`

    status=`aws elasticbeanstalk describe-environments --application-name "$NAME-application" --environment-name "$NAME" --query "Environments[*].Status" --output text`

    if [ "$current_version" != "$VERSION" ]; then
        echo "Tag not updated (currently $version). Waiting."
        sleep 10
        continue
    fi
    if [ "$status" != "Ready" ]; then
        echo "System not Ready -it's $status. Waiting."
        sleep 10
        continue
    fi
    break
done

@theseanstewart
Copy link

I'm new to Docker and came across this gist today while researching deployment strategies with Docker and Elastic Beanstalk. How does your application get built into the image? Are you pulling it from the repo as you build it? This is one thing I'm not super clear on.

@yefim
Copy link
Author

yefim commented Oct 4, 2016

@theseanstewart great question! I build the docker image with docker build -t $NAME:$VERSION . then push the built (and tagged) image to the Amazon EC2 Container Registry with docker push $AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/$NAME:$VERSION.

The way the Elastic Beanstalk application environment knows how to deploy itself is the Dockerrun.aws.json file. In that file I specify the docker image to use and create a new application version with that points to the newly built, tagged, and pushed docker image URL. Does that make sense?

@revl-ca
Copy link

revl-ca commented Oct 18, 2016

Is it possible to accomplish this with the awsebcli?

@mskutin
Copy link

mskutin commented Dec 13, 2016

https://gist.github.com/yefim/93fb5aa3291b3843353794127804976f#file-deploy-sh-L28
missing trailing slash
from:
sed -i='' "s/<NAME>/$NAME" Dockerrun.aws.json
to:
sed -i='' "s/<NAME>/$NAME/" Dockerrun.aws.json

@ericbmerritt
Copy link

@yefim any chance you can explicitly specify a license for this? I am assuming its public domain, but its always nice to be explicit.

Copy link

ghost commented Dec 29, 2016

This is really helpful. Thank you!

@rootux
Copy link

rootux commented Feb 17, 2017

Would like to add a comment for new comers that you need to create a EC2 container first here that will be the $NAME param - https://console.aws.amazon.com/ecs

@sibelius
Copy link

instead of this

aws s3 cp $ZIP s3://$EB_BUCKET/$ZIP

# Create a new application version with the zipped up Dockerrun file
aws elasticbeanstalk create-application-version --application-name $NAME-application \
    --version-label $VERSION --source-bundle S3Bucket=$EB_BUCKET,S3Key=$ZIP

# Update the environment to use the new application version
aws elasticbeanstalk update-environment --environment-name $NAME \
      --version-label $VERSION

you can use awsebcli

eb deploy ${EB_ENV_NAME}

@stephen-swensen
Copy link

Super helpful, thanks!

I adapted it so that it can be downloaded and executed with curl / eval as a single self-contained script with project specific env vars pulled out: https://github.com/relayfoods/aws-docker-deploy

@Alexhha
Copy link

Alexhha commented Apr 11, 2017

Note: deploy.sh script won't work for the first time. aws elasticbeanstalk update-environment command will work only for existing environment

# Replace the <NAME> with the real name
sed -i='' "s/<NAME>/$NAME" Dockerrun.aws.json

should contain trailing slash at the end

sed -i "s/<NAME>/$NAME/" Dockerrun.aws.json

@wprater
Copy link

wprater commented Jun 16, 2017

should probably be user to add node_modules to a .dockerignore file as well

@simonguldstrand
Copy link

simonguldstrand commented Jun 30, 2017

@Alexhha, would you just add the aws ecs register-task-definition to be able to use this script without existing env?

@codinronan
Copy link

@stephen-swensen unbelievable lifesaver. I spent 2 hours reading AWS docs and with your scripts completed the same in < 10 minutes.

Seriously, code shouts, documentation whispers.

@javahometech
Copy link

How elastic beanstalk knows the credentials of ECS registry?
Thanks,
Hari

@skunkworker
Copy link

If you are using docker-compose instead of docker build

In deploy.sh
instead of

docker build -t $NAME:$VERSION .

put this

export IMAGE_NAME=$NAME:$VERSION
docker-compose -f docker-compose.yml build

and add the image: key to your docker-compose.yml file

version: '3.3'
services:
  service_name:
     image: $IMAGE_NAME

Reference:
https://stackoverflow.com/questions/33816456/how-to-tag-docker-image-with-docker-compose

@lmanzieri
Copy link

Hello All,

I'm doing different.
I'm using AWSEBCLI to deploy the application with the structure bellow:

  • .
  • .elasticbeanstalk/config.yml
  • Dockerrun.aws.json
  • (Dockerfile and all files of application)

So in config.yml is necessary put the keys bellow:

deploy:
  artifact: Dockerrun.aws.json

The archive will be like this:

branch-defaults:
  master:
    environment: myEnv
deploy:
  artifact: Dockerrun.aws.json
global:
  application_name: myApp
  default_ec2_keyname: myKey
  default_platform: arn:aws:elasticbeanstalk:sa-east-1::platform/Docker running on
    64bit Amazon Linux/2.8.4
  default_region: sa-east-1
  include_git_submodules: true
  instance_profile: null
  platform_name: null
  platform_version: null
  profile: null
  sc: git
  workspace_type: Application

then is just execute the command 'eb deploy' in main directory os your app.

@rich-allen-gov
Copy link

@javahometech when using elastic beanstalk ,and ecr, you set up an iam role. So then when the following was run:

eval $(aws ecr get-login)

aws ecr get-login prints out a docker login command with a temporary credential. That output then gets executed with the eval statement and that's how auth is handled.

@ivarprudnikov
Copy link

ivarprudnikov commented Nov 25, 2018

sed command contains minor "error":
sed -i='' "s/<AWS_ACCOUNT_ID>/$AWS_ACCOUNT_ID/" Dockerrun.aws.json will make a backup file called Dockerrun.aws.json= which is not what would be expected. Assume because -i was used the expectation is that file will be modified in place.

Documentation https://linux.die.net/man/1/sed
-i='' needs to be replaced with either --in-place='' or with -i '' or with a sensible backup extension -i.bak
First 2 options will not work on all systems and I found the last one -i.bak a bit more reliable when running sed on OSX and on Linux

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment