Microservices in Production: Avoiding the Distributed Monolith Nightmare
Letâs be honest for a second. We have all read the Netflix whitepapers. We have all seen the "death to the monolith" conference talks. But here in the real worldâspecifically, here in Northern Europe where we actually have to worry about the Datatilsynet breathing down our necks about where our data livesâbreaking an application into fifty tiny pieces isn't a magic bullet. It's an operational hazard.
If you take a messy, slow monolith and split it into microservices without fixing your underlying infrastructure, you donât get agility. You get a distributed monolith. And now, instead of a function call that takes nanoseconds, you have a network call that takes milliseconds. In the hosting world, that factor of a million makes or breaks your SLA.
It is April 2018. GDPR enforcement is exactly one month away. If your architecture relies on sending user data to a generic cloud bucket in Virginia, you are about to have a very bad year. This guide covers how to architect microservices on solid, local VPS infrastructure (like CoolVDS) using the tools we actually trust today: Docker, NGINX, and proper Kernel tuning.
1. The Foundation: Why Virtualization Type Matters
Before we even touch Docker, look at your kernel. Many budget providers are still pushing OpenVZ containers as "VPS." In a microservices architecture, this is fatal. You cannot tune kernel parameters for high-concurrency networking inside an OpenVZ container because you share the kernel with every other tenant on the host.
Pro Tip: Always verify your virtualization technology. If `uname -a` returns a kernel version ending in `stab` (OpenVZ) instead of `generic` or a standard version, request a migration immediately. CoolVDS exclusively uses KVM, meaning you get your own kernel to panicâor optimize.
For microservices, we need to modify the TCP stack. When you have 20 services talking to each other, you run out of ephemeral ports fast. You need to enable port reuse.
Run this on your host node:
sysctl -w net.ipv4.tcp_tw_reuse=1
If you can't run that command because "Permission Denied" (and you are root), you are on the wrong hosting platform. Move to a KVM-based VPS.
2. The API Gateway Pattern with NGINX
Do not expose your microservices directly to the public internet. Just don't. You need a gatekeeper. While tools like Kong are gaining traction, good old NGINX is still the undisputed king of performance per watt in 2018. It handles SSL termination and routing so your backend services don't have to.
Here is a production-ready `nginx.conf` snippet for an API Gateway that handles load balancing across local Docker containers. Note the `keepalive` directiveâwithout it, the TCP handshake overhead will kill your latency.
upstream auth_service {
server 10.0.0.2:3000;
server 10.0.0.3:3000;
keepalive 64;
}
upstream inventory_service {
server 10.0.0.4:8080;
server 10.0.0.5:8080;
keepalive 64;
}
server {
listen 443 ssl http2;
server_name api.yoursite.no;
ssl_certificate /etc/letsencrypt/live/api.yoursite.no/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yoursite.no/privkey.pem;
location /auth/ {
proxy_pass http://auth_service/;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
}
location /inventory/ {
proxy_pass http://inventory_service/;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
This configuration assumes you have private networking set up between your VPS instances. At CoolVDS, we encourage using the private LAN interface for inter-service communication to keep traffic off the public meter and secure from external snooping.
3. Service Discovery: The Glue
Hardcoding IP addresses in 2018 is a sin. Docker 17.12+ has decent built-in swarm mode, but many of us are still relying on HashiCorp's Consul for robust service discovery. Itâs separate, itâs stable, and it works.
Deploying a Consul agent on every node ensures that when your `inventory` service crashes and restarts on a new port, NGINX (via `consul-template` or dynamic DNS) knows where to find it. Here is how we define the Consul cluster in `docker-compose.yml`:
version: '3.4'
services:
consul:
image: consul:1.0.7
command: agent -server -bootstrap-expect=3 -ui -client=0.0.0.0
volumes:
- ./consul/data:/consul/data
ports:
- "8500:8500"
- "8600:8600/udp"
networks:
- backend_grid
registrator:
image: gliderlabs/registrator:latest
volumes:
- /var/run/docker.sock:/tmp/docker.sock
command: consul://consul:8500
depends_on:
- consul
networks:
- backend_grid
networks:
backend_grid:
driver: bridge
The `gliderlabs/registrator` container is essential here. It listens to the Docker socket and automatically registers any new container with Consul. Itâs a "set and forget" tool that saves hours of manual configuration.
4. Storage Performance: The Bottleneck
Stateful microservices are pain. If you are running PostgreSQL or MongoDB in a container, disk I/O becomes your primary enemy. In a monolithic architecture, the database was on a massive dedicated server. Now, you might have sharded databases across multiple VPS instances.
Standard spinning rust (HDD) or even SATA SSDs often choke under the random I/O patterns generated by multiple containerized databases. This is where NVMe storage creates a competitive advantage.
To test if your current host is lying to you about disk speed, use `fio`. Don't just use `dd`. `dd` is for sequential writes; databases do random reads/writes.
fio --name=randwrite --ioengine=libaio --iodepth=1 --rw=randwrite --bs=4k --direct=0 --size=512M --numjobs=2 --runtime=240 --group_reporting
On a standard SATA SSD VPS, you might see 5,000 IOPS. On CoolVDS NVMe instances, we regularly clock significantly higher. When you have twenty microservices hitting the disk simultaneously, that headroom is the difference between a 200ms response time and a timeout.
5. The Norwegian Context: GDPR and Latency
We are weeks away from May 25, 2018. If you are handling PII (Personally Identifiable Information) for Norwegian citizens, you need to know exactly where that data sits physically. Hosting on a US-controlled cloud adds legal friction we simply don't have time for right now.
Using a Norwegian or European provider like CoolVDS simplifies compliance. Your data stays in the EEA. Furthermore, for local users, latency to NIX (Norwegian Internet Exchange) is critical.
Test your latency to the primary exchange in Oslo:
ping -c 5 nix.no
If you are hosting in a generic Frankfurt region, you are adding 20-30ms round trip. Hosting locally cuts this down to <5ms. In a microservices chain where Request A calls Service B which calls Service C, that latency compounds.
6. Monitoring: If You Can't See It, It's Down
Finally, you need eyes on the system. In 2018, the industry standard has firmly shifted to Prometheus and Grafana. Forget Nagios; it can't handle the ephemeral nature of containers.
Here is a basic `prometheus.yml` scrape config to monitor your nodes. We use the `node_exporter` to get raw hardware metrics from the CoolVDS instance.
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'vps_nodes'
static_configs:
- targets: ['10.0.0.2:9100', '10.0.0.3:9100']
- job_name: 'docker_containers'
static_configs:
- targets: ['10.0.0.2:9323'] # Docker experimental metrics
Make sure you open the firewall for these ports only on the private interface:
ufw allow in on eth1 to any port 9100
Final Thoughts
Microservices aren't just about code; they are about infrastructure discipline. You need Kernel control (KVM), high IOPS (NVMe), and low latency (Local Peering). Don't let your architecture fail because you chose a budget host that oversells their CPU cycles.
If you are ready to build a cluster that survives the GDPR audit and the Friday night traffic spike, deploy your first node with us. Don't let slow I/O kill your SEO. Deploy a test instance on CoolVDS in 55 seconds.