Deploy via Git

Posted 13.12.2009 um 14:04 von badboy_ - 0 Kommentare

Wie ich bereits hier schrieb, wollte ich das Deployment, also das Übertragen aller Daten meines Blogsystems optimieren. Orientiert habe ich mich dabei vor allem am Setup von Github.

Der Sinn dahinter:

Zuvor hat Capistrano das Handling von verschiedenen Versionen des Codes übernommen und dafür Dateien hin- und herkopiert und Symlinks angelegt.

Da der Code aber sowieso schon in einem git-Repository liegt, kann auch gleich git die Aufgabe der Versionierung übernehmen, eben dafür wurde es ja entwickelt.

Nun aber wie ich das ganze gemacht habe:

$ steht für die Shell des Clients, % für die Shell des Servers

Zuerst mal musste das Repository geklont werden, das hab ich mittels

$ git clone --bare ruby/rails/blog blog.git

auf meinem Laptop erledigt. Als nächstes musste das Repo natürlich auch auf den Server:

$ scp -r blog.git server:/var/git

Auf dem Server habe ich dann eine Gruppe und einen User speziell für git eingerichtet:

% groupadd git
% useradd -g git -s /usr/bin/git-shell -m git

Das letzte Kommando legt den User git an, erstellt das Homeverzeichnis (/home/git) und setzt die Usershell auf git-shell.

Nun muss noch das Verzeichnis für die git-Repos dem richtigen User zugeordnet werden:

% chown -r git:git /var/git

Nun noch seinen SSH-Key hochladen:

$ scp .ssh/id_rsa.pub server:

und auf dem Server in die richtige Datei schreiben:

% mkdir /home/git/.ssh
% cat id_rsa.pub >> /home/git/.ssh/authorized_keys

Nun sollte es möglich sein, das git-Repo zu pushen:

$ cd ruby/rails/blog
$ git remote add origin git@server:/var/git/blog.git
$ git push origin master

Wenn das geklappt hat, ist der größte Teil schon fertig.

Um nun auch die Daten für den Webserver mittels git up-to-date halten zu können, habe ich folgendes gemacht:

% cd /var/www/
% mkdir blog/current blog/cached

Mein apache liefert mittels Passenger die Daten in /var/www/blog/current/ aus (dort liegt meine Rails-App).

Noch ist aber da nichts drin. Also ersteinmal wieder das Repository klonen:

% cd /var/www/blog
% git clone -l /var/git/blog.git current

Der Serverteil ist nun erledigt. Apache sollte nun wieder alles wie gewohnt ausliefern.

Nun zum eigentlichen Deploy-Skript für Capistrano (nur die wichtigsten Teile):

# externe Datei nachladen
load 'config/deploy/symlinks'

# der Ordner, in dem der Blog liegt
set :deploy_to, "var/www/blog"
# Das Repository, nicht weiter verwendet
set :repository, "file:///var/git/blog.git"
# Der Branch, aus dem der Code genommen wird
set :branch, "origin/next"

namespace :deploy do
  desc "deploy the project"
  task :default do
    update
    restart
  end

  desc "update the deployed code."
  task :update_code, :except => { :no_release => true } do
    run "cd #{current_path}; git fetch origin; git reset --hard #{branch}"
  end

  namespace :rollback do
    desc "Rollback a single commit."
    task :code, :except => { :no_release => true } do
      set :branch, "HEAD^"
      deploy.default
    end

    task :default do
      rollback.code
    end
  end

  desc "Tell Passenger to restart the app."
  task :restart do
    run "touch #{current_path}/tmp/restart.txt"
  end
end

Der wichtiges Teil ist der nach "task :update_code":

Hier werden mittels "git fetch origin" erst alle Änderungen eingelesen und dann mittels "git reset --hard origin/next" alle Daten in dem aktuellen Verzeichnis zurückgesetzt.

Das ist schnell und einfach.

Was noch fehlt sind die Daten, die nicht in dem Repo liegen, so z.B. meine database.yml, die nicht versioniert wird.

Zuerst muss die auch auf dem Server landen:

# Erstellen der Ordner:
% mkdir -p /var/www/blog/shared/config
% mkdir -p /var/www/blog/shared/log
$ scp database.yml server:/var/www/blog/shared/config

Und nun kommt die config/deploy/symlinks.rb ins Spiel, die sieht so aus:

set :normal_symlinks, %w(
    config/database.yml
    log
)

set :weird_symlinks, {}

namespace :symlinks do
  desc "Make all the damn symlinks"
  task :make, :roles => :app, :except => { :no_release => true } do
    commands = normal_symlinks.map do |path|
      "rm -rf #{current_path}/#{path} && \
ln -s #{shared_path}/#{path} #{current_path}/#{path}"
    end

    commands += weird_symlinks.map do |from, to|
      "rm -rf #{current_path}/#{to} && \
ln -s #{shared_path}/#{from} #{current_path}/#{to}"
    end

    # needed for some of the symlinks
    run "mkdir -p #{current_path}/tmp"

    run (["cd #{current_path}"]+commands).join(" && ")
  end
end

after "deploy:update_code", "symlinks:make"

Das Skript ist nahezu eins zu eins von Github übernommen, nur das release_path habe ich überall durch current_path ersetzt. Capistrano versucht sonst nämlich einen Pfad wie blog/releases/YYYYMMDD zu benutzen, den es aber bei mir nicht mehr gibt.

Zu guter letzt reicht nun folgendes um den Code auf dem Server aktuell zu halten:

$ cd ruby/rails/blog
$ git checkout next
$ git add .
$ git commit -m 'update'
$ git push origin next
$ cap deploy

Bei mir dauert ein Update so nur noch knappe 2 Sekunden, was wirklich sehr schnell ist.

