Cluster Configuration
Upon agent installation, the node is considered part of its own 1-node cluster.
In /etc/opensvc/cluster.conf:
cluster.secretis initialized to a random value.cluster.nameis initialized to a random value.
Bootstrap a new cluster
If the node is to be enrolled in an existing cluster, skip this section.
Add Heartbeats
If the cluster seed node has no heartbeat setup, a unicast heartbeat with default settings is automatically added on daemon start.
This default heartbeat requires every nodename to be resolved to an ip address reachable on 1215/tcp.
If these requirements are not met, you can setup one or more custom heartbeats on the seed node before enrolling nodes.
For example, a custom heartbeat configuration would be:
om cluster config update --set hb#2.type=unicast --set hb#2.port=10002
The heartbeats status is summarized in the top section of the monitoring command output:
om mon
And the detailed status is shown by:
om daemon hb status
➡️ See Also
Add Stonith methods
Stonith is optional. Skip to the next section if not concerned.
On a new cluster, the stonith configuration can be applied on the first node. The enrolled nodes fetch this configuration from the cluster.
For example, a dummy stonith configuration would be
om cluster config update --set stonith#node2.command=/bin/true
This configuration will execute /bin/true on the node taking over a service which was previously running on the now stalled node2.
Good, isolated fencing packages are freely available. For one, https://github.com/ClusterLabs/fence-agents
Enroll a Node
A node is added to the cluster from the cluster side: a cluster node enrolls the new node, which leaves its own 1-node cluster and joins.
The node to enroll must be a 1-node cluster. A node that is still a member of a cluster with peers is refused, because its peers would keep it in their cluster.nodes.
The API tabs assume the
$TOKENand the listener endpoint set up in Cluster API. That token is a cluster token: it speaks tonode1only. The node to enroll is not a member of this cluster yet, so the calls addressed to it carry credentials of its own.
On the node to enroll node2, create a token with the join role, and store it in a file:
sudo om daemon auth --role join --duration 10m >/root/node2.token
The token is valid for 60 seconds by default, so --duration gives time to copy the file to a cluster node.
On any cluster node, for example node1, enroll node2 using the copied token file:
sudo om cluster enroll --node node2 --token /root/node2.token --wait
Node node2 accepted the join order
Cluster nodes updated
Waiting for node2 to beat
Node node2 is alive
--nodeis the location of the node to enroll, in the[<scheme>://]<addr>[:<port>]format.--tokenis the path of the token file. TheOSVC_JOIN_TOKENenvironment variable is used when the flag is not set.--join-addris the locationnode2must use to reachnode1, in the same format. By default, a name the cluster certificate is valid for is used. Set it when this name does not resolve to an address reachable fromnode2.--waitwaits for thenode2heartbeat to beat in the cluster. Without it, follow the join withom daemon events --filter 'NodeAlive,.node=node2'.--timeoutis the maximum duration of the join,1hby default.
The enroll requires the root grant.
The same enroll, driven from a host that is a member of neither cluster.
1. Create a user on the node to enroll
POST /api/auth/token only accepts the requests authenticated by the unix socket or by Basic Authentication, and a node freshly installed has no api user yet. So this first step is run locally on node2, once:
om system/usr/enroller create --kw grant=join
om system/usr/enroller key add --name=password --from /dev/urandom
om system/usr/enroller key decode --name=password
2. Ask that node for a token with the join role
curl -sSk -u enroller:$ENROLLER_PASSWORD -X POST \
"https://node2:1215/api/auth/token?role=join&access_duration=10m" \
| jq -r .access_token >/root/node2.token
role=joinis filtered against the grants of the user: the token carries thejoinrole only if the user holds it.access_durationdefaults to 60 seconds, which gives little time for the next call.-kis needed here:node2is still its own cluster, so its listener certificate is signed by its own certificate authority, which nothing outside of it trusts yet. That is also why thejoinrole token carries acaclaim: it is what lets the cluster trust that certificate for the rest of the enroll.
3. Post the enroll order to a cluster node
curl -s -H "Authorization: Bearer $TOKEN" -X POST \
"https://node1:1215/api/cluster/enroll" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile t /root/node2.token '{node:"node2", token:($t|rtrimstr("\n")), timeout:"1h"}')"
{"node":"node2"}
nodeis the location of the node to enroll, in the[<scheme>://]<addr>[:<port>]format. The scheme defaults tohttpsand the port to the daemon listener port.tokenis the token obtained at the previous step.join_addris the locationnode2must use to reachnode1, in the same format. By default, a name the cluster certificate is valid for is used. Set it when this name does not resolve to an address reachable fromnode2.timeoutis the lifetime of thejoinrole token the cluster hands tonode2,1hby default. It must outlive the node drain.
The requester must be granted the root role.
The response is sent as soon as node2 has accepted the join order, and carries the nodename it reads from its own configuration. The join itself runs asynchronously.
4. Follow the join
curl -s -N -H "Authorization: Bearer $TOKEN" \
"https://node1:1215/api/node/name/node1/daemon/event?duration=1h&filter=JoinSuccess,added_node=node2&filter=JoinIgnored,candidate_node=node2&filter=JoinError,candidate_node=node2&filter=NodeAlive,.node=node2"
JoinSuccesssays the api node has addednode2to itscluster.nodes. The node has not drained, reconfigured, or restarted yet at this point.JoinIgnoredsaysnode2already was a cluster node.JoinErrorsays the cluster nodes update failed.NodeAlivesays thenode2heartbeat beats in the cluster: it has fully joined. This is whatom cluster enroll --waitwaits for.
The enrolled node is drained before it leaves its cluster. As a 1-node cluster has nowhere to relocate its instances, they are stopped, stay down, and their configurations are removed.
Its configuration directory is moved to the backup directory, then the cluster, heartbeat and certificate configurations are fetched from the cluster.
Collector Registration
The collector registration id of the enrolled node, its node.uuid keyword, is in the configuration directory moved aside. When the node was registered on the collector, the join keeps it registered:
- Before its daemon starts, the enrolled node asks the cluster node it joins through to register it on the collector, with the registration id it held before the join.
- That cluster node, authenticated on the collector by its own registration id, registers it. The collector moves the enrolled node to the app and the cluster of that node, and hands it a new registration id, which the enrolled node stores in its
node.uuid. - Once its daemon has started, the enrolled node pushes the inventory
om node registerpushes: asset, packages, disks, sysreport and checks.
This requires a collector server api 1.0.4 or later, allowing a node to register another one (the collector allow_node_enroll setting, on by default), and a cluster node registered on it.
The join does not depend on the collector: a node that was not registered is left unregistered, and a node the collector refuses to register is warned. Register it with:
om node register --credential /root/collector.cred
➡️ See Also
Evict a Node
A node is removed from the cluster by another cluster node. The evicted node leaves the cluster, then restarts its daemon alone, in a new 1-node cluster.
The node to evict must be drained: frozen, with no running instance. Else its instances would stay up on a node the cluster no longer knows about, free to start a second time elsewhere.
On any other cluster node, for example node1, drain then evict node2:
sudo om node drain --node node2 --wait
sudo om cluster evict --node node2 --credential /root/node2.cred --wait
Node node2 accepted the leave order
Cluster nodes updated
Node node2 is restarting alone, its instances stay down
--nodeis the name of the cluster node to evict. It can not be the local node: run the command on one of its peers.--credentialis the path of a file holding the<username>:<password>of a user to create on the evicted node, in thesystemnamespace with therootgrant. TheOSVC_CREDENTIALenvironment variable is used when the flag is not set.--waitwaits for the cluster nodes to be updated. Without it, follow the eviction withom daemon events --filter 'LeaveSuccess,removed_node=node2'.--timeoutis the maximum duration of the leave,1hby default.
The evict requires the root grant.
1. Drain the node to evict
curl -s -H "Authorization: Bearer $TOKEN" -X POST \
"https://node1:1215/api/node/name/node2/action/drain"
{"orchestration_id":"e2ba0d0e-...."}
The drain is asynchronous, and the eviction is refused with a 409 while node2 is not drained, so wait for the drain to end before the next call. A drained node is frozen and runs no instance, which GET /api/cluster/status reports.
2. Post the evict order to another cluster node
curl -s -H "Authorization: Bearer $TOKEN" -X POST \
"https://node1:1215/api/cluster/evict" \
-H "Content-Type: application/json" \
-d '{"nodename":"node2","credential":"admin:secret","timeout":"1h"}'
{"detail":"node node2 is leaving the cluster","status":200,"title":"background cluster leave has been called"}
nodenameis the cluster node to evict. It can not be the node serving the request: post this to one of its peers.credentialis the<username>:<password>of a user to create on the evicted node, in thesystemnamespace with therootgrant. It is optional, and checked before anything is ordered.timeoutis the maximum duration of the leave forked on the evicted node,1hby default.
The requester must be granted the root role.
The response is sent as soon as node2 has accepted the leave order. The leave itself runs asynchronously.
3. Follow the eviction
curl -s -N -H "Authorization: Bearer $TOKEN" \
"https://node1:1215/api/node/name/node1/daemon/event?duration=1h&filter=LeaveSuccess,removed_node=node2&filter=LeaveIgnored,candidate_node=node2&filter=LeaveError,candidate_node=node2"
LeaveSuccesssays the cluster has droppednode2from itscluster.nodes. This is whatom cluster evict --waitwaits for.LeaveIgnoredsaysnode2was not a cluster node.LeaveErrorsays the cluster nodes update failed.
The daemon restart and the user creation happen after LeaveSuccess, on a node this cluster no longer observes, so no event reports them.
The evicted node ends up with a cluster secret of its own, so no user, token or certificate of the cluster can reach its api anymore. Without a credential, its api is only reachable locally, as root.
Its configuration directory is moved to the backup directory.
➡️ See Also