Service Registration¶
Servicekit provides automatic service registration with an orchestrator for service discovery in Docker Compose and Kubernetes environments.
Quick Start¶
Basic Registration¶
The simplest approach with auto-detected hostname:
from servicekit.api import BaseServiceBuilder, ServiceInfo
app = (
BaseServiceBuilder(info=ServiceInfo(display_name="My Service"))
.with_registration() # Reads SERVICEKIT_ORCHESTRATOR_URL from environment
.build()
)
Set the environment variable:
export SERVICEKIT_ORCHESTRATOR_URL=http://orchestrator:9000/services/$register
fastapi run your_file.py
Docker Compose¶
The recommended approach for multi-service deployments:
services:
orchestrator:
image: your-orchestrator:latest
ports:
- "9000:9000"
my-service:
image: your-service:latest
environment:
SERVICEKIT_ORCHESTRATOR_URL: http://orchestrator:9000/services/$register
# Hostname auto-detected from container name
depends_on:
- orchestrator
Custom ServiceInfo¶
For services with additional metadata:
from servicekit.api import ServiceInfo
class CustomServiceInfo(ServiceInfo):
"""Extended service info with custom fields."""
deployment_env: str = "production"
team: str = "platform"
capabilities: list[str] = []
app = (
BaseServiceBuilder(
info=CustomServiceInfo(
display_name="My Service",
version="1.0.0",
deployment_env="staging",
team="data-science",
capabilities=["ml-inference", "analytics"],
)
)
.with_registration()
.build()
)
Configuration Options¶
The .with_registration()
method accepts these parameters:
.with_registration(
orchestrator_url=None, # Direct value or env var
host=None, # Direct value, auto-detect, or env var
port=None, # Direct value, env var, or 8000
orchestrator_url_env="SERVICEKIT_ORCHESTRATOR_URL",
host_env="SERVICEKIT_HOST",
port_env="SERVICEKIT_PORT",
max_retries=5, # Number of registration attempts
retry_delay=2.0, # Seconds between retries
fail_on_error=False, # Abort startup on failure
timeout=10.0, # HTTP request timeout
)
Parameters¶
- orchestrator_url (
str | None
): Orchestrator registration endpoint URL. If None, reads from environment variable. - host (
str | None
): Service hostname. If None, auto-detects viasocket.gethostname()
or reads from environment variable. - port (
int | None
): Service port. If None, reads from environment variable or defaults to 8000. - orchestrator_url_env (
str
): Environment variable name for orchestrator URL. Default:SERVICEKIT_ORCHESTRATOR_URL
. - host_env (
str
): Environment variable name for hostname override. Default:SERVICEKIT_HOST
. - port_env (
str
): Environment variable name for port override. Default:SERVICEKIT_PORT
. - max_retries (
int
): Maximum number of registration attempts. Default: 5. - retry_delay (
float
): Delay in seconds between retry attempts. Default: 2.0. - fail_on_error (
bool
): If True, raise exception and abort startup on registration failure. If False, log warning and continue. Default: False. - timeout (
float
): HTTP request timeout in seconds. Default: 10.0.
How It Works¶
Registration Flow¶
- Service Starts: FastAPI application initializes during lifespan startup
- Hostname Resolution: Determines service hostname (see resolution order below)
- Port Resolution: Determines service port (see resolution order below)
- URL Construction: Builds service URL as
http://<hostname>:<port>
- Payload Creation: Serializes ServiceInfo to JSON (supports custom subclasses)
- Registration Request: Sends POST to orchestrator endpoint
- Retry on Failure: Retries with delay if request fails
- Logging: Logs all attempts and final outcome
Registration Payload¶
The service sends this payload to the orchestrator:
{
"url": "http://my-service:8000",
"info": {
"display_name": "My Service",
"version": "1.0.0",
"summary": "Service description",
...
}
}
For custom ServiceInfo subclasses:
{
"url": "http://ml-service:8000",
"info": {
"display_name": "ML Service",
"version": "2.0.0",
"deployment_env": "production",
"team": "data-science",
"capabilities": ["ml-inference", "feature-extraction"],
"priority": 5
}
}
Hostname Resolution¶
Priority order:
- Direct Parameter:
host="my-service"
in.with_registration()
- Auto-Detection:
socket.gethostname()
(returns Docker container name or hostname) - Environment Variable: Value of
SERVICEKIT_HOST
(or custom env var) - Error: Raises exception if
fail_on_error=True
, otherwise logs warning
Docker Behavior: In Docker Compose, socket.gethostname()
returns the service name or container ID, making auto-detection work seamlessly.
Port Resolution¶
Priority order:
- Direct Parameter:
port=8080
in.with_registration()
- Environment Variable: Value of
SERVICEKIT_PORT
(or custom env var) - Default: 8000
Important: SERVICEKIT_PORT
should match the container's internal port, not the host-mapped port.
Examples¶
Environment Variables (Production)¶
from servicekit.api import BaseServiceBuilder, ServiceInfo
app = (
BaseServiceBuilder(info=ServiceInfo(display_name="Production Service"))
.with_logging()
.with_health()
.with_registration() # Reads from environment
.build()
)
docker-compose.yml:
services:
my-service:
image: my-service:latest
environment:
SERVICEKIT_ORCHESTRATOR_URL: http://orchestrator:9000/services/$register
# SERVICEKIT_HOST auto-detected
# SERVICEKIT_PORT defaults to 8000
Direct Configuration (Testing)¶
app = (
BaseServiceBuilder(info=ServiceInfo(display_name="Test Service"))
.with_registration(
orchestrator_url="http://localhost:9000/services/$register",
host="test-service",
port=8080,
)
.build()
)
Custom Environment Variable Names¶
app = (
BaseServiceBuilder(info=ServiceInfo(display_name="My Service"))
.with_registration(
orchestrator_url_env="MY_APP_ORCHESTRATOR_URL",
host_env="MY_APP_HOST",
port_env="MY_APP_PORT",
)
.build()
)
Environment:
export MY_APP_ORCHESTRATOR_URL=http://orchestrator:9000/services/$register
export MY_APP_HOST=my-service
export MY_APP_PORT=8000
Fail-Fast Mode¶
For critical services that must register:
app = (
BaseServiceBuilder(info=ServiceInfo(display_name="Critical Service"))
.with_registration(
fail_on_error=True, # Abort startup if registration fails
max_retries=10,
retry_delay=1.0,
)
.build()
)
Custom Retry Strategy¶
app = (
BaseServiceBuilder(info=ServiceInfo(display_name="My Service"))
.with_registration(
max_retries=10, # More attempts
retry_delay=1.0, # Faster retries
timeout=5.0, # Shorter timeout
)
.build()
)
Docker Compose¶
Basic Setup¶
services:
orchestrator:
image: orchestrator:latest
ports:
- "9000:9000"
service-a:
image: my-service:latest
environment:
SERVICEKIT_ORCHESTRATOR_URL: http://orchestrator:9000/services/$register
depends_on:
- orchestrator
With Health Checks¶
Wait for orchestrator to be healthy before starting services:
services:
orchestrator:
image: orchestrator:latest
ports:
- "9000:9000"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/health"]
interval: 10s
timeout: 5s
retries: 3
service-a:
image: my-service:latest
environment:
SERVICEKIT_ORCHESTRATOR_URL: http://orchestrator:9000/services/$register
depends_on:
orchestrator:
condition: service_healthy # Wait for healthy status
Custom Port Mapping¶
When container port differs from host port:
services:
service-a:
image: my-service:latest
ports:
- "8001:8000" # Host:Container
environment:
SERVICEKIT_ORCHESTRATOR_URL: http://orchestrator:9000/services/$register
SERVICEKIT_PORT: "8000" # Use container port, not host port
Why: Other services connect using the internal Docker network, so they use the container port (8000), not the host-mapped port (8001).
Multiple Services¶
services:
orchestrator:
image: orchestrator:latest
ports:
- "9000:9000"
service-a:
image: my-service:latest
ports:
- "8000:8000"
environment:
SERVICEKIT_ORCHESTRATOR_URL: http://orchestrator:9000/services/$register
service-b:
image: my-service:latest
ports:
- "8001:8000"
environment:
SERVICEKIT_ORCHESTRATOR_URL: http://orchestrator:9000/services/$register
Both services register with different hostnames (service-a, service-b) but same internal port (8000).
Kubernetes¶
ConfigMap for Orchestrator URL¶
apiVersion: v1
kind: ConfigMap
metadata:
name: registration-config
data:
orchestrator-url: "http://orchestrator-service:9000/services/$register"
Deployment with Registration¶
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-service
spec:
replicas: 3
template:
metadata:
labels:
app: my-service
spec:
containers:
- name: my-service
image: my-service:latest
env:
- name: SERVICEKIT_ORCHESTRATOR_URL
valueFrom:
configMapKeyRef:
name: registration-config
key: orchestrator-url
- name: SERVICEKIT_HOST
valueFrom:
fieldRef:
fieldPath: metadata.name # Pod name
Service Discovery¶
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-service
ports:
- port: 8000
targetPort: 8000
Error Handling¶
Retry Behavior¶
By default, registration retries 5 times with 2-second delays:
Example timeline: - Attempt 1: Immediate - Attempt 2: After 2 seconds - Attempt 3: After 4 seconds (2s + 2s) - Attempt 4: After 6 seconds - Attempt 5: After 8 seconds
Fail on Error¶
Default (fail_on_error=False): Service starts even if registration fails
Fail-Fast (fail_on_error=True): Service aborts startup if registration fails
When to use fail-fast: - Critical services that require orchestrator awareness - Production environments with strict registration requirements - Services that cannot function without being registered
Structured Logging¶
All registration events are logged with structured data:
{
"event": "registration.starting",
"orchestrator_url": "http://orchestrator:9000/services/$register",
"service_url": "http://my-service:8000",
"host_source": "auto-detected",
"port_source": "default",
"max_retries": 5
}
Success:
{
"event": "registration.success",
"service_url": "http://my-service:8000",
"attempt": 1,
"status_code": 200
}
Failure:
{
"event": "registration.attempt_failed",
"service_url": "http://my-service:8000",
"attempt": 1,
"max_retries": 5,
"error": "Connection refused",
"error_type": "ConnectError"
}
Troubleshooting¶
Registration Fails¶
Check orchestrator is reachable:
Check environment variables:
View registration logs:
Hostname Auto-Detection Issues¶
Problem: Auto-detection fails or returns unexpected value
Solution: Override with environment variable
Debug hostname detection:
docker compose exec my-service hostname
docker compose exec my-service python -c "import socket; print(socket.gethostname())"
Port Mismatch¶
Problem: Orchestrator cannot reach service at registered URL
Common mistake: Using host-mapped port instead of container port
# WRONG
ports:
- "8001:8000"
environment:
SERVICEKIT_PORT: "8001" # ❌ Host port
# CORRECT
ports:
- "8001:8000"
environment:
SERVICEKIT_PORT: "8000" # ✅ Container port
Why: Services communicate via Docker's internal network using container ports, not host-mapped ports.
Orchestrator URL Missing¶
Problem: No orchestrator URL configured
Error: registration.missing_orchestrator_url
Solution: Set environment variable
Or use direct configuration:
Service Not Appearing in Registry¶
Check orchestrator logs:
Check service startup logs:
Verify orchestrator endpoint:
Production Considerations¶
High Availability¶
Use multiple orchestrator replicas:
services:
orchestrator:
image: orchestrator:latest
deploy:
replicas: 3
service-a:
environment:
SERVICEKIT_ORCHESTRATOR_URL: http://orchestrator:9000/services/$register
Retry Strategy¶
Adjust retries for production reliability:
.with_registration(
max_retries=10, # More attempts
retry_delay=1.0, # Faster retries
timeout=30.0, # Longer timeout
fail_on_error=True, # Fail fast in production
)
Security¶
Authentication: Add API keys or tokens to registration requests (requires custom implementation)
TLS: Use HTTPS for orchestrator communication
Monitoring¶
Monitor registration health: - Track registration success/failure rates - Alert on repeated registration failures - Monitor orchestrator availability - Log all registration attempts for audit
Related Examples¶
examples/registration/
- Complete registration demo with orchestratorcore_api/
- Basic CRUD servicemonitoring/
- Prometheus metricsauth_envvar/
- Environment-based authentication
See Also¶
- Health Checks - Configure health endpoints
- Monitoring - Prometheus metrics
- Authentication - API key authentication