Schritt für Schritt Anleitung: Ruby On Rails Deployment mit Capistrano, Mongrel, Apache, Subversion und Dir

Posted on 23 April 2008 by Johannes Fahrenkrug. Tags: Programming Tutorials rails Deutsch
Note to English speaking readers: I've written this tutorial in German because there are a lot of good Rails deployment tutorials out there in English, but not so many in German. If enough people email me or comment and say they want this in English I might translate it ;-). So, jetzt auf Deutsch: Du willst deine grossartige nagelneu programmierte Ruby On Rails Anwendung fuer die Welt (oder immerhin fuer deinem Kunden) nutzbar machen. Dann will ich dich nicht aufhalten und dir in 27 einfachen Schritten zeigen, wie es geht. Dann wollen wir mal...
  1. Log dich mit ssh in den Server ein, auf dem die Rails Applikation laufen soll.
  2. Wenn du dich nicht als root angemeldet hast, werde jetzt root (su -)
  3. Stell sicher, dass ein Editor wie vi richtig funktioniert. Wenn man von Mac OS X auf einen Linux Rechner geht kann es manchmal Probleme geben. Auf Debian Systemen schafft da oft
    apt-get install ncurses-term
    
    Abhilfe.
  4. Aktualisiere die verfuegbaren Pakete:
    apt-get update
  5. Installiere folgende Pakete:
    apt-get install mysql-server
    apt-get install ruby
    apt-get install ruby1.8-dev
    apt-get install rubygems
    apt-get install subversion
    apt-get install make
    apt-get install build-essential
    apt-get install curl
    apt-get install apache2
    
  6. Installiere folgende Ruby Gems:
    gem install --include-dependencies rails
    gem install --include-dependencies capistrano
    gem install --include-dependencies mongrel (jeweils die neuste ruby version nehmen)
    gem install --include-dependencies mongrel_cluster
    
  7. Gehe nochmal sicher, dass alles aktuell ist:
    gem update --system
    
    Wenn du irgendwo den Fehler
    /usr/bin/gem:23: uninitialized constant Gem::GemRunner (NameError)
    
    bekommst ist das damit zu loesen, folgende Zeile in /usr/bin/gem einzufuegen:
    require 'rubygems/gem_runner'
    
    Und um den Fehler
    undefined method `require_gem' for main:Object
    
    spaeter beim Deployment zu vermeiden solltest du nochmal
    gem install --remote rake
    
    ausfuehren. Wenn das nicht hilft, in /var/lib/gems/1.8/bin/rake
    require_gem 'rake', version
    
    in
    gem 'rake', version
    
    abaendern.
  8. Jetzt musst du die Datenbank einrichten. Wir nehmen hier MySQL und der Einfachheit halber nenne ich in dieser Anleitung die Datenbank, den Datenbank Benutzer und die Rails-Anwendung "springenwerk". Root Passwort setzen:
    mysqladmin -u root password ein_passwort_meiner_wahl
    
    (danach am besten die bash_history loeschen) Dann die Datenbank anlegen und die Berechtigung setzen:
    mysql -u root -p
    CREATE DATABASE springenwerk;
    GRANT ALL PRIVILEGES ON springenwerk.* TO 'springenwerk'@'localhost' IDENTIFIED BY 'ein_password_meiner_wahl' WITH GRANT OPTION;
    
    Die Angaben fuer die Produktions-Datenbank (Name, Username und Passwort) muessen den Angaben in der app/config/database.yml Datei der Rails Anwendung entsprechen. Jetzt kannst du den Mysql Client mit 'exit' verlassen.
  9. Die ausfuehrbaren Ruby Gems willst du jetzt natuerlich gerne in deinem Pfad haben. Also in der /etc/profile /var/lib/gems/1.8/bin an die PATH Variable haengen. Das kann man so machen:
    PATH=$PATH:/var/lib/gems/1.8/bin
    
    Du musst nur aufpassen, dass die das vor die Zeile "export PATH" schreibst.
  10. Dann die Aenderungen in der aktuellen Session nutzbar machen:
    source /etc/profile
    
    Wenn du dann
    rails -v
    
    ausfuehren kannst hat alles geklappt.
  11. Jetzt solltest du einen eigenen User fuer deine Rails Anwendung anlegen:
    useradd -m springenwerk
    
    und das Passwort setzten:
    passwd springenwerk
    
    Und ihn zu den sudoers packen:
    visudo
    
    und dann unten folgende Zeile hinzufuegen (natuerlich "springenwerk" wieder mit deinem Produktions-Usernamen ersetzen):
    springenwerk ALL=(ALL) ALL
    
  12. Da sich Capistrano per SSH einloggt aber auch ein gewisse Benutzerumgebung braucht (z.B. den PATH) musst du in die /etc/ssh/sshd_config folgende Zeilen einfuegen:
    # for capistrano
    PermitUserEnvironment yes
    
  13. Dann SSHD neu starten:
    /etc/init.d/ssh restart
    
  14. In /home/springenwerk/.ssh/environment (ggf. ".ssh" Verzeichnis und Datei anlegen) folgende Zeilen einfuegen:
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/var/lib/gems/1.8/bin
    RUBYOPT=rubygems
    
  15. Jetzt musst du auf dem CLIENT Capistrano und Mongrel Cluster installieren (ich gehe davon aus, dass Ruby und Rails schon installiert sind)
    sudo gem install capistrano
    sudo gem install capistrano-ext
    sudo gem install mongrel_cluster
    
    Ab Mac OS X Leopard ist capistrano schon dabei, aber stelle mit
    sudo gem update capistrano
    
    sicher, dass du mindestens Version 2.1 hast.
  16. Wechsle in dein Rails Projekt Verzeichnis:
    cd /Users/johannes/Code/springenwerk/trunk/springenwerk
    
  17. Jetzt erstellst du eine Konfigurationsdatei fuer den Mongrel Cluster:
    mongrel_rails cluster::configure -e production -p 8000 -a 127.0.0.1 -N 2 -c /home/springenwerk/springenwerk/current
    
    -p 8000 sagt, dass die Server ab Port 8000 lauschen, -N 2 sagt, dass es zwei Instanzen geben wird (also eine auf Port 8000, eine auf 8001) und mit -c gibt man das Verzeichnis an, wo auf dem PRODUKTIONSSERVER die Applikation liegen wird (in diesem Fall ein Unterverzeichnis, das so heisst wie die Applikation im Home-Verzeichnis des Users, den wir angelegt haben (welcher auch so heisst wie die Applikation) und mit dem Zusatz "current". Das musst da stehen, denn das ist ein Symlink, den Capistrano anlegt und der immer auf die aktuelle Version zeigt.
  18. Jetzt musst du dein Rails Projekt capistranofizieren ;-). Wechsle in dein Rails Projekt Verzeichnis:
    cd /Users/johannes/Code/springenwerk/trunk/springenwerk
    
    und fuehre capify aus:
    capify .
    
  19. Oeffne jetzt config/deploy.rb in deinem Editor und passe die Datei so an, dass sie so aussieht (mit deinen User- und Projektnamen und Passwoertern natuerlich):
    require 'mongrel_cluster/recipes'
    
    default_run_options[:pty] = true # to make sure password prompts are forwarded to you, the user
    
    set :application, "springenwerk"
    set :repository_url,  "svn://DEIN_SVN_USER@DEIN_SVN_SERVER/springenwerk/trunk/springenwerk"
    
    if ENV['svn_prompt'] and ENV['svn_prompt'] == '1'
      set :svn_user, Proc.new { Capistrano::CLI.password_prompt('SVN User: ') }
      set :svn_password, Proc.new { Capistrano::CLI.password_prompt('SVN Password: ') }
      set :repository, Proc.new { "--username #{svn_user} --password #{svn_password} #{repository_url}" }  
    else
      set :repository, repository_url
    end
    
    set :deploy_to, "/home/#{application}/#{application}" # defaults to "/u/apps/#{application}"
    set :deploy_via, "export"
    set :user, application         # in our case, the user and the application have the same name.
    
    set :mongrel_conf, "#{current_path}/config/mongrel_cluster.yml"
    
    role :app, "DEIN_PRODUCTION_SERVER"
    role :web, "DEIN_PRODUCTION_SERVER"
    role :db,  "DEIN_PRODUCTION_SERVER", :primary => true
    
    # Task to copy production db config after deployment
    task :after_update_code, :roles => :app do  
      db_config = "#{shared_path}/config/database.yml.production"
      run "cp #{db_config} #{release_path}/config/database.yml"
    end
    
    Die Datei werde ich hier nicht gross erklaeren, dazu gibt es andere Stellen. Diese deploy.rb Datei hat aber eine kleine Besonderheit (Danke an Jonathan). Da Subversion natuerlich einen Benutzernamen und ein Passwort erwartet wird normalerweise empfohlen, einmal irgendwo auf dem Produktionsserver das Rails Projekt auszuchecken, damit die Credentials gecachet werden und Capistrano nicht mehr nach SVN Benutzernamen und Passwort fragt. Das koennen wir uns jetzt sparen. Wir koennen naemlich jetzt angeben, dass Capistrano uns beim ersten Mal einfach nach den Zugangsdaten fuer SVN fragt. Dazu gleich mehr.
  20. Jetzt kann Capistrano die Verzeichnisse auf dem Produktionsserver anlegen. Auf deinem CLIENT fuehre folgendes aus:
    cap deploy:setup
    
  21. Stelle die neuen Konfigurationsdateien unter Versionskontrolle und checke sie ein:
    svn add Capfile config/mongrel_cluster.yml config/deploy.rb
    svn ci -m "capistrano config"
    
  22. Du willst aus Sicherheitsgruenden deine Produktionsdatenbank Passwoerter nicht ins SVN einchecken. Deshalb solltest du eine Datei namens database.yml.production anlegen, die die Zugangsdaten fuer deine Produktionsdatenbank enthaelt. Unser "after_update_code" Task in deploy.rb erwartet diese Datei und kopiert sie dann ueber die ausgecheckte database.yml. Zum Beispiel koennte sie so aussehen:
    development:
      adapter: mysql
      database: springenwerk_development
      username: root
      password: root
      host: localhost
      socket: /Applications/MAMP/tmp/mysql/mysql.sock
    
    # 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: mysql
      database: springenwerk_test
      username: root
      password: root
      host: localhost
                 
    production:  
      adapter: mysql
      database: springenwerk
      username: springenwerk
      password: DEIN_PASSWORT
      host: localhost
      socket: /var/run/mysqld/mysqld.sock
    
    Diese packst du auf dem Produktionsserver in folgendes Verzeichnis:
    /home/springenwerk/springenwerk/shared/config
    
    Das config Verzeichnis musst du vorher anlegen.
  23. Jetzt kannst du mit Capistrano von deinem CLIENT ein Erst-Deployment (cold deployment) durchfuehren (in deinem Rails Projektverzeichnis)
    svn_prompt=1 cap deploy:cold
    
    "svn_prompt" ist einen Umgebungsvariable, die unserem deploy.rb Skript sagt, dass es nach den SVN Zugangsdaten fragen soll. Denke daran, dass wenn Capistrano dich nach dem Passwort fuer deinen Produktionsserver fragt das Passwort fuer den nicht-root User (in diesem Beispiel "springenwerk") fragt! Achte darauf, dass es keine Fehler gibt, am Ende sollte
    command finished
    
    stehen.
  24. Pruefe auf dem SERVER, ob die Mongrel Dienste laufen:
    curl -I http://127.0.0.1:8000
    
    Wenn Mongrel laeuft, sollte die Ausgabe ungefaehr so aussehen:
    HTTP/1.1 302 Moved Temporarily
    Connection: close
    Date: Wed, 06 Feb 2008 11:42:33 GMT
    Set-Cookie: _springenwerk_session_id=f4a9b408f36d0df7561fd778e46d2c62; path=/
    Status: 302 Found
    Location: http://127.0.0.1:8000/session/new
    Cache-Control: no-cache
    Server: Mongrel 1.1.3
    Content-Type: text/html; charset=utf-8
    Content-Length: 101
    
    Sehr gut. Jetzt musst du nur noch Apache als Loadbalancer einrichten. Ein Kinderspiel ;-)
  25. Ok, Apache. Bevor du die folgenden Sachen machst, ein kleiner Disclaimer: ich bin kein Apache-Config-Profi. Es kann gut sein, dass es elegantere Wege gibt, das gute Stueck zu konfigurieren. Alle Hinweise die zur Ergreifung des Taeters fuehren werden sehr geschaetzt. Ans Ende von /etc/apache2/apache2.conf folgendes anfuegen (im vi mit G ans Ende der Datei springen):
    <Proxy balancer://mongrel_cluster>
      BalancerMember http://127.0.0.1:8000
      BalancerMember http://127.0.0.1:8001
    </Proxy>
    
    Der Inhalt der /etc/apache2/sites-available/000-default Datei muss so aussehen (ich gehe in dem Beispiel davon aus, dass der Apache nur fuer deine Rails Anwendung benutzt wird):
    <VirtualHost *:80>
        ServerAdmin webmaster@localhost
        
        DocumentRoot /home/springenwerk/springenwerk/current/public
        <Directory /home/springenwerk/springenwerk/current/public>
            Options FollowSymLinks
            AllowOverride None
            Order allow,deny
            allow from all
        </Directory>
    
        RewriteEngine On
    
        RewriteLog /var/log/apache2/rewrite.log
        RewriteLogLevel 9
    
        RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
        RewriteCond %{SCRIPT_FILENAME} !maintenance.html
        RewriteRule ^.*$ /system/maintenance.html [L] 
    
        RewriteRule ^/$ /index.html [QSA] 
        
        RewriteRule ^([^.]+)$ $1.html [QSA]
                 
        RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
                 
        RewriteRule ^/(.*)$ balancer://mongrel_cluster%{REQUEST_URI} [P,QSA,L]
                 
        ErrorLog /var/log/apache2/error.log
                 
        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn
                 
        CustomLog /var/log/apache2/access.log combined
        ServerSignature Off
                 
    </VirtualHost>
    
  26. Dann musst du in /etc/apache2/mods-enabled folgende Symlinks anlegen:
    ln -s ../mods-available/proxy_balancer.load 
    ln -s ../mods-available/proxy.conf          
    ln -s ../mods-available/proxy_http.load 
    ln -s ../mods-available/proxy.load      
    ln -s ../mods-available/rewrite.load 
    
    Der Inhalt der proxy.conf muss so aussehen:
    <IfModule mod_proxy.c>
            #turning ProxyRequests on and allowing proxying from all may allow
            #spammers to use your proxy to send email.
    
            ProxyRequests Off
    
            <Proxy *>
                    AddDefaultCharset off
                    Order allow,deny
                    Allow from all
            </Proxy>
    
            # Enable/disable the handling of HTTP/1.1 "Via:" headers.
            # ("Full" adds the server version; "Block" removes all outgoing Via: headers)
            # Set to one of: Off | On | Full | Block
    
            ProxyVia On
    </IfModule>
    
  27. Dann nur noch Apache neu starten:
    /etc/init.d/apache2 restart
    
  28. Feierabend. Das wars.
Wenn du jetzt eine neue Version deiner Anwendung live stellen willst, musst du nur in deinem Rails Projektverzeichnis
cap deploy
bzw
cap deploy:migrations
ausfuehren wenn es neue Datenbankmigrationen gibt. Capistrano macht dann den Rest! Bitte benutze die Kommentarfunktion wenn du beim Folgen dieser Schritte ein paar Tipps oder Probleme gefunden hast. Grosser Dank gilt noch dem grossartigen Tutorial von Coda. Da gibt es noch sehr viel Hintergrundinfos. Ich wollte das hier nicht alles wiederholen, sondern es eher kurz und knapp halten. Ich hoffe, dass es ganz hilfreich ist. Wenn das der Fall ist würde ich mich sehr über eine Empfehlung freuen:
Recommend Me
Danke!

Comments

Johannes Fahrenkrug said...

Hallo Markus,

hmmm... das sieht so aus als ob das SVN auf deinem Server in dem Repository unter file:///subversion/projekte/todo/trunk keine Revision mit der Nr 13 finden kann. Kann es sein, dass du das SVN Repository bei dir auf deiner Lokalen Entwicklungsmaschine laufen hast, sodass der Server, auf dem die Anwendung deployt werden soll, da nicht rankommt?
Du muss dein SVN Repository so aufsetzten, dass es von deiner Entwicklungsmaschine und von deinem Server erreicht werden kann...
ODER...
du nimmst die copy deploy stategy:

set :deploy_via, :copy
set :copy_strategy, :export

dann wir der kram lokal auf deiner entwicklungsmaschine exportiert, getart und gegzipt und dann per sftp auf deinen server geschoben. Der Server braucht dann keinen SVN Zugang mehr. Siehe auch http://www.capify.org/upgrade/whats-new

Viel Erfolg!

- Johannes

November 03, 2008 06:51 AM

M said...

hallo johannes ...
vielen dank für die tolle anleitung!
leider habe ich ein problem:
- bis zu "cap deploy:check" sieht alles gut aus
- auch "cap deploy:setup" läuft ohne fehler ab
- bei "cap deploy:cold" erhalte ich allerdings folgende fehlermeldung:
-----
root@ubu804s-1:/var/www/rails/todo# cap deploy:cold
* executing `deploy:cold'
* executing `deploy:update'
** transaction: start
* executing `deploy:update_code'
* executing "svn export -q -r13 file:///subversion/projekte/todo/trunk /var/www/rails/todo/releases/20081102094125 && (echo 13 > /var/www/rails/todo/releases/20081102094125/REVISION)"
servers: ["192.168.2.44"]
Password:
[192.168.2.44] executing command
** [192.168.2.44 :: err] svn: No such revision 13
command finished
*** [deploy:update_code] rolling back
* executing "rm -rf /var/www/rails/todo/releases/20081102094125; true"
servers: ["192.168.2.44"]
[192.168.2.44] executing command
command finished
failed: "sh -c \"svn export -q -r13 file:///subversion/projekte/todo/trunk /var/www/rails/todo/releases/20081102094125 && (echo 13 > /var/www/rails/todo/releases/20081102094125/REVISION)\"" on 192.168.2.44
root@ubu804s-1:/var/www/rails/todo#
-----
was mache ich falsch? wo finde ich hilfe?

vielen dank vorab für deine unterstützung!

gruss markus

November 02, 2008 08:48 AM

Comments

Please keep it clean, everybody. Comments with profanity will be deleted.

blog comments powered by Disqus