05.31
In /dev/random ,Android ,Linux | Tags: Linux, Programowanie
Widziałem w życiu kod wielu aplikacji, deployowałem jeszcze więcej, sporo “debugowałem” (tzn. szukanie czemu to %&*# nie działa mimo że gdzie indziej na “prawie” takiej samej konfiguracji działa), parę napisałem i jest parę rzeczy które wkurzają mnie jako admina, nie mówiąc już o zmarnowanym czasie.
Nie jestem ekspertem jezeli chodzi o pisanie aplikacji (“ekspertowanie się” jest niestety popularne w internecie “pisałem 5 lat koszmarne aplikacje to zaczne blogowac jakim to jestem awsom ekspertem w PHPie i uczyć ludzi pisać jeszcze gorsze aplikacje”), ale koszmaryzm deployowania i zarządzania niektórymi appami doprowadził do tej listy ;]
DONT’s
HTACCESS!!!
Tak wiem że jest to świetne narzędzie jak nie potrafisz w swoim języku programowania wyciągnąć całej ścieżki czy nagłówka “Host” z żądania ale uwierz mi, htaccess is not the way to go.
Jedyną rzeczą w htaccessie powinno być jedno przekierowanie w stylu “wszystko co nie jest obrazkiem/css/js wyślij do aplikacji” i tyle, żadnych głupot w stylu:
RewriteRule ^about$ index.php?page=about [NC,L]
Czy jeszcze gorzej,
RewriteRule ^about$ index.php?pageid=1 [NC,L]
Czemu ? Parę powodów:
- NIE WSZYSCY UŻYWAJĄ APACHA, gdy admin chce zainstalować lighttpd czy nginxa żeby odciążyć serwer, musi męczyć się z przerabianem rewritów na inny format + robić to po każdej zmianie w aplikacji (bo programiści zdają sie uczyć składni apachowego mod_rewrite jeszcze zanim nauczą robić SQLi)
- Masz 2 punkty w których musisz coś zmieniać gdy chcesz zmienić routing adresów w aplikacji, zarówno kod aplikacji jak i htaccess
- Nie każdy programista znający dobrze język w którym programujesz będzie znał htaccess, wiec zamiast robić użyteczną pracę będzie się go uczył ;]
A czym zastąpić takie rewrity ? W najprostrzym wypadku wsadzamy wszystko “nie dynamiczne” do katalogu static/ a resztę przekierowujemy na rdzeń aplikacji. Od razu wiadomo co backupować, aplikacja jest oddzielona od swoich danych, rewrity są nieskomplikowane a admin może potem łatwo wydzielić katalog ze statyczną częścią aplikacji.
SQL Injection
Naprawdę, obrona przed SQL injection jest tak łatwa że nikt nie powinien popełniac tego typu błędów, niestety na studiach uczą SQL ale nie uczą bezpiecznego użycia go w aplikacji webowych.
W skrócie zamiast po prostu robić “SELECT * FROM users WHERE user = $jakas_zmienna” (co bardziej zapoznani bawią się w escapowanie danych co jest dobrą praktyką, ale zbędną w tym przypadku, może z wyjątkiem wywalaniem ‘%’ w “LIKE a = $costam”) używamy magicznej rzeczy zwanej “kwerendą z parametrem” (jeżeli dobrze tłumaczę).
Otóż jest to takie magiczne cuś w którym zamiast martwić sie o escapowanie wejścia i pisać:
$data=$db->query("SELECT * FROM users WHERE user = '$user'");
piszemy
$data=$db->query("SELECT * FROM users WHERE user = ?", $user);
Co to daje? Baza danych przypisuje $user do pierwszego znaku ‘?’ i wie że to tyczy się tylko tego jednego porównania, więc ustawienie user na “a OR 1=1″ wyszuka nam usera “a OR 1=1″ a nie zrzuci całą zawartość tabeli user
Hardcoding
W sensie “hardcodowania użytecznych zmiennych jako stałych”. Może Ci się wydawać że jak piszesz serwis tanczacechomiki.pl to nazwa strony nigdy się nie zmieni, ale czasami trzeba zrobić dev.tanczacechomiki.pl, albo dev.server/tanczacechomiki a wtedy przy dużej ilości hardkodowanych rzeczy (tu gdzieś w kodzie tu coś w bazie itp.) sprowadza sie to do ostrego mailingu miedzy adminem a programistą/ami i stratą czasu.
Tak samo ścieżki, $imgpath="./img"; zamiast $imgpath="/var/www/img";, dostęp do bazy i wszystko inne co może się zmienić jak np lista newsów na głównej
Sesje w bazie
Tak wiem że to świetny sposób zapewnienia że sesja będzie jednocześnie na wszystkich serwerach aplikacji, ale póki nie masz zrobionego ładnego cachowania (memcached czy coś innego) i update-tylko-gdy-sie-zmienia-i-to-niezbyt-czesto-a-nie-co-refresh-strony sesji to każdy request = 1 select do bazy (która jest współdzielona pomiędzy appserwerami) zamiast 1 select = request na lokalny dysk. A z trzymaniem sesji usera do konkretnego serwera już admin sobie poradzi (np. haproxy).
DO’s
Używaj GITa!
Lub innego systemu kontroli wersji (najlepiej rozproszonego, darmowy backup i szybszy ;] ) do WSZYSTKIEGO (może oprócz obrazków dodanych przez userów). Im lepiej znasz swój VCS (version control system) tym wygodniej, szybiej i efektywniej będziesz go używał. Niech każdy programers podpisuje się swoim userem i emailem (żeby nie było commitów podpisanych kurczaczek@localhost), zrób brancha stable (z tym co aktualnie jest na prod) i tam commituj PRZETESTOWANE zmiany, robienie brancha na każdy większy feature i bug to też bardzo dobry pomysł.
Flow typu branch dev (tu wchodza wszystkie latest and greatest features) -> testing (testy przed wydaniem nowej wersji) -> stable (wersja stabilna czyli to co jest zdeployowane lub będzie zdeployowane) zwykle też dobrze sie sprawdza, w takim układzie:
- bugfixy do stable robione są jako branch wychodzący ze stable i mergowany do stable gdy przejdzie testy i bug
- w testing naprawiane są tylko bugi przed wejściem do stable
- w dev wchodzą nowe ficzery i bugfixy do tych ficzerów
Nawet gdy piszesz coś sam, zrobienie brancha “co jest na produkcji” umożliwia łatwe porównanie zmian (git diff stable)
Umożliwia to łatwe cofanie się w wypadku pojawienia się bugu na produkcji i bugfixy dodane w stable można łatwo wrzucić do reszty, wystarczy “git merge stable”.
Rozdziel pliki statyczne od reszty
Zrób katalog static/, lub po prostu s/ na wszystko co nie jest dynamiczne, ułatwia to rewrity i ew. rozdzielenie aplikacji na serwer serwujący pliki statyczne i serwer aplikacji. Jeszcze lepiej gdy rozdzielisz je na “static forever” (czyli css/js/obrazki należące do layoutu strony) i “user generated” czyli awatary, obrazki dodane przez userów itp.
Cache i DB master-slave
To już gdy aplikacja ma/jest popularna, rozdzielenie zapytań do bazy na to “do odczytu” i “do zapisu” (tzn. możliwość podania innej bazy do zapisu i do odczytu) znacznie ułatwia skalowania takiego np. MySQLa (tam postawienie mastera + 1 lub więcej slavów jest proste), tak samo dobrze przemyślane cachowanie (chociażby chyba najprostrzy w użyciu memcache) potrafi obciąć load serwera DB parokrotnie (taki query_cache w MySQLu się nie sprawdza przy większym ruchu bo każdy zapis do tabeli czyści cache). Ale to już jak masz naprawdę spory serwis :). Chyba że aplikacja korzysta z którejś z baz NoSQL która ma “wbudowane” skalowanie, wtedy już jest to załatwione od razu, chociaż i wtedy warto przystosować app do ew. dodania cache np. zrobić wrapper na funkcę odpytującą bazę z dodatkowym parametrem określającym ile sek. wynik może być cachowany (w najprostrzym przypadku)
Im mniej zapisów tym lepiej
Odczyty się skalują, zapisy nie, zwłaszcza jeżeli chodzi o bazę, często zrobienie jednego dodatkowego odczytu żeby uniknąć zapisu (tzn “zapisz tylko jeżeli coś się zmieniło”) jest dużo “tańsze”, zwłaszcza przy korzystaniu z cache. Ale o samym cachowaniu możnaby napisać książkę
Jeden skrypt deployujący/budujący aplikację
Tu chyba nie trzeba wyjaśniać. Łatwo wprowadzić nową osobę, łatwo przeprowadzać testy (nowa wersja ? odpal skrypt i idź na kawę). Tylko musi być dobrze skomentowany(szczególnie co niektóre regexpy ;) ) żeby w razie czego można było odtworzyć kroki skryptu.
I to tyle ;]. Może kiedyś powstanie część 2 ;]
Edit: Parę poprawek w “Cache i DB”


9 ResponsesLeave a comment ?
“Odczyty się skalują, zapisy nie”
To jest prawdziwe tylko przy podstawowych konfiguracjach. Dla np. MySQL-i możesz mieć rozbite tablice na różne urządzenia więc można skalować.
Dla noSQL-owych baz jak mongoDB możesz wzrost wydajności jest praktycznie liniowy wraz z dokładaniem kolejnych maszyn. Więc jest skalowalne.
“Różne urządzenia” ale dalej na jednym serwerze, często taniej jest wsadzić 3 pudełka i zrobić MySQL master-slave niż kupić jedno o 3x większej wydajności, już nie mówiąc o tym że gdy padnie jedno z tych 3 pudełek zawsze zostają 2 ;].
Poza tym Master-Slave ma też parę innych zalet, jak możliwość robienia backupu bez lockowania głównej bazy (odłącz slave, backup, podłacz slave), czy właśnie łatwy failover. W przypadku “jednego dużego mocnego serwera” musisz i tak mieć drugi o podobnej mocy w przypadku awarii.
A skoro masz już 2 równie dobrze drugi może pracować jako slave ;]
A jak używasz “czegoś” (czy to NoSQL czy warstwa między bazą a aplikacją) co potrafi zrobić to za Ciebie to jak najbardziej, tyle że (My)SQL jest dalej najpopularniejszy a zrobienie czegoś w stylu if (SELECT) send_here() else send somewhere_else() nie jest zbyt trudne
@XANi
Backup MySQL najlepiej robić na lvm:
1. LOCK
2. snapshot
3. UNLOCK
Potem podmontowujesz snapshot i robisz backup na fs :).
Nie do końca. Snapshot LVMowy “kosztuje” sporo wydajności gdy liczba zapisów jest duża (każdy sektor który jest zapisywany po raz pierwszy od zrobienia snapshota musi być najpierw odczytany, potem zapisany na snapshocie i dopiero potem “nowe” dane są zapisane na LV.
Ofc jak masz tylko jeden serwer to jest to najlepsze chyba rozwiazanie jezeli chcesz uniknąć downtime.
Z tym że gdy masz jakieś długie tranzakcje to sam LOCK może zając trochę
Całkiem zacny zestaw porad. Widać, że pisany ręką praktyka, któremu nie raz aplikacja zalazła za skórę :)
Do porady o htaccess chciałbym tylko dodać, że najlepiej byłoby zupełnie z niego zrezygnować. W przypadku Apache każdy request do www oznacza przecież poszukiwanie htaccessa. I to nie tylko w katalogu skryptu, ale także we wszystkich powyżej niego, aż do roota.
Mi wystarczyło tylko raz zrobić trace apache, żeby zrezygnować z tego wygodnego narzędzia.
Tak ale często (zwłaszcza w przypadku PHP) potrzebny jest do “ładnych linków”, a gdy reguły są proste w stylu “wszystko w /static to static a wszystko inne wyślij do aplikacji” to gdy przychodzi czas “działa wolno, optymalizujemy” przepisanie tego pod składnie nginxa czy lighttpd jest łatwe (czy chociażby zamiast w htaccess wrzucenie tego do configu vhosta i wyłaczenie szukania .htaccess)
Nie dodałem, że właśnie o to ostatnie rozwiązanie mi chodziło. Przerzucenie spraw “htaccessowych” do konfiguracji apache dość zgrabnie rozwiązuje problem.
Muszę się jeszcze DDDUUUŻŻŻOOO nauczyć :/ Jeżeli będziesz kiedyś likwidował bloga to albo daj znać wcześniej albo poproszę backup bo całkiem sensowne kompendium wiedzy dla rookie adminów zgromadziłeś.
Ta, np. nauka “na ile sposobów można napisać powaloną aplikację” zdaje nigdy się nie kończyć ;]. W takim tempie za 3 lata znajdę aplikację wymagającą instalacji przy pełni księżyca, albo backupy działające tylko gdy w telewizji akuralt leci “Moda na sukces”