Bare-Metal Provisioning z własnej inicjatywy, czyli automatyczne przygotowanie serwerów od podstaw

Cześć, nazywam się Denis i jednym z moich obszarów działalności jest rozwój rozwiązań infrastrukturalnych w X5. Dziś chciałbym podzielić się z Wami sposobem, w jaki można za pomocą ogólnodostępnych narzędzi uruchomić automatyczny system przygotowywania serwerów. Moim zdaniem, to ciekawe, proste i elastyczne rozwiązanie.

Bare-Metal Provisioning z własnej inicjatywy, czyli automatyczne przygotowanie serwerów od podstaw

Pod przygotowaniem rozumie się: przekształcenie nowego serwera z pudełka w w pełni skonfigurowany serwer z systemem Linux lub z hyperwizorem ESXi (Windows w tym artykule nie jest omawiany). serwerów serwery – serwery, które muszą być skonfigurowane.

Terminy:

  • serwer instalacyjny – główny serwer, który zapewnia cały proces przygotowania przez sieć.
  • Po co potrzebna jest automatyzacja?

Załóżmy, że mamy zadanie: masowo przygotowywać serwery od podstaw, w szczycie – 30 dziennie. Serwery różnych producentów i modeli, mogą być na nich instalowane różne systemy operacyjne, może być zainstalowany lub brakować hyperwizora.

Jakie operacje wchodzą w skład procesu konfiguracji (bez automatyzacji):

podłączyć klawiaturę, mysz, monitor do serwera;

  • skonfigurować BIOS, RAID, IPMI;
  • zaktualizować firmware komponentów;
  • wdrożyć obraz systemu plików (lub zainstalować hyperwizor i skopiować maszyny wirtualne);
  • Uwaga. Alternatywnie, wdrożenie systemu operacyjnego jest możliwe poprzez instalację z plikiem odpowiedzi. Jednak nie będzie to omawiane w artykule. Chociaż poniżej zobaczysz, że dodanie tej funkcji nie jest skomplikowane.

skonfigurować parametry systemu operacyjnego (hostname, IP, itp.).

  • W tym podejściu tak samo wykonywane są ustawienia kolejno na każdym serwerze. Efektywność takiej pracy jest bardzo niska.

Istota automatyzacji polega na wyeliminowaniu udziału człowieka w procesie przygotowania serwera. Tak bardzo, jak to możliwe.

Dzięki automatyzacji skraca się czas przestoju między operacjami, co pozwala na jednoczesne przygotowanie kilku serwerów. Zmniejsza się również znacznie prawdopodobieństwo wystąpienia błędów z powodu czynnika ludzkiego.

Jak przebiega automatyczna konfiguracja serwerów?

Bare-Metal Provisioning z własnej inicjatywy, czyli automatyczne przygotowanie serwerów od podstaw

Zajmijmy się szczegółowo wszystkimi etapami.

Masz serwer linuxowy, który używasz jako PXE serwer instalacyjny. Na nim zainstalowane i skonfigurowane są usługi: DHCP, TFTP.

Zatem, uruchamiamy serwer (który ma być skonfigurowany) przez PXE. Przypomnijmy sobie, jak to działa:

Na serwerze wybrano bootowanie z sieci.

  • Na serwerze wybrano bootowanie przez PXE.
  • Serwer ładuje PXE-ROM karty sieciowej i kontaktuje się z serwerem instalacyjnym przez DHCP w celu uzyskania adresu sieciowego.
  • Serwer instalacyjny DHCP wydaje adres oraz instrukcje dotyczące dalszego ładowania przez PXE.
  • Serwer ładuje program rozruchowy z serwera instalacyjnego przez PXE, a dalsze ładowanie przebiega zgodnie z plikiem konfiguracyjnym PXE.
  • Zachodzi ładowanie na podstawie otrzymanych parametrów (jądro, initramfs, punkty montowania, obraz squashfs i inne).

Uwaga. W artykule opisano ładowanie przez PXE w trybie BIOS. Obecnie producenci aktywnie wdrażają tryb rozruchu UEFI. Różnica dla PXE będzie polegać na konfiguracji serwera DHCP oraz obecności dodatkowego programu rozruchowego.

Rozważmy przykład konfiguracji serwera PXE (menu pxelinux).

Plik pxelinux.cfg/default:

default menu.c32
prompt 0
timeout 100
menu title X5 PXE Boot Menu
LABEL InstallServer Menu
	MENU LABEL InstallServer
	KERNEL menu.c32
	APPEND pxelinux.cfg/installserver
LABEL VMware Menu
	MENU LABEL VMware ESXi Install
	KERNEL menu.c32
	APPEND pxelinux.cfg/vmware
LABEL toolkit // menu domyślne
	MENU LABEL Linux Scripting Toolkits
	MENU default
	KERNEL menu.c32
	APPEND pxelinux.cfg/toolkit // przejście do następnego menu

Plik pxelinux.cfg/toolkit:

prompt 0
timeout 100
menu title X5 PXE Boot Menu
label mainmenu
    menu label ^Powrót do menu głównego
    kernel menu.c32
    append pxelinux.cfg/default
label x5toolkit-auto // domyślnie — tryb automatyczny
        menu label x5 toolkit autoinstalacja
        menu default
        kernel toolkit/tkcustom-kernel
        append initrd=toolkit/tk-initramfs.gz quiet net.ifnames=0 biosdevname=0 nfs_toolkit_ip=192.168.200.1 nfs_toolkit_path=tftpboot/toolkit nfs_toolkit_script=scripts/mount.sh script_cmd=master-install.sh CMDIS2="…"
label x5toolkit-shell // do debugowania - konsola
        menu label x5 toolkit shell
        kernel toolkit/tkcustom-kernel
        append initrd=toolkit/tkcustom-initramfs.gz quiet net.ifnames=0 biosdevname=0 nfs_toolkit_ip=192.168.200.1 nfs_toolkit_path=tftpboot/toolkit nfs_toolkit_script=scripts/mount.sh script_cmd=/bin/bash CMDIS2="…"

Jądro i initramfs na tym etapie to tymczasowy obraz linux, przy pomocy którego nastąpi główne przygotowanie i konfiguracja serwera.

Jak widać, program rozruchowy przekazuje wiele parametrów do jądra. Część tych parametrów jest wykorzystywana przez samo jądro, a niektóre możemy wykorzystać do własnych celów. O tym będzie mowa później, a na razie można zapamiętać, że wszystkie przekazane parametry będą dostępne w tymczasowym obrazie linux przez /proc/cmdline.

Skąd wziąć jądro i initramfs?
Można wybrać dowolny dystrybucję linuxa. Na co zwracamy uwagę przy wyborze:

  • obraz rozruchowy musi być uniwersalny (obecność sterowników, możliwość instalacji dodatkowych narzędzi);
  • prawdopodobnie będzie potrzeba dostosować initramfs.

Jak to zostało zrealizowane w naszym rozwiązaniu dla X5? Jako bazę wybrano CentOS 7. Wykonamy następujący myk: przygotujemy przyszłą strukturę obrazu, spakujemy ją w archiwum i stworzymy initramfs, w którego wnętrzu będzie nasze archiwum systemu plików. Podczas ładowania obrazu archiwum zostanie rozpakowane do tworzonego działu tmpfs. W ten sposób uzyskamy minimalny, ale pełnoprawny obraz live linux z wszystkimi niezbędnymi narzędziami, składający się z zaledwie dwóch plików: vmkernel i initramfs.

#создаем директории: 

mkdir -p /tftpboot/toolkit/CustomTK/rootfs /tftpboot/toolkit/CustomTK/initramfs/bin

#подготавливаем структуру:

yum groups -y install "Minimal Install" --installroot=/tftpboot/toolkit/CustomTK/rootfs/
yum -y install nfs-utils mariadb ntpdate mtools syslinux mdadm tbb libgomp efibootmgr dosfstools net-tools pciutils openssl make ipmitool OpenIPMI-modalias rng-tools --installroot=/tftpboot/toolkit/CustomTK/rootfs/
yum -y remove biosdevname --installroot=/tftpboot/toolkit/CustomTK/rootfs/

# подготавливаем initramfs:

wget https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64 -O /tftpboot/toolkit/CustomTK/initramfs/bin/busybox
chmod a+x /tftpboot/toolkit/CustomTK/initramfs/bin/busybox
cp /tftpboot/toolkit/CustomTK/rootfs/boot/vmlinuz-3.10.0-957.el7.x86_64 /tftpboot/toolkit/tkcustom-kernel

