diff --git a/nix/docker/README.md b/nix/docker/README.md index d4e485a3553..fd78af78977 100644 --- a/nix/docker/README.md +++ b/nix/docker/README.md @@ -39,7 +39,8 @@ cardano-node run \ 2. Run -e NETWORK=mainnet and check graceful shutdown SIGTERM with --detach 3. Run without -e NETWORK and check graceful shutdown SIGINT with -it 4. Run without -e NETWORK and check graceful shutdown SIGTERM with --detach -5. Check cardano-cli access +5. Run with -e CARDANO_UPDATE_TOPOLOGY and check cron job +6. Check cardano-cli access ### Run with NETWORK @@ -92,10 +93,31 @@ docker run --detach \ docker logs -f relay ``` +### Run with -e CARDANO_UPDATE_TOPOLOGY + +``` +docker rm -f relay +docker run --detach \ + --name=relay \ + -p 3001:3001 \ + -e CARDANO_UPDATE_TOPOLOGY=true \ + -e CARDANO_CUSTOM_PEERS="relay01.astorpool.net:3001" \ + -v node-data:/opt/cardano/data \ + -v node-ipc:/opt/cardano/ipc \ + inputoutput/cardano-node:dev run + +docker logs -f relay + +docker exec relay cat /opt/cardano/logs/topologyUpdateResult +docker exec relay cat /var/cardano/config/mainnet-topology.json +``` + ### Check cardano-cli ``` -docker run --rm -it \ +alias cardano-cli="docker run --rm -it \ -v node-ipc:/opt/cardano/ipc \ - inputoutput/cardano-node:dev cli query tip --mainnet + inputoutput/cardano-node:dev" + +cardano-cli query tip --mainnet ``` diff --git a/nix/docker/context/bin/run-node b/nix/docker/context/bin/run-node index 01427c999cb..249a3bd6020 100755 --- a/nix/docker/context/bin/run-node +++ b/nix/docker/context/bin/run-node @@ -24,6 +24,10 @@ if [[ -z $CARDANO_SOCKET_PATH ]]; then CARDANO_SOCKET_PATH="/opt/cardano/ipc/socket" fi +if [[ -z $CARDANO_LOG_DIR ]]; then + CARDANO_LOG_DIR="/opt/cardano/logs" +fi + if [[ -z $CARDANO_BIND_ADDR ]]; then CARDANO_BIND_ADDR="0.0.0.0" fi @@ -32,10 +36,30 @@ if [[ -z $CARDANO_PORT ]]; then CARDANO_PORT=3001 fi -if [ -v $CARDANO_BLOCK_PRODUCER ]; then +if [[ -z $CARDANO_BLOCK_PRODUCER ]]; then CARDANO_BLOCK_PRODUCER=false fi +if [[ -z $CARDANO_UPDATE_TOPOLOGY ]]; then + CARDANO_UPDATE_TOPOLOGY=false +fi + +if [[ -z $EKG_HOST ]]; then + EKG_HOST="127.0.0.1" +fi + +if [[ -z $EKG_PORT ]]; then + EKG_PORT=12788 +fi + +if [[ -z $EKG_TIMEOUT ]]; then + EKG_TIMEOUT=3 +fi + +if [[ -z $MAX_PEERS ]]; then + MAX_PEERS=15 +fi + ##################################################################### # # Print run environment @@ -46,21 +70,23 @@ printRunEnv () { echo "CARDANO_BLOCK_PRODUCER=$CARDANO_BLOCK_PRODUCER" echo "CARDANO_CONFIG=$CARDANO_CONFIG" echo "CARDANO_DATABASE_PATH=$CARDANO_DATABASE_PATH" + echo "CARDANO_LOG_DIR=$CARDANO_LOG_DIR" echo "CARDANO_PORT=$CARDANO_PORT" echo "CARDANO_SOCKET_PATH=$CARDANO_SOCKET_PATH" echo "CARDANO_TOPOLOGY=$CARDANO_TOPOLOGY" + echo "CARDANO_UPDATE_TOPOLOGY=$CARDANO_UPDATE_TOPOLOGY" - if [ "$CARDANO_BLOCK_PRODUCER" = true ]; then + if [[ ${CARDANO_BLOCK_PRODUCER} == true ]]; then - if [ -v $CARDANO_SHELLEY_KES_KEY ]; then + if [[ -z ${CARDANO_SHELLEY_KES_KEY} ]]; then CARDANO_SHELLEY_KES_KEY="$CARDANO_CONFIG_BASE/keys/kes.skey" fi - if [ -v $CARDANO_SHELLEY_VRF_KEY ]; then + if [[ -z ${CARDANO_SHELLEY_VRF_KEY} ]]; then CARDANO_SHELLEY_VRF_KEY="$CARDANO_CONFIG_BASE/keys/vrf.skey" fi - if [ -v $CARDANO_SHELLEY_OPERATIONAL_CERTIFICATE ]; then + if [[ -z ${CARDANO_SHELLEY_OPERATIONAL_CERTIFICATE} ]]; then CARDANO_SHELLEY_OPERATIONAL_CERTIFICATE="$CARDANO_CONFIG_BASE/keys/node.cert" fi @@ -70,6 +96,89 @@ printRunEnv () { fi } +##################################################################### +# +# Write root env file +# +writeRootEnv () { + +cat << EOF > /usr/local/bin/env +#!/usr/bin/env bash + +# Docker run ENV vars +CARDANO_CONFIG="$CARDANO_CONFIG" +CARDANO_TOPOLOGY="$CARDANO_TOPOLOGY" +CARDANO_BIND_ADDR="$CARDANO_BIND_ADDR" +CARDANO_PORT=$CARDANO_PORT +CARDANO_DATABASE_PATH="$CARDANO_DATABASE_PATH" +CARDANO_SOCKET_PATH="$CARDANO_SOCKET_PATH" +CARDANO_LOG_DIR="$CARDANO_LOG_DIR" + +CARDANO_PUBLIC_IP="$CARDANO_PUBLIC_IP" +CARDANO_CUSTOM_PEERS="$CARDANO_CUSTOM_PEERS" + +CARDANO_UPDATE_TOPOLOGY=$CARDANO_UPDATE_TOPOLOGY +CARDANO_BLOCK_PRODUCER=$CARDANO_BLOCK_PRODUCER + +# Mapping for topologyUpdater +CNODE_HOSTNAME="$CARDANO_PUBLIC_IP" +CNODE_PORT=$CARDANO_PORT +CUSTOM_PEERS="$CARDANO_CUSTOM_PEERS" +GENESIS_JSON="$CARDANO_CONFIG_BASE/mainnet-shelley-genesis.json" +TOPOLOGY="$CARDANO_TOPOLOGY" +LOG_DIR="$CARDANO_LOG_DIR" +EKG_HOST="$EKG_HOST" +EKG_PORT=$EKG_PORT +EKG_TIMEOUT=$EKG_TIMEOUT +MAX_PEERS=$MAX_PEERS +EOF +} + +##################################################################### +# +# Run the relay node in the background +# +runRelayNode () { + + if [[ $CARDANO_UPDATE_TOPOLOGY == true ]]; then + topologyUpdate -l & + fi + + effopts=(--config $CARDANO_CONFIG \ + --topology $CARDANO_TOPOLOGY \ + --database-path $CARDANO_DATABASE_PATH \ + --socket-path $CARDANO_SOCKET_PATH \ + --host-addr $CARDANO_BIND_ADDR \ + --port $CARDANO_PORT) + + effopts+=(${options[@]}) + + echo "cardano-node run ${effopts[@]}" + exec /usr/local/bin/cardano-node run ${effopts[@]} +} + +##################################################################### +# +# Run the block producer in the background +# +runBlockProducerNode () { + + effopts=(--config $CARDANO_CONFIG \ + --topology $CARDANO_TOPOLOGY \ + --database-path $CARDANO_DATABASE_PATH \ + --socket-path $CARDANO_SOCKET_PATH \ + --host-addr $CARDANO_BIND_ADDR \ + --port $CARDANO_PORT \ + --shelley-kes-key $CARDANO_SHELLEY_KES_KEY \ + --shelley-vrf-key $CARDANO_SHELLEY_VRF_KEY \ + --shelley-operational-certificate $CARDANO_SHELLEY_OPERATIONAL_CERTIFICATE) + + effopts+=(${options[@]}) + + echo "cardano-node run ${effopts[@]}" + exec /usr/local/bin/cardano-node run ${effopts[@]} +} + # Shift the first option by one index shift @@ -93,6 +202,9 @@ do --socket-path) CARDANO_SOCKET_PATH=${val}; found=true;; --host-addr) CARDANO_BIND_ADDR=${val}; found=true;; --port) CARDANO_PORT=${val}; found=true;; + --shelley-kes-key) CARDANO_SHELLEY_KES_KEY=${val}; found=true;; + --shelley-vrf-key) CARDANO_SHELLEY_VRF_KEY=${val}; found=true;; + --shelley-operational-certificate) CARDANO_SHELLEY_OPERATIONAL_CERTIFICATE=${val}; found=true;; esac if [[ $found == true ]]; then @@ -102,18 +214,13 @@ do done printRunEnv - -effopts=(--config $CARDANO_CONFIG \ - --topology $CARDANO_TOPOLOGY \ - --database-path $CARDANO_DATABASE_PATH \ - --socket-path $CARDANO_SOCKET_PATH \ - --host-addr $CARDANO_BIND_ADDR \ - --port $CARDANO_PORT) - -effopts+=(${options[@]}) +writeRootEnv # The IPC socket dir is not created on demand -mkdir -p `dirname $CARDANO_SOCKET_PATH` +mkdir -p `dirname ${CARDANO_SOCKET_PATH}` -echo "/usr/local/bin/cardano-node run ${effopts[@]}" -exec /usr/local/bin/cardano-node run ${effopts[@]} +if [[ ${CARDANO_BLOCK_PRODUCER} == true ]]; then + runBlockProducerNode +else + runRelayNode +fi diff --git a/nix/docker/context/bin/topologyUpdate b/nix/docker/context/bin/topologyUpdate new file mode 100755 index 00000000000..15c387414cd --- /dev/null +++ b/nix/docker/context/bin/topologyUpdate @@ -0,0 +1,174 @@ +#!/bin/bash +# shellcheck disable=SC2086,SC2034 +# shellcheck source=/dev/null + +# This is borrowed from https://github.com/cardano-community/guild-operators +# and mainly just removes the auto-update of the script itself +# +# i.e. we want a stable/known version of this script +# +# Also, this is hopefully only a temporary thing until +# the node can take care about it's p2p2 updates itself + +PARENT="$(dirname $0)" + +###################################### +# User Variables - Change as desired # +###################################### + +#CNODE_HOSTNAME="CHANGE ME" # (Optional) Must resolve to the IP you are requesting from +#CUSTOM_PEERS="None" # Additional custom peers to (IP:port[:valency]) to add to your target topology.json + # eg: "10.0.0.1:3001|10.0.0.2:3002|relays.mydomain.com:3003:3" + +CNODE_VALENCY=1 # (Optional) for multi-IP hostnames +MAX_PEERS=15 # Maximum number of peers to return on successful fetch + +if [[ ! -f ${PARENT}/env ]]; then + echo "[Error] Generated env file missing: ${PARENT}/env" + echo "This is a mandatory prerequisite\n" + exit 1 +fi + +# source generated env variables +if ! source ${PARENT}/env offline; then + echo "[Error] Cannot source: ${PARENT}/env" + exit 1; +fi + +# NetworkMagic extracted from the genesis file +NWMAGIC=$(jq -r .networkMagic < ${GENESIS_JSON}) + +# Installed by pkgs.cacert +# https://curl.se/docs/sslcerts.html +export CURL_CA_BUNDLE="/etc/ssl/certs/ca-bundle.crt" + +###################################### +# Do NOT modify code below # +###################################### + +usage() { + cat </dev/null | jq -er '.cardano.node.metrics.blockNum.int.val //0' ); do + ((fail_cnt++)) + if [[ ${fail_cnt} -eq 5 ]]; then echo "5 consecutive EKG queries failed, aborting!"; exit 1; fi + echo "(${fail_cnt}/5) Failed to grab blockNum from node EKG metrics, sleeping for 30s before retrying..." + sleep 30 + done +} + +##################################################################### +# +# Push the node config info to the topology endpoint +# +# If you run your node in IPv4/IPv6 dual stack network configuration and want announced the +# IPv4 address only please add the -4 parameter to the curl command below (curl -4 -s ...) +pushNodeInfo () { + + if [[ ${TU_PUSH} = "Y" ]]; then + + echo "[`date +'%Y-%m-%d %H:%M:%S %Z'`] Updating topology ..." + + fetchBlockNumber + + echo "curl -s -f https://api.clio.one/htopology/v1/?port=${CNODE_PORT}&blockNo=${blockNo}&valency=${CNODE_VALENCY}&magic=${NWMAGIC}${T_HOSTNAME}" + curl -s -f "https://api.clio.one/htopology/v1/?port=${CNODE_PORT}&blockNo=${blockNo}&valency=${CNODE_VALENCY}&magic=${NWMAGIC}${T_HOSTNAME}" | \ + tee -a ${LOG_DIR}/topologyUpdateResult + else + + echo "[Warning] Topology update push disabled" + fi +} + +##################################################################### +# +# Fetch the next topology update +# +fetchTopology () { + + if [[ ${TU_FETCH} = "Y" ]]; then + + echo "curl -s -f https://api.clio.one/htopology/v1/fetch/?max=${MAX_PEERS}&magic=${NWMAGIC}${CUSTOM_PEERS_PARAM}" + curl -s -f -o ${TOPOLOGY}.tmp "https://api.clio.one/htopology/v1/fetch/?max=${MAX_PEERS}&magic=${NWMAGIC}${CUSTOM_PEERS_PARAM}" \ + && mv ${TOPOLOGY}.tmp ${TOPOLOGY} && cat ${TOPOLOGY} + else + + echo "[Warning] Topology update fetch disabled" + fi +} + +########################################################################################################################################## +# +# Run a single topology update cycle +# +if [[ ${LOOP} = "N" ]]; then + + pushNodeInfo + fetchTopology + exit 0 +fi + +########################################################################################################################################## +# +# Run an endless topology update loop +# + +MIN=`date +"%-M"` +INITIAL_WAIT=10 +MIN=$(($MIN + $INITIAL_WAIT)) +if (( $MIN > 59 )); then MIN=$(($MIN - 60)); fi + +CRONJOB="$MIN * * * * root topologyUpdate" +echo "Topology update: $CRONJOB" + +echo "Initially waiting for ${INITIAL_WAIT} minutes ..." +sleep $(($INITIAL_WAIT * 60)) + +while true; do + pushNodeInfo + fetchTopology + sleep 3600 +done + +echo "[Warning] Exiting topology update loop" diff --git a/nix/docker/default.nix b/nix/docker/default.nix index ab651a11f89..bbee0f37460 100644 --- a/nix/docker/default.nix +++ b/nix/docker/default.nix @@ -114,6 +114,7 @@ in fromImage = baseImage; created = "now"; # Set creation date to build time. Breaks reproducibility contents = [ + pkgs.jq # Needed by topologyUpdater ]; # May require system-features = kvm in /etc/nix/nix.conf @@ -124,6 +125,7 @@ in mkdir -p opt/cardano/config mkdir -p opt/cardano/data mkdir -p opt/cardano/ipc + mkdir -p opt/cardano/logs mkdir -p usr/local/bin cp ${mainnetConfigs}/* opt/cardano/config cp ${runNetwork}/bin/* usr/local/bin