Elixir Deploys with Make
This is one of the first times I've used `make`, but I regret not using it more often. It's pretty simple to get started and really powerful. This was a mix of a lot of different resources.
This is one of the first times I've used make
, but I regret not
using it more often. It's pretty simple to get started and really
powerful. This was a mix of a lot of different resources.
This is part of a larger set of posts, see Deploying Elixir Umbrella Apps for an overview.
The Makefile
Here's the Makefile. There's a lot going on here that I'll try to explain.
.PHONY: help
AWS_PROFILE = company
AWS_REGION = us-west-2
AWS_ACCOUNT_ID = ACCOUNT_01
# APP_NAME := $(shell grep 'app:' mix.exs | sed -e 's/\[//g' -e 's/ //g' -e 's/app://' -e 's/[:,]//g')
APPS := web admin
APP_VSN := $(shell grep 'version:' mix.exs | cut -d '"' -f2)
BUILD := $(shell git rev-parse --short HEAD)
IS_PROD := $(filter prod, $(MAKECMDGOALS))
STAGE := $(if $(IS_PROD),prod,staging)
help:
@echo "$(APP_NAME):$(APP_VSN)-$(BUILD) in $(ENV)"
@echo " Deploying to $(STAGE)"
@perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
prod: ## Set the deploy target to prod
@echo "Setting Prod"
admin.build: ## Make a local build
mix do deps.get, deps.compile, compile
cd apps/company_admin/assets && \
yarn install && \
yarn deploy && \
cd .. && \
mix phx.digest;
cd ../../..
mix release --profile admin:prod --verbose
web.build: ## Make a local build
mix do deps.get, deps.compile, compile
mix release --profile web:prod --verbose
%.docker: ecr_login ## Make a docker image
@echo "$(basename $@) compile docker image"
docker build --build-arg APP_NAME=$(basename $@) \
--build-arg APP_VSN=$(APP_VSN) \
-t $(basename $@):$(APP_VSN)-$(BUILD) \
-t $(basename $@):latest \
-t $(AWS_ACCOUNT_ID).dkr.ecr.$(AWS_REGION).amazonaws.com/company:$(basename $@)-$(APP_VSN)-$(BUILD) .
docker push $(AWS_ACCOUNT_ID).dkr.ecr.$(AWS_REGION).amazonaws.com/company:$(basename $@)-$(APP_VSN)-$(BUILD)
%.dockerrun: %.docker ## Make a dockerrun file
@echo "$(basename $@) dockerrun.aws.json"
cp Dockerrun.aws.template.json Dockerrun.aws.json
# Replace the <AWS_ACCOUNT_ID> with your ID
sed -i'' "s/<ACCOUNT_ID>/$(AWS_ACCOUNT_ID)/" Dockerrun.aws.json
# Replace the <NAME> with the your name
sed -i'' "s/<NAME>/company/" Dockerrun.aws.json
# Replace the <REGION> with the selected region
sed -i'' "s/<REGION>/$(AWS_REGION)/" Dockerrun.aws.json
# Replace the <TAG> with the your version number
sed -i'' "s/<VERSION>/$(basename $@)-$(APP_VSN)-$(BUILD)/" Dockerrun.aws.json
%.zip: %.dockerrun ## Zip the dockerrun file
@echo "$(basename $@) dockerrun.aws.json.zip"
zip -r $(basename $@).zip Dockerrun.aws.json
rm Dockerrun.aws.json
%.s3: %.zip ## Upload dockerrun zip to s3
@echo "$(basename $@) pushing to s3"
aws --profile=$(AWS_PROFILE) s3 cp $(basename $@).zip s3://$(basename $@)-$(STAGE)-deployments/$(basename $@)-$(STAGE)-$(BUILD).zip
rm $(basename $@).zip
%.beanstalk: %.s3 ## Update beanstalk w/ new version
@echo "$(basename $@) beanstalk"
aws --profile=$(AWS_PROFILE) elasticbeanstalk create-application-version --application-name $(basename $@)-$(STAGE) --version-label $(APP_VSN):$(BUILD) --source-bundle S3Bucket=$(basename $@)-$(STAGE)-deployments,S3Key=$(basename $@)-$(STAGE)-$(BUILD).zip
# Update the environment to use the new application version
aws --profile=$(AWS_PROFILE) elasticbeanstalk update-environment --environment-name $(basename $@)-$(STAGE) --version-label $(APP_VSN):$(BUILD)
$(APPS): %: %.beanstalk
@echo "deploying $@"
all: $(APPS) ## Build all apps
@echo "Done"
%.run: ## Run the app in Docker
docker run --env-file config/docker.env \
--expose 4000 -p 4000:4000 \
--rm -it $@:latest
ecr_login: ## Login to ECR
`aws ecr get-login --profile=$(AWS_PROFILE) --no-include-email`
Running a Deploy
TIP: run make all -n
to see what make is gonna do
`--> make admin -n
`aws ecr get-login --profile=company --no-include-email`
echo "admin compile docker image"
docker build --build-arg APP_NAME=admin \
--build-arg APP_VSN=0.0.1 \
-t admin:0.0.1-c0db6a0 \
-t admin:latest \
-t ACCOUNT_01.dkr.ecr.us-west-2.amazonaws.com/company:admin-0.0.1-c0db6a0 .
docker push ACCOUNT_01.dkr.ecr.us-west-2.amazonaws.com/company:admin-0.0.1-c0db6a0
echo "admin dockerrun.aws.json"
cp Dockerrun.aws.template.json Dockerrun.aws.json
# Replace the <AWS_ACCOUNT_ID> with your ID
sed -i'' "s/<ACCOUNT_ID>/ACCOUNT_01/" Dockerrun.aws.json
# Replace the <NAME> with the your name
sed -i'' "s/<NAME>/company/" Dockerrun.aws.json
# Replace the <REGION> with the selected region
sed -i'' "s/<REGION>/us-west-2/" Dockerrun.aws.json
# Replace the <TAG> with the your version number
sed -i'' "s/<VERSION>/admin-0.0.1-c0db6a0/" Dockerrun.aws.json
echo "admin dockerrun.aws.json.zip"
zip -r admin.zip Dockerrun.aws.json
rm Dockerrun.aws.json
echo "admin pushing to s3"
aws --profile=company s3 cp admin.zip s3://admin-staging-deployments/admin-staging-c0db6a0.zip
rm admin.zip
echo "admin beanstalk"
aws --profile=company elasticbeanstalk create-application-version --application-name admin-staging --version-label 0.0.1:c0db6a0 --source-bundle S3Bucket=admin-staging-deployments,S3Key=admin-staging-c0db6a0.zip
# Update the environment to use the new application version
aws --profile=company elasticbeanstalk update-environment --environment-name admin-staging --version-label 0.0.1:c0db6a0
echo "deploying admin"
rm admin.zip admin.s3 admin.docker admin.dockerrun
- Make and tag the docker image.
- Make the
Dockerun.aws.json
with the needed info about the version. - Zip that into a APP-ENV-BUILD.zip file.
- Upload that to s3.
- Create a Beanstalk application version.
- Update the Beanstalk application to use the new version, causing a deploy.
The Makefile a bit more complicated because I'm using dynamic targets based on the APPS var.
Below, the %.docker
is the dynamic target. I use the basename
function to
grab the app I'm currently working on and build the docker image for that app.
Any of the targets starting with %
are doing a similar thing.
%.docker: ecr_login ## Make a docker image
@echo "$(basename $@) compile docker image"
...
Resources
Webmentions
These are webmentions via the IndieWeb and webmention.io. Mention this post from your site: