소스 검색

Add configurable CPU count option for containerized environments (#1717)

This PR adds a new `--cpus` configuration option to address CPU
detection issues in virtualized and containerized environments where
`_SC_NPROCESSORS_CONF` and `_SC_NPROCESSORS_ONLN` return host CPU counts
instead of allocated container CPUs.

## Problem
In containerized deployments, coturn detects the host's CPU count (e.g.,
128 CPUs) instead of the container's allocated CPUs (e.g., 2 CPUs). This
causes the server to create excessive relay threads and database
connections, leading to resource exhaustion and performance issues.

## Solution
Added a new `cpus` configuration option that allows manual override of
CPU detection:

### Command Line Usage
```bash
turnserver --cpus 2
```

### Configuration File Usage
```ini
# Override system CPU count detection for containers
cpus=2
```

## Key Features
- **Backward Compatible**: No changes needed for existing deployments
- **Input Validation**: Values must be between 1 and 128 with proper
error handling
- **Comprehensive Documentation**: Updated man pages and example config
files
- **Both Interfaces**: Works via command line and configuration file

## Testing
The implementation has been thoroughly tested:

```bash
# Container with 2 allocated CPUs on 128-CPU host
$ turnserver --cpus 2
INFO: System cpu num is 128       # Host detection
INFO: System enable num is 128    # Host detection  
INFO: Configured cpu num is 2     # Override applied
INFO: Total General servers: 2    # Correct thread count
```

- ✅ Command line option: `--cpus 8` creates 8 relay servers
- ✅ Config file option: `cpus=6` creates 6 relay servers  
- ✅ Error handling: Invalid values show appropriate errors
- ✅ Default behavior: Without option, uses system detection
- ✅ RFC5769 tests: All protocol tests still pass

## Files Modified
- `src/apps/relay/mainrelay.c` - Core implementation
- `src/apps/relay/mainrelay.h` - Added configuration flag
- `examples/etc/turnserver.conf` - Added documentation and example
- `man/man1/turnserver.1` - Updated man page

This change directly addresses the resource consumption issues in
containerized environments while maintaining full backward
compatibility.

Fixes #1628.
Copilot 3 달 전
부모
커밋
99984fbccd
4개의 변경된 파일59개의 추가작업 그리고 18개의 파일을 삭제
  1. 8 0
      examples/etc/turnserver.conf
  2. 8 0
      man/man1/turnserver.1
  3. 42 18
      src/apps/relay/mainrelay.c
  4. 1 0
      src/apps/relay/mainrelay.h

+ 8 - 0
examples/etc/turnserver.conf

@@ -164,6 +164,14 @@
 #
 #relay-threads=0
 
+# Override system CPU count detection. Use this number instead of the
+# auto-detected CPU count. Useful in virtualized/containerized environments
+# where the system reports the host CPU count instead of the allocated
+# container CPUs. This affects the default number of relay threads when
+# relay-threads is not explicitly specified.
+#
+#cpus=2
+
 # Lower and upper bounds of the UDP relay endpoints:
 # (default values are 49152 and 65535)
 #

+ 8 - 0
man/man1/turnserver.1

@@ -591,6 +591,14 @@ will be employed (OS\-dependent). In the older Linux systems
 per network listening endpoint \- unless "\fB\-m\fP 0" or "\fB\-m\fP 1" is set.
 .TP
 .B
+\fB\-\-cpus\fP
+Override system CPU count detection. Use this number instead of the
+auto\-detected CPU count. Useful in virtualized/containerized environments
+where the system reports the host CPU count instead of the allocated
+container CPUs. This affects the default number of relay threads when
+\fB\-\-relay\-threads\fP is not explicitly specified.
+.TP
+.B
 \fB\-\-min\-port\fP
 Lower bound of the UDP port range for relay
 endpoints allocation.

+ 42 - 18
src/apps/relay/mainrelay.c

