Skip to content

Commit

Permalink
feat: Support configuring JVM arguments (#919)
Browse files Browse the repository at this point in the history
* feat: Support configuring JVM arguments

* changelog

* Well I'm stupid...

* cargo fmt
  • Loading branch information
sbernauer authored Feb 27, 2025
1 parent b84bcf4 commit e01bf3d
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 86 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
config property `requestedSecretLifetime`. This helps reduce frequent Pod restarts ([#892]).
- Run a `containerdebug` process in the background of each Zookeeper container to collect debugging information ([#881]).
- Aggregate emitted Kubernetes events on the CustomResources ([#904]).
- Support configuring JVM arguments ([#919]).

### Changed

Expand All @@ -19,6 +20,7 @@ All notable changes to this project will be documented in this file.
[#892]: https://github.com/stackabletech/zookeeper-operator/pull/892
[#904]: https://github.com/stackabletech/zookeeper-operator/pull/904
[#905]: https://github.com/stackabletech/zookeeper-operator/pull/905
[#919]: https://github.com/stackabletech/zookeeper-operator/pull/919

## [24.11.1] - 2025-01-10

Expand Down
52 changes: 52 additions & 0 deletions deploy/helm/zookeeper-operator/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,32 @@ spec:
default: {}
description: '`envOverrides` configure environment variables to be set in the Pods. It is a map from strings to strings - environment variables and the value to set. Read the [environment variable overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#env-overrides) for more information and consult the operator specific usage guide to find out about the product specific environment variables that are available.'
type: object
jvmArgumentOverrides:
default:
add: []
remove: []
removeRegex: []
description: Allows overriding JVM arguments. Please read on the [JVM argument overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#jvm-argument-overrides) for details on the usage.
properties:
add:
default: []
description: JVM arguments to be added
items:
type: string
type: array
remove:
default: []
description: JVM arguments to be removed by exact match
items:
type: string
type: array
removeRegex:
default: []
description: JVM arguments matching any of this regexes will be removed
items:
type: string
type: array
type: object
podOverrides:
default: {}
description: In the `podOverrides` property you can define a [PodTemplateSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#podtemplatespec-v1-core) to override any property that can be set on a Kubernetes Pod. Read the [Pod overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#pod-overrides) for more information.
Expand Down Expand Up @@ -698,6 +724,32 @@ spec:
default: {}
description: '`envOverrides` configure environment variables to be set in the Pods. It is a map from strings to strings - environment variables and the value to set. Read the [environment variable overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#env-overrides) for more information and consult the operator specific usage guide to find out about the product specific environment variables that are available.'
type: object
jvmArgumentOverrides:
default:
add: []
remove: []
removeRegex: []
description: Allows overriding JVM arguments. Please read on the [JVM argument overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#jvm-argument-overrides) for details on the usage.
properties:
add:
default: []
description: JVM arguments to be added
items:
type: string
type: array
remove:
default: []
description: JVM arguments to be removed by exact match
items:
type: string
type: array
removeRegex:
default: []
description: JVM arguments matching any of this regexes will be removed
items:
type: string
type: array
type: object
podOverrides:
default: {}
description: In the `podOverrides` property you can define a [PodTemplateSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#podtemplatespec-v1-core) to override any property that can be set on a Kubernetes Pod. Read the [Pod overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#pod-overrides) for more information.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,11 @@ servers:

The ZooKeeper operator also supports Pod overrides, allowing you to override any property that you can set on a Kubernetes Pod.
Read the xref:concepts:overrides.adoc#pod-overrides[Pod overrides documentation] to learn more about this feature.

== JVM argument overrides

Stackable operators automatically determine the set of needed JVM arguments, such as memory settings or trust- and keystores.
Using JVM argument overrides you can configure the JVM arguments xref:concepts:overrides.adoc#jvm-argument-overrides[according to the concepts page].

One thing that is different for Zookeeper, is that all heap-related arguments will be passed in via the env variable `ZK_SERVER_HEAP`, all the other ones via `SERVER_JVMFLAGS`.
`ZK_SERVER_HEAP` can *not* have a unit suffix, it will always be an integer representing the number of megabytes heap available.
2 changes: 1 addition & 1 deletion docs/modules/zookeeper/partials/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
** xref:zookeeper:usage_guide/log_aggregation.adoc[]
** xref:zookeeper:usage_guide/using_multiple_role_groups.adoc[]
** xref:zookeeper:usage_guide/isolating_clients_with_znodes.adoc[]
** xref:zookeeper:usage_guide/configuration_environment_overrides.adoc[]
** xref:zookeeper:usage_guide/overrides.adoc[]
** xref:zookeeper:usage_guide/operations/index.adoc[]
*** xref:zookeeper:usage_guide/operations/cluster-operations.adoc[]
*** xref:zookeeper:usage_guide/operations/pod-placement.adoc[]
Expand Down
194 changes: 194 additions & 0 deletions rust/operator-binary/src/config/jvm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
use snafu::{OptionExt, ResultExt, Snafu};
use stackable_operator::{
memory::{BinaryMultiple, MemoryQuantity},
role_utils::{self, GenericRoleConfig, JavaCommonConfig, JvmArgumentOverrides, Role},
};

use crate::crd::{
v1alpha1::{ZookeeperCluster, ZookeeperConfig, ZookeeperConfigFragment},
LoggingFramework, JVM_SECURITY_PROPERTIES_FILE, LOG4J_CONFIG_FILE, LOGBACK_CONFIG_FILE,
METRICS_PORT, STACKABLE_CONFIG_DIR, STACKABLE_LOG_CONFIG_DIR,
};

const JAVA_HEAP_FACTOR: f32 = 0.8;

#[derive(Snafu, Debug)]
pub enum Error {
#[snafu(display("invalid memory resource configuration - missing default or value in crd?"))]
MissingMemoryResourceConfig,

#[snafu(display("invalid memory config"))]
InvalidMemoryConfig {
source: stackable_operator::memory::Error,
},

#[snafu(display("failed to merge jvm argument overrides"))]
MergeJvmArgumentOverrides { source: role_utils::Error },
}

/// All JVM arguments.
fn construct_jvm_args(
zk: &ZookeeperCluster,
role: &Role<ZookeeperConfigFragment, GenericRoleConfig, JavaCommonConfig>,
role_group: &str,
) -> Result<Vec<String>, Error> {
let logging_framework = zk.logging_framework();

let jvm_args = vec![
format!("-Djava.security.properties={STACKABLE_CONFIG_DIR}/{JVM_SECURITY_PROPERTIES_FILE}"),
format!("-javaagent:/stackable/jmx/jmx_prometheus_javaagent.jar={METRICS_PORT}:/stackable/jmx/server.yaml"),
match logging_framework {
LoggingFramework::LOG4J => format!("-Dlog4j.configuration=file:{STACKABLE_LOG_CONFIG_DIR}/{LOG4J_CONFIG_FILE}"),
LoggingFramework::LOGBACK => format!("-Dlogback.configurationFile={STACKABLE_LOG_CONFIG_DIR}/{LOGBACK_CONFIG_FILE}"),
},
];

let operator_generated = JvmArgumentOverrides::new_with_only_additions(jvm_args);
let merged = role
.get_merged_jvm_argument_overrides(role_group, &operator_generated)
.context(MergeJvmArgumentOverridesSnafu)?;
Ok(merged
.effective_jvm_config_after_merging()
// Sorry for the clone, that's how operator-rs is currently modelled :P
.clone())
}

/// Arguments that go into `SERVER_JVMFLAGS`, so *not* the heap settings (which you can get using
/// [`construct_zk_server_heap_env`]).
pub fn construct_non_heap_jvm_args(
zk: &ZookeeperCluster,
role: &Role<ZookeeperConfigFragment, GenericRoleConfig, JavaCommonConfig>,
role_group: &str,
) -> Result<String, Error> {
let mut jvm_args = construct_jvm_args(zk, role, role_group)?;
jvm_args.retain(|arg| !is_heap_jvm_argument(arg));

Ok(jvm_args.join(" "))
}

/// This will be put into `ZK_SERVER_HEAP`, which is just the heap size in megabytes (*without* the `m`
/// unit prepended).
pub fn construct_zk_server_heap_env(merged_config: &ZookeeperConfig) -> Result<String, Error> {
let heap_size_in_mb = (MemoryQuantity::try_from(
merged_config
.resources
.memory
.limit
.as_ref()
.context(MissingMemoryResourceConfigSnafu)?,
)
.context(InvalidMemoryConfigSnafu)?
* JAVA_HEAP_FACTOR)
.scale_to(BinaryMultiple::Mebi);

Ok((heap_size_in_mb.value.floor() as u32).to_string())
}

fn is_heap_jvm_argument(jvm_argument: &str) -> bool {
let lowercase = jvm_argument.to_lowercase();

lowercase.starts_with("-xms") || lowercase.starts_with("-xmx")
}

#[cfg(test)]
mod tests {
use super::*;
use crate::crd::{v1alpha1::ZookeeperConfig, ZookeeperRole};

#[test]
fn test_construct_jvm_arguments_defaults() {
let input = r#"
apiVersion: zookeeper.stackable.tech/v1alpha1
kind: ZookeeperCluster
metadata:
name: simple-zookeeper
spec:
image:
productVersion: "3.9.2"
servers:
roleGroups:
default:
replicas: 1
"#;
let (zookeeper, merged_config, role, rolegroup) = construct_boilerplate(input);
let non_heap_jvm_args = construct_non_heap_jvm_args(&zookeeper, &role, &rolegroup).unwrap();
let zk_server_heap_env = construct_zk_server_heap_env(&merged_config).unwrap();

assert_eq!(
non_heap_jvm_args,
"-Djava.security.properties=/stackable/config/security.properties \
-javaagent:/stackable/jmx/jmx_prometheus_javaagent.jar=9505:/stackable/jmx/server.yaml \
-Dlogback.configurationFile=/stackable/log_config/logback.xml"
);
assert_eq!(zk_server_heap_env, "409");
}

#[test]
fn test_construct_jvm_argument_overrides() {
let input = r#"
apiVersion: zookeeper.stackable.tech/v1alpha1
kind: ZookeeperCluster
metadata:
name: simple-zookeeper
spec:
image:
productVersion: "3.9.2"
servers:
config:
resources:
memory:
limit: 42Gi
jvmArgumentOverrides:
add:
- -Dhttps.proxyHost=proxy.my.corp
- -Dhttps.proxyPort=8080
- -Djava.net.preferIPv4Stack=true
roleGroups:
default:
replicas: 1
jvmArgumentOverrides:
# We need more memory!
removeRegex:
- -Xmx.*
- -Dhttps.proxyPort=.*
add:
- -Xmx40000m
- -Dhttps.proxyPort=1234
"#;
let (zookeeper, merged_config, role, rolegroup) = construct_boilerplate(input);
let non_heap_jvm_args = construct_non_heap_jvm_args(&zookeeper, &role, &rolegroup).unwrap();
let zk_server_heap_env = construct_zk_server_heap_env(&merged_config).unwrap();

assert_eq!(
non_heap_jvm_args,
"-Djava.security.properties=/stackable/config/security.properties \
-javaagent:/stackable/jmx/jmx_prometheus_javaagent.jar=9505:/stackable/jmx/server.yaml \
-Dlogback.configurationFile=/stackable/log_config/logback.xml \
-Dhttps.proxyHost=proxy.my.corp \
-Djava.net.preferIPv4Stack=true \
-Dhttps.proxyPort=1234"
);
assert_eq!(zk_server_heap_env, "34406");
}

fn construct_boilerplate(
zookeeper_cluster: &str,
) -> (
ZookeeperCluster,
ZookeeperConfig,
Role<ZookeeperConfigFragment, GenericRoleConfig, JavaCommonConfig>,
String,
) {
let zookeeper: ZookeeperCluster =
serde_yaml::from_str(zookeeper_cluster).expect("illegal test input");

let zookeeper_role = ZookeeperRole::Server;
let rolegroup_ref = zookeeper.server_rolegroup_ref("default");
let merged_config = zookeeper
.merged_config(&zookeeper_role, &rolegroup_ref)
.unwrap();
let role = zookeeper.spec.servers.clone().unwrap();

(zookeeper, merged_config, role, "default".to_owned())
}
}
1 change: 1 addition & 0 deletions rust/operator-binary/src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod jvm;
Loading

0 comments on commit e01bf3d

Please sign in to comment.