Skip to content

Instantly share code, notes, and snippets.

@lorn
Last active December 30, 2015 23:59
Show Gist options
  • Save lorn/7904456 to your computer and use it in GitHub Desktop.
Save lorn/7904456 to your computer and use it in GitHub Desktop.

Uma arquitetura de alta disponibilidade usando Starman

Continuando a introdução dada pelo nosso amigo que vence guerras, falarei um pouco mais sobre o Starman e, como ele, não falarei sobre PSGI/Plack porque esses assuntos já foram muito bem explorados em outros e equinocios.

Essa arquitetura é baseada na arquitetura que o Github usa/usava que eles explicam nesse post eles falam sobre como eles utilizam o Unicorn, o Starman foi baseado nesse cara o Miyagawa inclusive usou o nome Unicorn como "inspiração" para o nome Starman:

"The name Starman is taken from the song (Star na Otoko) by the Japanese rock band Unicorn (yes, Unicorn!). It's also known as a song by David Bowie, a power-up from Super Mario Brothers and a character from Earthbound, all of which I love."

Mais um pouco sobre a historia do nome do Starman no blog dele:

Good name for Perl UNIX forking web server?

O fonte desse artigo, assim como todos os arquivos de configuração e inicialização estão nesse gist não fique copiando e colando daqui, você pode pegar o arquivo separado lá ;)

Sendo amigo do seu syadmin

Eu fiz uma busca no APT agora, e não vi nenhum pacote do Starman.

Mas isso não significa que você vai jogar essa bomba na mão do seu sysadmin, seja gentil com ele como diria o Bruce Lee:

"Be L<Devops my friend|http://devops.com/>"

Faça scripts de inicialização, não é algo tão dificil e Shell Script é uma linguagem fácil e que faz de você um desenvolvedor melhor eu precisei subir o Starman em um CentOS e no Debian, abaixo estão os arquivos de inicialização para cada uma dessas distribuições a versão do Debian teve a revisão e ajustes do Nuba - eu tinha esquecido algum header/conf ;)

Debin:

#!/bin/bash
 
### BEGIN INIT INFO
# starman - this script starts and stops the starman daemon
# Provides:          starman_sppm
# Required-Start:    $local_fs $remote_fs $network $syslog
# Required-Stop:     $local_fs $remote_fs $network $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the starman server
# Description:       starts starman using start-stop-daemon
### END INIT INFO
 
NAME=starman
DESC=starman
 
test -x $DAEMON || exit 0
 
set -e
 
. /lib/lsb/init-functions
## Variáveis de conf da sua App, ALTERE ISSO ANTES DE QUALQUER COISA.
export starman="/usr/bin/starman"
export myapp_path="/var/www/myapp"
export pidfile="/var/run/starman.pid"
 
 
start() {
        echo -n $"Starting Starman: "
        ## eu inicio o starman para debug na porta 8080, é bom para testar algumas coisas dando bypass no nginx e tanto o log, quando o PID são criados com o usuário do nginx, para ficar mais fácil 
        $starman -I${myapp_path}/lib --user=nginx --listen :8080 --listen /var/run/nginx/starman.sock --daemonize --pid ${pidfile} --error-log /var/log/nginx/starman.log ${myapp_path}/${myapp}.psgi
        RETVAL=$?
        echo
        [ $RETVAL = 0 ]
        return $RETVAL
}
 
restart() {
        echo -n $"Restarting Starman: "
        export PID=`cat ${pidfile}`
        kill -s USR2 $PID 
        RETVAL=$?
        echo
        [ $RETVAL = 0 ]
        return $RETVAL
}
 
stop() {
        echo -n $"Stopping Starman: "
        killproc -p ${pidfile} -d 10 $starman
        RETVAL=$?
        echo
        [ $RETVAL = 0 ] && rm -f ${pidfile}
}
# See how we were called.
case "$1" in
  start)
        start
        ;;
  restart)
        restart
         ;;
  stop)
        stop
        ;;
  *)
        echo $"Usage: starman {start|restart|stop}"
        exit 1
esac
 
exit $RETVAL

CentOS

#!/bin/bash
 