# создаем /tftpboot/toolkit/CustomTK/initramfs/init (ниже содержание скрипта):

#!/bin/busybox sh
/bin/busybox --install /bin
mkdir -p /dev /proc /sys /var/run /newroot
mount -t proc proc /proc
mount -o mode=0755 -t devtmpfs devtmpfs /dev
mkdir -p /dev/pts /dev/shm /dev/mapper /dev/vc
mount -t devpts -o gid=5,mode=620 devpts /dev/pts
mount -t sysfs sysfs /sys
mount -t tmpfs -o size=4000m tmpfs /newroot
echo -n "Extracting rootfs... "
xz -d -c -f rootfs.tar.xz | tar -x -f - -C /newroot
echo "done"
mkdir -p /newroot/dev /newroot/proc /newroot/sys
mount --move /sys  /newroot/sys
mount --move /proc /newroot/proc
mount --move /dev  /newroot/dev
exec switch_root /newroot /sbin/init

# упаковываем rootfs и initramfs:

cd /tftpboot/toolkit/CustomTK/rootfs
tar cJf /tftpboot/toolkit/CustomTK/initramfs/rootfs.tar.xz --exclude ./proc --exclude ./sys --exclude ./dev .
cd /tftpboot/toolkit/CustomTK/initramfs
find . -print0 | cpio --null -ov --format=newc | gzip -9 > /tftpboot/toolkit/tkcustom-initramfs-new.gz

Zatem wskazaliśmy jądro i initramfs, które mają być załadowane. W rezultacie na tym etapie, ładując tymczasowy obraz linux przez PXE, otrzymamy konsolę OS.

Świetnie, ale teraz musimy przekazać kontrolę naszej 'automatyzacji'.

Można to zrobić w ten sposób.

Załóżmy, że po załadowaniu obrazu planujemy przekazać kontrolę do skryptu mount.sh.
Włączymy skrypt mount.sh do automatycznego uruchamiania. W tym celu należy zmodyfikować initramfs:

  • rozpakować initramfs (jeśli używamy powyżej wymienionego wariantu initramfs, to nie jest wymagane)
  • dodać do autostartu kod, który będzie analizował przekazywane parametry przez /proc/cmdline i przekazywał kontrolę dalej;
  • spakować initramfs.

Uwaga. W przypadku narzędzia X5 kontrola ładowania jest przekazywana do skryptu /opt/x5/toolkit/bin/hook.sh с помощью override.conf в getty tty1 (ExecStart=…)

Zatem ładowany jest obraz, w którym w autostarcie uruchamiany jest skrypt mount.sh. Następnie skrypt mount.sh w trakcie wykonywania analizuje przekazane parametry (script_cmd=) i uruchamia odpowiedni program/skrypt.

etykieta toolkit-auto
jądro …
załącz … nfs_toolkit_script=scripts/mount.sh script_cmd=master-install.sh

etykieta toolkit-shell
jądro …
załącz … nfs_toolkit_script=scripts/mount.sh script_cmd=/bin/bash

Bare-Metal Provisioning z własnej inicjatywy, czyli automatyczne przygotowanie serwerów od podstaw

Tutaj po lewej stronie — menu PXE, po prawej – schemat przekazywania kontroli.

Rozwiązałem problem z przekazywaniem kontroli. W zależności od wyboru menu PXE uruchamiany jest albo skrypt auto-config, albo konsola do debugowania.

W przypadku automatycznej konfiguracji montowane są niezbędne katalogi z serwera instalacyjnego, które zawierają:

  • skrypty;
  • zachowane szablony BIOS/UEFI różnych serwerów;
  • firmware;
  • narzędzia dla serwerów;
  • logi.

Następnie skrypt mount.sh przekazuje kontrolę do skryptu master-install.sh z katalogu ze skryptami.

