feat(config): integrate etcd for service discovery and config management

- Add etcd-client dependency for distributed configuration storage
- Implement EtcdConfig with priority: etcd > environment variables > defaults
- Add ServiceRegistry for service registration with lease keep-alive
- Integrate etcd-based service discovery for appks gRPC connections
- Add service watcher for real-time service instance updates
- Migrate Redis configuration from single URL to cluster node list
- Update Dockerfile with default IMKS_HOST and IMKS_PORT environment variables
- Add etcd bootstrap configuration through environment variables
- Implement Redis cluster URL building with optional authentication
This commit is contained in:
zhenyi
2026-06-11 22:50:38 +08:00
parent e72866db8d
commit c794b818ff
24 changed files with 984 additions and 952 deletions
+1 -4
View File
@@ -32,10 +32,7 @@ impl Default for TelemetryConfig {
Self {
service_name: env_or("OTEL_SERVICE_NAME", "imks"),
service_version: env_or("OTEL_SERVICE_VERSION", env!("CARGO_PKG_VERSION")),
otlp_endpoint: env_or(
"OTEL_EXPORTER_OTLP_ENDPOINT",
"http://localhost:4317",
),
otlp_endpoint: env_or("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317"),
otlp_protocol: detect_otlp_protocol(),
traces_enabled: env_bool("OTEL_TRACES_ENABLED", true),
metrics_enabled: env_bool("OTEL_METRICS_ENABLED", true),
+2 -9
View File
@@ -52,10 +52,7 @@ pub fn connections_active_count() -> u64 {
/// Returns the server uptime in seconds.
pub fn uptime_secs() -> u64 {
START_TIME
.get()
.map(|t| t.elapsed().as_secs())
.unwrap_or(0)
START_TIME.get().map(|t| t.elapsed().as_secs()).unwrap_or(0)
}
#[derive(Debug, Clone, Serialize)]
@@ -147,11 +144,7 @@ pub async fn health_check(checks: actix_web::web::Data<Arc<HealthCheckFns>>) ->
.iter()
.filter_map(|c| c.as_ref())
.all(|c| c.status == "up");
if all_up {
"healthy"
} else {
"degraded"
}
if all_up { "healthy" } else { "degraded" }
} else {
"healthy"
};
+7 -7
View File
@@ -2,12 +2,12 @@
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_otlp::{LogExporter, Protocol, WithExportConfig};
use opentelemetry_sdk::logs::SdkLoggerProvider;
use opentelemetry_sdk::Resource;
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::layer::SubscriberExt;
use opentelemetry_sdk::logs::SdkLoggerProvider;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::Registry;
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::layer::SubscriberExt;
use super::config::{OtlpProtocol, TelemetryConfig};
use crate::ImksResult;
@@ -35,7 +35,9 @@ pub fn init_subscriber(
let (logger_provider, log_bridge_layer) = if config.logs_enabled {
let exporter = build_log_exporter(config)?;
let resource = resource.cloned().unwrap_or_else(|| Resource::builder().build());
let resource = resource
.cloned()
.unwrap_or_else(|| Resource::builder().build());
let provider = SdkLoggerProvider::builder()
.with_resource(resource)
@@ -72,9 +74,7 @@ pub fn init_subscriber(
set_subscriber(subscriber);
}
(None, None) => {
let subscriber = Registry::default()
.with(env_filter)
.with(make_json_fmt());
let subscriber = Registry::default().with(env_filter).with(make_json_fmt());
set_subscriber(subscriber);
}
}
+8 -4
View File
@@ -2,11 +2,11 @@
use std::sync::OnceLock;
use opentelemetry::KeyValue;
use opentelemetry::global;
use opentelemetry::metrics::{Counter, Histogram, Meter, UpDownCounter};
use opentelemetry::KeyValue;
use opentelemetry_sdk::metrics::SdkMeterProvider;
use opentelemetry_sdk::Resource;
use opentelemetry_sdk::metrics::SdkMeterProvider;
use prometheus::{Encoder, Registry, TextEncoder};
use crate::ImksResult;
@@ -47,7 +47,9 @@ pub fn init_metrics(
let exporter = opentelemetry_prometheus::exporter()
.with_registry(registry)
.build()
.map_err(|e| crate::ImksError::Internal(format!("failed to build Prometheus exporter: {e}")))?;
.map_err(|e| {
crate::ImksError::Internal(format!("failed to build Prometheus exporter: {e}"))
})?;
let provider = SdkMeterProvider::builder()
.with_resource(resource.clone())
@@ -153,7 +155,9 @@ impl MetricsInstruments {
///
/// Encodes the Prometheus text format from the shared registry.
pub async fn metrics_handler() -> actix_web::HttpResponse {
let registry = PROMETHEUS_REGISTRY.get().expect("Prometheus registry not initialized");
let registry = PROMETHEUS_REGISTRY
.get()
.expect("Prometheus registry not initialized");
let metric_families = registry.gather();
let encoder = TextEncoder::new();
+2 -4
View File
@@ -2,9 +2,9 @@
use opentelemetry::trace::TracerProvider as _;
use opentelemetry_otlp::{Protocol, SpanExporter, WithExportConfig};
use opentelemetry_sdk::Resource;
use opentelemetry_sdk::propagation::TraceContextPropagator;
use opentelemetry_sdk::trace::{SdkTracerProvider, Tracer};
use opentelemetry_sdk::Resource;
use tracing_opentelemetry::OpenTelemetryLayer;
use tracing_subscriber::Registry;
@@ -24,9 +24,7 @@ fn build_span_exporter(config: &TelemetryConfig) -> ImksResult<SpanExporter> {
.with_protocol(Protocol::HttpBinary)
.with_endpoint(&config.otlp_endpoint)
.build()
.map_err(|e| {
crate::ImksError::Internal(format!("OTLP HTTP span exporter: {e}"))
}),
.map_err(|e| crate::ImksError::Internal(format!("OTLP HTTP span exporter: {e}"))),
}
}