LinuxLVIII · Clustered Service ArchitectureConstraints
Application architecture constraints - the limits of clustering
What you'll learn
- Identify application-level constraints on clustering
- Distinguish clustering-friendly from clustering-hostile designs
- Recognise common anti-patterns
- Design for clustering at the application level
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
The cluster is only as good as the application design. An application that is not cluster-friendly cannot be made cluster-friendly by adding nodes. This lesson covers the application-level constraints.
Common anti-patterns
Local file system state
Storing data in /var/lib/myapp ties the state to one host.
A cluster that uses this data is not really a cluster.
Fix: externalise to a database, object store, or shared filesystem.
In-memory state
Session state, caches, locks in process memory. A second instance does not share the state.
Fix: external state in Redis or a database. Stateless application.
Hard-coded IPs and hostnames
db_host = "10.0.0.10" # hard-coded
A second instance points to the same DB. If the DB moves, the app breaks.
Fix: service discovery (DNS, Consul, etcd).
Synchronous cross-host calls
# A calls B, B calls C, B waits for C
result = call_b()
# B is blocked while C is processing
A cluster of these is a cluster of bottlenecks.
Fix: async messaging (Kafka, RabbitMQ, SQS).
Long-running synchronous operations
A 30-second database query blocks the thread. A cluster of these is a cluster of slow requests.
Fix: async processing, pagination, or query optimisation.
Design for clustering
For a cluster-friendly application:
- Stateless where possible: any instance handles any request.
- External state: database, cache, or object store.
- Service discovery: by name, not IP.
- Async where possible: messaging over synchronous calls.
- Timeouts everywhere: never wait forever for a dependency.
- Retries with backoff: failures are expected; design for them.
- Circuit breakers: stop calling a failing dependency instead of timing out.
- Bulkheads: isolate failure domains (one slow dependency does not block the whole service).
Twelve-factor app
The 12-factor app methodology captures these patterns:
- One codebase, many deploys.
- Explicit dependencies.
- Config in environment.
- Backing services as attached resources.
- Build, release, run stages.
- Stateless processes.
- Port binding.
- Concurrency.
- Disposability (fast startup, graceful shutdown).
- Dev/prod parity.
- Logs as event streams.
- Admin processes.
A 12-factor app is cluster-friendly by design.
Migration path
For legacy applications:
- Externalise the most critical state (database, sessions).
- Add service discovery.
- Add async messaging where the synchronous calls are painful.
- Make the application stateless over time.
This is a multi-quarter project. Each step reduces the clustering pain.
Knowledge check
Knowledge check · 3 questions
Q1. What is the most common application-level anti-pattern for clustering?
Q2. Adding nodes to a cluster fixes a stateful application.
Q3. Which of the following are application-level constraints on clustering? Select all that apply.
Passing score: 75%. Answers are checked in this browser.