I have a RubyOnRails 3 project and I'm using rvm. I want to switch from a sysvinit script to supervisord. The sysvinit script can only start the software in case of an error it it gets killed and restarted by $something. Mostly me.
In the project folder there is a .ruby-version and a .ruby-gemset file so that the correct ruby version and gemset gets loaded automatically. Then the app is startet with a shell script which looks like this:
#!/bin/bash
RAILS_ENV="production" rails server -d
My init script looks like this and works besides restarting and stopping:
#!/bin/sh
### BEGIN INIT INFO
# Provides: myapp
# 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 myapp
# Description: starts the myapp software
### END INIT INFO
USER=myuser
PATH=$PATH
DAEMON=go.sh
DAEMON_OPTS=""
NAME=myapp
DESC="myapp for $USER"
PID=/home/$USER/myapp/tmp/pids/server.pid
case "$1" in
start)
CD_TO_APP_DIR="cd /home/$USER/myapp"
START_DAEMON_PROCESS="$DAEMON $DAEMON_OPTS"
echo -n "Starting $DESC: "
if [ $(whoami) = root ]; then
su - $USER -c "$CD_TO_APP_DIR > /dev/null 2>&1 && ./$START_DAEMON_PROCESS &"
else
$CD_TO_APP_DIR > /dev/null 2>&1 && ./$START_DAEMON_PROCESS &
fi
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
kill -QUIT `cat $PID`
echo "$NAME."
;;
restart)
echo -n "Restarting $DESC: "
kill -USR2 `cat $PID`
echo "$NAME."
;;
reload)
echo -n "Reloading $DESC configuration: "
kill -HUP `cat $PID`
echo "$NAME."
;;
*)
echo "Usage: $NAME {start|stop|restart|reload}" >&2
exit 1
;;
esac
exit 0
My supervisor config looks like this:
[program:myapp]
directory=/home/myuser/myapp/
command=/home/myuser/.rvm/wrappers/ruby-2.1.5#myapp/rails server -d
environment=RAILS_ENV="production"
autostart=true
autorestart=true
Problem is that there is no rails binary in the wrapper. so that the command fails. What is the correct way to do this? I'm out of ideas and would start putting some really ugly bash script together that does the job in a very wrong and bad way but does it. Btw I found rails in the gems folder.
$ ls /home/myuser/.rvm/wrappers/ruby-2.1.5#myapp/
bundle bundler erb executable-hooks-uninstaller gem irb rake rdoc ri ruby testrb
$ which rails
/home/ffwi/.rvm/gems/ruby-2.1.5#ffwi-extern/bin/rails
Try to source rvm in your script (this link describes usecases like yours).
You have to load RVM into the shell of your script manually:
source "$HOME/.rvm/scripts/rvm"
It is only enabled for interactive login shells automatically.
From this point on, you can cd into directories and rvm should do its business.
Related
I want to start rails server in production mode after installing, migrating and running some scripts in order for this script to be attached as pipeline deploy script.
The problem is that the same script doesn't work as service mode.
ubuntu#ip-x-y-z-w:~/backend.rails.com$ sudo vim /etc/init.d/rails-start-backend
#! /bin/sh
# Start/stop the rails server daemon.
#
### BEGIN INIT INFO
# Provides: rails server start
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start daemon at boot time
# Description: Enable service provided by daemon.
### END INIT INFO
PATH=/bin:/usr/bin:/sbin:/usr/sbin
DESC="rails daemon"
NAME=rails
DAEMON=/home/ubuntu/backend.rails.com/gitlab-ci.sh
PIDFILE=/var/run/rails.pid
test -f $DAEMON || exit 0
. /lib/lsb/init-functions
case "$1" in
start)
log_daemon_msg "Starting rails"
/home/ubuntu/backend.rails.com/gitlab-ci.sh > /home/ubuntu/backend.rails.com/log/start_script.log
start_daemon -p $PIDFILE $DAEMON $EXTRA_OPTS
log_end_msg $?
;;
stop) log_daemon_msg "Stopping rails" "rails"
sudo kill -9 $(sudo lsof -t -i:3000)
killproc -p $PIDFILE $DAEMON
RETVAL=$?
[ $RETVAL -eq 0 ] && [ -e "$PIDFILE" ] && rm -f $PIDFILE
log_end_msg $RETVAL
;;
restart) log_daemon_msg "Restarting " "rails"
$0 stop
$0 start
;;
reload|force-reload) log_daemon_msg "Reloading rails" "rails"
# rails reloads automatically
log_end_msg 0
;;
*) log_action_msg "Usage: /etc/init.d/rails {start|stop|status|restart|reload|force-reload}"
exit 2
;;
esac
exit 0
and thats my gitlab-ci.sh script
cd /home/ubuntu/backend.rails.com
sudo chmod +x gitlab-ci.sh
rm config/master.key
rm config/credentials.yml.enc
echo "credentials"
RAILS_ENV=production EDITOR="mate --wait" rails credentials:edit
export RAILS_ENV=production
export FRONTEND_BASE_URL=https://www.rails.com
echo "bundle install"
bundle install
echo "rails db:migrate"
bundle exec rails db:migrate
echo "rails rake application:initialize"
bundle exec rake application:initialize
echo "kill"
sudo kill -9 $(sudo lsof -t -i:3000)
echo "start"
rails s &
The problem comes when I restarted the sudo service rails-start-backend restart service. It seems that in that context, all bundle, rails and ruby versions and settings are not the same as when I execute the same script manually in ssh.
The errors I get are:
/usr/bin/env: ‘ruby_executable_hooks2.6’: No such file or directory
bundle: not found
here's my PATH when ssh-logged
/home/ubuntu/.rvm/gems/ruby-2.6.5/bin:/home/ubuntu/.rvm/gems/ruby-2.6.5#global/bin:/usr/share/rvm/rubies/ruby-2.6.5/bin:/usr/share/rvm/bin:/home/ubuntu/bin:/home/ubuntu/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
and then when the script is executed as service
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
It works after setting the right paths to the right variables (using 2.6.5 ruby version)
export PATH=/home/ubuntu/.rvm/gems/ruby-2.6.5/bin:/usr/share/rvm/bin:$PATH
export GEM_PATH=/home/ubuntu/.rvm/gems/ruby-2.6.5:/home/ubuntu/.rvm/gems/ruby-2.6.5#global:$GEM_PATH
source .bash_profile
source ~/.rvm/scripts/rvm
I'm trying to deploy a Ruby on Rails application using nginx and unicorn. When I try to run my unicorn init script, I get the following error:
$ sudo service rails_app start
Starting rails_app
-su: bundle: command not found
It fails to run on this command:
su - complab -c "bundle exec unicorn -c config/unicorn.rb -E production -D"
However, running unicorn manually while logged in as $USER works just fine:
$ bundle exec unicorn -c config/unicorn.rb -E production
I have verified that the path set on the init script is correct. On further investigation, I discovered that while $USER has ruby version 2.2.0 installed via rbenv, running su - $USER -c "ruby -v" shows that it's running on 1.9.3.
I have already run rbenv global 2.2.0 but still it doesn't work.
I thought su meant I was able to temporarily changing the user who owns the session? Why is it that su $USER shows a different ruby version from when I am actually logged in as $USER?
Hope you can help me out!
The reason is that sudo doesn't preserve environment variable. And since rbenv is using user environment it doesn't work out of the box. You can use a command like this to run ruby with sudo:
ruby=`which ruby` && sudo $ruby -v
If you want to run unicorn under the user you have installed rbenv into you have to change these to match your app:
!/bin/sh
### BEGIN INIT INFO
# Provides: unicorn
# Required-Start: $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts unicorn
# Description: starts uniconr using start-stop-daemon
### END INIT INFO
set -u
set -e
export PATH=/path/to/rbenv/bin:$PATH
export RBENV_DIR=/path/to/rbenv
export RBENV_ROOT=/path/to/rbenv
APP_ROOT=/path/to/rack_app/root
PID=$APP_ROOT/tmp/pids/unicorn.pid
RAILS_ENV=production
export PATH="$RBENV_ROOT/shims:$RBENV_ROOT/rbenv:$PATH"
CMD="bundle exec $RBENV_ROOT/shims/unicorn -D -E $RAILS_ENV -c config/unicorn/$RAILS_ENV.rb"
old_pid="$PID.oldbin"
cd $APP_ROOT || exit 1
sig () {
test -s "$PID" && kill -$1 `cat $PID`
}
oldsig () {
test -s $old_pid && kill -$1 `cat $old_pid`
}
case $1 in
start)
sig 0 && echo >&2 "Already running" && exit 0
$CMD
;;
stop)
sig QUIT && exit 0
echo >&2 "Not running"
;;
force-stop)
sig TERM && 0
echo >&2 "Not running"
;;
restart|reload)
sig HUP && echo reloaded OK && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
$CMD
;;
upgrade)
sig USR2 && exit 0
echo >&2 "Couldn't upgrade, starting '$CMD' instead"
;;
rotate)
sig USR1 && echo rotated logs OK && exit 0
echo >&2 "Couldn't rotate logs" && 1
;;
*)
echo >&2 "Usage $0 <start|stop|restart|upgrade|rotate|force-stop>"
exit 1
;;
esac
I recently created a VPS on DigitalOcean to host a rails app. I followed their guide to setup Unicorn with my application. https://www.digitalocean.com/community/tutorials/how-to-deploy-a-rails-app-with-unicorn-and-nginx-on-ubuntu-14-04
A problem occurred when I ran sudo service unicorn_appxyz start. The error given was -su: bundle: command not found
I traced the init.d script and pasted the evaluated server start up command in terminal and it works fine when executed under the user joe (the user which rbenv is install and the owner of the app). The evaluated command is
su - joe -c cd /home/joe/appxyz && bundle exec unicorn -c config/unicorn.rb -E production -D
I then sudo su - into root user and ran service unicorn_appxyz start the error was of course the same. Then I ran the evaluated command under root and it return with this error
The program 'bundle' is currently not installed. You can install it by typing:
apt-get install bundler
It seems the script is not switching the user? This is likely the cause of unicorn not starting when i boot my VPS.
The full unicorn upstart script is here:
#!/bin/sh
### BEGIN INIT INFO
# Provides: unicorn
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the unicorn app server
# Description: starts unicorn using start-stop-daemon
### END INIT INFO
set -e
USAGE="Usage: $0 <start|stop|restart|upgrade|rotate|force-stop>"
# app settings
USER="joe"
APP_NAME="appxyz"
APP_ROOT="/home/$USER/$APP_NAME"
ENV="production"
# environment settings
PATH="/home/$USER/.rbenv/shims:/home/$USER/.rbenv/bin:$PATH"
CMD="cd $APP_ROOT && bundle exec unicorn -c config/unicorn.rb -E $ENV -D"
PID="$APP_ROOT/shared/pids/unicorn.pid"
OLD_PID="$PID.oldbin"
# make sure the app exists
cd $APP_ROOT || exit 1
sig () {
test -s "$PID" && kill -$1 `cat $PID`
}
oldsig () {
test -s $OLD_PID && kill -$1 `cat $OLD_PID`
}
case $1 in
start)
sig 0 && echo >&2 "Already running" && exit 0
echo "Starting $APP_NAME"
su - $USER -c "$CMD"
;;
stop)
echo "Stopping $APP_NAME"
sig QUIT && exit 0
echo >&2 "Not running"
;;
force-stop)
echo "Force stopping $APP_NAME"
sig TERM && exit 0
echo >&2 "Not running"
;;
restart|reload|upgrade)
sig USR2 && echo "reloaded $APP_NAME" && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
$CMD
;;
rotate)
sig USR1 && echo rotated logs OK && exit 0
echo >&2 "Couldn't rotate logs" && exit 1
;;
*)
echo >&2 $USAGE
exit 1
;;
esac
More related info
here are the paths for ruby, rails and bundler under user joe. Under root they are not found.
joe#vps:~$ which ruby
/home/joe/.rbenv/shims/ruby
joe#vps:~$ which rails
/home/joe/.rbenv/shims/rails
joe#vps:~$ which bundle
/home/joe/.rbenv/shims/bundle
It make sense bundler could not be found under root user but the upstart command should have switched to user 'joe' to run the bundle command. This is the part I don't understand.
I found out the issue. Explanation follows,
root user on startup will first su - into the rails user (in this case 'joe') then executes bundle to start up unicorn. rbenv is single user, only 'joe' has bundle installed. The path to bundle is likely stored in my .bashrc file. However .bashrc file which is not invoked by login in through su - and that caused the bundle not installed error.
I included the paths related to rbenv in .profile. This way when root su - into 'joe' the paths are loaded.
On Unbutu I created I file /etc/profile.d/rbenv.sh with this content:
export RBENV_ROOT=/home/YOUR_USER_PATH/.rbenv
export PATH=$RBENV_ROOT/shims:$RBENV_ROOT/bin:$PATH
How do I set up unicorn shell file to use ruby-2.1.2#my_gemset so that it can run as sudo service unicorn start. My Rails Application config folder contain unicorn.sh file which has the following content.
#!/bin/sh
### BEGIN INIT INFO
# Provides: unicorn
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Manage unicorn server
# Description: Start, stop, restart unicorn server for a specific application.
### END INIT INFO
set -e
# Feel free to change any of the following variables for your app:
TIMEOUT=${TIMEOUT-90}
APP_ROOT=/home/user/Documents/workspace/MyApp
PID=$APP_ROOT/tmp/pids/unicorn.pid
CMD="cd $APP_ROOT;bundle exec bin/unicorn -c $APP_ROOT/config/unicorn.rb -E development"
AS_USER="www-data"
action="$1"
set -u
OLD_BIN="$PID.oldbin"
sig () {
test -s "$PID" && kill -$1 `cat $PID`
}
oldsig () {
test -s $OLD_BIN && kill -$1 `cat $OLD_BIN`
}
run () {
if [ "$(id -un)" = "$AS_USER" ]; then
eval $1
else
su -c "$1" - $AS_USER
fi
}
case "$1" in
start)
sig 0 && echo >&2 "Already running" && exit 0
run "$CMD"
;;
stop)
sig QUIT && exit 0
echo >&2 "Not running"
;;
force-stop)
sig TERM && exit 0
echo >&2 "Not running"
;;
restart|reload)
sig HUP && echo reloaded OK && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
run "$CMD"
;;
upgrade)
if sig USR2 && sleep 3
then
n=$TIMEOUT
while test -s $OLD_BIN && test $n -ge 0
do
printf '.' && sleep 1 && n=$(( $n - 1 ))
done
echo
if test $n -lt 0 && test -s $OLD_BIN
then
echo >&2 "$OLD_BIN still exists after $TIMEOUT seconds"
exit 1
fi
exit 0
fi
echo >&2 "Couldn't upgrade, starting '$CMD' instead"
run "$CMD"
;;
reopen-logs)
sig USR1
;;
*)
echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
exit 1
;;
esac
but when i run sudo service unicorn start I got this error
/var/lib/gems/1.8/gems/bundler-1.5.3/lib/bundler/runtime.rb:220: warning: Insecure world writable dir /usr/local/bin in PATH, mode 040777
Could not find rake-10.4.2 in any of the sources
Run `bundle install` to install missing gems.
But actually i am using ruby-2.1.2#my_gemset. How do I tell shell script to use this gemset using rvm.
My bin/unicorn file has this content
#!/usr/bin/env ruby
#
# This file was generated by Bundler.
#
# The application 'unicorn' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path('unicorn', 'unicorn')
I know i have to change the path of /usr/bin/env ruby to my ruby but don't know how to do this. Also please let me know how can I set up unicorn shell script to use specific gemset with specific ruby.Thanks
I'm using rvm in my remote server to manage my gems. I've committed the .ruby-gemset, and .ruby-version so that it's uniform. In my deployment script, I added the snippet below to make sure I'm running the command in the right folder with the correct set of gems. So execute:
source ~/.rvm/scripts/rvm; cd #{current_path}; /etc/init.d/unicorn #{command}"
I'm pretty sure this can be refactored further but I haven't gotten around to updating my script for a while. But so far, it's been working. I'm using Capistrano 3.
I haven't done this before but i think this will work -
Add this line -
CMD_TO_USE_GEMSET="cd $APP_ROOT; rvm use ruby-2.1.2#my_gemset"
after
CMD="cd $APP_ROOT;bundle exec bin/unicorn -c $APP_ROOT/config/unicorn.rb -E development"
in your unicorn.sh.
And write this in "start" and "restart|reload" section of your unicorn.sh after run "$CMD" -
run "$CMD_TO_USE_GEMSET"
and now run the command to restart unicorn -
sudo service unicorn start
I am trying to get an init script running for unicorn (on Ubuntu) with unicorn installed as a gem:
/home/tim/.rvm/gems/ruby-1.9.2-p180#polco/bin/unicorn
My init script is:
#! /bin/sh
### BEGIN INIT INFO
# Provides: unicorn
# 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 unicorn web server
# Description: starts unicorn
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/home/tim/.rvm/gems/ruby-1.9.2-p180#polco/bin/unicorn
DAEMON_OPTS="-c /home/deployer/shop/current/unicorn.rb -E production -D"
NAME=unicorn_rails
DESC=unicorn_rails
PID=/home/deployer/shop/shared/pids/unicorn.pid
case "$1" in
start)
echo -n "Starting $DESC: "
$DAEMON $DAEMON_OPTS
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
kill -QUIT `cat $PID`
echo "$NAME."
;;
restart)
echo -n "Restarting $DESC: "
kill -QUIT `cat $PID`
sleep 1
$DAEMON $DAEMON_OPTS
echo "$NAME."
;;
reload)
echo -n "Reloading $DESC configuration: "
kill -HUP `cat $PID`
echo "$NAME."
;;
*)
echo "Usage: $NAME {start|stop|restart|reload}" >&2
exit 1
;;
esac
exit 0
The whole operation seems fragile to me with the rvm executable. Does rvm have something like $CURRENT_RVM_PATH?
O.k. i solved this for me. I stopped caring if I was root or not and decided to run this as, ironically, the user 'passenger' and I needed to run unicorn_rails.
here is my startup script:
#! /bin/sh
### BEGIN INIT INFO
# Provides: unicorn
# 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 unicorn web server
# Description: starts unicorn
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
#DAEMON=/home/tim/.rvm/gems/ruby-1.9.2-p180#polco/bin/unicorn
DAEMON=/home/passenger/.rvm/gems/ruby-1.9.2-p180/bin/unicorn_rails
DAEMON_OPTS="-c /home/passenger/polco/current/config/unicorn_tacitus.rb -E production -D"
NAME=unicorn_rails
DESC=unicorn_rails
PID=/home/passenger/polco/shared/pids/unicorn.pid
case "$1" in
start)
echo -n "Starting $DESC: "
$DAEMON $DAEMON_OPTS
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
kill -QUIT `cat $PID`
echo "$NAME."
;;
restart)
echo -n "Restarting $DESC: "
kill -QUIT `cat $PID`
sleep 1
$DAEMON $DAEMON_OPTS
echo "$NAME."
;;
reload)
echo -n "Reloading $DESC configuration: "
kill -HUP `cat $PID`
echo "$NAME."
;;
*)
echo "Usage: $NAME {start|stop|restart|reload}" >&2
exit 1
;;
esac
exit 0
To execute this -- I now have the simple capistrano task:
desc "restart unicorn"
task :restart, :roles => :app, :except => { :no_release => true } do
run "#{current_path}/unicorn_exec restart"
end
after "deploy:restart","unicorn:restart"
The last question I is why I have to restart unicorn_rails. I thought I could reload via: kill -HUP cat $PID, but everything only works if I restart.
Since google might find this question for other people, here is my unicorn restart script I'm using on 3 different servers. I just need to add USR2 The repo is called eunic and i may hack on this later.
All that to say, here is another way to do it.