Und noch ein kleinen Trick habe ich von Github übernommen:

Rails hängt an alle "Assets", also alle Javascript-, Stylesheet- und auch Bilddateien eine ID an, damit Browser diese Daten cachen und eben nur neu laden müssen, wenn sie wirklich verändert wurden, sich also der Timestamp der Datei geändert hat.

Nun habe ich analog zu dem Weg von Github einfach folgendes in meiner config/environments/production.rb stehen:

repo = Grit::Repo.new(RAILS_ROOT)
js = repo.log('next', 'public/javascripts', :max_count => 1).first
css = repo.log('next', 'public/stylesheets', :max_count => 1).first

ENV['RAILS_ASSET_ID'] = js.committed_date > css.committed_date ? js.id : css.id

Nun wird nicht mehr das Datum des letzten Zugriffs auf die Datei verwendet, sondern einfach der SHA1-Hash des letzten Commits, der an den Dateien etwas geändert hat. Ebenfalls simpel und einfach.

Das ganze klappt bislang prima. Einziges Problem: die deploy.rb ist nicht komplett portabel. Das erstmalige Klonen habe ich noch selbst gemacht. Im Skript von Github gibt es dafür einen eigenen Task:

desc "Setup a GitHub-style deployment."
task :setup, :except => { :no_release => true } do
  run "git clone #{repository} #{current_path}"
end

ein

$ cap deploy:setup

sollte nun auch diesen ersten Schritt erledigen.

Serversoftware

Posted 09.12.2009 um 17:38 von badboy_ - 1 Kommentar

Da Florian hier fragte, welche Software ich denn so einsetze, veröffentliche ich das hier auch mal (viel spektakuläres gibt es da nun auch nicht unbedingt, aber gut)

Gemietet habe ich den Server bei netcup zu wirklich super Konditionen. Als Betriebssystem auf dem Server läuft das standard mäßig vorinstallierte Debian Etch.

Nun zur eigentlichen Software:

Für den Mailserver laufen:

Für den Webserver:

Weiteres:

  • Ruby (selbstkompiliert)
  • monit: zur Systemüberwachung (das sendet fleißig Emails bei Fehlern/Auffälligkeiten)
  • git: als Versionskontrollsystem unter anderem für die Blogsoftware
  • mysql: als Datenbankserver

Habe ich noch was vergessen? Dann fragt halt noch mal nach ;)

Edit (20:38): MySQL hinzugefügt.

Probleme beim Deploy

Posted 09.12.2009 um 14:30 von badboy_ - 0 Kommentare

Die Software hinter diesem Blog ist noch voll in der Entwicklung. Auf dem Server hier nutze ich aber nicht unbedingt immer gleich die stabilste Version sondern den next-Branch aus dem dazugehörigen git-Repository. Auf den Server kommt das ganze dann per Capistrano-Task.

Der wichte Teil der Konfiguration sieht dabei so aus:

set :deploy_to, "/var/www/blog"
set :deploy_via, :copy
set :scm, "none"
set :repository, "."

(aus der config/deploy.rb)

Hier wird definiert, wo auf dem Server die Daten liegen und wie das ganze von meinem Laptop auf den Server kommt: es wird per scp kopiert.

Simpel aber effektiv. Problem hierbei: arbeite ich in einem extra Branch in meinem git-Repo und muss aber nun schnell eine Konfiguration anpassen (wie zuletzt die Rails-Version), bleiben natürlich noch Dateien über, die so nicht auf den Server sollen.

Nun habe ich zuletzt bei eben diesem Fall vergessen, den Branch wieder zu wechseln und schon war es nach einem "cap deploy" passiert: Die Dateien, die garnicht hochgeladen werden sollten, waren hochgeladen und Rails verabschiedete sich mit einme 500er.

Dabei unterstützt Capistrano so gut von Haus aus. Mein nächstes Ziel ist es nun also auf dem Server selbst einfach das git-Repo anzulegen, Capistrano kann dann von da den richtigen Branch klonen, nachdem ich vorher natürlich ein "git push" ausgeführt habe.

Das sollte nicht alzu schwer sein. Ein "git clone --bare" sollte schon fast ausreichen :)

Wandkalender für 2010

Posted 25.11.2009 um 19:45 von badboy_ - 0 Kommentare

Und mal wieder startet ein Online-Händler eine Blogger- und Twitteraktion.

Diesmal ist druckerei.de verantwortlich. Verlost werden Wandkalender für 2010 im Twitter-Style. Die sehen dann so aus:

Wandkalender

(das Bild ist natürlich von druckerei.de)

Die Aktion dazu findet ihr unter folgendem Link: Wandkalender 2010-Aktion

Alternativ kann man auch wieder mal per Twitter teilnehmen. Einfach @druckerei_de folgen ("followen") und einen Tweet mit folgendem (oder ähnlichem) Inhalt posten:

Ich möchte einen Twitter-Wandkalender 2010 von @druckerei_de bekommen. Zur Aktion von www.druckerei.de -> http://bit.ly/4vTob5

Für alle Blogger gibt's aber auch hierbei einen Vorteil: Laut Aktionsseite kriegen die nämlich auf jedenfall drei Wandkalender.

Mit dem Tweet nimmt man nur an einer Verlosung teil.

Find ich persönlich ja mal wieder 'ne tolle Aktion und hoffe dann auch mal, dass ich meinen Wandkalender kriege :)

Externer Post

Posted 21.11.2009 um 20:15 von badboy_ - 0 Kommentare

So! ich hab mir jetzt mal ein Skript geschrieben, um Posts auch via Konsole zu schreiben. Dadurch kann ich die Vorteile von Vim nutzen.

Das hier ist der erste Test ob's klappt.