Jak zoptymalizować infrastrukturę produkcyjną Node.js: najlepsze praktyki

Przedmowa

W Forward Email spędziliśmy lata doskonaląc konfigurację naszego środowiska produkcyjnego Node.js. Ten kompleksowy przewodnik przedstawia nasze sprawdzone w boju najlepsze praktyki wdrażania Node.js w środowisku produkcyjnym, koncentrując się na optymalizacji wydajności, monitorowaniu oraz wnioskach, jakie wyciągnęliśmy, skalując aplikacje Node.js do obsługi milionów transakcji dziennie.

Nasza rewolucja w optymalizacji wydajności pojedynczego rdzenia o 573%

Po migracji z procesorów Intel na AMD Ryzen osiągnęliśmy 573% wzrost wydajności w naszych aplikacjach Node.js. Nie była to jedynie drobna optymalizacja – radykalnie zmieniła ona sposób działania naszych aplikacji Node.js w środowisku produkcyjnym i pokazuje, jak ważna jest optymalizacja wydajności pojedynczego rdzenia dla każdej aplikacji Node.js.

Tip

W przypadku najlepszych praktyk wdrażania Node.js w środowisku produkcyjnym, wybór sprzętu ma kluczowe znaczenie. Wybraliśmy hosting DataPacket ze względu na dostępność procesorów AMD Ryzen, ponieważ wydajność pojedynczego rdzenia jest kluczowa dla aplikacji Node.js, ponieważ wykonywanie JavaScript jest jednowątkowe.

Dlaczego optymalizacja wydajności pojedynczego rdzenia ma znaczenie dla Node.js

Migracja z procesorów Intel na procesory AMD Ryzen przyniosła następujące efekty:

Wzrost wydajności był tak znaczący, że obecnie uważamy procesory AMD Ryzen za niezbędne dla każdego poważnego wdrożenia produkcyjnego Node.js, niezależnie od tego, czy uruchamiasz aplikacje internetowe, API, mikrousługi, czy jakiekolwiek inne obciążenie Node.js.

Aby uzyskać więcej informacji na temat naszych opcji infrastrukturalnych, sprawdź:

Konfiguracja środowiska produkcyjnego Node.js: Nasz zestaw technologii

Nasze najlepsze praktyki wdrażania Node.js w środowisku produkcyjnym obejmują przemyślane wybory technologiczne oparte na wieloletnim doświadczeniu w produkcji. Oto, z czego korzystamy i dlaczego te wybory mają zastosowanie w każdej aplikacji Node.js:

Menedżer pakietów: pnpm dla wydajności produkcyjnej

Czego używamy: pnpm (wersja przypięta)

Wybraliśmy pnpm zamiast npm i yarn do konfiguracji naszego środowiska produkcyjnego Node.js, ponieważ:

  • Krótszy czas instalacji w potokach CI/CD
  • Efektywność wykorzystania miejsca na dysku dzięki twardym linkom
  • Ścisłe rozwiązywanie zależności zapobiegające powstawaniu zależności fantomowych
  • Lepsza wydajność we wdrożeniach produkcyjnych

Note

W ramach naszych najlepszych praktyk wdrażania Node.js w środowisku produkcyjnym przypinamy dokładne wersje kluczowych narzędzi, takich jak pnpm, aby zapewnić spójne działanie we wszystkich środowiskach i na komputerach członków zespołu.

Szczegóły implementacji:

Framework internetowy: Koa dla nowoczesnej produkcji Node.js

Czego używamy:

Wybraliśmy Koa zamiast Express dla naszej infrastruktury produkcyjnej Node.js ze względu na nowoczesną obsługę async/await i bardziej przejrzystą strukturę oprogramowania pośredniczącego. Nasz założyciel, Nick Baugh, przyczynił się do rozwoju zarówno Express, jak i Koa, zapewniając nam dogłębną wiedzę na temat obu frameworków pod kątem zastosowań produkcyjnych.

Wzorce te mają zastosowanie niezależnie od tego, czy tworzysz interfejsy API REST, serwery GraphQL, aplikacje internetowe czy mikrousługi.

