Backend, Blockchain

Jak uruchomić mainnetowy node Ethereum na AWS

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.

#top_oferta: Lead Software Engineer (full stack)

30000 - 40000 pln

Aplikuj

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:

Resources:
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ...
      ContainerDefinitions:
        ...
        PortMappings:
          - ContainerPort: 30303
            HostPort: 30303
            Protocol: tcp

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:

PUBLIC_IP=`curl -s http://169.254.169.254/latest/meta-data/public-ipv4`
/parity/parity --config config.toml --nat extip:$PUBLIC_IP

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.

Resources:
  ECSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      ...
      SecurityGroupIngress:
        - FromPort: 30303
          ToPort: 30303
          CidrIp: 0.0.0.0/0
          IpProtocol: tcp

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:

!Fn::Import MainnetParity-AccessSecurityGroup

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

Resources:
  ECSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      ...
      SecurityGroupIngress:
        - FromPort: 8545
          ToPort: 8545
          SourceSecurityGroupId: !GetAtt AccessSecurityGroup.GroupId
          IpProtocol: tcp
        - FromPort: 8546
          ToPort: 8546
          SourceSecurityGroupId: !GetAtt AccessSecurityGroup.GroupId
          IpProtocol: tcp

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

Resources:
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ...
      ContainerDefinitions:
        ...
        PortMappings:
          - ContainerPort: 8545
            HostPort: 8545
            Protocol: tcp
          - ContainerPort: 8546
            HostPort: 8546
            Protocol: tcp

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

!Fn::Import MainnetParity-NameServer

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:

2018-05-11T09:27:56.202Z ++ curl -s http://169.254.169.254/latest/meta-data/public-ipv4
2018-05-11T09:27:56.253Z + PUBLIC_IP=18.196.95.41
2018-05-11T09:27:56.253Z + /parity/parity --config config.toml --nat extip:18.196.95.41
2018-05-11T09:27:56.297Z Loading config file from config.toml
2018-05-11T09:27:56.350Z 2018-05-11 09:27:56 UTC Starting Parity/v1.10.3-stable-b9ceda3-20180507/x86_64-linux-gnu/rustc1.25.0
2018-05-11T09:27:56.350Z 2018-05-11 09:27:56 UTC Keys path /root/.local/share/io.parity.ethereum/keys/Foundation
2018-05-11T09:27:56.350Z 2018-05-11 09:27:56 UTC DB path /root/.local/share/io.parity.ethereum/chains/ethereum/db/906a34e69aec8c0d
2018-05-11T09:27:56.350Z 2018-05-11 09:27:56 UTC Path to dapps /root/.local/share/io.parity.ethereum/dapps
2018-05-11T09:27:56.350Z 2018-05-11 09:27:56 UTC State DB configuration: fast
2018-05-11T09:27:56.350Z 2018-05-11 09:27:56 UTC Operating mode: active
2018-05-11T09:27:56.361Z 2018-05-11 09:27:56 UTC Configured for Foundation using Ethash engine
2018-05-11T09:27:56.730Z 2018-05-11 09:27:56 UTC Public node URL: enode://ec52f4ae94c624b1f8bf9c9b60fd63261beb42af6fea9d0fa4aeb6f52047fdf4afd92d9e3cd9c0f3387e892f378b3491ed8d85c38349ad50dce99539e952e38f@18.196.95.41:30303
2018-05-11T09:27:57.057Z 2018-05-11 09:27:57 UTC Updated conversion rate to Ξ1 = US$694.89 (6852745.5 wei/gas)
2018-05-11T09:28:06.806Z 2018-05-11 09:28:06 UTC Syncing       #0 d4e5…8fa3     0 blk/s    0 tx/s   0 Mgas/s      0+    0 Qed        #0    1/25 peers      8 KiB chain    3 MiB db  0 bytes queue   10 KiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T09:28:16.806Z 2018-05-11 09:28:16 UTC Syncing snapshot 9/1370        #0    2/25 peers      8 KiB chain    3 MiB db  0 bytes queue   10 KiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T09:28:21.807Z 2018-05-11 09:28:21 UTC Syncing snapshot 15/1370        #0    2/25 peers      8 KiB chain    3 MiB db  0 bytes queue   10 KiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T09:28:26.808Z 2018-05-11 09:28:26 UTC Syncing snapshot 21/1370        #0    2/25 peers      8 KiB chain    3 MiB db  0 bytes queue   10 KiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T09:28:31.809Z 2018-05-11 09:28:31 UTC Syncing snapshot 27/1370        #0    3/25 peers      8 KiB chain    3 MiB db  0 bytes queue   10 KiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T09:28:36.809Z 2018-05-11 09:28:36 UTC Syncing snapshot 29/1370        #0    3/25 peers      8 KiB chain    3 MiB db  0 bytes queue   10 KiB sync  RPC:  0 conn,  0 req/s,   0 µs

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:

