Skip to content

Commit 3547ce7

Browse files
committed
Tezos. Mount EBS volume, fix CW dashboard for #98
1 parent 8dc453b commit 3547ce7

File tree

8 files changed

+128
-82
lines changed

8 files changed

+128
-82
lines changed

lib/tezos/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ The result should be like this (the actual balance might change):
124124
```bash
125125
pwd
126126
# Make sure you are in aws-blockchain-node-runners/lib/tezos
127-
npx cdk deploy snapshot-node --json --outputs-file sync-node-deploy.json
127+
npx cdk deploy tz-snapshot-node --json --outputs-file sync-node-deploy.json
128128
```
129129
> **NOTE:** *The default VPC must have at least two public subnets in different Availability Zones, and public subnet must set `Auto-assign public IPv4 address` to `YES`.*
130130

lib/tezos/app.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ const snapshotNode = new TzSnapshotNodeStack(app, "tz-snapshot-node", {
4343
historyMode: config.baseNodeConfig.historyMode,
4444
snapshotsUrl:config.baseNodeConfig.snapshotsUrl,
4545
octezDownloadUri: config.baseNodeConfig.octezDownloadUri,
46-
downloadSnapshot: config.baseNodeConfig.downloadSnapshot == "true"
46+
downloadSnapshot: config.baseNodeConfig.downloadSnapshot == "true",
47+
dataVolume: config.baseNodeConfig.dataVolume,
4748
})
4849

4950
const haNodeStack = new TzHANodesStack(app, "tz-ha-nodes", {

lib/tezos/lib/assets/copy-data-to-s3.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ systemctl stop node.service
55

66
source /etc/environment
77
# aws s3 sync ~/.tezos-node/ s3://$S3_SYNC_BUCKET/
8-
s5cmd sync /home/tezos/.tezos-node/ s3://$S3_SYNC_BUCKET/node/
8+
s5cmd sync /data/ s3://$S3_SYNC_BUCKET/node/
99

1010
echo "Synced node to S3"
1111

lib/tezos/lib/assets/download-snapshot.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ echo "Downloading TZ snapshot from $TZ_SNAPSHOTS_URI."
88

99
TZ_SNAPSHOTS_FILE_NAME=snapshot
1010

11-
mkdir ~/tezos-snapshots
12-
wget -O ~/tezos-snapshots/$TZ_SNAPSHOTS_FILE_NAME $TZ_SNAPSHOTS_URI
13-
octez-node snapshot import ~/tezos-snapshots/$TZ_SNAPSHOTS_FILE_NAME
14-
rm ~/tezos-snapshots/$TZ_SNAPSHOTS_FILE_NAME
11+
mkdir /home/tezos/tezos-snapshots
12+
wget -O /home/tezos/tezos-snapshots/$TZ_SNAPSHOTS_FILE_NAME $TZ_SNAPSHOTS_URI
13+
octez-node snapshot import /home/tezos/tezos-snapshots/$TZ_SNAPSHOTS_FILE_NAME --data-dir=/data
14+
rm /home/tezos/tezos-snapshots/$TZ_SNAPSHOTS_FILE_NAME
1515

1616

1717
echo "TZ snapshot is ready !!!"

lib/tezos/lib/assets/node-cw-dashboard.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,25 @@ export const SyncNodeCWDashboardJSON = {
145145
"title": "Tezos Client Block Height"
146146
}
147147
},
148+
{
149+
"height": 4,
150+
"width": 6,
151+
"y": 0,
152+
"x": 12,
153+
"type": "metric",
154+
"properties": {
155+
"metrics": [
156+
[ "CWAgent", "tz_minutes_behind", "InstanceId", "${INSTANCE_ID}", { "label": "${INSTANCE_ID}-${INSTANCE_NAME}" } ]
157+
],
158+
"sparkline": true,
159+
"view": "timeSeries",
160+
"stacked": false,
161+
"region": "${REGION}",
162+
"stat": "Maximum",
163+
"period": 60,
164+
"title": "Tezos Client Minutes Behind"
165+
}
166+
},
148167
{
149168
"height": 5,
150169
"width": 6,

lib/tezos/lib/assets/sync-checker/syncchecker-tezos.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ else
1010
TZ_CURRENT_BLOCK=$(octez-client rpc get /chains/test/blocks/head/header/shell | jq -r ".level")
1111
fi
1212

13+
TZ_CURRENT_BLOCK_TIMESTAMP_ISO=$(octez-client get timestamp)
14+
TZ_CURRENT_BLOCK_TIMESTAMP_EPOCH=$(date -d"$TZ_CURRENT_BLOCK_TIMESTAMP_ISO" +%s)
15+
CURRENT_EPOCH=$(date +%s)
16+
TZ_SECONDS_BEHIND=$(($CURRENT_EPOCH - $TZ_CURRENT_BLOCK_TIMESTAMP_EPOCH))
17+
TZ_MINUTES_BEHIND=$(($TZ_SECONDS_BEHIND / 60))
1318

1419
# Sending data to CloudWatch
1520
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
@@ -18,4 +23,4 @@ REGION=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/la
1823
TIMESTAMP=$(date +"%Y-%m-%dT%H:%M:%S%:z")
1924

2025
aws cloudwatch put-metric-data --metric-name tz_current_block --namespace CWAgent --value $TZ_CURRENT_BLOCK --timestamp $TIMESTAMP --dimensions InstanceId=$INSTANCE_ID --region $REGION
21-
#aws cloudwatch put-metric-data --metric-name tz _blocks_behind --namespace CWAgent --value $TZ_HIGHEST_BLOCK_DATE --timestamp $TIMESTAMP --dimensions InstanceId=$INSTANCE_ID --region $REGION
26+
aws cloudwatch put-metric-data --metric-name tz_minutes_behind --namespace CWAgent --value $TZ_MINUTES_BEHIND --timestamp $TIMESTAMP --dimensions InstanceId=$INSTANCE_ID --region $REGION

lib/tezos/lib/assets/user-data/node.sh

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ echo "LIFECYCLE_HOOK_NAME=${_LIFECYCLE_HOOK_NAME_}" >> /etc/environment
1515
echo "AUTOSCALING_GROUP_NAME=${_AUTOSCALING_GROUP_NAME_}" >> /etc/environment
1616
echo "INSTANCE_TYPE=${_INSTANCE_TYPE_}" >> /etc/environment
1717
echo "S3_SYNC_BUCKET=${_S3_SYNC_BUCKET_}" >> /etc/environment
18+
echo "TEZOS_NODE_DIR=/data" >> /etc/environment
1819
source /etc/environment
1920

2021
arch=$(uname -m)
@@ -112,29 +113,54 @@ if [ "$arch" == "x86_64" ]; then
112113
mv ./octez-arm64/* /usr/local/bin/
113114
fi
114115

115-
116-
117116
find /usr/local/bin/ -name "octez-*" -exec chmod +x {} \;
118117
groupadd tezos
119118
adduser -g tezos tezos
120-
#mkdir -p /var/tezos/node
121-
#chown -R tezos:tezos /var/tezos
119+
mkdir -p /data
122120

123121
echo "Changing user to tezos"
124122

125-
126123
echo "Installing zcash dependency"
127124
curl -o /tmp/fetch-params.sh https://raw.githubusercontent.com/zcash/zcash/713fc761dd9cf4c9087c37b078bdeab98697bad2/zcutil/fetch-params.sh
128125
chmod +x /tmp/fetch-params.sh
129126
su tezos -c "/tmp/fetch-params.sh"
130127

131-
echo "Configuring node"
132-
su tezos -c "octez-node config init --network=$TZ_NETWORK --history-mode=$TZ_HISTORY_MODE --net-addr='[::]:9732' --rpc-addr='[::]:8732' --allow-all-rpc [::]:8732"
128+
echo "Configuring /data volume"
129+
if [ "$INSTANCE_TYPE" == "SNAPSHOT" ] || [ "$INSTANCE_TYPE" == "SINGLE" ]; then
130+
# Signal completion to CFN
131+
echo "Single node. Signaling completion to CloudFormation to make EBS volumes available."
132+
cfn-signal --stack $STACK_NAME --resource $RESOURCE_ID --region $AWS_REGION
133+
134+
echo "Single node. Wait for one minute for the volume to be available"
135+
sleep 60
136+
fi
137+
138+
if $(lsblk | grep -q nvme1n1); then
139+
echo "nvme1n1 is found. Configuring attached storage"
140+
141+
mkfs -t xfs /dev/nvme1n1
133142

134-
# Signal completion to CFN
135-
echo "Signaling completion to CloudFormation. The node will still sync/import data"
136-
cfn-signal --stack $STACK_NAME --resource $RESOURCE_ID --region $AWS_REGION
143+
sleep 10
144+
# Define the line to add to fstab
145+
uuid=$(lsblk -n -o UUID /dev/nvme1n1)
146+
line="UUID=$uuid /data xfs defaults 0 2"
137147

148+
# Write the line to fstab
149+
echo $line | sudo tee -a /etc/fstab
150+
151+
mount -a
152+
153+
else
154+
echo "nvme1n1 is not found. Not doing anything"
155+
fi
156+
157+
chown -R tezos:tezos /data
158+
chmod -R 755 /data
159+
160+
lsblk -d
161+
162+
echo "Configuring node"
163+
su tezos -c "octez-node config init --network=$TZ_NETWORK --history-mode=$TZ_HISTORY_MODE --data-dir /data --net-addr='[::]:9732' --rpc-addr='[::]:8732' --allow-all-rpc [::]:8732"
138164

139165
# download snapshot if network is mainnet
140166
if [ "$INSTANCE_TYPE" == "SNAPSHOT" ] || [ "$INSTANCE_TYPE" == "SINGLE" ]; then
@@ -145,10 +171,9 @@ if [ "$INSTANCE_TYPE" == "SNAPSHOT" ] || [ "$INSTANCE_TYPE" == "SINGLE" ]; then
145171
fi
146172
fi
147173

148-
149174
if [[ "$INSTANCE_TYPE" == "HA" ]]; then
150175
# su tezos -c "aws s3 sync s3://$S3_SYNC_BUCKET/node ~/.tezos-node/node"
151-
su tezos -c "s5cmd sync 's3://$S3_SYNC_BUCKET/node/*' /home/tezos/.tezos-node/"
176+
su tezos -c "s5cmd sync 's3://$S3_SYNC_BUCKET/node/*' /data/"
152177
fi
153178

154179
if [[ "$INSTANCE_TYPE" == "SNAPSHOT" ]]; then
@@ -167,7 +192,7 @@ Description="Run the octez-node"
167192
[Service]
168193
User=tezos
169194
Group=tezos
170-
ExecStart=octez-node run
195+
ExecStart=octez-node run --data-dir=/data
171196
172197
[Install]
173198
WantedBy=multi-user.target

lib/tezos/lib/snapshot-node-stack.ts

Lines changed: 57 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import * as cdk from "aws-cdk-lib";
22
import * as cdkConstructs from "constructs";
33
import * as ec2 from "aws-cdk-lib/aws-ec2";
44
import * as iam from "aws-cdk-lib/aws-iam";
5-
import * as s3 from "aws-cdk-lib/aws-s3";
6-
import { AmazonLinuxGeneration, AmazonLinuxImage } from "aws-cdk-lib/aws-ec2";
75
import * as s3Assets from "aws-cdk-lib/aws-s3-assets";
86
import * as configTypes from "./config/tzConfig.interface";
97
import { TzNodeSecurityGroupConstructs } from "./constructs/tz-node-security-group";
108
import * as fs from "fs";
119
import * as path from "path";
1210
import * as constants from "../../constructs/constants";
13-
import { HANodesConstruct } from "../../constructs/ha-rpc-nodes-with-alb";
11+
import * as nodeCwDashboard from "./assets/node-cw-dashboard"
12+
import * as cw from 'aws-cdk-lib/aws-cloudwatch';
13+
import { SingleNodeConstruct } from "../../constructs/single-node"
1414
import * as nag from "cdk-nag";
1515
import { SnapshotsS3BucketConstruct } from "../../constructs/snapshots-bucket";
1616

@@ -23,6 +23,7 @@ export interface TzSnapshotNodeStackProps extends cdk.StackProps {
2323
downloadSnapshot: boolean;
2424
snapshotsUrl: string;
2525
octezDownloadUri: string;
26+
dataVolume: configTypes.TzDataVolumeConfig;
2627
}
2728

2829
export class TzSnapshotNodeStack extends cdk.Stack {
@@ -32,9 +33,9 @@ export class TzSnapshotNodeStack extends cdk.Stack {
3233
const REGION = cdk.Stack.of(this).region;
3334
const STACK_NAME = cdk.Stack.of(this).stackName;
3435
const STACK_ID = cdk.Stack.of(this).stackId;
35-
const AWS_ACCOUNT_ID = cdk.Stack.of(this).account
36-
const lifecycleHookName = STACK_NAME;
37-
const autoScalingGroupName = STACK_NAME;
36+
const AWS_ACCOUNT_ID = cdk.Stack.of(this).account;
37+
const availabilityZones = cdk.Stack.of(this).availabilityZones;
38+
const chosenAvailabilityZone = availabilityZones.slice(0, 1)[0];
3839

3940
const {
4041
instanceType,
@@ -45,6 +46,7 @@ export class TzSnapshotNodeStack extends cdk.Stack {
4546
downloadSnapshot,
4647
snapshotsUrl,
4748
octezDownloadUri,
49+
dataVolume,
4850
} = props;
4951

5052
// using default vpc
@@ -112,68 +114,62 @@ export class TzSnapshotNodeStack extends cdk.Stack {
112114
actions: ["s3:ListBucket", "s3:*Object", "s3:GetBucket*"],
113115
})
114116
);
115-
const snapshotNodeScript = fs.readFileSync(path.join(__dirname, "assets", "user-data", "node.sh")).toString();
116117

117-
118-
const snapshotNode = new ec2.Instance(this, "tz-snapshot-node", {
119-
instanceName: "tz-snapshot-node",
120-
instanceType: instanceType,
118+
// Setting up the node using generic Sync Node constract
119+
const node = new SingleNodeConstruct(this, "sync-node", {
120+
instanceName: STACK_NAME,
121+
instanceType,
122+
dataVolumes: [dataVolume],
121123
machineImage: new ec2.AmazonLinux2023ImageSsmParameter({
122124
kernel: ec2.AmazonLinux2023Kernel.KERNEL_6_1,
123125
cpuType: instanceCpuType,
124126
}),
125-
vpc: vpc,
126-
blockDevices: [
127-
{
128-
// ROOT VOLUME
129-
deviceName: "/dev/xvda",
130-
volume: ec2.BlockDeviceVolume.ebs(46, {
131-
deleteOnTermination: true,
132-
encrypted: true,
133-
iops: 3000,
134-
volumeType: ec2.EbsDeviceVolumeType.GP3,
135-
}),
136-
},
137-
],
138-
detailedMonitoring: true,
139-
propagateTagsToVolumeOnCreation: true,
140-
role: snapshotInstanceRole,
127+
vpc,
128+
availabilityZone: chosenAvailabilityZone,
129+
role: instanceRole,
141130
securityGroup: instanceSG.securityGroup,
142-
});
143-
144-
const modifiedSnapshotNodeScript = cdk.Fn.sub(snapshotNodeScript, {
145-
_AWS_REGION_: REGION,
146-
_STACK_NAME_: STACK_NAME,
147-
_TZ_SNAPSHOTS_URI_: snapshotsUrl,
148-
_STACK_ID_: STACK_ID,
149-
_TZ_HISTORY_MODE_: historyMode,
150-
_TZ_DOWNLOAD_SNAPSHOT_ : String(downloadSnapshot),
151-
_TZ_OCTEZ_DOWNLOAD_URI_ : octezDownloadUri,
152-
_TZ_NETWORK_: tzNetwork,
153-
_S3_SYNC_BUCKET_: snapshotsBucket.bucketName,
154-
_NODE_CF_LOGICAL_ID_: snapshotNode.instance.logicalId,
155-
_LIFECYCLE_HOOK_NAME_: lifecycleHookName,
156-
_AUTOSCALING_GROUP_NAME_: autoScalingGroupName,
157-
_ASSETS_S3_PATH_: `s3://${asset.s3BucketName}/${asset.s3ObjectKey}`,
158-
_INSTANCE_TYPE_: "SNAPSHOT",
159-
});
160-
161-
snapshotNode.addUserData(modifiedSnapshotNodeScript);
162-
163-
164-
// Getting logical ID of the instance to send ready signal later once the instance is initialized
165-
const snapshotNodeCfn = snapshotNode.node.defaultChild as ec2.CfnInstance;
166-
167-
// CloudFormation Config: wait for 15 min for the node to start
168-
const creationPolicy: cdk.CfnCreationPolicy = {
169-
resourceSignal: {
170-
count: 1,
171-
timeout: "PT90M",
131+
vpcSubnets: {
132+
subnetType: ec2.SubnetType.PUBLIC,
172133
},
173-
};
174-
175-
176-
snapshotNodeCfn.cfnOptions.creationPolicy = creationPolicy;
134+
});
135+
136+
// Parsing user data script and injecting necessary variables
137+
const userData = fs.readFileSync(path.join(__dirname, "assets", "user-data", "node.sh")).toString();
138+
const modifiedUserData = cdk.Fn.sub(userData, {
139+
_AWS_REGION_: REGION,
140+
_ASSETS_S3_PATH_: `s3://${asset.s3BucketName}/${asset.s3ObjectKey}`,
141+
_STACK_NAME_: STACK_NAME,
142+
_TZ_SNAPSHOTS_URI_: snapshotsUrl,
143+
_STACK_ID_: STACK_ID,
144+
_NODE_CF_LOGICAL_ID_: node.nodeCFLogicalId,
145+
_TZ_HISTORY_MODE_: historyMode,
146+
_TZ_DOWNLOAD_SNAPSHOT_ : String(downloadSnapshot),
147+
_TZ_OCTEZ_DOWNLOAD_URI_ : octezDownloadUri,
148+
_TZ_NETWORK_: tzNetwork,
149+
_LIFECYCLE_HOOK_NAME_: constants.NoneValue,
150+
_AUTOSCALING_GROUP_NAME_: constants.NoneValue,
151+
_INSTANCE_TYPE_: "SNAPSHOT",
152+
_S3_SYNC_BUCKET_: snapshotsBucket.bucketName,
153+
});
154+
155+
// Adding modified userdata script to the instance prepared fro us by Single Node constract
156+
node.instance.addUserData(modifiedUserData);
157+
158+
// Adding CloudWatch dashboard to the node
159+
const dashboardString = cdk.Fn.sub(JSON.stringify(nodeCwDashboard.SyncNodeCWDashboardJSON), {
160+
INSTANCE_ID:node.instanceId,
161+
INSTANCE_NAME: STACK_NAME,
162+
REGION: REGION,
163+
})
164+
165+
new cw.CfnDashboard(this, 'sync-cw-dashboard', {
166+
dashboardName: `${STACK_NAME}-${node.instanceId}`,
167+
dashboardBody: dashboardString,
168+
});
169+
170+
new cdk.CfnOutput(this, "sync-instance-id", {
171+
value: node.instanceId,
172+
});
177173

178174
new cdk.CfnOutput(this, "TezosSnapshotBucket", {
179175
value: snapshotsBucket.bucketName,

0 commit comments

Comments
 (0)