Adapter and ambassador containers
Two special kinds of sidecar. An adapter is a translator: it takes what your app produces and turns it into the format the outside world expects. An ambassador is a messenger: your app hands it outgoing calls, and it deals with the complicated outside world on the app's behalf.
Three helpers, one Pod
All three are sidecars (chapter 34). What differs is the direction of the work. Click each helper:
Sidecar (general)
Any helper running beside the app: log shipper, git-sync, config reloader. The umbrella term.
Adapter
Faces inward-looking tools like monitoring. Others read from it; it reads from your app and answers in a standard format.
Ambassador
Faces the outside services your app calls. The app talks to localhost; the ambassador finds the real service, adds security, retries, caching.
The adapter: a translator
Imagine six services written by six teams. One logs JSON, one plain text, one exposes stats at /stats, another at /debug/vars. Your monitoring tool wants one format from everyone. Rewriting six apps is a lot of work. Instead, put a small adapter next to each app that converts its output into the standard format.
Without adapters
The monitoring team must understand every app's special format. Every new app means new monitoring code.
With adapters
Every Pod looks the same from outside: same port, same format. The app does not change at all.
Common adapters: metrics exporters (turn app or database stats into Prometheus format), log normalisers (turn odd log lines into structured JSON).
Example: metrics for Prometheus
Say posts already has an endpoint /stats that returns its own JSON. Prometheus expects a plain-text format at /metrics. Step through:
// posts app, unchanged GET http://localhost:4000/stats { "postsCreated": 1284, "avgResponseMs": 41 }The app speaks its own language.
Prometheus ──GET──▶ posts-pod:9100/metrics (the adapter's port)Prometheus asks the adapter, not the app.
adapter ──GET──▶ localhost:4000/stats (same Pod, so localhost works)The adapter fetches the app's own stats. Same Pod, shared network (chapter 6).
# answer from the adapter, in Prometheus format posts_created_total 1284 posts_response_ms_avg 41Translated. Prometheus is happy, and posts never knew Prometheus existed.
containers:
- name: posts
image: micro-posts:1.0
ports: [{ containerPort: 4000 }]
- name: metrics-adapter
image: example/json-to-prometheus:1.2
args: ["--source=http://localhost:4000/stats", "--listen=:9100"]
ports: [{ containerPort: 9100, name: metrics }]
The ambassador: a messenger
A country's ambassador handles a foreign country's language, customs and paperwork, so the people at home do not have to. An ambassador container does the same for calls going out of your app:
Your code stays as simple as on your laptop: "connect to localhost:6379". Locally that is a plain Redis; in production it is the ambassador, which knows where the real Redis is and how to talk to it safely.
Ambassador examples
containers:
- name: query
image: micro-query:1.0
env:
- name: DB_HOST
value: "127.0.0.1" # just localhost
- name: cloud-sql-proxy
image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.11.0
args: ["--port=5432", "my-project:europe-west1:blog-db"]Cloud providers ship proxies like this: they handle encrypted connections and cloud login, so your app connects to 127.0.0.1:5432 without any cloud-specific code.
app ──▶ localhost:8080 ──▶ ambassador ──▶ payments API
retries 3×, timeout 2s,
stops calling for 30s if it keeps failingRetry and "stop hammering a broken service" logic (a circuit breaker) lives in one reusable container instead of in every app, in every language.
app: GET user 42 ──▶ localhost:6379 ──▶ ambassador ──▶ redis-shard-2
app: GET user 7 ──▶ localhost:6379 ──▶ ambassador ──▶ redis-shard-0The ambassador knows which of several servers holds which keys. The app thinks there is only one Redis.
From ambassador to service mesh
Take the ambassador idea, put one proxy next to every Pod, and let it handle incoming traffic too: that is a service mesh such as Istio or Linkerd. The proxies give you encryption between all services, retries, traffic splitting for canaries (chapter 15) and detailed metrics, with no app changes. It is powerful, but a mesh is a big piece of infrastructure; one hand-written ambassador is the small version of the same idea.
Which one is it?
| Sidecar | Adapter | Ambassador | |
|---|---|---|---|
| Job | Add a feature | Make the app look standard to others | Make outside services look simple to the app |
| Direction | Any | Outside world reads the app | App calls the outside world |
| Example | git-sync, log shipper | Prometheus exporter | Cloud SQL proxy, Redis proxy |
- A container that turns MySQL's internal status into Prometheus metrics
It translates outgoing information into a standard format.
- A container so the app can reach a cloud database at
localhost:5432It handles the app's outgoing calls.
- A container that rewrites plain-text log lines into JSON for the log system
Translation into a format others expect.
- A container that retries failed calls to a payments API
It sits on the app's outgoing path.
Recap
- Adapters and ambassadors are sidecars with a specific job.
- Adapter: translates the app's output (metrics, logs) into a standard format.
- Ambassador: a local proxy for the app's outgoing calls, hiding addresses, security, retries.
- Both work because containers in a Pod share
localhost. - A service mesh is the ambassador idea applied to every Pod.
Quick check
Show answer
The app only knows localhost:6379. On a laptop you run a plain Redis there; in production the ambassador sits there. The app code is the same everywhere.
Adapter and ambassador patterns: common questions
What is the difference between an adapter and an ambassador container?
An adapter translates what the app exposes, such as metrics, into a standard format for others. An ambassador proxies the app outgoing calls, handling addresses, security and retries.