Category: Other

Własny serwer deweloperski – dlaczego warto?

Wstęp

Dzisiejszy wpis postanowiłem poświęcić opisowi rzeczy według mnie bardzo ważnej i bardzo przydatnej: własnemu serwerowi deweloperskiemu. Większość z nas przechodziła (bądź przechodzi) mniej więcej taki sam cykl rozwojowy jeśli chodzi o "serwerowość":

  1. Darmowy hosting (np. do prostych skryptów w PHP)
  2. Minimalnie płatny shared hosting
  3. Lepszy shared hosting
  4. VPS lub dedyk
  5. Rozwiązania klasy enterprise

Wszystko rozbija się o potrzeby oraz zasobność portfela. Jednak gdzieś na uboczu stoi jeszcze jedno bardzo przydatne (i stosunkowo niedrogie) rozwiązanie jakim jest serwer deweloperski.

Dlaczego warto?

Tak naprawdę argumentów za posiadaniem własnej maszyny jest bardzo wiele. Ja podam te które przekonały mnie.

Testy, testy, testy...

Wszyscy pisząc testy odpalamy je na naszych laptopach/stacjonarkach, testując daną funkcjonalność. Zdarza się jednak od czasu do czasu, że robimy dość znaczną refaktoryzację kodu, na poziomie który "dotyka" całości lub znacznej części systemu. W takich wypadkach odpalenie wszystkich testów wydaje się bardzo zasadne. Odpalenie ich jednak - równoznaczne jest z godziną lub dwoma bez komputera. Ciężko pracuje się na maszynie której CPU trzyma się cały czas w granicach 100%. I to jest właśnie pierwszy z argumentów przemawiających za kupnem dodatkowej maszyny: możliwość wykonywania niezliczonej ilości testów - bez konieczności obciążania nimi maszyny na której pracujemy. Proste GUI do odpalania skryptów testowych można zbudować w jeden lub dwa dni, dzięki czemu możemy sobie je odpalać wprost z przeglądarki internetowej, obserwując postęp w czasie rzeczywistym (przykład poniżej).

Interfejs do odpalania testów

Bardzo wygodnie jest móc włączyć całość testów kiedy się chce, nie przejmując się tym, że trwają dość długo. Ja dodatkowo postanowiłem zapamiętywać całość wyników z każdej kolejnej serii testów - dzięki czemu mam wgląd w historię testów. Przydaje się to szczególnie w przypadku RCova, gdzie mogę śledzić praktycznie linia po linii postępy z kolejnych analiz.

Wersje alfa, beta, itp

Zdarza się, że chcemy pokazać nasze oprogramowanie osobom trzecim, jeszcze zanim trafi na produkcję. Czasem sami chcemy sprawdzić czy np. cacheowanie w środowisku produkcyjnym działa prawidłowo. Serwer deweloperski sprawdza się w takich kwestiach doskonale. Prosty skrypt wykonujący deployment, podpięcie tego pod GUI i już w przeciągu kilku minut możemy w razie potrzeby wystawić aktualną wersję oprogramowania na zewnątrz.

Wersjonowanie

Github githubem ale co swoje to swoje :) Nie do przecenienia jest posiadanie własnego serwera obsługującego zarówno SVNa jak i GITa. Dla mnie było to naturalne połączenie jeśli chodzi o dwie poprzednie funkcjonalności. Dzięki temu że wersjonuję swoje projekty na swoim serwerze, mogę momentalnie po zatwierdzeniu wersji uruchomić testy lub dokonać deploymentu wersji produkcyjnej. Takie rozwiązanie bardzo oszczędza czas.

Backup

Nie boję się przyznać, że jestem pedantem jeśli chodzi o bezpieczeństwo danych. Wersjonowanie, codzienna kopia najważniejszych danych, cotygodniowy backup całości danych + raz na miesiąc kopia na DVD. W wykonywaniu codziennych pomaga odpowiednia ilość miejsca na serwerze i odpowiedni soft. DejaDup (lub samo duplicity) + serwer = doskonałe środowisko do codziennych automatycznych kopii. Jeśli mamy dużo danych, to pierwsza kopia powinna być robiona w sieci lokalnej (czas przesyłu danych), lecz każda kolejna jest różnicowa, więc ilość danych do przesłania będzie stosunkowo niewielka. Ważną zaletą takiego rozwiązania jest też to, że w przypadku kradzieży bądź zniszczenia naszej maszyny - kopia znajduje się w bezpiecznym miejscu.

Nauka

Gdzie najlepiej testować nowe rozwiązania, jeśli nie na swoim własnym serwerze? Awaria czy też chwilowy przestój takiej maszyny, na pewno kosztują dużo mniej niż np. awaria maszyny produkcyjnej. Z drugiej strony na takim serwerze można instalować masę softu, nie martwiąc się tym, że coś pójdzie nie tak lub że dany soft zbytnio obciąży maszynę. Pierwsze kroki w świecie noSQLa stawiałem właśnie na swoim serwerze. No i nigdy nie wiadomo kiedy umiejętność stawiania własnego serwera okaże się przydatna.

Dostęp do danych

Coraz popularniejsze stają się NAS'y, ja jednak wciąż mam starą poczciwą macierz z dwoma dyskami 1TB na USB. Dostęp do tych danych stał się możliwy dzięki SSHFSowi (o czym pisałem tutaj). Dodatkową zaletą takiego rozwiązania jest to, że takie dyski (na USB) są dużo tańsze niż macierze NAS.

Pobieranie/wysyłanie danych

Niejednokrotnie potrzebowałem pobrać jakąś większą partię danych z internetu. Screen + Wget lub ew. Torrentflux nadają się do tego idealnie. A w drugą stronę? 2GB zdjęć z Erasmusa samo się do Turcji nie wysłało ;) Paczki po 500MB + wystawienie zdjęć przez HTTP z Basic Authentication i problem z głowy.

Spamowanie (oraz inne czasochłonne zadania)

No może niezupełnie ;) ale newsletter sam się nie wyśle. Aktualnie (mimo że stoi na VPSie) - Senpuu nie ma własnego serwera poczty - korzysta z rozwiązania od Google, które umożliwia wysłanie max 200 emaili dziennie. W związku z tym - 24 tysiące emaili rozsyłamy za pośrednictwem mojego serwera. Trwa to około 1,5 - 2 godzin. Czasem wykonuję też inne czasochłonne zadania - jak np. próby "zamulenia" tworzonych przeze mnie systemów (żeby sprawdzić jak duży nacisk wytrzymają). Takie testy niejednokrotnie trwają po parę dni - w związku z czym wykonywanie ich z poziomu laptopa byłoby wręcz niemożliwe.

Podsumowanie

Oczywiście są to tylko niektóre z zalet własnej maszyny. Usilnie zachęcam każdą osobę mającą możliwość do postawienia własnego serwera deweloperskiego i czerpania z niego pełnymi garściami.

Ruby, DRMAA, DRMAA4Ruby, Sun Grid + Monte Carlo integration

Wstęp

Przyjęło się, że wykonywanie obliczeń z pomocą gridów jest domeną języków C, C++ i Javy. To samo tyczy się zarządzania gridem. Okazuje się jednak, że z powodzenie można do tego wykorzystać także Rubiego. Jako przykład wykorzystamy całkowanie metodą Monte Carlo, zaś zarządzać gridem będziemy za pośrednictwem DRMAA'y oraz biblioteki DRMAA4Ruby.

DRMAA4Ruby

DRMAA4Ruby jest to interfejs do języka Ruby - który jest bindingiem do DRMAA'y dla C. Dzięki niemu możemy w bardzo prosty sposób rozdzielać zadania na mniejsze fragmenty i kolejkować je na gridzie.

DRMAA4Ruby ma jednak także swoje wady - nowa wersja tej biblioteki nie jest kompatybilna z wciąż popularną wersją Rubiego - 1.8.7. Występują także problemy z odnalezieniem i użyciem modułu libdrmaa.so. Na szczęście - oba te problemy nie są trudne do rozwiązania.

DRMAA4Ruby znajdziemy tutaj. W katalogu lib/ znajdują się pliki:

  • drmaa.rb
  • original-drmaa.rb

Jeśli korzystasz z Rubiego w wersji 1.8.7 (lub starszej), zamiast ładować drmaa.rb musisz ładować original-drmaa.rb.

Drugim z problemów jest błąd podczas ładowania pliku libdrmaa.so. Ruby nie może go namierzyć. Aby to zmienić, wystarczy podać dokładną ścieżkę do tegoż pliku (można to zrobić używając polecenia find w powłoce). Podmiany ścieżki na bezwzględną dokonujemy w linijce 191 (dla original-drmaa.rb):


@libdrmaa = DL.dlopen('/sciezka/bezwzgledna/libdrmaa.so')

Po tym zabiegu biblioteka powinna ładować się (i działać bez problemu).

Monte Carlo

Nie będę tutaj opisywał jak dokładnie działa całkowanie metodą Monte Carlo - zainteresowanych odsyłam do wikipedii. Poniżej przedstawię jednak kod realizujący taką metodę całkowania (oczywiście w Rubym):


xp = ARGV[0].to_f
xk = ARGV[1].to_f
parts = ARGV[2].to_i
part = ENV['SGE_TASK_ID'].to_i
STEPS=10_000

step = (xk-xp)/parts

p = xp+step*(part-1)
k = p+step

xp = p; xk = k

f = lambda { |x| x*x+3 }

dx=(xk-xp).to_f
s = 0

STEPS.times { s += f.call(xp+rand*dx) }
s = dx*s/STEPS

print s

Kod nie jest skomplikowany, a dla nas ważne jest to jak go odpalamy. Przyjmuje on 3 parametry jawne:

  • początek przedziału całkowania
  • koniec przedziału całkowania
  • ilość części na jakie będziemy dzielić nasze całe całkowanie

Co znaczy "ilość części"? Musimy pamiętać, że obliczenia wykonywane będą na gridzie, z tego względu musimy nasze zadanie rozbić na subzadania. Każde subzadanie będzie liczyć "kawałek" całki, po czym całość zsumujemy. Nasz kod ma jeszcze jeden parametr - brany ze zmiennej środowiskowej SGE_TASK_ID. Tym parametrem jest informacja który kawałek ma liczyć dana instancja programu. Po ustawieniu zmiennych nie pozostaje nam nic innego jak policzyć i wyświetlić wynik. To by było na tyle jeśli chodzi o samo zadanie wywoływane na gridzie. Nie ma tu wielkiej filozofii - wystarczy pamiętać o tym, że duże zadanie rozbijamy na mniejsze subtaski które są względem siebie niezależne (mogą funkcjonować autonomicznie).

DRMAA + Ruby + JobTemplate

Pora stworzyć kod który doda do grida nasze zadanie, a następnie zbierze wyniki i wyświetli. Na początek wymagane biblioteki i dwie stałe:


$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))

require 'original-drmaa.rb'

STD_PATH = "/home/path/"
SOURCE_PATH = "/home/path/"

  • STD_PATH - gdzie ma domyślnie zapisywać wyniki obliczeń z poszczególnych instancji zadania
  • SOURCE_PATH - gdzie znajduje się nasze zadanie które chcemy uruchomić

Dalej zdefiniujemy sobie JobTemplate - który odwzorowuje nasze zadanie w gridzie:

class Carlos < DRMAA::JobTemplate
  def initialize
	  super
	  self.command = "/usr/bin/ruby"
	  self.arg = ["#{SOURCE_PATH}/monte_carlo.rb", 0, 100, 10]
	  self.stdout = ":#{STD_PATH}/"
	  self.join = true
	  end
end

Warto zauważyć, że jako polecenie nie podajemy pliku z kodem ale ścieżkę do Rubiego. Plik z kodem stanowi pierwszy parametr (da się to obejść całkiem prosto - jak? Pozostawiam to wam).
Następnie podajemy 3 parametry:

  1. o - początek przedziału całki
  2. 100 - koniec przedziału
  3. 10 - ilość subzadań na gridzie

Uruchamiamy nasze zadanie (opis metod znajduje się na stronie DRMAA'y - można skorzystać z opisów do Javy, ponieważ interfejs jest praktycznie ten sam):

session = DRMAA::Session.new

jobs = []
session.run_bulk(Carlos.new, 1, 10).each { |job| jobs << job }

Zanim przejdziemy do zbierania i sumowania wyników, dla przypomnienia wspomnę, że wyniki z subzadań zapisywane są przez DRMAA'ę - tak więc nie musimy się martwić o ich zapis, tak długo, jak długo drukujemy coś na ekranie. DRMAA numeruje pliki w mniej więcej taki sposób:

  1. Nazwa polecenia (u nas ruby)
  2. .o (tak - kropka i literka "o")
  3. ID zadania
  4. . (kropka)
  5. task ID

Np: ruby.o15.61

Wiedząc o tym i majac w tablicy jobs informacje o kolejnych taskach - możemy poiterować pliki i odczytać z nich wyniki. Następnie wystarczy suma i zwrócenie wyniku całościowego:

result = 0
session.wait_each{ |info|
	if ! info.wifexited?
		puts "failed: " + info.job
	else
		file_name = "#{STD_PATH}/ruby.o#{info.job}"
		begin
			File.open(file_name) do |inl|
				while (line = inl.gets)
					result += line.to_f
				end
			end
			File.delete(file_name)
		rescue
		end
	end
}
print result

I to by było na tyle - z pomocą tego kodu możemy policzyć na gridzie całkę metodą Monte Carlo. Całość kodu managera:

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))

require 'original-drmaa.rb'

STD_PATH = "/home/path"
SOURCE_PATH = "/home/path"

class Carlos < DRMAA::JobTemplate
  def initialize
	  super
	  self.command = "/usr/bin/ruby"
	  self.arg = ["#{SOURCE_PATH}/monte_carlo.rb", 0, 100, 10]
	  self.stdout = ":#{STD_PATH}/"
	  self.join = true
	  end
end


session = DRMAA::Session.new

jobs = []
session.run_bulk(Carlos.new, 1, 10).each { |job| jobs << job }

result = 0
session.wait_each{ |info|
	if ! info.wifexited?
		puts "failed: " + info.job
	else
		file_name = "#{STD_PATH}/ruby.o#{info.job}"
		begin
			File.open(file_name) do |inl|
				while (line = inl.gets)
					result += line.to_f
				end
			end
			File.delete(file_name)
		rescue
		end
	end
}
print result

Copyright © 2025 Closer to Code

Theme by Anders NorenUp ↑