Drzewo skryptów (kolejność ich uruchamiania) wygląda mniej więcej tak:

  • master-install
  • sharefunctions (funkcje wspólne)
  • info (wyświetlanie informacji)
  • models (ustawianie parametrów instalacji na podstawie modelu serwera)
  • prepare_utils (instalacja niezbędnych narzędzi)
  • fwupdate (aktualizacja firmware'u)
  • diag (elementarna diagnoza)
  • biosconf (konfiguracja BIOS/UEFI)
  • clockfix (ustawienie czasu na płycie głównej)
  • srmconf (konfiguracja interfejsu zdalnego)
  • raidconf (konfiguracja wolumenów logicznych)

jeden z:

  • preinstall (przekazanie kontroli do instalatora systemu operacyjnego lub hypervisora, np. ESXi)
  • merged-install (bezpośredni start rozpakowywania obrazu)

Teraz wiemy:

  • jak uruchomić serwer przez PXE;
  • jak przekazać kontrolę do własnego skryptu.


Kontynuujemy. Pojawiają się następujące pytania:

  • Jak zidentyfikować serwer, który przygotowujemy?
  • Jakie narzędzia i jak skonfigurować serwer?
  • Jak uzyskać ustawienia dla konkretnego serwera?

Jak zidentyfikować serwer, który przygotowujemy?

To proste – DMI:

dmidecode –s system-product-name
dmidecode –s system-manufacturer
dmidecode –s system-serial-number

Znajdziesz tutaj wszystko, co potrzebne: producenta, model i numer seryjny. Jeśli nie jesteś pewien, czy te informacje są dostępne na wszystkich serwerach, mogą one być zidentyfikowane po adresie MAC. Możesz także użyć obu metod jednocześnie, jeżeli producenci serwerów są różni i niektóre modele nie mają informacji o numerze seryjnym.

Na podstawie uzyskanych informacji montowane są sieciowe foldery z serwera instalacyjnego i ładowane wszystko, co potrzebne (narzędzia, firmware itd.).

Jakie narzędzia i jak skonfigurować serwer?

Podam narzędzia dla Linuxa dla niektórych producentów. Wszystkie narzędzia są dostępne na oficjalnych stronach producentów.

Bare-Metal Provisioning z własnej inicjatywy, czyli automatyczne przygotowanie serwerów od podstaw

Z firmwarem, myślę, że wszystko jest jasne. Zazwyczaj są one dostarczane w postaci spakowanych plików wykonywalnych. Plik wykonywalny kontroluje proces aktualizacji firmware'u i informuje o kodzie zwrotnym.

BIOS i IPMI zazwyczaj konfiguruje się za pomocą szablonów. W razie potrzeby szablon można edytować tuż przed samym uruchomieniem.

Narzędzia RAID niektórych producentów również mogą być konfigurowane za pomocą szablonów. Jeśli nie jest to możliwe, będziesz musiał napisać skrypt konfiguracyjny.

Kolejność konfiguracji RAID najczęściej jest następująca:

  • Zbieramy aktualną konfigurację.
  • Jeśli już istnieją wolumeny logiczne – usuwamy je.
  • Sprawdzamy, jakie fizyczne dyski są obecne i ile ich jest.
  • Tworzymy nowy wolumen logiczny. Przerywamy proces w przypadku błędu.

Jak uzyskać ustawienia dla konkretnego serwera?

Zakładając, że ustawienia wszystkich serwerów będą przechowywane na serwerze instalacyjnym. W takim przypadku, aby odpowiedzieć na nasze pytanie, najpierw musimy rozwiązać, w jaki sposób przekazać ustawienia do serwera instalacyjnego.

Na początek można spokojnie korzystać z plików tekstowych. (W przyszłości można używać pliku tekstowego jako awaryjnego sposobu przesyłania ustawień).

Można "udostępnić" plik tekstowy na serwerze instalacyjnym. I dodać jego montowanie w skrypcie mount.sh.

Linie będą miały na przykład taki format:

Te linie będą przesyłane do pliku przez inżyniera z jego roboczego komputera. A następnie, podczas konfiguracji serwera, parametry dla konkretnego serwera będą odczytywane z pliku.

Jednak w dłuższej perspektywie lepiej jest wykorzystać bazę danych do przechowywania ustawień, stanów i dzienników instalacji serwerów.

Oczywiście, sama baza danych to za mało, i będzie trzeba stworzyć część kliencką, dzięki której będą przesyłane ustawienia do bazy. Realizacja tego jest trudniejsza w porównaniu z plikiem tekstowym, ale tak naprawdę wszystko nie jest takie trudne, jak się wydaje. Minimalną wersję klienta, która będzie po prostu przesyłać dane do bazy, można spokojnie napisać samodzielnie. A w przyszłości klienta można będzie rozwijać w trybie swobodnym (raporty, drukowanie etykiet, wysyłanie powiadomień i inne, co tylko przyjdzie do głowy).

Przesyłając odpowiednie zapytanie do bazy i podając numer seryjny serwera, uzyskamy niezbędne parametry do konfiguracji serwera.

Ponadto, nie będziemy musieli wymyślać blokad dla jednoczesnego dostępu, jak w przypadku pliku tekstowego.

Dziennik konfiguracji możemy na wszystkich etapach zapisywać w bazie danych i proces instalacji kontrolować za pomocą zdarzeń i flag etapów przygotowania.

Teraz wiemy, jak:

  • załadować serwer za pomocą PXE;
  • przekazać kontrolę naszemu skryptowi;
  • zidentyfikować serwer, który należy przygotować, po numerze seryjnym;
  • skonfigurować serwer odpowiednimi narzędziami;
  • przesyłać ustawienia do bazy danych serwera instalacyjnego za pomocą części klienckiej.

Ustaliliśmy, w jaki sposób:

  • instalowany serwer otrzymuje niezbędne ustawienia z bazy danych;
  • cały postęp przygotowania jest rejestrowany w bazie danych (logi, zdarzenia, flagi etapów).

Co z różnymi typami instalowanego oprogramowania? Jak zainstalować hypervisor, skopiować VM i skonfigurować to wszystko?

W przypadku wdrażania obrazu systemu plików (linux) na sprzęcie wszystko jest dość proste:

  • Po skonfigurowaniu wszystkich komponentów serwera wdrażamy obraz.
  • Instalujemy bootloader grub.
  • Tworzymy chroot i konfigurujemy wszystko, co potrzebne.

Jak przekazać sterowanie instalatorowi OS (na przykładzie ESXi).

  • Organizujemy przekazanie sterowania z naszego skryptu do instalatora hipernadzorcy za pomocą pliku odpowiedzi automatycznej (kickstart):
  • Usuwamy obecne partycje na dysku.
  • Tworzymy partycję o rozmiarze 500 MB.
  • Oznaczamy ją jako startową.
  • Formatujemy w FAT32.
  • Kopiujemy na nią pliki instalacyjne ESXi do katalogu głównego.
  • Instalujemy syslinux.
  • Kopiujemy syslinux.cfg do /syslinux/

default esxi
prompt 1
timeout 50
label esxi
kernel mboot.c32
append -c boot.cfg

  • Kopiujemy mboot.c32 do /syslinux.
  • W boot.cfg powinno być kernelopt=ks=ftp:///ks_esxi.cfg
  • Restartujemy serwer.

Po restarcie serwera z jego dysku twardego uruchomi się instalator ESXi. Wszystkie potrzebne pliki instalatora załadują się do pamięci, a następnie rozpocznie się instalacja ESXi zgodnie z podanym plikiem odpowiedzi automatycznej.

Przytoczę tutaj kilka linii z pliku odpowiedzi automatycznej ks_esxi.cfg:

%firstboot --interpreter=busybox
…
# pobieramy numer seryjny

SYSSN=$(esxcli hardware platform get | grep Serial | awk -F " " '{print $3}')

# pobieramy IP

IPADDRT=$(esxcli network ip interface ipv4 get | grep vmk0 | awk -F " " '{print $2}')
LAST_OCTET=$(echo $IPADDRT | awk -F'.' '{print $4}')

# łączymy się z serwerem instalacyjnym NFS

esxcli storage nfs add -H is -s /srv/nfs_share -v nfsshare1

# kopiujemy tymczasowe ustawienia ssh, aby skorzystać z klienta ssh

mv /etc/ssh /etc/ssh.tmp
cp -R /vmfs/volumes/nfsshare1/ssh /etc/
chmod go-r /etc/ssh/ssh_host_rsa_key

# kopiujemy ovftool, aby teraz wdrożyć VM, może się przydać później

cp -R /vmfs/volumes/nfsshare1/ovftool /vmfs/volumes/datastore1/

# wdrażamy VM

/vmfs/volumes/datastore1/ovftool/tools/ovftool --acceptAllEulas --noSSLVerify --datastore=datastore1 --name=VM1 /vmfs/volumes/nfsshare1/VM_T/VM1.ova vi://root:esxi_password@127.0.0.1
/vmfs/volumes/datastore1/ovftool/tools/ovftool --acceptAllEulas --noSSLVerify --datastore=datastore1 --name=VM2 /vmfs/volumes/nfsshare1/VM_T/VM2.ova vi://root:esxi_password@127.0.0.1

# pobieramy linijkę z ustawieniami naszego serwera

ssh root@is "mysql -h'192.168.0.1' -D'servers' -u'user' -p'secretpassword' -e "SELECT ... WHERE servers.serial='$SYSSN'"" | grep -v ^$ | sed 's/NULL//g' > /tmp/servers
...
# generujemy skrypt konfiguracji sieci

echo '#!/bin/sh' > /vmfs/volumes/datastore1/netconf.sh
echo "esxcli network ip interface ipv4 set -i=vmk0 -t=static --ipv4=$IPADDR --netmask=$S_SUB || exit 1" >> /vmfs/volumes/datastore1/netconf.sh
echo "esxcli network ip route ipv4 add -g=$S_GW -n=default || exit 1" >> /vmfs/volumes/datastore1/netconf.sh
chmod a+x /vmfs/volumes/datastore1/netconf.sh

# ustawiamy parametr guestinfo.esxihost.id, wskazujemy w nim numer seryjny

echo "guestinfo.esxihost.id = "$SYSSN"" >> /vmfs/volumes/datastore1/VM1/VM1.vmx
echo "guestinfo.esxihost.id = "$SYSSN"" >> /vmfs/volumes/datastore1/VM2/VM2.vmx
...
# aktualizujemy informacje w bazie danych

SYSNAME=$(esxcli hardware platform get | grep Product | sed 's/Product Name://;s/^ *//')
UUID=$(vim-cmd hostsvc/hostsummary | grep uuid | sed 's/ //g;s/,$//;s/^uuid="//;s/"$//')
ssh root@is "mysql -D'servers' -u'user' -p'secretpassword' -e "UPDATE servers ... SET ... WHERE servers.serial='$SYSSN'""
ssh root@is "mysql -D'servers' -u'user' -p'secretpassword' -e "INSERT INTO events ...""

# przywracamy ustawienia SSH

rm -rf /etc/ssh
mv /etc/ssh.tmp /etc/ssh

# konfigurowanie sieci i restart

esxcli system hostname set --fqdn=esx-${G_NICK}.x5.ru
/vmfs/volumes/datastore1/netconf.sh
reboot

Na tym etapie hipernawigator został zainstalowany i skonfigurowany, a maszyny wirtualne zostały skopiowane.

Jak teraz skonfigurować maszyny wirtualne?

Trochę oszukaliśmy: podczas instalacji ustawiliśmy parametr guestinfo.esxihost.id = "$SYSSN" w pliku VM1.vmx, wskazując w nim numer seryjny fizycznego serwera.

Teraz po uruchomieniu maszyna wirtualna (z zainstalowanym pakietem vmware-tools) może uzyskać dostęp do tego parametru:

ESXI_SN=$(vmtoolsd --cmd "info-get guestinfo.esxihost.id")

To znaczy, VM będzie w stanie zidentyfikować się (zna numer seryjny fizycznego hosta), złożyć zapytanie do bazy danych instalacyjnego serwera i uzyskać parametry, które należy skonfigurować. Wszystko to jest realizowane w skrypcie, który powinien być uruchamiany automatycznie przy starcie guestos vm (jednak tylko raz: RunOnce).

Teraz wiemy, jak:

  • załadować serwer za pomocą PXE;
  • przekazać kontrolę naszemu skryptowi;
  • zidentyfikować serwer, który należy przygotować, po numerze seryjnym;
  • konfigurować serwer przy użyciu odpowiednich narzędzi;
  • przekazywać ustawienia do bazy danych instalacyjnego serwera za pomocą części klienckiej;
  • konfigurować różne typy oprogramowania, w tym rozwijać hypervisor esxi i konfigurować maszyny wirtualne (wszystko automatycznie).

Ustaliliśmy, w jaki sposób:

  • instalowany serwer otrzymuje niezbędne ustawienia z bazy danych;
  • cały postęp przygotowania jest rejestrowany w bazie danych (logi, zdarzenia, flagi etapów).


Podsumowanie:

Sądzę, że wyjątkowość tego rozwiązania leży w jego elastyczności, prostocie, możliwościach i uniwersalności.

Proszę, napisz w komentarzach, co o tym myślisz.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster