Last active
September 21, 2020 23:49
-
-
Save rfunduk/f35587db83d8d32b02c0404eb8ffae8c to your computer and use it in GitHub Desktop.
Manual Elixir red/green deployments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Unit] | |
Description=AppName Phoenix | |
After=network.target | |
[Install] | |
WantedBy=multi-user.target | |
[Service] | |
User=ubuntu | |
Group=www-data | |
Environment=PORT=ACTIVE_PORT | |
EnvironmentFile=/home/ubuntu/APP_NAME.ACTIVE.env | |
#ExecStartPre=/usr/bin/install -m 777 /dev/null /tmp/APP_NAME.ACTIVE.sock | |
ExecStart=/home/ubuntu/APP_NAME.deployment/APP_NAME.ACTIVE/bin/APP_NAME start | |
ExecStop=/home/ubuntu/APP_NAME.deployment/APP_NAME.ACTIVE/bin/APP_NAME stop | |
ExecReload=/home/ubuntu/APP_NAME.deployment/APP_NAME.ACTIVE/bin/APP_NAME restart | |
SyslogIdentifier=APP_NAME | |
Restart=always | |
RestartSec=5 | |
StartLimitBurst=3 | |
StartLimitInterval=10 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -e | |
BIN_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")") | |
cd $BIN_DIR/.. | |
export MIX_ENV=prod | |
source .env.$MIX_ENV | |
rm -rf /tmp/$APP_NAME-deploy | |
mkdir -p /tmp/$APP_NAME-deploy | |
git archive master | tar -x -C /tmp/$APP_NAME-deploy | |
git rev-parse --short HEAD > /tmp/$APP_NAME-deploy/REVISION | |
cd /tmp/$APP_NAME-deploy | |
MIX_QUIET=1 mix local.hex --force && mix local.rebar --force | |
MIX_QUIET=1 mix deps.get --only prod | |
mix compile | |
(cd ./assets && yarn install --frozen-lockfile) | |
(cd ./assets && yarn run deploy) | |
mix phx.digest | |
MIX_QUIET=1 mix release --overwrite |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -e | |
if [ ! -z "$(git status --porcelain)" ]; then | |
echo "Working directory not clean." | |
exit 1 | |
fi | |
BIN_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")") | |
cd $BIN_DIR/.. | |
perl -lpe 'BEGIN { sub inc { my ($num) = @_; ++$num } } s/(?<=version: \"0.0.)(\d+)/(inc($1))/eg' -i mix.exs | |
git add mix.exs | |
git commit --no-verify -m "Bump version to $(MIX_QUIET=1 mix version)" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -e | |
BIN_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")") | |
cd $BIN_DIR/.. | |
APP_NAME=myapp | |
APP_NAMESPACE=Myapp | |
TARGET_HOST=$1 | |
if [ ! -z "$(git status --porcelain)" ]; then | |
echo "Working directory not clean." | |
exit 1 | |
fi | |
./bin/bump.sh && git push | |
if [ -z $TARGET_HOST ]; then | |
echo "Specify target host as argument." | |
exit 1 | |
fi | |
APP_VERSION=$(MIX_QUIET=1 mix version) | |
PREFIX="~/$APP_NAME.deployment/$APP_NAME-$APP_VERSION" | |
echo -n "Determine current active release: " | |
OLD_ACTIVE=$(ssh $TARGET_HOST "cat $APP_NAME.active || echo -n 'b'") | |
echo $OLD_ACTIVE | |
case $OLD_ACTIVE in | |
"a") NEW_ACTIVE="b";; | |
"b") NEW_ACTIVE="a";; | |
esac | |
declare -A PORTS=( ["a"]="3001" ["b"]="3002" ) | |
TARGET="~/$APP_NAME.deployment/$APP_NAME.$NEW_ACTIVE" | |
echo "Deploying to new active: $NEW_ACTIVE" | |
echo | |
echo "*******************************************" | |
echo "************* BUILD RELEASE ***************" | |
echo "*******************************************" | |
APP_NAME=$APP_NAME ./bin/build.sh | |
echo -e "Done.\n\n" | |
echo "*******************************************" | |
echo "************* UPLOAD RELEASE **************" | |
echo "*******************************************" | |
ssh $TARGET_HOST "mkdir -p ~/$APP_NAME.deployment" | |
rsync -ra --quiet /tmp/$APP_NAME-deploy/_build/prod/rel/$APP_NAME \ | |
$TARGET_HOST:$APP_NAME.deployment/$APP_NAME-$APP_VERSION | |
# maybe like this instead? | |
# (cd /tmp/$APP_NAME-deploy/_build/prod/rel/ && tar czf - $APP_NAME) | | |
# ssh $TARGET_HOST 'cd $APP_NAME && tar xzf -' | |
echo -e "Done.\n\n" | |
echo "*******************************************" | |
echo "************* UPDATE CONFIG ***************" | |
echo "*******************************************" | |
for v in a b; do | |
cat config/deploy/nginx.conf | | |
sed "s/ACTIVE_PORT/${PORTS["$v"]}/g" - | | |
sed "s/ACTIVE/$v/g" - | | |
ssh $TARGET_HOST "cat - | sudo tee /etc/nginx/sites-available/$APP_NAME.$v.conf" > /dev/null | |
done | |
for v in a b; do | |
cat config/deploy/app.service | | |
sed "s/ACTIVE_PORT/${PORTS["$v"]}/g" - | | |
sed "s/ACTIVE/$v/g" - | | |
ssh $TARGET_HOST "cat - | sudo tee /etc/systemd/system/$APP_NAME.$v.service" > /dev/null | |
done | |
ssh $TARGET_HOST "sudo systemctl daemon-reload" | |
echo "Ensure old inactive stopped." | |
ssh $TARGET_HOST "sudo systemctl stop $APP_NAME.$NEW_ACTIVE || exit 0" | |
echo -e "Done.\n\n" | |
echo "*******************************************" | |
echo "************* UPGRADE RELEASE *************" | |
echo "*******************************************" | |
echo "Upload production env..." | |
scp .env.prod $TARGET_HOST:$APP_NAME.$NEW_ACTIVE.env | |
echo "Establish new active release..." | |
ssh $TARGET_HOST "rm -f $TARGET && ln -sf $PREFIX/$APP_NAME $TARGET" | |
echo "Symlink static..." | |
ssh $TARGET_HOST "ln -sf $TARGET/lib/$APP_NAME-$APP_VERSION/priv/static $TARGET/static" | |
echo "Migrate database..." | |
ssh $TARGET_HOST "set -o allexport; source ~/$APP_NAME.$NEW_ACTIVE.env; set +o allexport && PORT=0 $TARGET/bin/$APP_NAME eval '$APP_NAMESPACE.Release.migrate'" | |
echo "Swap active to $NEW_ACTIVE..." | |
# start new release | |
ssh $TARGET_HOST "sudo systemctl start $APP_NAME.$NEW_ACTIVE" | |
# wait until new release is booted and accepting connections | |
echo "Waiting for release to boot..." | |
ssh $TARGET_HOST "while true; do curl -s localhost:${PORTS["$NEW_ACTIVE"]} > /dev/null && { echo -e \"\nRelease is up.\"; break; } || { echo -n '.'; sleep 0.5; } done" | |
# swap nginx configs to new active | |
ssh $TARGET_HOST "sudo rm -f /etc/nginx/sites-enabled/$APP_NAME.conf && sudo ln -sf /etc/nginx/sites-available/$APP_NAME.$NEW_ACTIVE.conf /etc/nginx/sites-enabled/$APP_NAME.conf" | |
# reload nginx to start sending traffic to new active | |
ssh $TARGET_HOST "sudo systemctl reload nginx" | |
# update new ACTIVE | |
ssh $TARGET_HOST "echo $NEW_ACTIVE > ~/$APP_NAME.active" | |
echo "Ensure old active stopped..." | |
ssh $TARGET_HOST "sudo systemctl stop $APP_NAME.$OLD_ACTIVE || exit 0" | |
echo -e "Done.\n\n" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
upstream phoenix { | |
server localhost:ACTIVE_PORT; | |
} | |
map $http_upgrade $connection_upgrade { | |
default upgrade; | |
'' close; | |
} | |
server { | |
listen 80; | |
server_name your.domain.com; | |
return 301 https://your.domain.com$request_uri; | |
} | |
server { | |
listen 443 ssl; | |
server_name your.domain.com; | |
root /home/ubuntu/APP_NAME.deployment/APP_NAME.ACTIVE/static; | |
client_max_body_size 1M; | |
keepalive_timeout 70; | |
ssl on; | |
ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem; | |
ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem; | |
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; | |
ssl_ciphers HIGH:!aNULL:!MD5; | |
try_files $uri @phoenix; | |
location ~ ^/(js|css)/ { | |
gzip_static on; | |
expires max; | |
add_header Cache-Control public; | |
add_header ETag ""; | |
break; | |
} | |
location /live/websocket { | |
proxy_pass http://phoenix; | |
proxy_http_version 1.1; | |
proxy_set_header Host $http_host; | |
proxy_set_header X-Real-IP $remote_addr; | |
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
proxy_set_header X-Forwarded-Proto $scheme; | |
proxy_set_header Upgrade $http_upgrade; | |
proxy_set_header Connection $connection_upgrade; | |
} | |
location @phoenix { | |
proxy_intercept_errors on; | |
error_page 502 /502.html; | |
error_page 500 /500.html; | |
error_page 404 /404.html; | |
proxy_read_timeout 240; | |
proxy_send_timeout 240; | |
proxy_set_header Host $http_host; | |
proxy_set_header X-Real-IP $remote_addr; | |
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
proxy_set_header X-Forwarded-Proto $scheme; | |
proxy_redirect off; | |
proxy_pass http://phoenix; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment