Skip to content

Commit bd86ebc

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/tls-support
2 parents 8257b95 + 2dc4385 commit bd86ebc

16 files changed

Lines changed: 313 additions & 28 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ All notable changes to this project will be documented in this file.
66

77
### Added
88

9+
- Add the role group as a node attribute ([#63]).
10+
11+
[#63]: https://github.com/stackabletech/opensearch-operator/pull/63
12+
13+
## [25.11.0] - 2025-11-07
14+
15+
## [25.11.0-rc1] - 2025-11-06
16+
17+
### Added
18+
919
- Add end-of-support checker which can be controlled with environment variables and CLI arguments ([#38]).
1020
- `EOS_CHECK_MODE` (`--eos-check-mode`) to set the EoS check mode. Currently, only "offline" is supported.
1121
- `EOS_INTERVAL` (`--eos-interval`) to set the interval in which the operator checks if it is EoS.
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
= Scaling OpenSearch clusters
2+
:description: OpenSearch clusters can be scaled after provisioning but manual steps are required.
3+
4+
OpenSearch clusters can be scaled after provisioning.
5+
CPU and memory settings can be easily adjusted, as detailed in the xref:opensearch:usage-guide/storage-resource-configuration.adoc#_resource_requests[Resource Requests].
6+
However, when changing the number of nodes or resizing volumes, the following considerations must be kept in mind.
7+
8+
Horizontal scaling, which involves adjusting the replica count of role groups, can be easily accomplished for non-data nodes by modifying the OpenSearchCluster specification.
9+
Additionally, the number of data nodes can be increased.
10+
However, reducing the number of data nodes requires manual intervention.
11+
If a pod that manages data is simply shut down, its data becomes inaccessible.
12+
Therefore, it is necessary to manually drain the data from the nodes before removing them.
13+
14+
Vertical scaling, which refers to changing the volume size of nodes, is not supported by the operator.
15+
Whether the size of a volume can be changed depends on its CSI driver.
16+
OpenSearch allows for multiple data paths within a single data node, but adding volumes to additional data paths typically does not resolve low disk space issues, as the data is not automatically rebalanced across multiple data paths.
17+
18+
[NOTE]
19+
====
20+
The OpenSearch operator is currently in the early stages of development.
21+
Smart scaling (adapting resources without data loss) and auto scaling (scaling the cluster based on load) are not supported.
22+
====
23+
24+
== Manually scaling
25+
26+
As noted earlier, scaling can be quite challenging;
27+
however, an easy workaround exists, which will be presented here.
28+
29+
For example, the following OpenSearchCluster has been deployed with three cluster-manager nodes and five small data nodes:
30+
31+
[source,yaml]
32+
----
33+
spec:
34+
nodes:
35+
roleGroups:
36+
cluster-manager:
37+
config:
38+
nodeRoles:
39+
- cluster_manager
40+
replicas: 3
41+
data-small:
42+
config:
43+
nodeRoles:
44+
- data
45+
- ingest
46+
- remote_cluster_client
47+
resources:
48+
storage:
49+
data:
50+
capacity: 10Gi
51+
replicas: 5
52+
----
53+
54+
You have decided that three large data nodes would be more suitable than five small ones.
55+
To implement this change, you can replace the role group `data-small` with your preferred option.
56+
57+
First, add the new role group `data-large` with three replicas, each having a capacity of 100 Gi per node:
58+
59+
[source,yaml]
60+
----
61+
spec:
62+
nodes:
63+
roleGroups:
64+
cluster-manager:
65+
config:
66+
nodeRoles:
67+
- cluster_manager
68+
replicas: 3
69+
data-small:
70+
config:
71+
nodeRoles:
72+
- data
73+
- ingest
74+
- remote_cluster_client
75+
resources:
76+
storage:
77+
data:
78+
capacity: 10Gi
79+
replicas: 5
80+
data-large:
81+
config:
82+
nodeRoles:
83+
- data
84+
- ingest
85+
- remote_cluster_client
86+
resources:
87+
storage:
88+
data:
89+
capacity: 100Gi
90+
replicas: 3
91+
----
92+
93+
The data must now be transferred from `data-small` to `data-large`.
94+
By using the cluster setting `cluster.routing.allocation.exclude`, you can exclude nodes from shard allocation.
95+
If rebalancing has not been disabled, existing data will automatically move from the specified nodes to the allowed ones—in this case, from `data-small` to `data-large`.
96+
97+
[TIP]
98+
====
99+
The OpenSearch operator assigns a role group attribute to each OpenSearch node, making it easier to reference all nodes associated with a specific role group.
100+
====
101+
102+
The following REST call excludes the `data-small` role group from shard allocation:
103+
104+
[source,http]
105+
----
106+
PUT _cluster/settings
107+
{
108+
"persistent": {
109+
"cluster": {
110+
"routing": {
111+
"allocation.exclude": {
112+
"role-group": "data-small"
113+
}
114+
}
115+
}
116+
}
117+
}
118+
----
119+
120+
You must wait until all data has been transferred from `data-small` to `data-large`.
121+
You can request the current shard allocation at the `_cat/shards` endpoint, for example:
122+
123+
[source,http]
124+
----
125+
GET _cat/shards?v
126+
index shard prirep state docs store ip node
127+
logs 0 r STARTED 14074 6.9mb 10.244.0.60 opensearch-nodes-data-large-2
128+
logs 0 p RELOCATING 14074 8.5mb 10.244.0.52 opensearch-nodes-data-small-4
129+
-> 10.244.0.59 NFjQBBmWSm-pijXcxrXnvQ opensearch-nodes-data-large-1
130+
...
131+
132+
GET _cat/shards?v
133+
index shard prirep state docs store ip node
134+
logs 0 r STARTED 14074 6.9mb 10.244.0.60 opensearch-nodes-data-large-2
135+
logs 0 p STARTED 14074 6.9mb 10.244.0.59 opensearch-nodes-data-large-1
136+
...
137+
----
138+
139+
Statistics, particularly the document count, can be retrieved from the `_nodes/role-group:data-small/stats` endpoint, for example:
140+
141+
[source,http]
142+
----
143+
GET _nodes/role-group:data-small/stats/indices/docs
144+
{
145+
"_nodes": {
146+
"total": 5,
147+
"successful": 5,
148+
"failed": 0
149+
},
150+
"cluster_name": "opensearch",
151+
"nodes": {
152+
"wjaeQJUXQX6eNWYUeiScgQ": {
153+
"timestamp": 1761992580239,
154+
"name": "opensearch-nodes-data-small-4",
155+
"transport_address": "10.244.0.52:9300",
156+
"host": "10.244.0.52",
157+
"ip": "10.244.0.52:9300",
158+
"roles": [
159+
"data",
160+
"ingest",
161+
"remote_cluster_client"
162+
],
163+
"attributes": {
164+
"role-group": "data-small",
165+
"shard_indexing_pressure_enabled": "true"
166+
},
167+
"indices": {
168+
"docs": {
169+
"count": 14686,
170+
"deleted": 0
171+
}
172+
}
173+
},
174+
...
175+
}
176+
}
177+
178+
GET _nodes/role-group:data-small/stats/indices/docs
179+
{
180+
"_nodes": {
181+
"total": 5,
182+
"successful": 5,
183+
"failed": 0
184+
},
185+
"cluster_name": "opensearch",
186+
"nodes": {
187+
"wjaeQJUXQX6eNWYUeiScgQ": {
188+
"timestamp": 1761992817422,
189+
"name": "opensearch-nodes-data-small-4",
190+
"transport_address": "10.244.0.52:9300",
191+
"host": "10.244.0.52",
192+
"ip": "10.244.0.52:9300",
193+
"roles": [
194+
"data",
195+
"ingest",
196+
"remote_cluster_client"
197+
],
198+
"attributes": {
199+
"role-group": "data-small",
200+
"shard_indexing_pressure_enabled": "true"
201+
},
202+
"indices": {
203+
"docs": {
204+
"count": 0,
205+
"deleted": 0
206+
}
207+
}
208+
},
209+
...
210+
}
211+
}
212+
213+
----
214+
215+
Once all shards have been transferred, the `data-small` role group can be removed from the OpenSearchCluster specification:
216+
217+
[source,yaml]
218+
----
219+
spec:
220+
nodes:
221+
roleGroups:
222+
cluster-manager:
223+
config:
224+
nodeRoles:
225+
- cluster_manager
226+
replicas: 3
227+
data-large:
228+
config:
229+
nodeRoles:
230+
- data
231+
- ingest
232+
- remote_cluster_client
233+
resources:
234+
storage:
235+
data:
236+
capacity: 100Gi
237+
replicas: 3
238+
----
239+
240+
Finally, the shard exclusion should be removed from the cluster settings:
241+
242+
[source,http]
243+
----
244+
PUT _cluster/settings
245+
{
246+
"persistent": {
247+
"cluster": {
248+
"routing": {
249+
"allocation.exclude": {
250+
"role-group": null
251+
}
252+
}
253+
}
254+
}
255+
}
256+
----
257+
258+
If your OpenSearch clients connected to the cluster exclusively through the cluster-manager nodes, the switch from one data role group to another should have been seamless for them.

docs/modules/opensearch/pages/usage-guide/storage-resource-configuration.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ nodes:
1313
config:
1414
resources:
1515
storage:
16-
logDirs:
16+
data:
1717
capacity: 50Gi
1818
----
1919

docs/modules/opensearch/partials/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
** xref:opensearch:usage-guide/monitoring.adoc[]
1010
** xref:opensearch:usage-guide/logging.adoc[]
1111
** xref:opensearch:usage-guide/opensearch-dashboards.adoc[]
12+
** xref:opensearch:usage-guide/scaling.adoc[]
1213
** xref:opensearch:usage-guide/operations/index.adoc[]
1314
*** xref:opensearch:usage-guide/operations/cluster-operations.adoc[]
1415
*** xref:opensearch:usage-guide/operations/pod-placement.adoc[]

rust/operator-binary/src/controller/build/node_config.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
controller::OpenSearchRoleGroupConfig,
1111
crd::v1alpha1,
1212
framework::{
13-
ServiceName,
13+
RoleGroupName, ServiceName,
1414
builder::pod::container::{EnvVarName, EnvVarSet},
1515
role_group_utils,
1616
},
@@ -41,6 +41,10 @@ pub const CONFIG_OPTION_INITIAL_CLUSTER_MANAGER_NODES: &str =
4141
/// Type: string
4242
pub const CONFIG_OPTION_NETWORK_HOST: &str = "network.host";
4343

44+
/// The custom node attribute "role-group"
45+
/// Type: string
46+
pub const CONFIG_OPTION_NODE_ATTR_ROLE_GROUP: &str = "node.attr.role-group";
47+
4448
/// A descriptive name for the node.
4549
/// Type: string
4650
pub const CONFIG_OPTION_NODE_NAME: &str = "node.name";
@@ -98,6 +102,7 @@ const DEFAULT_OPENSEARCH_HOME: &str = "/stackable/opensearch";
98102
/// Configuration of an OpenSearch node based on the cluster and role-group configuration
99103
pub struct NodeConfig {
100104
cluster: ValidatedCluster,
105+
role_group_name: RoleGroupName,
101106
role_group_config: OpenSearchRoleGroupConfig,
102107
pub discovery_service_name: ServiceName,
103108
}
@@ -107,11 +112,13 @@ pub struct NodeConfig {
107112
impl NodeConfig {
108113
pub fn new(
109114
cluster: ValidatedCluster,
115+
role_group_name: RoleGroupName,
110116
role_group_config: OpenSearchRoleGroupConfig,
111117
discovery_service_name: ServiceName,
112118
) -> Self {
113119
Self {
114120
cluster,
121+
role_group_name,
115122
role_group_config,
116123
discovery_service_name,
117124
}
@@ -169,6 +176,10 @@ impl NodeConfig {
169176
CONFIG_OPTION_PLUGINS_SECURITY_NODES_DN.to_owned(),
170177
json!(["CN=generated certificate for pod".to_owned()]),
171178
);
179+
config.insert(
180+
CONFIG_OPTION_NODE_ATTR_ROLE_GROUP.to_owned(),
181+
json!(self.role_group_name),
182+
);
172183

173184
config
174185
}
@@ -424,6 +435,8 @@ mod tests {
424435
let image: ProductImage = serde_json::from_str(r#"{"productVersion": "3.1.0"}"#)
425436
.expect("should be a valid ProductImage");
426437

438+
let role_group_name = RoleGroupName::from_str_unsafe("data");
439+
427440
let role_group_config = OpenSearchRoleGroupConfig {
428441
replicas: test_config.replicas,
429442
config: ValidatedOpenSearchConfig {
@@ -490,6 +503,7 @@ mod tests {
490503

491504
NodeConfig::new(
492505
cluster,
506+
role_group_name,
493507
role_group_config,
494508
ServiceName::from_str_unsafe("my-opensearch-cluster-manager"),
495509
)
@@ -507,6 +521,7 @@ mod tests {
507521
"cluster.name: \"my-opensearch-cluster\"\n",
508522
"discovery.type: \"zen\"\n",
509523
"network.host: \"0.0.0.0\"\n",
524+
"node.attr.role-group: \"data\"\n",
510525
"plugins.security.nodes_dn: [\"CN=generated certificate for pod\"]\n",
511526
"plugins.security.ssl.http.enabled: \"true\"\n",
512527
"plugins.security.ssl.http.pemcert_filepath: \"/stackable/opensearch/config/tls/http/tls.crt\"\n",

rust/operator-binary/src/controller/build/role_group_builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ impl<'a> RoleGroupBuilder<'a> {
107107
cluster: cluster.clone(),
108108
node_config: NodeConfig::new(
109109
cluster.clone(),
110+
role_group_name.clone(),
110111
role_group_config.clone(),
111112
discovery_service_name,
112113
),

tests/templates/kuttl/external-access/opensearch.yaml.j2

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ spec:
77
image:
88
{% if test_scenario['values']['opensearch'].find(",") > 0 %}
99
custom: "{{ test_scenario['values']['opensearch'].split(',')[1] }}"
10-
productVersion: "{{ test_scenario['values']['opensearch'].split(',')[0] }}"
11-
{% else %}
12-
productVersion: "{{ test_scenario['values']['opensearch'] }}"
1310
{% endif %}
11+
productVersion: "{{ test_scenario['values']['opensearch'].split(',')[0] }}"
1412
pullPolicy: IfNotPresent
1513
{% if lookup('env', 'VECTOR_AGGREGATOR') %}
1614
vectorAggregatorConfigMapName: vector-aggregator-discovery

0 commit comments

Comments
 (0)