Nasze przykłady implementacji:

Przetwarzanie zadań w tle: Bree dla niezawodności produkcji

Czego używamy: harmonogram bree

Stworzyliśmy i utrzymujemy Bree, ponieważ istniejące harmonogramy zadań nie spełniały naszych potrzeb w zakresie obsługi wątków roboczych i nowoczesnych funkcji JavaScript w produkcyjnych środowiskach Node.js. Dotyczy to każdej aplikacji Node.js, która wymaga przetwarzania w tle, zaplanowanych zadań lub wątków roboczych.

Nasze przykłady implementacji:

Obsługa błędów: @hapi/boom dla niezawodności produkcji

Czego używamy: @hapi/boom

Używamy @hapi/boom do ustrukturyzowanej obsługi błędów w naszych aplikacjach produkcyjnych Node.js. Ten wzorzec sprawdza się w każdej aplikacji Node.js, która wymaga spójnej obsługi błędów.

Nasze przykłady implementacji:

Jak monitorować aplikacje Node.js w środowisku produkcyjnym

Nasze podejście do monitorowania aplikacji Node.js w środowisku produkcyjnym ewoluowało przez lata uruchamiania aplikacji na dużą skalę. Wdrażamy monitorowanie na wielu warstwach, aby zapewnić niezawodność i wydajność każdego typu aplikacji Node.js.

Monitorowanie produkcji Node.js na poziomie systemu

Nasza podstawowa implementacja: helpers/monitor-server.js

Czego używamy: node-os-utils

Nasze progi monitorowania produkcji (na podstawie naszego rzeczywistego kodu produkcyjnego):

  • Limit rozmiaru sterty 2 GB z automatycznymi alertami
  • Próg ostrzegawczy 25% wykorzystania pamięci
  • Próg ostrzegawczy 80% wykorzystania procesora
  • Próg ostrzegawczy 75% wykorzystania dysku

Warning

Te progi działają dla naszej konkretnej konfiguracji sprzętowej. Podczas wdrażania monitorowania produkcji Node.js, zapoznaj się z naszą implementacją monitor-server.js, aby zrozumieć dokładną logikę i dostosować wartości do swojej konfiguracji.

Monitorowanie na poziomie aplikacji dla środowiska produkcyjnego Node.js

Nasza klasyfikacja błędu: helpers/is-code-bug.js

Ten pomocnik rozróżnia:

  • Rzeczywiste błędy w kodzie wymagające natychmiastowej uwagi
  • Błędy użytkownika, które są oczekiwanym zachowaniem
  • Awarie usług zewnętrznych, na które nie mamy wpływu

Ten wzorzec ma zastosowanie w przypadku dowolnej aplikacji Node.js — aplikacji internetowych, interfejsów API, mikrousług i usług działających w tle.

Nasza implementacja rejestrowania: helpers/logger.js

Wdrażamy kompleksową redagowanie pól w celu ochrony poufnych informacji, jednocześnie zachowując przydatne możliwości debugowania w naszym środowisku produkcyjnym Node.js.

Monitorowanie specyficzne dla aplikacji

Nasze implementacje serwerowe:

Monitorowanie kolejek: Wprowadzamy limity kolejek 5 GB i 180-sekundowe limity czasu dla przetwarzania żądań, aby zapobiec wyczerpaniu zasobów. Te wzorce dotyczą każdej aplikacji Node.js z kolejkami lub przetwarzaniem w tle.

Monitorowanie produkcji Node.js za pomocą kontroli stanu PM2

Udoskonaliliśmy konfigurację naszego środowiska produkcyjnego Node.js, wykorzystując PM2 przez lata doświadczeń w produkcji. Nasze kontrole stanu PM2 są niezbędne do utrzymania niezawodności każdej aplikacji Node.js.

Nasz system kontroli stanu PM2

Nasza podstawowa implementacja: jobs/check-pm2.js

