Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dokku ps #298

Closed
wants to merge 60 commits into from
Closed
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
4d466d4
Disabled php test app because it failed
slaskis Nov 5, 2013
9917040
Set CACHE_DIR to use DOKKU_ROOT instead of HOME
slaskis Nov 5, 2013
ca692a8
Started on ps plugin
slaskis Nov 5, 2013
d6403d9
Added ps install script
slaskis Nov 5, 2013
c9ad455
ps:scale now simply writes to /PS
slaskis Nov 6, 2013
c2fb899
Also ps:stop and ps:restart echoes the proper commands now
slaskis Nov 6, 2013
16ff437
Added internal ps:_deploy and ps:_create
slaskis Nov 6, 2013
963d3ed
Escape the upstart instance variables
slaskis Nov 6, 2013
9572f29
ps_create now creates all the apps processes
slaskis Nov 6, 2013
a14d521
No more echo. Running the actual start/restart/stop
slaskis Nov 6, 2013
24e5726
Don't fail if no process was there to be stopped on deploy
slaskis Nov 6, 2013
0f34115
Must kill it too before removing it
slaskis Nov 6, 2013
9e51a6a
Added a README to keep me focused
slaskis Nov 7, 2013
9ac943f
Removed post-deploy/release hooks and instead integrating it into dok…
slaskis Nov 7, 2013
bd76607
Added sudo access for start/restart/stop to dokku user on install
slaskis Nov 7, 2013
82f5944
Fixed the upstart script to kill the docker container properly
slaskis Nov 7, 2013
1b0999b
Added some TODOs
slaskis Nov 7, 2013
b53e109
Default to web=1
slaskis Nov 7, 2013
7eeb458
Merge ps_release and ps_deploy and expose ps:deploy (but not in help)
slaskis Nov 7, 2013
072ba89
Tweaked the help output
slaskis Nov 7, 2013
3c2b0a2
Implemented proper ps_status()
slaskis Nov 7, 2013
8e4797a
Minor cleanup
slaskis Nov 7, 2013
d45d8dc
Deploy after a scale
slaskis Nov 7, 2013
3ac2f67
Removed unecessary sleep
slaskis Nov 7, 2013
01648dc
Added a nginx-pre-reload hook which updates the upstream backends
slaskis Nov 8, 2013
bf7d6ad
Added post-delete script which stops the upstart instance
slaskis Nov 8, 2013
1235bcd
Reverted back to default dokku deploy
slaskis Nov 8, 2013
3fa3503
Moved ps_deploy into pre-deploy hook
slaskis Nov 8, 2013
ebc3708
Properly update the nginx upstreams from dokku ps
slaskis Nov 8, 2013
a1aa2c1
Removed uneccessary sleep and silence upstart script
slaskis Nov 8, 2013
7f62fce
Removed some logs
slaskis Nov 8, 2013
21f7cd1
Added post-deploy script which kills the original deploy
slaskis Nov 8, 2013
b79e5ad
Silence some logs
slaskis Nov 8, 2013
d7e2e09
Use dokku deploy after ps:scale
slaskis Nov 8, 2013
bf3282e
Added back the port to post-deploy
slaskis Nov 8, 2013
6490e56
Strange bash issue.
Nov 11, 2013
37e8634
Also set the default web=1 if /PS exists but is empty
Nov 11, 2013
c714454
Also check for empty PS in pre-deploy
slaskis Nov 11, 2013
bf49ec0
Don't output the final PS after scale. Just re-deploy
slaskis Nov 11, 2013
d8e88fc
Added a short sleep after nginx reload to make sure it has reloaded w…
slaskis Nov 11, 2013
f22ef2a
I found double check_deploy confusing. Went middleground
slaskis Nov 11, 2013
b140216
Added a ruby/rack test which also tests 'dokku config' and 'dokku ps'
slaskis Nov 11, 2013
f01ba4e
Don't exit if grep fails
slaskis Nov 11, 2013
e24b447
Don't remove newlines from Procfile
slaskis Nov 11, 2013
97d6fe6
Only start processes when more than 0
slaskis Nov 11, 2013
d7483cb
Always print the status even of 0 processes
slaskis Nov 11, 2013
8c504dc
Don't fail during nginx-pre-reload on grep either
slaskis Nov 11, 2013
73446e8
Also don't exit on grep fail in post-delete
slaskis Nov 11, 2013
8f60caf
Only add upstream servers with a port
slaskis Nov 11, 2013
8db8674
Changed from normal pipe to weird one. Seems to work better
slaskis Nov 11, 2013
db25b85
Don't echo 'Scaled processes' it breaks the tests (will have to nail …
slaskis Nov 11, 2013
295ff91
Use a tmp file while writing PS. It much better
slaskis Nov 11, 2013
54e3515
Added server id header to test nginx round robin load balancing
slaskis Nov 11, 2013
5810c78
Added ruby version to ruby test app
slaskis Nov 11, 2013
e9c0cef
Some refactoring of the ruby ps/config test
slaskis Nov 11, 2013
e68dd7b
Try adding a newline after EOF to avoid warnings on travis
slaskis Nov 11, 2013
12d0aee
Added loads of 'ok'-logs
slaskis Nov 11, 2013
49e1d8c
Added set -x
slaskis Nov 11, 2013
24d2874
Try adding set -x to ps/commands
slaskis Nov 11, 2013
5a61c5d
Shellchecked
slaskis Nov 11, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 11 additions & 12 deletions dokku
Expand Up @@ -20,7 +20,7 @@ case "$1" in
;;

build)
APP="$2"; IMAGE="$3"; CACHE_DIR="$HOME/$APP/cache"
APP="$2"; IMAGE="$3"; CACHE_DIR="$DOKKU_ROOT/$APP/cache"
id=$(cat | docker run -i -a stdin progrium/buildstep /bin/bash -c "mkdir -p /app && tar -xC /app")
test $(docker wait $id) -eq 0
docker commit $id $IMAGE > /dev/null
Expand All @@ -47,18 +47,17 @@ case "$1" in
APP="$2"; IMAGE="$3"
pluginhook pre-deploy $APP $IMAGE

# kill the app when running
if [[ -f "$DOKKU_ROOT/$APP/PORT" ]]; then
oldid=$(< "$DOKKU_ROOT/$APP/CONTAINER")
docker kill $oldid > /dev/null
fi
dokku ps:deploy $APP
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should figure out a way to make it so dokku doesn't depend on a plugin.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's a bit of a specific hack but would it suffice to wrap the current code in an if?

if [[ ! -f "$APP_PATH/PS" ]]; then 
  # current kill / start code here
fi

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't think I'd +1 this without a real solution to this.

On Thu, Nov 7, 2013 at 12:37 PM, Robert Sköld notifications@github.comwrote:

In dokku:

@@ -47,18 +47,17 @@ case "$1" in
APP="$2"; IMAGE="$3"
pluginhook pre-deploy $APP $IMAGE

  • kill the app when running

  • if [[ -f "$DOKKU_ROOT/$APP/PORT" ]]; then
  •  oldid=$(< "$DOKKU_ROOT/$APP/CONTAINER")
    
  •  docker kill $oldid > /dev/null
    
  • fi
  • dokku ps:deploy $APP

I know it's a bit of a specific hack but would it suffice to wrap the
current code in an if?

if [[ ! -f "$APP_PATH/PS" ]]; then

current kill / start code here

fi


Reply to this email directly or view it on GitHubhttps://github.com//pull/298/files#r7509242
.

Jeff Lindsay
http://progrium.com

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of this other PR: #296, maybe it could provide a bit more flexible solution?

But, if I understood the current implementation of pluginhook correctly, it would still do multiple deploys which seems a bit wasteful but maybe a flag to pluginhook which makes it only execute the last hook? Or is there a case when multiple deploy scripts would be desirable?


# start the app
id=$(docker run -d -p 5000 -e PORT=5000 $IMAGE /bin/bash -c "/start web")
echo $id > "$DOKKU_ROOT/$APP/CONTAINER"
port=$(docker port $id 5000 | sed 's/0.0.0.0://')
echo $port > "$DOKKU_ROOT/$APP/PORT"
echo "http://$(< "$DOKKU_ROOT/HOSTNAME"):$port" > "$DOKKU_ROOT/$APP/URL"
# only use web.1 for now...
port=$(docker port $APP.web.1 5000 | sed 's/0.0.0.0://') || true
if [[ -n "$port" ]]; then
echo $port > "$DOKKU_ROOT/$APP/PORT"
echo "http://$(< "$DOKKU_ROOT/HOSTNAME"):$port" > "$DOKKU_ROOT/$APP/URL"
else
echo " ! Failed to get port from $APP.web.1"
exit 1
fi

pluginhook post-deploy $APP $port
;;
Expand Down
37 changes: 37 additions & 0 deletions plugins/ps/README.md
@@ -0,0 +1,37 @@
dokku-ps
========

A process management plugin for [dokku](https://github.com/progrium/dokku).

Modeled after the process/dyno management of heroku.

Written to solve these problems:

1. Restarting crashed containers.

Because sometimes the process exits because of something like a memory leak and, until we have it fixed, we just want it to keep going.

And the idea is to use upstart to handle the containers, as described in [dockers host integration docs](http://docs.docker.io/en/latest/use/host_integration/).

2. Being able to start up non `web` processes.

Bonus problems to solve:

1. Monitor & notify.

I'm guessing since upstart is monitoring the respawning of containers it should be able to notify me by email, irc or something even cooler.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Would be a great separate plugin


2. Process scaling.

If we are able to add each process as a backend to the nginx config we should easily be able to load balance the requests over multiple processes. [This PR](https://github.com/progrium/dokku/pull/267) seems like it would help a lot as we would pretty much just generate `$APP_ROOT/nginx.conf` with the processes defined in `$APP_ROOT/PS`. Something like:

CONF="$APP_ROOT/nginx.conf"
echo "upstream $APP {" > $CONF
grep "^web=" $APP_ROOT/PS | while read line; do
NUM=${line#*=}
for ((i=1; i<=$NUM; i++)); do
PORT=$(docker port $APP.web.$i 5000 | sed 's/0.0.0.0://')
echo "server 127.0.0.1:$PORT;" >> $CONF
done
done
echo "}" >> $CONF
228 changes: 228 additions & 0 deletions plugins/ps/commands
@@ -0,0 +1,228 @@
#!/usr/bin/env bash
set -eo pipefail

#
# dokku ps
#
# ex.
#
# $ dokku ps
#
# === web: `bundle exec thin start -p $PORT`
# web.1: up for 5m
#
# === worker: `bundle exec rake jobs`
# worker.1: running for 1h
#
#
# dokku ps:scale PROC1=AMOUNT1 [PROC2=AMOUNT2 ...]
# dokku ps:restart PROC
# dokku ps:stop PROCS
#

function ps_list(){
APP="$1";
APP_ROOT="$DOKKU_ROOT/$APP"
APP_PS="$APP_ROOT/PS"
echo
ps_proc_list $APP | while read i; do
NAME=$(cut -d":" -f1 <<< "$i")
CMD=$(cut -d":" -f2 <<< "$i")
echo "=== $NAME: \`${CMD/ /}\`"
grep "^$NAME=" $APP_PS | while read line; do
NUM=${line#*=}
for ((i=1; i<=$NUM; i++)); do
ps_status $APP $NAME $i
done
done
done
echo
}

function ps_scale(){
APP="$1"; shift; PAIRS="$@";
APP_ROOT="$DOKKU_ROOT/$APP"
APP_PS="$APP_ROOT/PS"

# convert the arguments into an array
pairs=( $PAIRS )

# store the current list of processes
PS=$(cat $APP_PS)

# reset PS
# TODO use temp file instead and then `mv` should be safer
echo -n > $APP_PS

# get the list of available procs (according to Procfile)
ps_proc_list $APP | while read i; do
NAME=$(cut -d":" -f1 <<< "$i")

# a variable to check if it was found
found=""

# see if $NAME is found among the $PAIRS
for line in ${PAIRS[@]}; do
name=${line%%=*}
to=${line#*=}

# found it
if [ "$name" = "$NAME" ]; then
# verify that $to is a number
if [ $to -ge 0 2>/dev/null ]; then
found=1
echo "$name=$to" >> $APP_PS
else
echo " Skipping \"$name\". \"$to\" is not an integer."
fi
fi
done

# in case it wasn't an argument use the current one
if [ -z "$found" ]; then
grep "^$NAME=" <<< $PS >> $APP_PS
fi
done

# output the final PS
cat $APP_PS

# now deploy!
ps_deploy $APP
}

function ps_deploy(){
APP="$1"; IMAGE="app/$APP"
APP_ROOT="$DOKKU_ROOT/$APP"
APP_PS="$APP_ROOT/PS"

cat $APP_PS | while read line; do
NAME=${line%%=*}
NUM=${line#*=}

# stop any running processes first otherwise
# `docker kill` will just make it restart
ps_each stop $APP $NAME $NUM || true

for ((i=1; i<=$NUM; i++)); do
# remove container if already exists
# outputs the id if existed, nothing otherwise
docker kill "$APP.$NAME.$i" > /dev/null
docker rm "$APP.$NAME.$i" > /dev/null

# create a named container using `docker run`
id=$(docker run -d -name "$APP.$NAME.$i" -p 5000 -e PORT=5000 $IMAGE /start $NAME)

# TODO make sure it started?

# stop the container again (to be started using upstart instead)
docker stop $id > /dev/null # outputs the id if existed, nothing otherwise
done
echo "-----> Starting $NAME"
ps_each start $APP $NAME $NUM
echo " $NUM processes up and running"
sleep 5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to add a mandatory 5 seconds to all deploys?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woops, that was left there from when I thought docker needed some time to give me a port.

done
}

function ps_stop(){
APP="$1"; NAME="$2";
APP_ROOT="$DOKKU_ROOT/$APP"
APP_PS="$APP_ROOT/PS"

# require name
if [ -z "$NAME" ]; then
echo "Missing PROC for \"$APP\""
exit 1
fi

grep "^$NAME=" $APP_PS | while read line; do
ps_each stop $APP ${line%%=*} ${line#*=}
done
}

function ps_status(){
APP="$1"; NAME="$2"; SEQ="$3";
# formatting is based on
# https://github.com/dotcloud/docker/blob/b038b0cd44e152fa4158f2bfe145de6d774dd8d0/state.go#L20
STATUS=$(docker ps -a | grep "$APP.$NAME.$SEQ" | grep -Eo '(Up [0-9]+ [a-z]+|Ghost|Exit [0-9]+)')
echo "$NAME.$SEQ: $STATUS"
}

function ps_restart(){
APP="$1"; NAME="$2";
APP_ROOT="$DOKKU_ROOT/$APP"
APP_PS="$APP_ROOT/PS"

# default to all procs
if [ -z "$NAME" ]; then
NAME=$(ps_proc_list $APP | cut -d":" -f1) || {
echo "App \"$APP\" has no processes to restart" >&2
exit 1
}
fi

grep "^$NAME=" $APP_PS | while read line; do
ps_each restart $APP ${line%%=*} ${line#*=}
done
}

function ps_each(){
CMD="$1"; APP="$2"; NAME="$3"; NUM="$4"
for ((i=1; i<=$NUM; i++)); do
sudo $CMD dokku-ps APP=$APP NAME=$NAME SEQ=$i || {
# echo " -- failed with exit $? --"
true # don't skip the rest if one was already stopped/started
}
sleep 1
done
}

function ps_proc_list(){
APP="$1"; IMAGE="app/$APP"
# TODO this should probably be something defined in /app/.release
# so it doesn't have to fire up a container every time?
docker run -rm $IMAGE cat /app/Procfile
}

function ps_usage(){
cat<<EOF
ps <app> List processes for an app
ps:restart <app> [PROC] Restart an app process
ps:scale <app> PROC1=NUM1 [PROC2=NUM2...] Scale processes by the given amount
ps:stop <app> PROC Stop an app process

EOF
}

# Check if name is specified
if [[ $1 == ps ]] || [[ $1 == ps:* ]]; then
if [[ -z $2 ]]; then
echo "You must specify an app name"
exit 1
else
APP="$2"
PS_FILE="$DOKKU_ROOT/$APP/PS"

# Check if app exists with the same name
if [ ! -d "$DOKKU_ROOT/$APP" ]; then
echo "App $APP does not exist"
exit 1
fi

# Check if app has a PS file
if [ ! -f "$PS_FILE" ]; then
echo "web=1" > $PS_FILE
fi
fi
fi

case "$1" in
ps) ps_list $APP; exit ;;
ps:scale) shift 2; ps_scale $APP "$@"; exit ;;
ps:restart) shift 2; ps_restart $APP $1; exit ;;
ps:stop) shift 2; ps_stop $APP $1; exit ;;
ps:deploy) ps_deploy $APP; exit ;; # internal
help) ps_usage ;;
esac
cat
23 changes: 23 additions & 0 deletions plugins/ps/install
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -eo pipefail

sed -i 's/docker -d -r=true$/docker -d -r=false/' /etc/init/docker.conf

cat<<EOF > /etc/sudoers.d/dokku-ps
%dokku ALL=(ALL)NOPASSWD:/sbin/start dokku-ps *
%dokku ALL=(ALL)NOPASSWD:/sbin/restart dokku-ps *
%dokku ALL=(ALL)NOPASSWD:/sbin/stop dokku-ps *
EOF

cat<<EOF > /etc/init/dokku-ps.conf
instance \$APP.\$NAME.\$SEQ
description "Dokku PS \$APP.\$NAME.\$SEQ"
author "Dokku PS"
start on filesystem and started lxc-net and started docker
stop on runlevel [!2345]
respawn
respawn limit 10 5
kill signal KILL
exec /usr/bin/docker start -a "\$APP.\$NAME.\$SEQ"
post-stop exec /usr/bin/docker kill "\$APP.\$NAME.\$SEQ"
EOF
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion tests/test_deploy
Expand Up @@ -13,7 +13,7 @@ git commit -m 'initial commit'
REPO="test-$(basename $APP)-$RANDOM"
git remote add target dokku@$TARGET:$REPO
git push target master
URL=$(ssh dokku@$TARGET url $REPO)$FORWARDED_PORT
URL=$(echo | ssh dokku@$TARGET url $REPO)$FORWARDED_PORT
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

echo?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the ssh connections never ends otherwise so it just stalls. And ssh -t didn't work for me either so echo | it is :)

sleep 2
./check_deploy $URL && echo "-----> Deploy success!" || {
sleep 4
Expand Down