Service Discovery
How Services Find Each Other
In PCF, applications discover and communicate with each other through:
- Environment Variables (injected from service bindings)
- Internal Routes (DNS for app-to-app communication)
- Service Brokers (provisioning and credential injection)
Service Binding Discovery
When service is bound to application:
bash
$ cf bind-service my-api my-database
$ cf env my-api
Application receives credentials via VCAP_SERVICES:
json
{
"postgresql": [{
"credentials": {
"uri": "postgresql://user:pass@postgres-host:5432/db"
}
}]
}
Application code:
javascript
const db_url = process.env.DATABASE_URL;
const pool = new Pool({ connectionString: db_url });
App-to-App Communication
Applications communicate via HTTP using internal routes:
my-web-app → my-api-app → my-database (service)
Configuration:
```bash
my-web-app calls my-api-app
my-api-app.cf-k8s.io (internal route)
```
Environment variable in my-web-app:
javascript
const apiUrl = process.env.API_URL ||
'http://my-api-app.cf-k8s.io';
Service-to-Service Communication
For microservices:
```bash
Deploy services
$ cf push service-1 $ cf push service-2 $ cf push service-3
Service discovery automatic via naming
service-1.cf-k8s.io
service-2.cf-k8s.io
service-3.cf-k8s.io
```
Call between services:
javascript
// service-1 calling service-2
fetch('http://service-2.cf-k8s.io/api')
.then(r => r.json())
.then(data => res.json(data))
Network Policies
Restrict service-to-service communication:
```bash
Allow service-1 to call service-2 on port 8080
$ cf add-network-policy service-1 service-2 \ --protocol tcp --port 8080
View policies
$ cf network-policies
Remove if needed
$ cf remove-network-policy service-1 service-2 \ --protocol tcp --port 8080 ```
Dynamic Service Discovery Pattern
For applications that need to find services dynamically:
```javascript // Query VCAP_SERVICES at runtime const services = JSON.parse(process.env.VCAP_SERVICES || '{}');
function getServiceUrl(serviceType) {
if (services[serviceType]) {
return services[serviceType][0].credentials.uri;
}
throw new Error(Service ${serviceType} not found);
}
const dbUrl = getServiceUrl('postgresql'); const cacheUrl = getServiceUrl('redis'); ```
Health Checks in Service Mesh
Each service exposes health endpoint:
javascript
app.get('/health', (req, res) => {
res.json({ status: 'healthy' });
});
Consumer services check health before calling:
``javascript
async function checkService(url) {
try {
const response = await fetch(${url}/health`);
return response.ok;
} catch {
return false;
}
}
// Use circuit breaker pattern if (await checkService(serviceUrl)) { // Call service } else { // Return cached response or error } ```
Best Practices
- Use Environment Variables: Injected by CF
- Health Checks: Each service exposes /health
- Retry Logic: Handle transient failures
- Circuit Breakers: Prevent cascading failures
- Logging: Request tracing across services
- Timeouts: Set reasonable timeout values
See Labs 1-3 for hands-on examples of service binding and communication.