This isn't the prettiest solution, but it's what worked for us. If you have ideas on how to clean this up, I'm all for suggestions.
The setup
- Developing locally on macOS
- Production using Elastic Beanstalk container
- EB container uses Docker
The concept
We had issues getting Crystal's cross compile to work properly, so we had to use a couple extra steps.
- Boot Docker locally to compile release build of app
- Move release binary, and required files to temp directory
- Zip up temp directory to
app.zip
as per EB deployment instructions - Use
eb
command locally to pushapp.zip
to EB container
If we happen to get cross-compilation working, we could remove the first two steps in this process.
The code
There's a few files we need to create. I'll define those first, then show the code.
-
./script/deploy
. Be sure tochmod +x ./script/deploy
./docker/BuildDockerfile
./docker/docker_run_build.sh
./docker/Dockerfile
./docker/Dockerrun.aws.json
Deploy script
Used to actually push the code to production.
#!/bin/bash
set -e
# don't execute next commands on error
trap 'exit' ERR
# let echo interpret escape chars (\n)
shopt -s xpg_echo
#
START=`date +%s`
DATE=`date '+%Y%m%d@%H%M%S'`
BUILD_DIR=build-temp-${DATE}
SHA1=`git rev-parse HEAD`
RANDOM=`awk -v min=5 -v max=1000000 'BEGIN{srand(); print int(min+rand()*(max-min+1))}'`
# deploy to production when on master branch
# deploy to staging when on other branches
branch=$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')
if [ "$branch" == "master" ]
then
STAGE=production
else
STAGE=staging
fi
LABEL=$STAGE-$SHA1-$RANDOM-$DATE
# Remove the old build
rm -rf build/app
# Build assets
echo "Building Assets"
rm -rf public/assets/* public/mix-manifest.json
yarn prod
# Build the application
echo "Starting Docker"
docker build -t lucky-app-build -f docker/BuildDockerfile .
docker run -v $(pwd)/build:/app/build lucky-app-build
# Create a temporary directory to stage the files
mkdir ${BUILD_DIR}
# Stage the files
cp .env.${STAGE} ${BUILD_DIR}/.env
cp build/app ${BUILD_DIR}
cp -r public ${BUILD_DIR}
cp docker/Dockerfile ${BUILD_DIR}
cp docker/Dockerrun.aws.json ${BUILD_DIR}
cp -r .ebextensions ${BUILD_DIR}
# Getting them zipped up
cd ${BUILD_DIR}; zip -Xr app.zip * .env .ebextensions; mv app.zip ../; cd ..
# Deploy the application
eb deploy --label $LABEL
# Cleanup
rm -rf ${BUILD_DIR} app.zip
END=`date +%s`
echo Deploy ended with success! Time elapsed: $((END-START)) seconds
BuildDockerfile
Used for building the release binary
FROM crystallang/crystal:0.29.0
ADD . /app
ADD ./docker/docker_run_build.sh /app/docker_run_build.sh
WORKDIR /app
RUN shards update && \
rm -rf /app/build/app && \
crystal build src/start_server.cr --release -o app
RUN chmod +x docker_run_build.sh
CMD ["./docker_run_build.sh"]
docker_run_build
I honestly don't know why we have this, but it's here, so here it is.
#!/bin/sh
cp app /app/build/
Dockerfile
The actual Dockerfile used in production on EB
FROM phusion/baseimage
ENV APP_DOMAIN=localhost
ENV SECRET_KEY_BASE=abc123abc123
ENV LUCKY_ENV=production
ENV PORT=8000
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get -q update && \
apt-get -qy install build-essential libgc-dev libssl-dev libxml2-dev libyaml-dev libevent-dev && \
apt-get -y install tzdata && \
apt-get -y autoremove && \
apt-get -y clean && \
rm -rf /var/lib/apt/lists/* && \
rm -rf /tmp/*
RUN mkdir /app
ADD ./app /app
ADD ./.env /app/.env
ADD ./public /app/public
WORKDIR /app
EXPOSE 8000
CMD trap exit TERM; ./app & wait
Dockerrun
This file is used by EB. Learn more on Single Container Docker for AWS EB.
{
"AWSEBDockerrunVersion": "1",
"Logging": "/var/log/app.log",
"Ports": [
{
"ContainerPort": "8000"
}
]
}
Final Notes
You'll want to make sure you update your .gitignore
file with things like:
/build*
*.zip
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
And lastly, this all assumes you have your elastic beanstalk setup. That would require getting the eb
binary installed locally, getting your container setup, and adding in all your config stuff.
Top comments (0)