Neu: udemy Kurs Ruby für Anfänger von Stefan Wintermeyer

15.3. Webserver ohne Deployment

Bitte loggen Sie sich als root auf dem Webserver ein.

Warnung

Bitte beachten Sie bei der Verwendung dieser Installationsanleitung für andere Rails-Projekte, dass der Name "blog" (für die jeweilige Rails-Applikation) in allen Konfigurationsdateien und Skripten mit dem jeweiligen Projektnamen ersetzt werden muss.

Neues Rails-Projekt aufsetzen

Um diese Anleitung so einfach wie möglich zu halten, erstellen wir als User deployer ein einfaches Blog direkt auf dem Server.
root@debian:~# su - deployer
deployer@debian:~$ rails new blog
[...]
deployer@debian:~$ cd blog
deployer@debian:~/blog$ rails generate scaffold post subject content:text
[...]
deployer@debian:~/blog$

Gemfile anpassen

Bitte schreiben Sie in die Datei Gemfile folgenden Inhalt:
source 'https://rubygems.org'

gem 'rails', '3.2.6'

gem 'sqlite3'

# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'sass-rails',   '~> 3.2.3'
  gem 'coffee-rails', '~> 3.2.1'

  # See https://github.com/sstephenson/execjs#readme for more supported runtimes
  # gem 'therubyracer', :platforms => :ruby

  gem 'uglifier', '>= 1.0.3'
end

gem 'jquery-rails'

# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'

group :production do
  # Use MySQL as the production database
  gem 'mysql'

  # Use unicorn as the app server
  gem 'unicorn'
end
Danach mit bundle install alle Gems installieren:
deployer@debian:~/blog$ bundle install
[...]
deployer@debian:~/blog$

Production Datenbank-Konfiguration

In der Datei config/database.yml müssen wir für das Produktionssystem die Datenbank-Konfiguration für die MySQL-Datenbank eintragen. Bitte achten Sie darauf, das korrekte Passwort einzutragen.
# SQLite version 3.x
#   gem install sqlite3
#
#   Ensure the SQLite 3 gem is defined in your Gemfile
#   gem 'sqlite3'
development:
  adapter: sqlite3
  database: db/development.sqlite3
  pool: 5
  timeout: 5000

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  adapter: sqlite3
  database: db/test.sqlite3
  pool: 5
  timeout: 5000

production:
  adapter: mysql
  encoding: utf8
  database: blog
  username: deployer
  password: IhrLieblingsPasswort

Unicon-Konfiguration

Für die Unicorn-Konfiguration nehmen wir als Basis die Datei https://raw.github.com/defunkt/unicorn/master/examples/unicorn.conf.rb und speichern sie an unseren Server angepasst wie folgt in der Datei config/unicorn.rb ab:
# Use at least one worker per core if you're on a dedicated server,
# more will usually help for _short_ waits on databases/caches.
worker_processes 4

# Since Unicorn is never exposed to outside clients, it does not need to
# run on the standard HTTP port (80), there is no reason to start Unicorn
# as root unless it's from system init scripts.
# If running the master process as root and the workers as an unprivileged
# user, do this to switch euid/egid in the workers (also chowns logs):
user "deployer", "www-data"

# Help ensure your application will always spawn in the symlinked
# "current" directory that Capistrano sets up.
APP_PATH = "/var/www/blog/current"
working_directory APP_PATH

# listen on both a Unix domain socket and a TCP port,
# we use a shorter backlog for quicker failover when busy
listen "/tmp/unicorn.blog.sock", :backlog => 64
listen 8080, :tcp_nopush => true

# nuke workers after 30 seconds instead of 60 seconds (the default)
timeout 30

# feel free to point this anywhere accessible on the filesystem
pid APP_PATH + "/tmp/pids/unicorn.pid"

# By default, the Unicorn logger will write to stderr.
# Additionally, some applications/frameworks log to stderr or stdout,
# so prevent them from going to /dev/null when daemonized here:
stderr_path APP_PATH + "/log/unicorn.blog.stderr.log"
stdout_path APP_PATH + "/log/unicorn.blog.stdout.log"

before_fork do |server, worker|
  # the following is highly recomended for Rails + "preload_app true"
  # as there's no need for the master process to hold a connection
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.connection.disconnect!
  end

  # Before forking, kill the master process that belongs to the .oldbin PID.
  # This enables 0 downtime deploys.
  old_pid = "/tmp/unicorn.my_site.pid.oldbin"
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end
end

after_fork do |server, worker|
  # the following is *required* for Rails + "preload_app true",
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.establish_connection
  end

  # if preload_app is true, then you may also want to check and
  # restart any other shared sockets/descriptors such as Memcached,
  # and Redis.  TokyoCabinet file handles are safe to reuse
  # between any number of forked children (assuming your kernel
  # correctly implements pread()/pwrite() system calls)
end

before_exec do |server|
  ENV["BUNDLE_GEMFILE"] = "/var/www/blog/current/Gemfile"
end

rake db:migration

Wir müssen noch die Datenbank anlegen:
deployer@debian:~/blog$ rake db:migrate RAILS_ENV=production
[...]
deployer@debian:~/blog$ 

Wichtig

Bitte beachten Sie, dass rake db:migrate mit einem RAILS_ENV=production abgeschlossen wird. Nur so wird die Produktionsdatenbank migriert.

rake assets:precompile

rake assets:precompile stellt sicher, dass alle in der Asset Pipeline vorhandenen Assets für die Produktionsumgebung bereitgestellt werden (siehe Kapitel 12, Asset Pipeline).
deployer@debian:~/blog$ rake assets:precompile
/home/deployer/.rvm/rubies/ruby-1.9.3-p194/bin/ruby /home/deployer/.rvm/gems/ruby-1.9.3-p194@global/bin/rake assets:precompile:all RAILS_ENV=production RAILS_GROUPS=assets
deployer@debian:~/blog$

Unicorn Init-Skript

Jetzt müssen Sie als User root weiterarbeiten:
deployer@debian:~$ exit
Abgemeldet
root@debian:~# 
Erstellen Sie nachfolgend das Init-Skript /etc/init.d/unicorn_blog mit folgendem Inhalt:
#!/bin/bash

### 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: Unicorn webserver
# Description:       Unicorn webserver for the blog
### END INIT INFO

UNICORN=/home/deployer/.rvm/bin/bootup_unicorn
UNICORN_ARGS="-D -c /home/deployer/blog/config/unicorn.rb -E production"
KILL=/bin/kill
PID=/home/deployer/tmp/unicorn.pid

sig () {
  test -s "$PID" && kill -$1 `cat $PID`
}

case "$1" in
        start)
                echo "Starting unicorn..."
                $UNICORN $UNICORN_ARGS
                ;;
        stop)
                sig QUIT && exit 0
                echo >&2 "Not running"
                ;;
        restart)
                $0 stop
                $0 start
                ;;
        status)
                ;;
        *)
                echo "Usage: $0 {start|stop|restart|status}"
                ;;
esac
Das Init-Skript muss jetzt noch scharf geschaltet und Unicorn gestartet werden:
root@debian:~# chmod +x /etc/init.d/unicorn_blog 
root@debian:~# update-rc.d -f unicorn_blog defaults
update-rc.d: using dependency based boot sequencing
root@debian:~# /etc/init.d/unicorn_blog start
root@debian:~# 
Ihr Rails-Projekt ist jetzt über die IP-Adresse des Webservers erreichbar.

nginx-Konfiguration

Für das Rails-Projekt fügen wir eine neue Konfigurationdatei /etc/nginx/conf.d/blog.conf mit folgendem Inhalt hinzu:
upstream unicorn {
  server unix:/tmp/unicorn.blog.sock fail_timeout=0;
}

server {
  listen 80 default deferred;
  # server_name example.com;
  root /home/deployer/blog/public;

  location / {
    gzip_static on;
  }

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  try_files $uri/index.html $uri @unicorn;
  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://unicorn;
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 4G;
  keepalive_timeout 10;
}
Die Default-Konfigurationsdatei benennen wir um, damit sie nicht mehr ausgeführt wird. Danach restarten wir nginx.
root@debian:~# mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.backup
root@debian:~# /etc/init.d/nginx restart
Restarting nginx: nginx.
root@debian:~#

Neue Version des Rails-Projektes einspielen

Wie die Überschrift schon sagt, haben wir bei dieser Variante keine automatischen Deployment-Mechanismen zur Verfügung. Diese können Sie sich aber leicht selbst per Skript realisieren.
Wenn Sie Veränderungen am Rails-Projekt aktivieren wollen, so müssen Sie als User deployer nach einem Update des Codes mit rake assets:precompile sicherstellen, dass alle in der Asset Pipeline vorhandenen Assets für die Produktionsumgebung wieder bereitgestellt werden (siehe Kapitel 12, Asset Pipeline).
deployer@debian:~/blog$ rake assets:precompile
/home/deployer/.rvm/rubies/ruby-1.9.3-p194/bin/ruby /home/deployer/.rvm/gems/ruby-1.9.3-p194@global/bin/rake assets:precompile:all RAILS_ENV=production RAILS_GROUPS=assets
deployer@debian:~/blog$
Sollten Sie neue Migrationen einspielen, müssen Sie natürlich auch rake db:migrate RAILS_ENV=production ausführen:
deployer@debian:~/blog$ rake db:migrate RAILS_ENV=production
[...]
deployer@debian:~/blog$ 
Danach müssen Sie als User root Unicorn neu starten:
root@debian:~# /etc/init.d/unicorn_blog restart
root@debian:~# 

Tipp

In den meisten Fällen ist eine Installation mit einem Capistrano-Deployment sinnvoller. Es ist initial ein Tick mehr Aufwand (siehe Abschnitt 15.4, „Webserver mit Capistrano-Deployment“), aber bietet dann mehr Komfort und Sicherheit.

Autor

Stefan Wintermeyer