@@ -225,6 +225,7 @@ turn_params_t turn_params = {
 
     ///////////// CPUs //////////////////
     DEFAULT_CPUS_NUMBER,
+    false, /* cpus_configured */
 
     ///////// Encryption /////////
     "",                                     /* secret_key_file */
@@ -1024,6 +1025,11 @@ static char Usage[] =
     "						In older systems (pre-Linux 3.9) the number of UDP relay threads "
     "always equals\n"
     "						the number of listening endpoints (unless -m 0 is set).\n"
+    " --cpus				<number>	Override system CPU count detection. Use this number\n"
+    "						instead of the auto-detected CPU count.\n"
+    "						Useful in virtualized/containerized environments where\n"
+    "						the system reports the host CPU count instead of\n"
+    "						the allocated container CPUs.\n"
     " --min-port			<port>		Lower bound of the UDP port range for relay endpoints "
     "allocation.\n"
     "						Default value is 49152, according to RFC 5766.\n"
@@ -1506,7 +1512,8 @@ enum EXTRA_OPTS {
   STUN_BACKWARD_COMPATIBILITY_OPT,
   RESPONSE_ORIGIN_ONLY_WITH_RFC5780_OPT,
   RESPOND_HTTP_UNSUPPORTED_OPT,
-  VERSION_OPT
+  VERSION_OPT,
+  CPUS_OPT
 };
 
 struct myoption {
@@ -1652,6 +1659,7 @@ static const struct myoption long_options[] = {
     {"respond-http-unsupported", optional_argument, NULL, RESPOND_HTTP_UNSUPPORTED_OPT},
     {"version", optional_argument, NULL, VERSION_OPT},
     {"syslog-facility", required_argument, NULL, SYSLOG_FACILITY_OPT},
+    {"cpus", required_argument, NULL, CPUS_OPT},
     {NULL, no_argument, NULL, 0}};
 
 static const struct myoption admin_long_options[] = {
@@ -2367,6 +2375,20 @@ static void set_option(int c, char *value) {
   case RESPOND_HTTP_UNSUPPORTED_OPT:
     turn_params.respond_http_unsupported = get_bool_value(value);
     break;
+  case CPUS_OPT: {
+    int cpus = atoi(value);
+    if (cpus < 1) {
+      TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "ERROR: cpus value must be positive\n");
+    } else if (cpus > MAX_NUMBER_OF_GENERAL_RELAY_SERVERS) {
+      TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: max number of cpus is %d.\n",
+                    MAX_NUMBER_OF_GENERAL_RELAY_SERVERS);
+      turn_params.cpus = MAX_NUMBER_OF_GENERAL_RELAY_SERVERS;
+      turn_params.cpus_configured = true;
+    } else {
+      turn_params.cpus = (unsigned long)cpus;
+      turn_params.cpus_configured = true;
+    }
+  } break;
 
   /* these options have been already taken care of before: */
   case 'l':
@@ -3034,23 +3056,6 @@ int main(int argc, char **argv) {
   // Zero pass apply the log options.
   read_config_file(argc, argv, 0);
 
-  {
-    unsigned long cpus = get_system_active_number_of_cpus();
-    if (cpus > 0) {
-      turn_params.cpus = cpus;
-    }
-    if (turn_params.cpus < DEFAULT_CPUS_NUMBER) {
-      turn_params.cpus = DEFAULT_CPUS_NUMBER;
-    } else if (turn_params.cpus > MAX_NUMBER_OF_GENERAL_RELAY_SERVERS) {
-      turn_params.cpus = MAX_NUMBER_OF_GENERAL_RELAY_SERVERS;
-    }
-
-    turn_params.general_relay_servers_number = (turnserver_id)turn_params.cpus;
-
-    TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "System cpu num is %lu\n", get_system_number_of_cpus());
-    TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "System enable num is %lu\n", get_system_active_number_of_cpus());
-  }
-
   // First pass read other config options
   read_config_file(argc, argv, 1);
 
@@ -3063,6 +3068,25 @@ int main(int argc, char **argv) {
     }
   }
 
+  // CPU detection and configuration
+  if (!turn_params.cpus_configured) {
+    unsigned long cpus = get_system_active_number_of_cpus();
+    if (cpus > 0) {
+      turn_params.cpus = cpus;
+    }
+  }
+  if (turn_params.cpus < DEFAULT_CPUS_NUMBER) {
+    turn_params.cpus = DEFAULT_CPUS_NUMBER;
+  } else if (turn_params.cpus > MAX_NUMBER_OF_GENERAL_RELAY_SERVERS) {
+    turn_params.cpus = MAX_NUMBER_OF_GENERAL_RELAY_SERVERS;
+  }
+
+  turn_params.general_relay_servers_number = (turnserver_id)turn_params.cpus;
+
+  TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "System cpu num is %lu\n", get_system_number_of_cpus());
+  TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "System enable num is %lu\n", get_system_active_number_of_cpus());
+  TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "Configured cpu num is %lu\n", turn_params.cpus);
+
   // Second pass read -u options
   read_config_file(argc, argv, 2);
 

+ 1 - 0
src/apps/relay/mainrelay.h

@@ -334,6 +334,7 @@ typedef struct _turn_params_ {
   /////// CPUs //////////////
 
   unsigned long cpus;
+  bool cpus_configured;
 
   ///////// Encryption /////////
   char secret_key_file[1025];