Skip to main content

Ambassadors

Adapted from: Bilgin Ibryam & Roland Huß, Kubernetes Patterns (O'Reilly, 2019), Chapter 4 — rewritten and condensed for teaching.

The Sidecar Pattern introduced a container that augments an existing application — often without the app knowing. The ambassador pattern is different: an ambassador container brokers interactions between the application container and the rest of the world.

As with other single-node patterns, the two containers are tightly linked — co-scheduled to one machine in the same Pod — but the division of labor shifts:

PatternAmbassador's job
SidecarAdds capability to the app (TLS, config sync, monitoring)
AmbassadorSpeaks to the outside world on behalf of the app (sharding, service discovery, traffic splitting)

The app typically connects to localhost, believing it talks to one simple backend. The ambassador hides sharded storage, environment-specific service locations, or A/B routing behind that single address.

flowchart LR
APP[Application container]
AMB[Ambassador container]
WORLD[External services / shards]
APP -->|"localhost"| AMB
AMB --> WORLD

Why bother? Modular, reusable containers — same benefits as sidecars. The ambassador can be shared across many apps; sharding or discovery logic is built once, tested once, and deployed consistently.


Using an Ambassador to Shard a Service​

When data outgrows one machine, you shard the storage layer — split it into disjoint pieces, each on its own host. This lesson focuses on the single-node ambassador that lets an existing client talk to a sharded backend without embedding shard-routing logic in application source.

Two deployment styles:

  1. Server-side ambassador — a stateless load balancer in front of the shards (ambassador-as-a-service). Simpler clients; more complex shard deployment.
  2. Client-side ambassador — a proxy container in the Pod next to the app. Simpler shard deployment; each client Pod carries routing logic (or an off-the-shelf proxy like twemproxy).

Neither is universally correct — team boundaries, who writes code vs. deploys off-the-shelf software, and operational ownership all matter.

flowchart LR
subgraph pod["Pod"]
APP[App]
TWEM[twemproxy ambassador]
end
S0[shard-0]
S1[shard-1]
S2[shard-2]
APP -->|"127.0.0.1:6379"| TWEM
TWEM --> S0
TWEM --> S1
TWEM --> S2

The app only knows "Redis on localhost." The ambassador hashes keys and forwards each request to the right shard.

Check: sharding ambassadors​

Quiz

Client-side vs server-side sharding

1 / 2

What does the application container believe it is talking to when a sharding ambassador is deployed in the same Pod?

Pick an answer to see if you are right.


Hands On: Implementing a Sharded Redis​

We'll deploy three Redis shards with a StatefulSet, give them stable DNS names with a headless Service, configure twemproxy (Twitter's open-source Redis/memcached proxy) as the ambassador, and mount that config from a ConfigMap.

Step 1 — StatefulSet for Redis shards​

Complete the StatefulSet so Kubernetes creates three Redis pods with stable names (sharded-redis-0, sharded-redis-1, …). You need the fields that wire this StatefulSet to its headless Service and set the replica count. Deploy with kubectl apply -f redis-shards.yaml.

Step 2 — Headless Service for stable DNS​

A headless Service (clusterIP: None) creates per-pod DNS entries instead of one virtual IP. Complete the Service so its name pairs with the StatefulSet serviceName and pods resolve as sharded-redis-0.redis, sharded-redis-1.redis, and sharded-redis-2.redis inside the cluster.

Step 3 — twemproxy (nutcracker) configuration​

The ambassador listens on localhost so the application container in the same Pod can connect without knowing about shards. Add the listen address twemproxy should bind to, then create the ConfigMap with:

kubectl create configmap twem-config --from-file=nutcracker.yaml

Step 4 — Ambassador Pod​

Mount the ConfigMap and run twemproxy. Wire the volume mount path to match the -c flag in the command, and reference the ConfigMap you created in Step 3. The application container slot is left commented — inject your real app alongside the ambassador.


Using an Ambassador for Service Brokering​

Portable apps must run in a public cloud (managed MySQL SaaS), a private cloud (dynamically provisioned VM), or a data center — each with different service discovery.

Without an ambassador, the application embeds environment-specific lookup logic. With a service broker ambassador, the app always connects to MySQL on localhost; the ambassador introspects the environment and brokers the real connection.

flowchart LR
APP[Application]
BRK[Service broker ambassador]
MYSQL[(MySQL — location varies by env)]
APP -->|"localhost:3306"| BRK
BRK --> MYSQL

Same separation of concerns as sharding: slim application code, reusable broker container.

Practice: service broker Pod sketch​

The frontend should always reach MySQL through loopback; add a second container that brokers the real database endpoint. Name and configure both containers appropriately.


Using an Ambassador for Experimentation and Request Splitting​

Production systems often need request splitting:

  • A/B experiments — send a fraction of traffic to a beta version (e.g. 10% experiment, 90% production).
  • Traffic teeing — duplicate requests to a shadow service; return only production responses (load-test the new version without user impact).

The application still connects to localhost. The ambassador proxies to production and/or experiment backends and returns the appropriate response.

Sticky routing matters: if users flip between experiment and production on every request, results are meaningless. IP hashing (or consistent hashing on a user id) keeps each client on one variant for a consistent experience.

Check: request splitting​

Quiz

Experimentation ambassadors

1 / 2

Why does the nginx experiment config use ip_hash in the upstream block?

Pick an answer to see if you are right.


Hands On: Implementing 10% Experiments​

Configure nginx as an ambassador. Set upstream weights so roughly 90% of traffic goes to web and 10% to experiment, keeping ip_hash for sticky routing.

Create the ConfigMap:

kubectl create configmap experiment-config --from-file=nginx.conf

nginx will not start until both upstream Services exist. Complete each Service selector so it targets the right Deployment labels.

Finally, deploy the ambassador Pod — mount the ConfigMap from nginx.conf at the path nginx expects:

Ambassador vs standalone experiment service

You can also run the experiment router as a separate microservice in front of your app instead of a Pod-local ambassador. That adds another service to maintain, scale, and monitor. If experimentation is a permanent platform feature, a shared router may be worth it; for occasional tests, a client-side ambassador is often simpler.


Key takeaways
  • Ambassadors broker between the application and the outside world; sidecars augment the application itself.
  • Sharding: app talks to localhost; ambassador (e.g. twemproxy) routes to the correct shard — client-side vs server-side routing is a deployment trade-off.
  • Service brokering: app always connects to localhost; ambassador discovers the real MySQL (or other) endpoint per environment.
  • Request splitting: ambassador sends a fraction of traffic to an experiment backend; use sticky routing (ip_hash) so users do not flip-flop.
  • Reusable ambassadors encapsulate complex platform logic once — same modularity goals as sidecars, different direction of proxying.

Further Reading​