Skip to content

Commit eecdc68

Browse files
committed
Add Docker support
1 parent 85a3f79 commit eecdc68

12 files changed

Lines changed: 442 additions & 95 deletions

Dockerfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
FROM ubuntu:24.04
2+
3+
RUN apt-get update && apt-get install -y curl openssl iproute2 && rm -rf /var/lib/apt/lists/*
4+
5+
WORKDIR /root
6+
7+
COPY . .
8+
9+
ARG BUILDING_DOCKER_IMAGE=true
10+
ARG NETWORK
11+
ARG FEATURE
12+
13+
RUN chmod +x node-installer.sh && ./node-installer.sh
14+
RUN chmod +x bin/start-in-docker.sh
15+
16+
CMD ["./bin/start-in-docker.sh"]

README.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@
5151
- [Fast Syncing with Archival State Download](#-fast-syncing-with-archival-state-download)
5252
- [Using the Fast Sync Command](#using-the-fast-sync-command)
5353
- [Contributing OS Support](#contributing-os-support)
54+
- [Using Docker](#-using-docker)
55+
- [Download Node Installer Code](#download-node-installer-code)
56+
- [Building Node Image](#building-node-image)
57+
- [Running](#running)
58+
- [Running Query Commands](#running-query-commands)
59+
- [Fast Syncing](#fast-syncing)
5460

5561
## 📋 Prerequisites
5662

@@ -63,6 +69,7 @@ successfully on Ubuntu 24.10, official support is limited to the LTS version
6369
listed above. Compatibility with other versions may vary.
6470

6571
The installer has modular OS logic support. See [Contributing OS Support](#contributing-os-support) to learn how to add support for your Linux distribution.
72+
If you don't have a system that meets the requirements, you may use the Docker setup instead.
6673

6774
## 📦 Packages
6875

@@ -371,3 +378,161 @@ Replace <your-distro> with your distro's ID from `/etc/os-release`.
371378
2. Update `install_deps` to use your package manager. Update `configure_logrotate` if the path or behavior differs on your OS.
372379
3. Test your script on a fresh instance of your OS.
373380
4. Submit a pull request with the new file.
381+
## 🐳 Using Docker
382+
383+
If you don't have a system that meets the prerequisites to run a node, you may run with
384+
Docker instead.
385+
386+
First, if you don't have Docker installed, follow the [official guide](https://docs.docker.com/desktop/)
387+
to install it.
388+
389+
### Download Node Installer Code
390+
391+
To build the Docker image for a node, you first need to download the code for the version of the
392+
node installer you're going to use.
393+
394+
```sh
395+
wget https://github.com/dusk-network/node-installer/archive/refs/tags/<version>.zip
396+
```
397+
398+
For example, if you're using version v0.5.6, then you'll run:
399+
400+
```sh
401+
wget https://github.com/dusk-network/node-installer/archive/refs/tags/v0.5.6.zip
402+
```
403+
404+
Ensure that the version you select is a version that supports Docker.
405+
406+
After downloading the code, unzip it and change your working directory:
407+
408+
```sh
409+
unzip node-installer-<version>
410+
cd node-installer-<version>
411+
```
412+
413+
Or if you want to use the latest unreleased code:
414+
415+
```sh
416+
git clone https://github.com/dusk-network/node-installer.git
417+
cd node-installer
418+
```
419+
420+
### Building Node Image
421+
422+
To build the image:
423+
424+
```sh
425+
docker build -t node-installer .
426+
```
427+
428+
This will build a Docker image with all the prerequisites and binaries to run a node.
429+
By default, the dependencies installed will be for provisioner nodes on mainnet.
430+
431+
To change that, you need to specify build args to indicate which network you want to run on and
432+
which feature to use.
433+
434+
```sh
435+
docker build -t node-installer --build-arg NETWORK=<mainnet|testnet> --build-arg FEATURE=<default|archive> .
436+
```
437+
438+
Building the image without specifying any build args is equivalent to:
439+
440+
```sh
441+
docker build -t node-installer --build-arg NETWORK=mainnet --build-arg FEATURE=default .
442+
```
443+
444+
You can combine args to build for a specific network and feature. For example, if you want to run
445+
an archive node on testnet:
446+
447+
```sh
448+
docker build -t node-installer --build-arg NETWORK=testnet --build-arg FEATURE=archive .
449+
```
450+
451+
### Running
452+
453+
Before running, you need to get your consensus keys. You can create them by following
454+
[Set Consensus Keys](#-set-consensus-keys).
455+
456+
To start a node:
457+
458+
```sh
459+
docker run -d \
460+
--name rusk \
461+
-e DUSK_CONSENSUS_KEYS_PASS=<consensus-keys-password> \
462+
-v /path/to/consensus.keys:/opt/dusk/conf/consensus.keys \
463+
-v /path/to/rusk-profile:/opt/dusk/rusk \
464+
-p 9000:9000/udp \
465+
node-installer
466+
```
467+
468+
Replacing:
469+
- `<consensus-keys-password>` with your actual consensus keys password. This will set the environment variable
470+
`DUSK_CONSENSUS_KEYS_PASS`.
471+
- `/path/to/consensus.keys` with the actual path to where your consensus keys are located. This will enabled rusk to
472+
access them from within the container.
473+
- `/path/to/rusk-profile` with the path to the rusk profile, a directory where the state will be stored. This will allow
474+
rusk to save its current state outside the container so that the state will persist between runs.
475+
476+
This will run a container named rusk in the background.
477+
478+
To check the logs:
479+
480+
```sh
481+
docker logs rusk
482+
```
483+
484+
To stop a node:
485+
486+
```sh
487+
docker stop rusk
488+
```
489+
490+
To restart a previously stopped node:
491+
492+
```sh
493+
docker start rusk
494+
```
495+
496+
### Running Query Commands
497+
498+
You can run any `ruskquery` command to check diagnostics or the installer version:
499+
500+
Checking the block height:
501+
502+
```sh
503+
docker exec rusk ruskquery block-height
504+
```
505+
506+
Checking the installer version:
507+
508+
```sh
509+
docker exec rusk ruskquery version
510+
```
511+
512+
### Fast Syncing
513+
514+
If you built the image with the `NETWORK` arg set to mainnet, then you can also run the `download_state`
515+
command for fast archival state syncing.
516+
517+
1. If you've already run the container, stop it and remove it:
518+
```sh
519+
docker stop rusk
520+
docker rm rusk
521+
```
522+
523+
2. Execute the fast sync command
524+
```sh
525+
docker run --rm -it -v /path/to/rusk-profile:/opt/dusk/rusk node-installer download_state
526+
```
527+
This will execute the command within a temporary container but the state is saved in
528+
`/path/to/rusk-profile`.
529+
530+
Or syncing up to a specific height.
531+
```sh
532+
docker run --rm -it -v /path/to/rusk-profile:/opt/dusk/rusk node-installer download_state 369876
533+
```
534+
535+
Follow the prompts to confirm the operation.
536+
537+
3. Restart your node:
538+
Follow the [Running](#running) guide to start a node.

bin/detect_ips.sh

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,8 @@
11
#!/bin/bash
22

3-
# Fetch IPv4 WAN address using ifconfig.me, fallback to ipinfo.io
4-
PUBLIC_IP=$(curl -4 -s https://ifconfig.me)
5-
if [ -z "$PUBLIC_IP" ]; then
6-
PUBLIC_IP=$(curl -4 -s https://ipinfo.io/ip)
7-
fi
3+
detect_ips_output=$(/opt/dusk/bin/detect_ips_inner.sh)
4+
PUBLIC_IP=$(echo "$detect_ips_output" | sed -n '1p')
5+
LISTEN_IP=$(echo "$detect_ips_output" | sed -n '2p')
86

9-
# Validate IPv4 address
10-
if [[ -z "$PUBLIC_IP" || ! "$PUBLIC_IP" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
11-
echo "Error: Unable to retrieve a valid WAN IPv4 address"
12-
exit 1
13-
fi
14-
15-
int2ip() { printf ${2+-v} $2 "%d.%d.%d.%d" \
16-
$(($1>>24)) $(($1>>16&255)) $(($1>>8&255)) $(($1&255)) ;}
17-
ip2int() { local _a=(${1//./ }) ; printf ${2+-v} $2 "%u" \
18-
$(( _a<<24 | ${_a[1]} << 16 | ${_a[2]} << 8 | ${_a[3]} )) ;}
19-
20-
while IFS=$' :\t\r\n' read a b c d; do
21-
[ "$a" = "0.0.0.0" ] && [ "$c" = "$a" ] && iFace=${d##* } gWay=$b
22-
done < <(/sbin/route -n 2>&1)
23-
ip2int $gWay gw
24-
localIp="$($(which ip) -j -4 -br addr | jq -r ". | map(select(.ifname == \"$iFace\")) | .[].addr_info.[0].local")"
25-
ip2int $localIp ip
26-
mask="$($(which ipcalc) -n -b $localIp | grep Netmask | awk '{print $2}')"
27-
ip2int $mask mask
28-
(( ( ip & mask ) == ( gw & mask ) )) &&
29-
int2ip $ip myIp && int2ip $mask netMask
30-
31-
echo "KADCAST_PUBLIC_ADDRESS=$PUBLIC_IP:9000"
32-
if [ -z "$myIp" ]; then
33-
echo "KADCAST_LISTEN_ADDRESS=$PUBLIC_IP:9000"
34-
else
35-
echo "KADCAST_LISTEN_ADDRESS=$myIp:9000"
36-
fi
7+
echo "KADCAST_PUBLIC_ADDRESS=$PUBLIC_IP"
8+
echo "KADCAST_LISTEN_ADDRESS=$LISTEN_IP"

bin/detect_ips_inner.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/bash
2+
3+
# Fetch IPv4 WAN address using ifconfig.me, fallback to ipinfo.io
4+
PUBLIC_IP=$(curl -4 -s https://ifconfig.me)
5+
if [ -z "$PUBLIC_IP" ]; then
6+
PUBLIC_IP=$(curl -4 -s https://ipinfo.io/ip)
7+
fi
8+
9+
# Validate IPv4 address
10+
if [[ -z "$PUBLIC_IP" || ! "$PUBLIC_IP" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
11+
echo "Error: Unable to retrieve a valid WAN IPv4 address"
12+
exit 1
13+
fi
14+
15+
int2ip() { printf ${2+-v} $2 "%d.%d.%d.%d" \
16+
$(($1>>24)) $(($1>>16&255)) $(($1>>8&255)) $(($1&255)) ;}
17+
ip2int() { local _a=(${1//./ }) ; printf ${2+-v} $2 "%u" \
18+
$(( _a<<24 | ${_a[1]} << 16 | ${_a[2]} << 8 | ${_a[3]} )) ;}
19+
20+
while IFS=$' :\t\r\n' read a b c d; do
21+
[ "$a" = "0.0.0.0" ] && [ "$c" = "$a" ] && iFace=${d##* } gWay=$b
22+
done < <(/sbin/route -n 2>&1)
23+
ip2int $gWay gw
24+
localIp="$($(which ip) -j -4 -br addr | jq -r ". | map(select(.ifname == \"$iFace\")) | .[].addr_info.[0].local")"
25+
ip2int $localIp ip
26+
mask="$($(which ipcalc) -n -b $localIp | grep Netmask | awk '{print $2}')"
27+
ip2int $mask mask
28+
(( ( ip & mask ) == ( gw & mask ) )) &&
29+
int2ip $ip myIp && int2ip $mask netMask
30+
31+
echo "$PUBLIC_IP:9000"
32+
if [ -z "$myIp" ]; then
33+
echo "$PUBLIC_IP:9000"
34+
else
35+
echo "$myIp:9000"
36+
fi

bin/docker/download_state.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Download the state
5+
source /opt/dusk/bin/download_state_inner.sh $@
6+
7+
replace_state_with_newly_downloaded
8+
9+
echo "Operation completed successfully."
Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
#!/bin/bash
22
set -e
33

4+
# This script interfaces with the user to download state at
5+
# a specific height from an archiver but doesn't do the actual
6+
# state replacement itself. Instead it provides a function to
7+
# replace the state to the main script that invokes this one.
8+
#
9+
# The reason for this is to allow the main download_state script
10+
# to do additional things before and after replacing the state
11+
# depending on whether or not it's run in a Docker container
12+
# without having to use environment variables.
13+
414
STATE_LIST_URL="https://nodes.dusk.network/state/list"
515

616
# Function to display a warning message
@@ -49,6 +59,12 @@ get_latest_state() {
4959
curl -f -L -s "$STATE_LIST_URL" | tail -n 1
5060
}
5161

62+
replace_state_with_newly_downloaded() {
63+
rm -rf /opt/dusk/rusk/state
64+
rm -rf /opt/dusk/rusk/chain.db
65+
tar -xvf /tmp/state.tar.gz -C /opt/dusk/rusk/
66+
}
67+
5268
# Check if an argument is provided, otherwise use the fallback value (348211)
5369
if [ "$1" = "--list" ]; then
5470
# List all possible states
@@ -74,12 +90,3 @@ if ! curl -f -so /tmp/state.tar.gz -L "$STATE_URL"; then
7490
echo "Error: Download failed. Exiting."
7591
exit 1
7692
fi
77-
78-
service rusk stop
79-
80-
rm -rf /opt/dusk/rusk/state
81-
rm -rf /opt/dusk/rusk/chain.db
82-
tar -xvf /tmp/state.tar.gz -C /opt/dusk/rusk/
83-
chown -R dusk:dusk /opt/dusk/
84-
85-
echo "Operation completed successfully."

bin/start-in-docker.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
cd /opt/dusk
6+
7+
export RUST_BACKTRACE=full
8+
export RUSK_PROFILE_PATH=/opt/dusk/rusk
9+
export RUSK_RECOVERY_INPUT=/opt/dusk/conf/genesis.toml
10+
11+
/opt/dusk/bin/rusk recovery state
12+
if [ -z "$DUSK_CONSENSUS_KEYS_PASS" ]; then
13+
echo "DUSK_CONSENSUS_KEYS_PASS is not set"
14+
exit 1
15+
fi
16+
17+
if [ ! -f /opt/dusk/conf/consensus.keys ]; then
18+
echo "Consensus keys file was not found. Mount it on /opt/dusk/conf/consensus.keys"
19+
exit 1
20+
fi
21+
22+
detect_ips_output=$(/opt/dusk/bin/detect_ips_inner.sh)
23+
export KADCAST_PUBLIC_ADDRESS=$(echo "$detect_ips_output" | sed -n '1p')
24+
export KADCAST_LISTEN_ADDRESS=$(echo "$detect_ips_output" | sed -n '2p')
25+
26+
/opt/dusk/bin/rusk --config /opt/dusk/conf/rusk.toml

bin/systemd/download_state.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/bash
2+
set -e
3+
4+
source /opt/dusk/bin/download_state_inner.sh
5+
6+
service rusk stop
7+
8+
replace_state_with_newly_downloaded
9+
10+
chown -R dusk:dusk /opt/dusk/
11+
12+
echo "Operation completed successfully."

0 commit comments

Comments
 (0)