A Kubernetes-native way of deploying Monero nodes, networks, and miners: express your intention and let Kubernetes run it for you.
you: "Hi, I'd like two public nodes, and three miners please".
k8s: "Sure thing"
k8s: "It looks like you want two public nodes, but I see 0 running - let me create them for you."
k8s: "It looks like you want three miners, but I see 0 running - let me create them for you."
you: "Actually, I changed my mind - I don't want to mine on
minexmr
, I wantcryptonode.social
".k8s: "Good choice, pool concentration sucks - let me update your miners for you :)"
See ./docs for detailed documentation about each resource.
To run a single full node, all you need to do is create a single-replica MoneroNodeSet
.
kind: MoneroNodeSet
apiVersion: utxo.com.br/v1alpha1
metadata:
name: full-node
spec:
replicas: 1
With a MoneroNodeSet
you express the intention of having a set of monerod
nodes running with a particular configuration: you express your intent, and
monero-operator
takes care of making it happen.
For instance, with kubectl-tree we can see that the operator took care of instantiating a StatefulSet
$ kubectl tree moneronodeset.utxo.com.br full-node
NAMESPACE NAME READY
default MoneroNodeSet/full-node True
default ├─Service/full-node -
default │ └─EndpointSlice/full-node-d2crv -
default └─StatefulSet/full-node -
default ├─ControllerRevision/full-node-856644d54d -
default └─Pod/full-node-0 True
with a pre-configured set of flags
$ kubectl get pod full-node-0 -ojsonpath={.spec.containers[*].command} | jq '.'
[
"monerod",
"--data-dir=/data",
"--log-file=/dev/stdout",
"--no-igd",
"--no-zmq",
"--non-interactive",
"--p2p-bind-ip=0.0.0.0",
"--p2p-bind-port=18080",
"--rpc-restricted-bind-ip=0.0.0.0",
"--rpc-restricted-bind-port=18089"
]
and a PersistentVolumeClaim attached with enough disk space for it.
$ kubectl get pvc
kubectl get pvc
NAME STATUS VOLUME CAPACITY
data-full-node-0 Bound pvc-1c60e835-d5f9-41c9-8509-b0e4b3b71f6b 200Gi
With that all being declarative, updating our node is a matter of expressing
our new intent by updating the MoneroNodeSet
definition, and letting the
operator take care of updating things behind the scene.
For instance, assuming we want to now make it public, accepting lots of peers, having a higher bandwidth limit and using dns blocklist and checkpointing, we'd patch the object with the following:
kind: MoneroNodeSet
apiVersion: utxo.com.br/v1alpha1
metadata:
name: full-set
spec:
replicas: 1
monerod:
args:
- --public
- --enable-dns-blocklist
- --enforce-dns-checkpointing
- --out-peers=1024
- --in-peers=1024
- --limit-rate=128000
Which would then lead to an update to the node (Kubernetes takes care of
signalling the monerod
, waiting for it to finish gracefully - did I mention
that it has properly set readiness probes too? -, detaching the disk, etc etc).
Similar to MoneroNodeSet
, with a MoneroMiningNodeSet
you express the
intention of having a cluster o x replicas running, and then the operator
takes care of making that happen.
For instance, to run a set of 5 miners spread across different Kubernetes nodes:
kind: MoneroMiningNodeSet
apiVersion: utxo.com.br/v1alpha1
metadata:
name: miners
spec:
replicas: 5
hardAntiAffinity: true
xmrig:
args:
- -o
- cryptonote.social:5556
- -u
- 891B5keCnwXN14hA9FoAzGFtaWmcuLjTDT5aRTp65juBLkbNpEhLNfgcBn6aWdGuBqBnSThqMPsGRjWVQadCrhoAT6CnSL3.node-$(id)
- --tls
ps.: $(id)
is indeed a thing - wherever you put it, it's going to be interpolated with an integer that identifies the replica
pps.: xmrig
is used under the hood
With a MoneroNetwork
you express the intention of having a network of
inter-connected Monero nodes, taking care of not only bringing monerod
up for
you, but also providing the proper flags for each daemon so that they are
exclusive nodes of themselves.
For instance, consider the following private regtest setup:
kind: MoneroNetwork
apiVersion: utxo.com.br/v1alpha1
metadata:
name: regtest
spec:
replicas: 3
template:
spec:
monerod:
args:
- --regtest
- --fixed-difficulty=1
Under the hood, the following tree of objects gets formed:
$ kubectl tree moneronetwork.utxo.com.br regtest
NAME
MoneroNetwork/regtest
├─MoneroNodeSet/regtest-0
│ ├─Service/regtest-0
│ │ └─EndpointSlice/regtest-0-plf9m
│ └─StatefulSet/regtest-0
│ ├─ControllerRevision/regtest-0-6dc6799f4b
│ └─Pod/regtest-0-0
├─MoneroNodeSet/regtest-1
│ ├─Service/regtest-1
│ │ └─EndpointSlice/regtest-1-7sd9z
│ └─StatefulSet/regtest-1
│ ├─ControllerRevision/regtest-1-5b5c6b7b8d
│ └─Pod/regtest-1-0
└─MoneroNodeSet/regtest-2
├─Service/regtest-2
│ └─EndpointSlice/regtest-2-rhmd9
└─StatefulSet/regtest-2
├─ControllerRevision/regtest-2-7fdbcdb57b
└─Pod/regtest-2-0
with each node with the flags properly set so that they are interconnected:
$ kubectl get pods -ojsonpath={.items[*].spec.containers[*].command} | jq '.'
[
"monerod",
"--add-exclusive-node=regtest-1",
"--add-exclusive-node=regtest-2",
"--fixed-difficulty=1",
...
]
[
"monerod",
"--add-exclusive-node=regtest-0",
"--add-exclusive-node=regtest-2",
"--fixed-difficulty=1",
...
]
[
"monerod",
"--add-exclusive-node=regtest-0",
"--add-exclusive-node=regtest-1",
"--fixed-difficulty=1",
...
]
- install
# submit the customresourcedefinition objects, deployment, role-base access
# control configs, etc.
#
kubectl apply -f ./config/release.yaml
See ./LICENSE