2018-05-11T10:26:46.793Z 2018-05-11 10:26:46 UTC Syncing snapshot 1327/1370        #0   26/50 peers      8 KiB chain    3 MiB db  0 bytes queue   10 KiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T10:26:56.798Z 2018-05-11 10:26:56 UTC Syncing snapshot 1346/1370        #0   26/50 peers      8 KiB chain    3 MiB db  0 bytes queue   10 KiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T10:27:08.097Z 2018-05-11 10:27:08 UTC Syncing #5590000 b084…309c     0 blk/s    0 tx/s   0 Mgas/s      0+    0 Qed  #5590000   24/25 peers     63 KiB chain    1 KiB db  0 bytes queue    6 MiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T10:27:16.794Z 2018-05-11 10:27:16 UTC Syncing #5590000 b084…309c     0 blk/s    0 tx/s   0 Mgas/s   1750+    1 Qed  #5591752   26/50 peers    174 KiB chain   39 KiB db   95 MiB queue   11 MiB sync  RPC:  0 conn,  0 req/s,   0 µs

Wykonanie tego etapu zajmie jeszcze około godzinę.

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

2018-05-11T15:24:30.011Z 2018-05-11 15:24:30 UTC Syncing #5595608 f2fe…d003     0 blk/s    0 tx/s   0 Mgas/s      0+    7 Qed  #5595619   11/25 peers     33 MiB chain  182 MiB db    1 MiB queue    8 MiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T15:24:41.386Z 2018-05-11 15:24:41 UTC Updated conversion rate to Ξ1 = US$679.41 (7008882.5 wei/gas)
2018-05-11T15:24:41.795Z 2018-05-11 15:24:41 UTC Imported #5595620 ef95…d8b2 (181 txs, 7.98 Mgas, 4237.27 ms, 27.63 KiB) + another 3 block(s) containing 330 tx(s)
2018-05-11T15:24:48.290Z 2018-05-11 15:24:48 UTC Imported #5595622 221b…509d (162 txs, 7.99 Mgas, 1194.76 ms, 25.13 KiB)
2018-05-11T15:24:51.186Z 2018-05-11 15:24:51 UTC Imported #5595623 b744…cf9c (183 txs, 7.98 Mgas, 1698.02 ms, 33.23 KiB)
2018-05-11T15:25:27.225Z 2018-05-11 15:25:27 UTC     #40653   13/25 peers     37 MiB chain  182 MiB db  0 bytes queue   24 MiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T15:25:27.241Z 2018-05-11 15:25:27 UTC     #40653   13/25 peers     37 MiB chain  182 MiB db  0 bytes queue   24 MiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T15:25:27.252Z 2018-05-11 15:25:27 UTC     #40653   13/25 peers     37 MiB chain  182 MiB db  0 bytes queue   24 MiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T15:25:27.310Z 2018-05-11 15:25:27 UTC     #40653   13/25 peers     37 MiB chain  182 MiB db  0 bytes queue   24 MiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T15:25:41.464Z 2018-05-11 15:25:41 UTC Imported #5595627 a4a9…9dc0 (136 txs, 7.98 Mgas, 529.92 ms, 19.68 KiB)
2018-05-11T15:26:02.263Z 2018-05-11 15:26:02 UTC     #78637   23/25 peers     37 MiB chain  183 MiB db  241 KiB queue   22 MiB sync  RPC:  0 conn,  0 req/s,   0 µs
2018-05-11T15:26:03.398Z 2018-05-11 15:26:03 UTC Reorg to #5595628 8fc3…7c58 (a4a9…9dc0 18c7…4d47 #5595625 f6c1…feae 3faf…012d af04…83a8)

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.

najwięcej ofert html

Podobne artykuły

[wpdevart_facebook_comment curent_url="https://geek.justjoin.it/uruchomic-mainnetowy-node-ethereum-aws/" order_type="social" width="100%" count_of_comments="8" ]