# starman - this script starts and stops the starman daemon
#
# chkconfig:   - 85 15
# description: Starman 
# processname: starman
# pidfile :    /var/run/starman.pid
# www file:    /var/www/myapp
 
 
# Source function library.
. /etc/rc.d/init.d/functions
 
## Variáveis de conf da sua App, ALTERE ISSO ANTES DE QUALQUER COISA.
export starman="/usr/bin/starman"
export myapp_path="/var/www/myapp"
export pidfile="/var/run/starman.pid"
 
 
start() {
        echo -n $"Starting Starman: "
        ## eu inicio o starman para debug na porta 8080, é bom para testar algumas coisas dando bypass no nginx e tanto o log, quando o PID são criados com o usuário do nginx, para ficar mais fácil 
        $starman -I${myapp_path}/lib --user=nginx --listen :8080 --listen /var/run/nginx/starman.sock --daemonize --pid ${pidfile} --error-log /var/log/nginx/starman.log ${myapp_path}/${myapp}.psgi
        RETVAL=$?
        echo
        [ $RETVAL = 0 ]
        return $RETVAL
}
 
restart() {
        echo -n $"Restarting Starman: "
        export PID=`cat ${pidfile}`
        kill -s USR2 $PID 
        RETVAL=$?
        echo
        [ $RETVAL = 0 ]
        return $RETVAL
}
 
stop() {
        echo -n $"Stopping Starman: "
        killproc -p ${pidfile} -d 10 $starman
        RETVAL=$?
        echo
        [ $RETVAL = 0 ] && rm -f ${pidfile}
}
# See how we were called.
case "$1" in
  start)
        start
        ;;
  restart)
        restart
         ;;
  stop)
        stop
        ;;
  *)
        echo $"Usage: starman {start|restart|stop}"
        exit 1
esac
 
exit $RETVAL

Nginx + Starman

Como vocês leram, ou não, no post do github a arquitetura deles consistem em usar o servidor do Unicorn atrás do Nginx e a comunidação entre eles é feita via Unix Socket \,,/

O Nginx fala diretamente com os "workers" do Starman atráves do Starman Master, quando o Starman inicia ele sobe 'n' workers que são controlados pelo processo principal, toda requisição é processado pelos workers.

Com isso você ganha algumas funcionalidades interessantes como hotdeploy, que é o ato de você subir uma nova versão da sua aplicação com ZERO DOWNTIME!!!!111

Como funciona, basta você atualizar os arquivos da sua aplicação - não irá alterar nada até você reiniciar o serviço - e na hora de reiniciar o serviço é só mandar o sinal USR2 para o PID Master do Starman, isso irá dizer para ele que a aplicação precisa ser reiniciada ele irá reiniciar os seus workers paulatinamente, assim que eles ficarem ociosos até que todos estejam rodando a nova versão da aplicação.

Ah, a função restart dos arquivos de inicialização que eu falei lá em cima já faz esse trabalho :)

Por padrão o Starman inicia apenas 5 workers, eles podem aguentar bastante coisa, mas dependendo da sua configuração de hardware você pode ter mais mas cuidado com swap, se a memoria está indo para swap é bom você diminuir esse número.

upstream myapp_starman {
  server unix:/tmp/starman.sock fail_timeout=0;
}
   
server {
  listen 80;
   
client_max_body_size 1024m;
client_body_buffer_size 8k;
proxy_read_timeout 300;
   
##
# basic
##
server_name www.localhost.com;
root /home/user/MyApp/root;
keepalive_timeout 0;
   
##
# logging
##
access_log /var/log/nginx/myapp.access combined;
error_log /var/log/nginx/myapp.error;

location /static {
      root  /home/user/MyApp/root/;
      autoindex on;
}
   
##
# proxy
##
location / {
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $http_host;
  proxy_redirect off;
  proxy_buffering off;
  if (!-f $request_filename) {
    proxy_pass http://myapp_starman;
  }
}
   }

AUTHOR

Lindolfo "Lorn" Rodrigues - lorn at cpan.org

#!/bin/bash
### BEGIN INIT INFO
# starman - this script starts and stops the starman daemon
# Provides: starman_sppm
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop: $local_fs $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the starman server
# Description: starts starman using start-stop-daemon
### END INIT INFO
NAME=starman
DESC=starman
test -x $DAEMON || exit 0
set -e
. /lib/lsb/init-functions
## Variáveis de conf da sua App, ALTERE ISSO ANTES DE QUALQUER COISA.
export starman="/usr/bin/starman"
export myapp_path="/var/www/myapp"
export pidfile="/var/run/starman.pid"
start() {
echo -n $"Starting Starman: "
## eu inicio o starman para debug na porta 8080, é bom para testar algumas coisas dando bypass no nginx e tanto o log, quando o PID são criados com o usuário do nginx, para ficar mais fácil
$starman -I${myapp_path}/lib --user=nginx --listen :8080 --listen /var/run/nginx/starman.sock --daemonize --pid ${pidfile} --error-log /var/log/nginx/starman.log ${myapp_path}/${myapp}.psgi
RETVAL=$?
echo
[ $RETVAL = 0 ]
return $RETVAL
}
restart() {
echo -n $"Restarting Starman: "
export PID=`cat ${pidfile}`
kill -s USR2 $PID
RETVAL=$?
echo
[ $RETVAL = 0 ]
return $RETVAL
}
stop() {
echo -n $"Stopping Starman: "
killproc -p ${pidfile} -d 10 $starman
RETVAL=$?
echo
[ $RETVAL = 0 ] && rm -f ${pidfile}
}
# See how we were called.
case "$1" in
start)
start
;;
restart)
restart
;;
stop)
stop
;;
*)
echo $"Usage: starman {start|restart|stop}"
exit 1
esac
exit $RETVAL
upstream myapp_starman {
server unix:/tmp/starman.sock fail_timeout=0;
}
server {
listen 80;
client_max_body_size 1024m;
client_body_buffer_size 8k;
proxy_read_timeout 300;
##
# basic
##
server_name www.localhost.com;
root /home/user/MyApp/root;
keepalive_timeout 0;
##
# logging
##
access_log /var/log/nginx/myapp.access combined;
error_log /var/log/nginx/myapp.error;
location /static {
root /home/user/MyApp/root/;
autoindex on;
}
##
# proxy
##
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_buffering off;
if (!-f $request_filename) {
proxy_pass http://myapp_starman;
}
}
}
#!/bin/bash
# starman - this script starts and stops the starman daemon
#
# chkconfig: - 85 15
# description: Starman
# processname: starman
# pidfile : /var/run/starman.pid
# www file: /var/www/myapp
# Source function library.
. /etc/rc.d/init.d/functions
## Variáveis de conf da sua App, ALTERE ISSO ANTES DE QUALQUER COISA.
export starman="/usr/bin/starman"
export myapp_path="/var/www/myapp"
export pidfile="/var/run/starman.pid"
start() {
echo -n $"Starting Starman: "
## eu inicio o starman para debug na porta 8080, é bom para testar algumas coisas dando bypass no nginx e tanto o log, quando o PID são criados com o usuário do nginx, para ficar mais fácil
$starman -I${myapp_path}/lib --user=nginx --listen :8080 --listen /var/run/nginx/starman.sock --daemonize --pid ${pidfile} --error-log /var/log/nginx/starman.log ${myapp_path}/${myapp}.psgi
RETVAL=$?
echo
[ $RETVAL = 0 ]
return $RETVAL
}
restart() {
echo -n $"Restarting Starman: "
export PID=`cat ${pidfile}`
kill -s USR2 $PID
RETVAL=$?
echo
[ $RETVAL = 0 ]
return $RETVAL
}
stop() {
echo -n $"Stopping Starman: "
killproc -p ${pidfile} -d 10 $starman
RETVAL=$?
echo
[ $RETVAL = 0 ] && rm -f ${pidfile}
}
# See how we were called.
case "$1" in
start)
start
;;
restart)
restart
;;
stop)
stop
;;
*)
echo $"Usage: starman {start|restart|stop}"
exit 1
esac
exit $RETVAL
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment