Aktualizowanie aplikacji internetowej bez żadnych przestojów

31

To aplikacja PHP. Jak zminimalizować czas przestoju podczas aktualizacji całej bazy kodu?

Simon Hayter
źródło

Odpowiedzi:

44

To, co zazwyczaj robimy, w pracy to:

  • przed aktualizacją katalog główny serwera to:
    • w /www/app-2009-09-01
    • ale jest dostępny za pośrednictwem dowiązania symbolicznego, zwanego /www/application
  • umieściliśmy całą nową bazę kodu /www/app-2009-09-08
  • gdy cała baza kodu będzie już dostępna:
    • usuwamy stary symboliczny link
    • tworzymy nowy symboliczny link, nadal nazywany /www/application, ale który wskazuje na nowe źródła:/www/app-2009-09-08
  • przeładowujemy apache, aby wymusić uwzględnienie modyfikacji.

Cały ten proces odbywa się za pomocą automatycznego skryptu (jedyną nieautomatyczną rzeczą jest uruchomienie go w razie potrzeby). To znaczy :

  • Wszystko idzie szybko (szczególnie zmiana dowiązania symbolicznego, co jest ważną częścią)
  • Bez ryzyka popełnienia błędu: skrypt został dobrze przetestowany i działa od miesięcy / lat


Kolejną zaletą tego symbolicznego precedensu dla łącza symbolicznego jest to, że bardzo łatwo jest „wycofać” aktualizację, jeśli zauważymy katastrofalny błąd dopiero po uruchomieniu nowej wersji źródeł: musimy tylko przełączyć linki symboliczne z powrotem.

Oczywiście nie przeszkadza to w testowaniu nowej wersji na serwerze testowym przed wprowadzeniem jej do produkcji - ale kto wie ... Czasami istnieje naprawdę duży błąd, którego nikt nie był w stanie zobaczyć testowanie :-(
Na przykład, ponieważ nie wykonuje się regularnych testów obciążenia na maszynie pomostowej.
(Widziałem, że funkcja „cofania” używała czegoś takiego jak 4 lub 5 razy w ciągu 3 lat - za każdym razem uratował dzień - i strony internetowe ^^)


Oto jakiś szybki przykład: załóżmy, że mam VirtualHost w mojej konfiguracji Apache:

<VirtualHost *>
        ServerName example.com
        DocumentRoot /www/application
        <Directory /www/application>
            # Whatever you might need here (this example is copy-pasted from a test server and test application ^^ )
            Options Indexes FollowSymLinks MultiViews +SymLinksIfOwnerMatch
            AllowOverride All
            php_value   error_reporting 6135
            php_value short_open_tag  on
        </Directory>
</VirtualHost>

Dość „standardowy” ... Jedyne, co nie jest, /www/applicationto nie jest prawdziwy katalog: to tylko symboliczny link do bieżącej wersji źródeł.
Co oznacza, że ​​po umieszczeniu źródeł na serwerze, ale jeszcze nie przełączeniu, będziesz mieć coś takiego:

root@shark:/www
# ll
total 8
drwxr-xr-x 2 root root 4096 2009-09-08 22:07 app-2009-09-01
drwxr-xr-x 2 root root 4096 2009-09-08 22:07 app-2009-09-08
lrwxrwxrwx 1 root root   19 2009-09-08 22:08 application -> /www/app-2009-09-01

Zauważ, że symlinc wskazuje na „starą wersję”

Teraz, gdy nowa wersja została całkowicie przesłana na serwer, przełączmy:

root@shark:/www
# rm /www/application
root@shark:/www
# ln -s /www/app-2009-09-08 /www/application

A teraz /www/applicationpunkty do nowej wersji źródeł:

root@shark:/www
# ll
total 8
drwxr-xr-x 2 root root 4096 2009-09-08 22:07 app-2009-09-01
drwxr-xr-x 2 root root 4096 2009-09-08 22:07 app-2009-09-08
lrwxrwxrwx 1 root root   19 2009-09-08 22:09 application -> /www/app-2009-09-08

Musimy tylko ponownie uruchomić Apache:

root@shark:/www
# /etc/init.d/apache2 restart
 * Restarting web server apache2

Trzy kroki: „ usuń łącze; utwórz nowe łącze; uruchom ponownie apache ” powinny być wykonane szybko; tj. za pomocą automatycznego skryptu, a nie przez człowieka.

Korzystanie z tego rozwiązania:

  • możesz poświęcić tyle czasu, ile potrzebujesz na przesłanie nowej wersji źródeł: apache nie będzie ich używać, dopóki symlic nie zostanie zmieniony
  • kiedy wszystko jest w porządku, po prostu przełącz dowiązanie symboliczne: przejdzie ono szybciej niż zmiana nawet 1 lub 2 plików ... Co oznacza praktycznie brak przestojów :-)

A jeśli użyję pamięci podręcznej opcode, takiej jak APC z opcją stat na 0, to może to oznaczać jeszcze mniejsze ryzyko przestoju, jak sądzę.


Oczywiście jest to „prosta” wersja - jeśli na przykład masz jakieś przesłane pliki, będziesz musiał użyć innego łącza symbolicznego, innego VirtualHost lub cokolwiek ...


Mam nadzieję, że to jest bardziej jasne :-)

Pascal MARTIN
źródło
To także rodzaj wymiany serwerów. :-)
Wim ten Brink
mod_rewrite do zarządzania dowiązaniami symbolicznymi?
@gAMBOOKa: nie: tylko kwestia DocumentRoot (lub VirtualHost DocumentRoot) Apache, czyli / www / application ;; tj. łącze symboliczne - bez względu na to, na co wskazuje.
2
Wspaniała odpowiedź. Jeszcze jedna wskazówka: możesz sprawić, że dowiązanie symboliczne nastąpi bez rozłączania go. Jak cytowano: „Trzy kroki… powinny zostać wykonane szybko, tj. Za pomocą automatycznego skryptu, a nie przez człowieka”. Polecenie mv jest operacją atomową, więc możesz utworzyć dowiązanie symboliczne, takie jak „ln -s / www / app-2011-01-28 / www / application-temp”, a następnie wykonać polecenie „mv -T / www / application-temp” / www / application ”.
1
Coś nie było objęte metodą dowiązania symbolicznego. Twoja droga działa z Apache + mod_php, ale może zawieść na lighttpd + fastcgi. W witrynie o dużym ruchu żądanie zostanie obsłużone w trakcie zamiany linku, że zależność kodu php zakończy się niepowodzeniem w przypadku wersji mieszanej.
Dennis C,
2

Czy nie możesz pobrać istniejącego kodu i przeprowadzić migrację projektu do osobnego testowego pliku php i użyć go podczas aktualizacji? Mam na myśli to, że powinieneś mieć serwer testowy i serwer produkcyjny, aby podczas aktualizacji nie było żadnych przestojów.

Społeczność
źródło
1

Skonfiguruj drugi serwer ze zaktualizowaną bazą kodów i przełączaj je tak szybko, jak to możliwe. :-)

Jeśli nie jest to możliwe, upewnij się, że twoja baza kodu jest podzielona na dziesiątki mniejszych części. Wtedy przestoje byłyby ograniczone do jednej podsekcji w tym samym czasie. Mniejsze kody są łatwiejsze do wymiany i większość z nich będzie działać bez problemów. Najpierw jednak wypróbuj to w środowisku testowym!

Wim ten Brink
źródło
Ponieważ aplikacja nie została przetestowana na pofragmentowanych modułach, może to spowodować nieoczekiwane scenariusze.
Co oznacza, że ​​będzie to na twojej liście rzeczy do zrobienia po tej aktualizacji. :-) Uczyń go bardziej modułowym i możesz aktualizować dla każdego modułu.
Wim ten Brink
1
To jest na liście rzeczy do zrobienia, ale jest celem długoterminowym. Jesteśmy młodym startupem, więc organizacja w zespole deweloperów naturalnie potrwa. = D
1

Po pierwsze, często używam i lubię metodę podobną do odpowiedzi Pascala Martina.

Inną metodą, która mi się podoba, jest użycie mojego SCM do wypchnięcia nowego kodu. Dokładny proces zależy od typu SCM (git vs svn vs ...). Jeśli używasz svn, lubię tworzyć oddział „online” lub „produkcyjny”, który kasuję jako katalog główny dokumentu na serwerze. Następnie, ilekroć chcę przesłać nowy kod z innej gałęzi / znacznika / pnia, po prostu zatwierdzam nowy kod w gałęzi „online” i uruchamiam aktualizację svn w katalogu głównym dokumentu. Pozwala to na bardzo łatwe wycofywanie zmian, ponieważ istnieje kompletny dziennik zmian tego, co poszło w górę / w dół do serwera oraz kto to zrobił i kiedy. Możesz również z łatwością uruchomić tę gałąź „online” na polu testowym, co pozwala zweryfikować aplikację, którą chcesz wypchnąć.

Proces jest podobny w przypadku git i innych stylów SCM, po prostu zmodyfikowanych, aby były bardziej naturalne dla ich stylu pracy.

Chcesz pobrać / sondować zamiast wypychać aktualizacje? Po prostu miej zadanie crona lub inny, mądrzejszy mechanizm automatycznie uruchomi aktualizację svn.

Dodatkowo: możesz również użyć tego procesu do tworzenia kopii zapasowych plików zapisanych przez aplikację na dysku. Wystarczy mieć zadanie crona lub inny mechanizm uruchamiający svn commit. Teraz tworzone są kopie zapasowe plików utworzonych przez aplikację w SCM, rejestrowane wersje itp. (Np. Jeśli użytkownik aktualizuje plik na dysku, ale chce go przywrócić, wystarczy wcisnąć starą wersję).


źródło
0

Używam również podobnego podejścia do Pascala MARTINA. Ale zamiast przesyłać wiele wersji mojej aplikacji na serwer produkcyjny, „kompilacje” trzymam za zaporą ogniową, każda w osobnym katalogu z numerem kompilacji i datą. Kiedy chcę załadować nową wersję, używam prostego skryptu, który zawiera „rsync -avh --delay-updates”. Flaga „opóźnienie = aktualizacje” spowoduje przesłanie wszystkiego (innego) do folderu tymczasowego, dopóki wszystkie aktualizacje nie będą dostępne, a następnie przeniesienie wszystkiego naraz na koniec przesyłania na właściwe ścieżki, aby aplikacja nigdy nie była w stan na wpół stary na wpół nowy. Ma taki sam efekt jak powyższa metoda, z tym wyjątkiem, że przechowuję tylko jedną wersję aplikacji na stronie produkcyjnej (najlepiej mieć tylko same niezbędne pliki na serwerze produkcyjnym, IMO).


źródło