async-service-supervisor-envoy 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +60 -6
- data/context/index.yaml +1 -1
- data/lib/async/service/supervisor/envoy/monitor.rb +56 -21
- data/lib/async/service/supervisor/envoy/version.rb +1 -1
- data/readme.md +10 -2
- data/releases.md +8 -0
- data.tar.gz.sig +0 -0
- metadata +5 -5
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e8bbbdeea238ea31ad9ff156f47a43d168b52965e1cc0e54f972ccb2a994cfaa
|
|
4
|
+
data.tar.gz: e07f1e1ba197e0c7b4a629072dc8177a371d9f31189f4f6c8e03bf4899fd93c5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de6300d313bc18e8002defade3348a29216571401b6aba6effa3d4a9c2389704765014f395a3e0f4121fa8ae7e2e254dd0049b93fecd779838601444bd2f0d60
|
|
7
|
+
data.tar.gz: 4d36bd1ceb4d691e15b02042a3c0d214a656a170fd518585d5a266338011381a8eef97956a0bc9caecc9a6e3d02595e92411381294934628f835bbad256a5d6f
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/context/getting-started.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Getting Started
|
|
2
2
|
|
|
3
|
-
This guide explains how to use `async-service-supervisor-envoy` to publish supervised worker endpoints to Envoy using xDS.
|
|
3
|
+
This guide explains how to use `async-service-supervisor-envoy` to publish supervised worker clusters and endpoints to Envoy using xDS.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -16,10 +16,12 @@ The gem depends on `async-service-supervisor` and `async-grpc-xds`.
|
|
|
16
16
|
|
|
17
17
|
`async-service-supervisor-envoy` provides:
|
|
18
18
|
|
|
19
|
-
- {ruby Async::Service::Supervisor::Envoy::Monitor} - A supervisor monitor that publishes worker endpoints through
|
|
19
|
+
- {ruby Async::Service::Supervisor::Envoy::Monitor} - A supervisor monitor that publishes worker clusters through CDS and their endpoints through EDS.
|
|
20
20
|
- {ruby Async::Service::Supervisor::Envoy::Endpoint} - A small value object for endpoint state.
|
|
21
21
|
|
|
22
|
-
The monitor
|
|
22
|
+
The monitor always serves a dedicated Endpoint Discovery Service stream. By default it also serves Cluster Discovery Service. It does not claim Envoy's Aggregated Discovery Service, so another control plane can use ADS for listeners, routes, and other configuration.
|
|
23
|
+
|
|
24
|
+
CDS describes the logical services exposed by supervised workers, including their supported protocol, active health checks, and load-balancing policy. EDS supplies the concrete workers currently available for each service.
|
|
23
25
|
|
|
24
26
|
## Endpoint State
|
|
25
27
|
|
|
@@ -62,11 +64,59 @@ Add the monitor to your supervisor environment:
|
|
|
62
64
|
require "async/service/supervisor/envoy"
|
|
63
65
|
|
|
64
66
|
Async::Service::Supervisor::Envoy::Monitor.new(
|
|
65
|
-
bind: "http://127.0.0.1:18000"
|
|
67
|
+
bind: "http://127.0.0.1:18000",
|
|
68
|
+
management_cluster: "xds_cluster"
|
|
66
69
|
)
|
|
67
70
|
```
|
|
68
71
|
|
|
69
|
-
By default, workers are grouped into clusters by `state[:name]`.
|
|
72
|
+
By default, workers are grouped into clusters by `state[:name]`. When cluster publication is enabled, `management_cluster` must match the static Envoy cluster used to reach the monitor.
|
|
73
|
+
|
|
74
|
+
If another control plane owns cluster configuration, disable CDS publication while retaining supervisor-owned endpoint discovery:
|
|
75
|
+
|
|
76
|
+
``` ruby
|
|
77
|
+
Async::Service::Supervisor::Envoy::Monitor.new(
|
|
78
|
+
bind: "http://127.0.0.1:18000",
|
|
79
|
+
publish_clusters: false
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
In this mode, the external control plane or bootstrap configuration must define each cluster, configure it to use the monitor's dedicated EDS service, and supply its protocol, health checks, and load-balancing policy. The cluster's EDS service name must match the worker cluster name published by the monitor.
|
|
84
|
+
|
|
85
|
+
## Envoy Configuration
|
|
86
|
+
|
|
87
|
+
Configure Envoy to obtain clusters from the monitor's dedicated CDS service. The monitor configures each discovered cluster to obtain its endpoints from the dedicated EDS service on the same management server:
|
|
88
|
+
|
|
89
|
+
``` yaml
|
|
90
|
+
dynamic_resources:
|
|
91
|
+
cds_config:
|
|
92
|
+
resource_api_version: V3
|
|
93
|
+
api_config_source:
|
|
94
|
+
api_type: GRPC
|
|
95
|
+
transport_api_version: V3
|
|
96
|
+
grpc_services:
|
|
97
|
+
- envoy_grpc:
|
|
98
|
+
cluster_name: xds_cluster
|
|
99
|
+
|
|
100
|
+
static_resources:
|
|
101
|
+
clusters:
|
|
102
|
+
- name: xds_cluster
|
|
103
|
+
connect_timeout: 1s
|
|
104
|
+
type: STRICT_DNS
|
|
105
|
+
http2_protocol_options: {}
|
|
106
|
+
load_assignment:
|
|
107
|
+
cluster_name: xds_cluster
|
|
108
|
+
endpoints:
|
|
109
|
+
- lb_endpoints:
|
|
110
|
+
- endpoint:
|
|
111
|
+
address:
|
|
112
|
+
socket_address:
|
|
113
|
+
address: 127.0.0.1
|
|
114
|
+
port_value: 18000
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The bootstrap cluster name must match the monitor's `management_cluster`. Envoy uses it for independent CDS and EDS gRPC streams; no `ads_config` is required.
|
|
118
|
+
|
|
119
|
+
If a static route refers to a cluster delivered by CDS, set `validate_clusters: false` on that route configuration. Envoy can then load the route before the cluster arrives and will begin routing once CDS and EDS have warmed it.
|
|
70
120
|
|
|
71
121
|
## Custom Mapping
|
|
72
122
|
|
|
@@ -113,10 +163,14 @@ utilization_monitor = Async::Service::Supervisor::UtilizationMonitor.new(interva
|
|
|
113
163
|
]
|
|
114
164
|
```
|
|
115
165
|
|
|
116
|
-
The supervisor utilization monitor manages each worker's shared-memory allocation and registration. The Envoy monitor samples it through `sample_by_worker`, combines each worker's `requests_total` counter with processor usage from `process-metrics`, and serves the resulting ORCA reports from the same HTTP/2 endpoint as
|
|
166
|
+
The supervisor utilization monitor manages each worker's shared-memory allocation and registration. The Envoy monitor samples it through `sample_by_worker`, combines each worker's `requests_total` counter with processor usage from `process-metrics`, and serves the resulting ORCA reports from the same HTTP/2 endpoint as CDS and EDS. It also configures each discovered cluster to use Envoy's client-side weighted-round-robin policy.
|
|
117
167
|
|
|
118
168
|
The first sample establishes a baseline. Subsequent reports contain normalized `cpu_utilization` and `rps_fractional` values for each worker. Reports are removed immediately when a worker disconnects.
|
|
119
169
|
|
|
170
|
+
Workers are identified by the `hostname` published with each endpoint, which Envoy sends as the request authority when it opens an out-of-band reporting stream. Enabling ORCA therefore publishes one endpoint per worker rather than one per shared listener.
|
|
171
|
+
|
|
172
|
+
The generated load-balancing policy uses the monitor's bind port and reporting interval. Envoy dials that port on each endpoint's own address, which reaches the monitor because it shares a network namespace with the workers.
|
|
173
|
+
|
|
120
174
|
Out-of-band ORCA requires:
|
|
121
175
|
|
|
122
176
|
- Envoy 1.39 or later.
|
data/context/index.yaml
CHANGED
|
@@ -9,4 +9,4 @@ files:
|
|
|
9
9
|
- path: getting-started.md
|
|
10
10
|
title: Getting Started
|
|
11
11
|
description: This guide explains how to use `async-service-supervisor-envoy` to
|
|
12
|
-
publish supervised worker endpoints to Envoy using xDS.
|
|
12
|
+
publish supervised worker clusters and endpoints to Envoy using xDS.
|
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require "async/http/endpoint"
|
|
7
|
-
require "async/service/supervisor/monitor"
|
|
8
|
-
require "async/service/supervisor/utilization_monitor"
|
|
9
6
|
require "async/grpc/xds/client_side_weighted_round_robin"
|
|
7
|
+
require "async/grpc/xds/cluster_discovery_service"
|
|
8
|
+
require "async/grpc/xds/config_source"
|
|
10
9
|
require "async/grpc/xds/control_plane"
|
|
10
|
+
require "async/grpc/xds/endpoint_discovery_service"
|
|
11
11
|
require "async/grpc/xds/server"
|
|
12
|
+
require "async/http/endpoint"
|
|
13
|
+
require "async/service/supervisor/monitor"
|
|
14
|
+
require "async/service/supervisor/utilization_monitor"
|
|
12
15
|
require "process/metrics"
|
|
13
16
|
require "xds/data/orca/v3/orca_load_report_pb"
|
|
14
17
|
|
|
@@ -22,12 +25,17 @@ module Async
|
|
|
22
25
|
module Supervisor
|
|
23
26
|
# Provides Envoy integration for supervisor-managed services.
|
|
24
27
|
module Envoy
|
|
25
|
-
# Represents a supervisor monitor that publishes worker endpoints to Envoy using xDS.
|
|
28
|
+
# Represents a supervisor monitor that publishes worker endpoints and optionally clusters to Envoy using xDS.
|
|
29
|
+
#
|
|
30
|
+
# The monitor always serves a dedicated EDS stream and can additionally serve
|
|
31
|
+
# CDS, leaving ADS available to another control plane.
|
|
26
32
|
class Monitor < Async::Service::Supervisor::Monitor
|
|
27
33
|
# Initialize the monitor.
|
|
28
|
-
# @parameter bind [String | Nil] The optional address for the
|
|
34
|
+
# @parameter bind [String | Nil] The optional address for the discovery server.
|
|
29
35
|
# @parameter delegate [Delegate] The delegate used to map supervisor state into Envoy endpoints.
|
|
30
36
|
# @parameter control_plane [Async::GRPC::XDS::ControlPlane] The xDS control plane to update.
|
|
37
|
+
# @parameter management_cluster [String] The static Envoy cluster used to reach this discovery server.
|
|
38
|
+
# @parameter publish_clusters [Boolean] Whether to publish derived cluster configuration through CDS.
|
|
31
39
|
# @parameter health_checks [Array(Envoy::Config::Core::V3::HealthCheck)] The active health checks applied to published clusters.
|
|
32
40
|
# @parameter orca [Boolean] Whether to collect and serve per-worker ORCA load reports.
|
|
33
41
|
# @parameter processor [Process::Metrics::Processor | Nil] The optional process CPU sampler.
|
|
@@ -37,6 +45,8 @@ module Async
|
|
|
37
45
|
bind: nil,
|
|
38
46
|
delegate: Delegate.new,
|
|
39
47
|
control_plane: Async::GRPC::XDS::ControlPlane.new,
|
|
48
|
+
management_cluster: "xds_cluster",
|
|
49
|
+
publish_clusters: true,
|
|
40
50
|
health_checks: [],
|
|
41
51
|
orca: false,
|
|
42
52
|
processor: nil,
|
|
@@ -49,12 +59,14 @@ module Async
|
|
|
49
59
|
@bind = bind
|
|
50
60
|
@delegate = delegate
|
|
51
61
|
@control_plane = control_plane
|
|
62
|
+
@eds_config = Async::GRPC::XDS::ConfigSource.grpc(management_cluster)
|
|
63
|
+
@publish_clusters = publish_clusters
|
|
52
64
|
@health_checks = health_checks
|
|
53
65
|
@interval = interval
|
|
54
66
|
@orca = orca
|
|
55
67
|
@controllers = {}
|
|
56
68
|
@published_clusters = {}
|
|
57
|
-
@
|
|
69
|
+
@published_endpoints = {}
|
|
58
70
|
@mutex = Mutex.new
|
|
59
71
|
|
|
60
72
|
if @orca
|
|
@@ -105,15 +117,21 @@ module Async
|
|
|
105
117
|
end
|
|
106
118
|
end
|
|
107
119
|
|
|
108
|
-
# Run the monitor and optional
|
|
109
|
-
# @parameter parent [Async::Task] The parent task used for the
|
|
120
|
+
# Run the monitor and optional discovery server task.
|
|
121
|
+
# @parameter parent [Async::Task] The parent task used for the server.
|
|
110
122
|
# @returns [Async::Task] The monitor task.
|
|
111
123
|
def run(parent: Async::Task.current)
|
|
112
124
|
task = super(parent: parent)
|
|
113
125
|
|
|
114
126
|
if @bind
|
|
115
|
-
|
|
116
|
-
|
|
127
|
+
parent.async do
|
|
128
|
+
services = [Async::GRPC::XDS::EndpointDiscoveryService]
|
|
129
|
+
services.unshift(Async::GRPC::XDS::ClusterDiscoveryService) if @publish_clusters
|
|
130
|
+
|
|
131
|
+
server = Async::GRPC::XDS::Server.new(
|
|
132
|
+
@control_plane,
|
|
133
|
+
services: services
|
|
134
|
+
)
|
|
117
135
|
server.dispatcher.register(ORCAService.new(self, minimum_interval: @interval)) if @orca
|
|
118
136
|
server.run(server_endpoint)
|
|
119
137
|
end
|
|
@@ -192,17 +210,26 @@ module Async
|
|
|
192
210
|
records_by_cluster = build_records_by_cluster
|
|
193
211
|
clusters = build_clusters(records_by_cluster)
|
|
194
212
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
@
|
|
200
|
-
|
|
213
|
+
if @publish_clusters
|
|
214
|
+
records_by_cluster.each do |cluster, records|
|
|
215
|
+
configuration = cluster_configuration(records)
|
|
216
|
+
|
|
217
|
+
unless @published_clusters[cluster] == configuration
|
|
218
|
+
@control_plane.update_cluster(cluster, **configuration)
|
|
219
|
+
@published_clusters[cluster] = configuration
|
|
220
|
+
end
|
|
201
221
|
end
|
|
222
|
+
elsif @orca
|
|
223
|
+
records_by_cluster.each_value{|records| validate_orca_records(records)}
|
|
202
224
|
end
|
|
203
225
|
|
|
204
|
-
|
|
205
|
-
|
|
226
|
+
# Skipping an identical assignment only avoids a redundant version bump:
|
|
227
|
+
(@published_endpoints.keys | clusters.keys).each do |cluster|
|
|
228
|
+
endpoints = clusters.fetch(cluster, [])
|
|
229
|
+
next if @published_endpoints[cluster] == endpoints
|
|
230
|
+
|
|
231
|
+
@control_plane.update_endpoints(cluster, endpoints)
|
|
232
|
+
@published_endpoints[cluster] = endpoints
|
|
206
233
|
end
|
|
207
234
|
end
|
|
208
235
|
|
|
@@ -218,6 +245,9 @@ module Async
|
|
|
218
245
|
records_by_cluster.transform_values do |records|
|
|
219
246
|
if @orca
|
|
220
247
|
records.map do |record|
|
|
248
|
+
# Envoy sends the endpoint hostname as the request authority when
|
|
249
|
+
# it opens an out-of-band reporting stream, which is how a single
|
|
250
|
+
# ORCA service identifies which worker a report is for:
|
|
221
251
|
{
|
|
222
252
|
addresses: record[:endpoint].addresses,
|
|
223
253
|
healthy: record[:healthy],
|
|
@@ -248,13 +278,12 @@ module Async
|
|
|
248
278
|
|
|
249
279
|
configuration = {
|
|
250
280
|
protocol: envoy_protocol(common_protocols),
|
|
281
|
+
eds_config: @eds_config,
|
|
251
282
|
health_checks: @health_checks,
|
|
252
283
|
}
|
|
253
284
|
|
|
254
285
|
if @orca
|
|
255
|
-
|
|
256
|
-
raise ArgumentError, "Out-of-band ORCA reporting requires IP endpoints!"
|
|
257
|
-
end
|
|
286
|
+
validate_orca_records(records)
|
|
258
287
|
|
|
259
288
|
configuration[:load_balancing_policy] = Async::GRPC::XDS::ClientSideWeightedRoundRobin.build(
|
|
260
289
|
@orca_port,
|
|
@@ -265,6 +294,12 @@ module Async
|
|
|
265
294
|
configuration
|
|
266
295
|
end
|
|
267
296
|
|
|
297
|
+
def validate_orca_records(records)
|
|
298
|
+
if records.any?{|record| record[:endpoint].addresses.any?{|address| address[:path]}}
|
|
299
|
+
raise ArgumentError, "Out-of-band ORCA reporting requires IP endpoints!"
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
268
303
|
def sample_load_reports
|
|
269
304
|
controllers = @mutex.synchronize{@controllers.dup}
|
|
270
305
|
workers = @utilization_monitor.sample_by_worker
|
data/readme.md
CHANGED
|
@@ -8,7 +8,7 @@ Provides an Envoy xDS monitor for `async-service-supervisor`.
|
|
|
8
8
|
|
|
9
9
|
`async-service-supervisor-envoy` publishes supervised worker endpoints to Envoy:
|
|
10
10
|
|
|
11
|
-
- **
|
|
11
|
+
- **Cluster and endpoint discovery** - Runs dedicated CDS and EDS services backed by `async-grpc-xds`, leaving ADS free for listener, route, and other configuration.
|
|
12
12
|
- **Supervisor integration** - Registers and removes endpoints from supervisor worker lifecycle events.
|
|
13
13
|
- **Multiple clusters** - Groups workers by `state[:name]` by default.
|
|
14
14
|
- **Endpoint contract** - Converts concrete post-bind worker listeners into Envoy upstream endpoints, including grouped IP or Unix socket addresses.
|
|
@@ -19,12 +19,20 @@ Provides an Envoy xDS monitor for `async-service-supervisor`.
|
|
|
19
19
|
|
|
20
20
|
Please see the [project documentation](https://socketry.github.io/async-service-supervisor-envoy/) for more details.
|
|
21
21
|
|
|
22
|
-
- [Getting Started](https://socketry.github.io/async-service-supervisor-envoy/guides/getting-started/index) - This guide explains how to use `async-service-supervisor-envoy` to publish supervised worker endpoints to Envoy using xDS.
|
|
22
|
+
- [Getting Started](https://socketry.github.io/async-service-supervisor-envoy/guides/getting-started/index) - This guide explains how to use `async-service-supervisor-envoy` to publish supervised worker clusters and endpoints to Envoy using xDS.
|
|
23
23
|
|
|
24
24
|
## Releases
|
|
25
25
|
|
|
26
26
|
Please see the [project releases](https://socketry.github.io/async-service-supervisor-envoy/releases/index) for all releases.
|
|
27
27
|
|
|
28
|
+
### v0.5.0
|
|
29
|
+
|
|
30
|
+
- Serve clusters and endpoints through dedicated CDS and EDS services instead of the aggregated discovery service, leaving ADS available for listener, route, and other configuration.
|
|
31
|
+
- Allow cluster publication to be disabled when clusters are owned by another control plane.
|
|
32
|
+
- Configure generated clusters to obtain endpoint assignments from the dedicated EDS service.
|
|
33
|
+
- Publish endpoint assignments only when they change, instead of on every reconciliation.
|
|
34
|
+
- Use normalized processor utilization from `process-metrics` v0.13.
|
|
35
|
+
|
|
28
36
|
### v0.4.0
|
|
29
37
|
|
|
30
38
|
- Publish configured active health checks with Envoy clusters.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.5.0
|
|
4
|
+
|
|
5
|
+
- Serve clusters and endpoints through dedicated CDS and EDS services instead of the aggregated discovery service, leaving ADS available for listener, route, and other configuration.
|
|
6
|
+
- Allow cluster publication to be disabled when clusters are owned by another control plane.
|
|
7
|
+
- Configure generated clusters to obtain endpoint assignments from the dedicated EDS service.
|
|
8
|
+
- Publish endpoint assignments only when they change, instead of on every reconciliation.
|
|
9
|
+
- Use normalized processor utilization from `process-metrics` v0.13.
|
|
10
|
+
|
|
3
11
|
## v0.4.0
|
|
4
12
|
|
|
5
13
|
- Publish configured active health checks with Envoy clusters.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async-service-supervisor-envoy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -58,14 +58,14 @@ dependencies:
|
|
|
58
58
|
requirements:
|
|
59
59
|
- - "~>"
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0.
|
|
61
|
+
version: '0.4'
|
|
62
62
|
type: :runtime
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0.
|
|
68
|
+
version: '0.4'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: async-http
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -100,14 +100,14 @@ dependencies:
|
|
|
100
100
|
requirements:
|
|
101
101
|
- - "~>"
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '0.
|
|
103
|
+
version: '0.13'
|
|
104
104
|
type: :runtime
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
108
|
- - "~>"
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: '0.
|
|
110
|
+
version: '0.13'
|
|
111
111
|
executables: []
|
|
112
112
|
extensions: []
|
|
113
113
|
extra_rdoc_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|