Ostatnio AWS opublikował szablony CloudFormation, pozwalające uruchomić prywatną sieć testową Ethereum. Ich rozwiązanie zawiera aplikację do przeglądania bloków, minera, itp. Podejście AWS jest dobre dla uruchomienia prywatnej sieci testowej, natomiast jest trudne do przystosowania, żeby działać w sieci mainnet.


Marek Kowalski. Współzałożyciel i CTO w Rumble Fish Blockchain Development. Marek jest przodującym programistą w zakresie technologii Blockchain. Podczas swojej ponad dziesięcioletniej kariery w IT pracował między innymi w software housach we Włoszech, Hiszpanii i Stanach Zjednoczonych. Doświadczenie, zainteresowanie innowacyjnymi rozwiązaniami oraz wnikliwość sprawiły, że jest teraz jednym z wiodących programistów Blockchain w Europie. Pod jego kierownictwem zespół Rumble Fish prowadzi pomyślną współpracę z klientami, wprowadzając nowe produkty oraz osiągając doskonałe wyniki.


Po pierwsze, spróbujmy odpowiedzieć, dlaczego ktoś może potrzebować prywatnie hostowanego node’a Ethereum na mainnet, zamiast polegać na Infurze. W Rumble Fish mamy kilka projektów, w których jakiś komponent backendowy wchodzi w interakcje z Ethereum blockchain. W niektórych przypadkach reaguje on na powiadomienia o eventach, w innych jest odpowiedzialny za zamknięcie transakcji finansowej i musi działać błyskawicznie.

Nigdy nie jest dobrym pomysłem, aby proces o kluczowym znaczeniu dla firmy polegał na usłudze zewnętrznej. Jeśli cokolwiek stałoby się z Infurą, usunięcie problemu będzie poza naszym zasięgiem. Jeśli padnie, możemy tylko czekać aż zewnętrzny team usunie awarię. Dla wielu aplikacji to może nie stanowić problemu, ale w naszym przypadku musieliśmy wyeliminować takie ryzyko.

Wyzwania związane z uruchamianiem node’a na mainnecie

Trwałość danych Blockchain

Największym wyzwaniem związanym z uruchomienie mainnetowego node’a jest jego synchronizacja. Synchronizacja nowo podłączonego node’a od zera zajmuje 2-3 godziny, aby dostać się do bieżących bloków. Następnie potrzebne jest jeszcze 2-3 dni, aby zakończyć proces weryfikacji kryptograficznej wszystkich bloków. Proces weryfikacji korzysta ze wszystkich dostępnych IOPSów, co sprawia, że ​​node jest mało responsywny. W tym czasie node może ulec opóźnieniu nawet o 30 bloków, przez co robi się mało przydatny. Dopiero po zakończeniu procesu weryfikacji node robi się stabilny i można na nim polegać. Stacki oferowane przez AWS nie rozwiązują tego problemu — każdy node zaczyna synchronizacje od zera. To jest ok, o ile nie masz dużej ilości danych do zsynchronizowania z innych node’ów, więc nie nadaje się dla mainnetu.

Ponieważ uzyskanie nowego node’a „kosztuje” 3 dni, konieczne jest utrzymanie ściągniętych danych między uruchomieniami node’a. Istnieje więcej niż jeden sposób, aby to osiągnąć. Rozwiązanie, które proponujemy to umieszczenie danych blockchainu na EBS i wykonanie snapshotu, kiedy blockchain zostanie w pełni zsynchronizowany. Jeśli node zostanie zrestartowany, a nowy przejmie jego funkcje, musi on dociągnąć bloki od momentu snapshotu, a nie całe 3 lata historii blockchainu.

Oczywiście to nie jest idealne rozwiązanie i wolelibyśmy mieć trwałe dane bez konieczności ponownego zsynchronizowania wszystkich bloków.

Podejścia, które zlekceważyliśmy

Pojedynczy trwały EBS

Jedną z rzeczy, którą próbowaliśmy, było utworzenie trwałego EBS volume poza kontekstem maszyny EC2 i podłączenie go do node’a podczas uruchamiania. Takie podejście ma swoje zalety. Kiedy tworzy się nowa maszyna, podejmuje synchronizacje w miejscu, gdzie poprzednia maszyna przerwała. To podejście minimalizuje czas regeneracji maszyny.

Z drugiej strony takie podejście nie działa dobrze z rosnącą liczbą instancji w górę i w dół. W scenariuszu, w którym chcielibyśmy mieć więcej node’ów do przełączenia awaryjnego lub zrównoważenia obciążenia, trzeba dodać dodatkową warstwę, aby zdecydować, który dysk EBS mamy użyć lub w miarę możliwości stworzyć nowy. Odrzuciliśmy to podejście jako zbyt skomplikowane.

Elastic File System (EFS)

Kolejną interesującą próbą rozwiązania problemu było użycie EFS. W przeciwieństwie do EBS ten system może być połączony z wieloma instancjami, które współdzielą go za pomocą protokołu podobnego do NFS. Niestety widzieliśmy, że node’y z blockchainowymi danymi na EFS bardzo długo się synchronizują. Parity używa dużo IOPS i wydajność oferowana przez EFS jest daleko niewystarczająca.

Dostęp do publicznej sieci dla warstwy synchronizacji

Aby się zsynchronizować, node musi być w stanie akceptować połączenia od innych node’ów. Wymagane jest, aby jedna strona połączenia mogła akceptować połączenia, więc teoretycznie moglibyśmy obyć się bez dostępu do publicznej sieci i polegać na możliwości połączenia do partnerów.

W takim scenariuszu nasz node mógłby pracować tylko z node’ami oferującymi dostęp publiczny, co eliminuje znaczną część populacji partnerów.

Aby zapewnić publiczny dostęp, skorzystaliśmy z następujących kroków.

1. Parity jest uruchomione w kontenerze dockera. Port 30303 jest otwarte przez następujący przepis:

2. Ponadto node musi znać swój publiczny adres IP, ponieważ jest używany jako identyfikator enode używany przez inne node’y do identyfikacji. Poniższe rozwiązanie polega na API wystawionym przez EC2, więc jest specyficzne dla AWS.

From docker/run_parity.sh:

3. Aby port maszyny EC2 był dostępny, należy go również otworzyć w konfiguracji grupy zabezpieczeń. Jest za to odpowiedzialna poniższa część stacku.

Prywatny dostęp do json-rpc i interfejsów websocket

Parity ma jeszcze dwa interfejsy sieciowe do uzyskiwania dostępu do danych blockchain.

  • port 8545 jest używany przez interfejs json-rpc: umieszczenie transakcji i uzyskiwanie wszelkiego rodzaju informacji,
  • port 8546 umożliwia to samo ale poprzez protokół websocket.

Najpierw omówmy, dlaczego uważamy, że json-rpc nie powinien być dostępny publicznie. W zależności od konkretnego przypadku otworzenie json-rpc może nie sprawiać problemu. Jednak w Rumble Fish wierzymy, że cokolwiek co może być ukryte powinno pozostać ukryte.

Pozostawienie otwartego interfejsu json-rpc nie stanowi zagrożenia dla środków na koncie. Niemniej łatwo sobie wyobrazić, że osoba atakująca może po prostu uruchomić wiele zapytań na node’zie, aby go przeciążyć. Warto więc się postarać i zabezpieczyć tą część infrastruktury.

Nasze podejście do prywatnego dostępu składa się z następujących elementów.

1. Cloudformation stack tworzy i eksportuje specjalną SecurityGroup używaną do uzyskiwania dostępu do node’a. Można ją zaimportować w innym stacku używając:

2. Ta grupa ma dostęp do instancji używając następującego ustawienia w grupie SecurityGroup Instancji EC2.

Te porty są kierowane do docker kontenera, analogicznie do tego co wcześniej robiliśmy z portem 30303.

3. Klient łączący się z json-rpc / websocketem musi używać prywatnego adresu IP instancji. Osiągamy to, tworząc Route53 HostedZone i rejestrując IP instancji przy uruchomieniu maszyny.

Cloudformation stack eksportuje serwery DNS tej strefy jako

Powinieneś umieścić tą wartość jako wpis NS w konfiguracji swojej domeny DNS.

Monitorowanie i logowanie

Stack jest skonfigurowany do zbierania interesujących plików z maszyny i przesyłania ich do CloudWatch log stream’u MainnetParity-logs.

Proces synchronizacji i weryfikacji

Tutaj interesują nas nazwy plików /parity/parity/... które są wyjściem z procesu Parity. Przy pierwszym uruchomieniu stack użyje warp sync, aby pobrać historię blockchainu przy użyciu protokołu pobierania zbiorczego Parity.

Na wyjściu wygląda to tak:

Proces synchronizacji snapshotów zajmuje około 3 godziny. Po zsynchronizowaniu snapshotów Parity pobierze wszystkie bloki utworzone od ostatniego snapshotu, aż do obecnie najnowszego bloku. Ta faza wygląda w logach tak:

Wykonanie tego etapu zajmie jeszcze około godzinę.

Po zakończeniu tej fazy log zmieni się i będzie wyglądał następująco:

Nowy typ linii logowania rozpoczynający się od numeru bloku (#40653 ...) pochodzi z procesu weryfikacji pobranych bloków. W tym procesie Parity weryfikuje każdy blok kryptograficznie i zapewnia, że ​​nikt nie zmanipulował danych, które pobraliśmy od innych node’ów.

Ten proces trwa około 3 dni, gdy jest uruchamiany na maszynie t2.medium z dyskiem EBS gp2 z 300 IOPS. Podczas jego działania można obserwować w monitorowaniu EBS, że wszystkie dostępne IOPSy są używane. Zrzut ekranu poniżej przedstawia moment zakończenia procesu weryfikacji. Możesz zobaczyć różnicę we wzorcu użycia.

Read IOPS

Write IOPS

Ponieważ proces weryfikacji jest ograniczony przez dostępne IOPSy, można go przyspieszyć dostarczając dodatkowe IOPSy. W naszym CloudFormation stack używamy gp2 VolumeType o rozmiarze 100 GB. AWS zapewnia 300 IOPSów dla takiego dysku. Jeśli chcesz przyspieszyć weryfikację, możesz zmodyfikować VolumeType na io1 i prowizjonować mu np. 1200 IOPS. Na tym poziomie obserwujemy w zakładce monitoringu, że proces weryfikacji nie jest już ograniczony przez dostępne IOPS, ale brakuje mu mocy CPU. Dlatego można go dalej przyspieszyć, zmieniając typ maszyny EC2 z t2.medium na c5.large. Działając na c5.large zauważyliśmy, że podczas weryfikacji Parity używa 2000 IOPS i może zakończyć cały proces w około 7 godzin, więc to jest dobry trick, jeśli bardziej niż na pieniądzach zależy nam na czasie.

Pamiętaj jednak, że dodatkowo prowizjonowane IOPSy nie są tanie — miesięczny koszt pozostawienia dysku o tym rozmiarze oraz IOPS, wyniesie w okolicach 100 USD, więc lepiej uważać.

Utrzymanie synchronizacji

Gdy node jest w pełni zsynchronizowany, zwykle pozostaje zsynchronizowany z najnowszym blockiem.

Powyższy obrazek przedstawia efekt wywołania eth_blockNumber na naszym node’ie i na Infurze. Przez większość czasu node’y są zsynchronizowane. Sporadycznie nasz node lub Infura spada o 1-4 bloki do tyłu. Pamiętaj, że obecnie to repozytorium nie zawiera Lambdy odpowiedzialnej za gromadzenie powyższych danych. Ta część infrastruktury znajduje się w narzędziu wyższego rzędu.

Zapraszamy do dyskusji
Nie ma więcej wpisów

Send this to a friend