Nasze monitorowanie produkcji Node.js z kontrolą stanu PM2 obejmuje:

  • Uruchamia się co 20 minut poprzez harmonogram cron
  • Wymaga minimum 15 minut sprawności przed uznaniem procesu za prawidłowy
  • Sprawdza stan procesu i zużycie pamięci
  • Automatycznie restartuje nieudane procesy
  • Zapobiega pętlom restartów poprzez inteligentne sprawdzanie stanu

Caution

Zgodnie z najlepszymi praktykami wdrażania Node.js w środowisku produkcyjnym, wymagamy co najmniej 15 minut nieprzerwanej pracy, zanim uznamy proces za sprawny, aby uniknąć pętli restartów. Zapobiega to kaskadowym awariom, gdy procesy mają problemy z pamięcią lub innymi problemami.

Nasza konfiguracja produkcji PM2

Konfiguracja naszego ekosystemu: Zapoznaj się z plikami startowymi naszego serwera w celu konfiguracji środowiska produkcyjnego Node.js:

Wzorce te mają zastosowanie niezależnie od tego, czy uruchamiasz aplikacje Express, serwery Koa, interfejsy API GraphQL czy dowolną inną aplikację Node.js.

Automatyczne wdrażanie PM2

Wdrażanie PM2: ansible/playbooks/node.yml

Całą konfigurację PM2 automatyzujemy za pomocą Ansible, aby zapewnić spójne wdrożenia produkcyjne Node.js na wszystkich naszych serwerach.

System obsługi i klasyfikacji błędów produkcyjnych

Jedną z naszych najważniejszych praktyk wdrażania Node.js w środowisku produkcyjnym jest inteligentna klasyfikacja błędów, która ma zastosowanie w każdej aplikacji Node.js:

Nasza implementacja isCodeBug dla produkcji

Źródło: helpers/is-code-bug.js

Ten pomocnik zapewnia inteligentną klasyfikację błędów dla aplikacji Node.js w środowisku produkcyjnym, aby:

  • Priorytetyzacja rzeczywistych błędów nad błędami użytkownika
  • Poprawa reakcji na incydenty poprzez skupienie się na rzeczywistych problemach
  • Zmniejszenie zmęczenia alertami z powodu przewidywanych błędów użytkownika
  • Lepsze zrozumienie problemów generowanych przez aplikację w porównaniu z problemami generowanymi przez użytkownika

Ten wzorzec sprawdza się w przypadku każdej aplikacji Node.js — niezależnie od tego, czy tworzysz witryny e-commerce, platformy SaaS, interfejsy API czy mikrousługi.

Integracja z naszym rejestrowaniem produkcji

Nasza integracja rejestratora: helpers/logger.js

Nasz rejestrator używa isCodeBug do określania poziomów alertów i redagowania pól, dzięki czemu otrzymujemy powiadomienia o rzeczywistych problemach, jednocześnie filtrując szum w naszym środowisku produkcyjnym Node.js.

Dowiedz się więcej o naszych wzorcach obsługi błędów:

Zaawansowane debugowanie wydajności z v8-profiler-next i cpupro

Używamy zaawansowanych narzędzi profilujących do analizy migawek sterty i debugowania problemów z OOM (brak pamięci), wąskich gardeł wydajnościowych oraz problemów z pamięcią Node.js w naszym środowisku produkcyjnym. Narzędzia te są niezbędne w przypadku każdej aplikacji Node.js, w której występują wycieki pamięci lub problemy z wydajnością.

Nasze podejście do profilowania w środowisku produkcyjnym Node.js

Narzędzia, które polecamy:

  • v8-profiler-next – Do generowania migawek sterty i profili procesora
  • cpupro – Do analizowania profili procesora i migawek sterty

Tip

Używamy v8-profiler-next i cpupro razem, aby stworzyć kompletny proces debugowania wydajności dla naszych aplikacji Node.js. Ta kombinacja pomaga nam identyfikować wycieki pamięci, wąskie gardła wydajnościowe i optymalizować nasz kod produkcyjny.

Jak wdrażamy analizę migawek sterty

Nasza implementacja monitorowania: helpers/monitor-server.js

Nasz monitoring produkcji obejmuje automatyczne generowanie migawek sterty w przypadku przekroczenia progów pamięci. Pomaga nam to debugować problemy z brakiem pamięci (OOM), zanim spowodują one awarie aplikacji.

Kluczowe wzorce implementacji:

  • Automatyczne migawki, gdy rozmiar stosu przekroczy próg 2 GB
  • Profilowanie oparte na sygnałach do analizy na żądanie w środowisku produkcyjnym
  • Zasady przechowywania do zarządzania pamięcią masową migawek
  • Integracja z naszymi zadaniami czyszczenia w celu automatycznej konserwacji

Przepływ pracy debugowania wydajności

Zapoznaj się z naszą rzeczywistą implementacją:

Do analizy migawek sterty:

  1. Zainstaluj v8-profiler-next do generowania migawek
  2. Użyj cpupro do analizy wygenerowanych migawek
  3. Wdróż progi monitorowania podobne do naszego monitor-server.js
  4. Skonfiguruj automatyczne czyszczenie do zarządzania pamięcią masową migawek
  5. Utwórz procedury obsługi sygnałów do profilowania na żądanie w środowisku produkcyjnym

Do profilowania procesora:

  1. Generuj profile procesora w okresach dużego obciążenia
  2. Analizuj za pomocą cpupro w celu identyfikacji wąskich gardeł
  3. Skup się na ścieżkach krytycznych i możliwościach optymalizacji
  4. Monitoruj przed/po poprawkach wydajności

Warning

Generowanie migawek sterty i profili procesora może mieć wpływ na wydajność. Zalecamy wdrożenie ograniczania przepustowości i włączanie profilowania tylko podczas badania konkretnych problemów lub w okresach konserwacji.

Integracja z naszym monitorowaniem produkcji

Nasze narzędzia profilowania integrują się z naszą szerszą strategią monitorowania:

  • Automatyczne wyzwalanie na podstawie progów pamięci/procesora
  • Integracja alertów w przypadku wykrycia problemów z wydajnością
  • Analiza historyczna w celu śledzenia trendów wydajności w czasie
  • Korelacja z metrykami aplikacji w celu kompleksowego debugowania

Dzięki temu podejściu udało nam się zidentyfikować i rozwiązać problemy z wyciekami pamięci, zoptymalizować ścieżki często występującego kodu i utrzymać stabilną wydajność w naszym środowisku produkcyjnym Node.js.

Bezpieczeństwo infrastruktury produkcyjnej Node.js

Wdrażamy kompleksowe zabezpieczenia naszej infrastruktury produkcyjnej Node.js poprzez automatyzację Ansible. Te praktyki dotyczą każdej aplikacji Node.js:

Bezpieczeństwo na poziomie systemu dla środowiska produkcyjnego Node.js

Nasza implementacja Ansible: ansible/playbooks/security.yml

Nasze kluczowe środki bezpieczeństwa dla środowisk produkcyjnych Node.js:

  • Wyłączona wymiana, aby zapobiec zapisywaniu poufnych danych na dysku
  • Wyłączona funkcja zrzutu pamięci, aby zapobiec zrzutom pamięci zawierającym poufne informacje
  • Zablokowana pamięć USB, aby zapobiec nieautoryzowanemu dostępowi do danych
  • Dostrajanie parametrów jądra pod kątem bezpieczeństwa i wydajności

Warning

Podczas wdrażania najlepszych praktyk wdrażania produkcyjnego Node.js, wyłączenie wymiany może spowodować zamknięcie aplikacji z powodu braku pamięci, jeśli przekroczy ona dostępną pamięć RAM. Uważnie monitorujemy wykorzystanie pamięci i odpowiednio dobieramy rozmiar naszych serwerów.

Bezpieczeństwo aplikacji dla aplikacji Node.js

Nasza redakcja pola dziennika: helpers/logger.js

Usuwamy wrażliwe pola z logów, w tym hasła, tokeny, klucze API i dane osobowe. Chroni to prywatność użytkowników, jednocześnie zachowując możliwości debugowania w dowolnym środowisku produkcyjnym Node.js.

Automatyzacja bezpieczeństwa infrastruktury

Nasza kompletna konfiguracja Ansible dla środowiska produkcyjnego Node.js:

Nasza treść dotycząca bezpieczeństwa

Dowiedz się więcej o naszym podejściu do kwestii bezpieczeństwa:

Architektura bazy danych dla aplikacji Node.js

Wykorzystujemy hybrydowe podejście do baz danych zoptymalizowane pod kątem naszych aplikacji Node.js. Te wzorce można dostosować do dowolnej aplikacji Node.js:

Implementacja SQLite dla środowiska produkcyjnego Node.js

Czego używamy:

Nasza konfiguracja: ansible/playbooks/sqlite.yml

W naszych aplikacjach Node.js do obsługi danych specyficznych dla użytkownika używamy bazy SQLite, ponieważ zapewnia ona:

  • Izolacja danych dla każdego użytkownika/najemcy
  • Lepsza wydajność w przypadku zapytań pojedynczego użytkownika
  • Uproszczone tworzenie kopii zapasowych i migracja
  • Mniejsza złożoność w porównaniu z bazami danych współdzielonymi

Ten wzorzec sprawdza się w aplikacjach SaaS, systemach wielodostępnych i dowolnych aplikacjach Node.js wymagających izolacji danych.

Implementacja MongoDB dla środowiska produkcyjnego Node.js

Czego używamy:

Nasza implementacja konfiguracji: helpers/setup-mongoose.js

Nasza konfiguracja: config/mongoose.js

W naszym środowisku produkcyjnym Node.js do obsługi danych aplikacji używamy MongoDB, ponieważ zapewnia ono:

  • Elastyczny schemat dla ewoluujących struktur danych
  • Lepsza wydajność dla złożonych zapytań
  • Możliwości skalowania poziomego
  • Bogaty język zapytań

Note

Nasze hybrydowe podejście optymalizuje się pod kątem konkretnego przypadku użycia. Przeanalizuj rzeczywiste wzorce wykorzystania bazy danych w kodzie, aby dowiedzieć się, czy to podejście spełnia potrzeby Twojej aplikacji Node.js.

Przetwarzanie zadań w tle w środowisku produkcyjnym Node.js

Zbudowaliśmy naszą architekturę zadań w tle wokół Bree, aby zapewnić niezawodne wdrożenie produkcyjne Node.js. Dotyczy to każdej aplikacji Node.js wymagającej przetwarzania w tle:

Nasza konfiguracja serwera Bree do produkcji

Nasza główna implementacja: bree.js

Nasze wdrożenie Ansible: ansible/playbooks/bree.yml

Przykłady stanowisk produkcyjnych

Monitorowanie stanu zdrowia: jobs/check-pm2.js

Automatyzacja czyszczenia: jobs/cleanup-tmp.js

Wszystkie nasze oferty pracy: Przeglądaj nasz kompletny katalog ofert pracy

Wzorce te mają zastosowanie do dowolnej aplikacji Node.js, która wymaga:

  • Zaplanowane zadania (przetwarzanie danych, raporty, czyszczenie)
  • Przetwarzanie w tle (zmiana rozmiaru obrazu, wysyłanie wiadomości e-mail, import danych)
  • Monitorowanie stanu i konserwacja
  • Wykorzystanie wątków roboczych do zadań intensywnie wykorzystujących procesor

Nasze wzorce harmonogramowania zadań dla środowiska produkcyjnego Node.js

Zapoznaj się z naszymi aktualnymi schematami harmonogramowania pracy w naszym katalogu ofert pracy, aby zrozumieć:

  • Jak wdrażamy harmonogramowanie podobne do crona w środowisku produkcyjnym Node.js
  • Nasza obsługa błędów i logika ponawiania prób
  • Jak wykorzystujemy wątki robocze do zadań intensywnie wykorzystujących procesor

Automatyczna konserwacja aplikacji produkcyjnych Node.js

Wdrażamy proaktywną konserwację, aby zapobiegać typowym problemom produkcyjnym Node.js. Poniższe wzorce dotyczą każdej aplikacji Node.js:

Nasza implementacja oczyszczania

Źródło: jobs/cleanup-tmp.js

Nasze zautomatyzowane utrzymanie aplikacji produkcyjnych Node.js ma na celu:

  • Pliki tymczasowe starsze niż 24 godziny
  • Pliki dziennika przekraczające limity przechowywania
  • Pliki pamięci podręcznej i dane tymczasowe
  • Przesłane pliki, które nie są już potrzebne
  • Migawki sterty z debugowania wydajności

Wzorce te mają zastosowanie do wszystkich aplikacji Node.js, które generują pliki tymczasowe, dzienniki lub dane w pamięci podręcznej.

Zarządzanie przestrzenią dyskową dla środowiska produkcyjnego Node.js

Nasze progi monitorowania: helpers/monitor-server.js

  • Limit kolejki dla przetwarzania w tle
  • 75% użycie dysku próg ostrzegawczy
  • Automatyczne czyszczenie po przekroczeniu progów

Automatyzacja konserwacji infrastruktury

Nasza automatyzacja Ansible dla produkcji Node.js:

Przewodnik po wdrażaniu Node.js w środowisku produkcyjnym

Przeanalizuj nasz rzeczywisty kod, aby poznać najlepsze praktyki produkcyjne

Rozpocznij konfigurację środowiska produkcyjnego Node.js od tych kluczowych plików:

  1. Konfiguracja: config/index.js
  2. Monitorowanie: helpers/monitor-server.js
  3. Obsługa błędów: helpers/is-code-bug.js
  4. Logowanie: helpers/logger.js
  5. Stan procesu: jobs/check-pm2.js

Ucz się z naszych wpisów na blogu

Nasze przewodniki po implementacji technicznej dla środowiska produkcyjnego Node.js:

Automatyzacja infrastruktury dla środowiska produkcyjnego Node.js

Nasze podręczniki Ansible do nauki wdrażania Node.js w środowisku produkcyjnym:

Nasze studia przypadków

Nasze wdrożenia korporacyjne:

Wnioski: Najlepsze praktyki wdrażania Node.js w środowisku produkcyjnym

Nasza infrastruktura produkcyjna Node.js dowodzi, że aplikacje Node.js mogą osiągnąć niezawodność klasy korporacyjnej dzięki:

  • Sprawdzone rozwiązania sprzętowe (AMD Ryzen dla optymalizacji wydajności pojedynczego rdzenia na poziomie 573%)
  • Przetestowany w boju monitoring produkcji Node.js z określonymi progami i zautomatyzowanymi reakcjami
  • Inteligentna klasyfikacja błędów w celu usprawnienia reakcji na incydenty w środowiskach produkcyjnych
  • Zaawansowane debugowanie wydajności z v8-profiler-next i cpupro w celu zapobiegania niedoborowi zasobów (OOM)
  • Kompleksowe wzmocnienie bezpieczeństwa dzięki automatyzacji Ansible
  • Hybrydowa architektura bazy danych zoptymalizowana pod kątem potrzeb aplikacji
  • Automatyczna konserwacja w celu zapobiegania typowym problemom produkcyjnym Node.js

Kluczowy wniosek: Zamiast stosować się do ogólnych, najlepszych praktyk, zapoznaj się z naszymi plikami implementacyjnymi i wpisami na blogu. Nasza baza kodu zawiera praktyczne wzorce wdrażania Node.js w środowisku produkcyjnym, które można dostosować do dowolnej aplikacji Node.js – aplikacji webowych, interfejsów API, mikrousług czy usług w tle.

Kompletna lista zasobów dla środowiska produkcyjnego Node.js

Nasze podstawowe pliki implementacyjne

Nasze implementacje serwerowe

Nasza infrastruktura Automatyzacja

Nasze wpisy na blogu technicznym

Nasze studia przypadków przedsiębiorstw