-
Notifications
You must be signed in to change notification settings - Fork 5.3k
upstream: add load_assigment field to Cluster #3261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0c95fab
a974b87
036a319
88090ed
8da2c60
ed08cd2
9ba78d3
14b0d23
bd6ac42
8b2ee7f
e92c5e5
1626fab
3213dd1
d887de6
8629c7b
11c77b3
f2bbd12
cf34a06
c019448
df77682
2708782
c5db329
fc8251e
675bb90
5879fd5
4bfd57e
1a48994
a34c195
669fa63
cb69f92
307e247
c724f2a
37c6c72
81fc59d
9c6254f
1a2f404
572cf12
decca86
d3ed76e
b79ddbe
af559be
b88200c
3cc11be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -13,6 +13,7 @@ import "envoy/api/v2/core/health_check.proto"; | |||||
| import "envoy/api/v2/core/protocol.proto"; | ||||||
| import "envoy/api/v2/cluster/circuit_breaker.proto"; | ||||||
| import "envoy/api/v2/cluster/outlier_detection.proto"; | ||||||
| import "envoy/api/v2/eds.proto"; | ||||||
| import "envoy/type/percent.proto"; | ||||||
|
|
||||||
| import "google/api/annotations.proto"; | ||||||
|
|
@@ -41,7 +42,7 @@ service ClusterDiscoveryService { | |||||
| // [#protodoc-title: Clusters] | ||||||
|
|
||||||
| // Configuration for a single upstream cluster. | ||||||
| // [#comment:next free field: 32] | ||||||
| // [#comment:next free field: 34] | ||||||
| message Cluster { | ||||||
| // Supplies the name of the cluster which must be unique across all clusters. | ||||||
| // The cluster name is used when emitting | ||||||
|
|
@@ -150,12 +151,26 @@ message Cluster { | |||||
| // when picking a host in the cluster. | ||||||
| LbPolicy lb_policy = 6 [(validate.rules).enum.defined_only = true]; | ||||||
|
|
||||||
| // If the service discovery type is | ||||||
| // Setting this is a requirement for clusters with discovery type | ||||||
| // :ref:`STATIC<envoy_api_enum_value_Cluster.DiscoveryType.STATIC>`, | ||||||
| // :ref:`STRICT_DNS<envoy_api_enum_value_Cluster.DiscoveryType.STRICT_DNS>` | ||||||
| // or :ref:`LOGICAL_DNS<envoy_api_enum_value_Cluster.DiscoveryType.LOGICAL_DNS>`, | ||||||
| // then hosts is required. | ||||||
| repeated core.Address hosts = 7; | ||||||
| // or :ref:`LOGICAL_DNS<envoy_api_enum_value_Cluster.DiscoveryType.LOGICAL_DNS>`. | ||||||
| // **This field is deprecated**. Set the :ref:`load_assignment<envoy_api_field_Cluster.load_assignment>` | ||||||
| // field instead. | ||||||
| repeated core.Address hosts = 7 [deprecated = true]; | ||||||
|
|
||||||
| // Setting this is required for specifying members of | ||||||
| // :ref:`STATIC<envoy_api_enum_value_Cluster.DiscoveryType.STATIC>`, | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you update
Address depends on the cluster type. For static, it will be expected to be a direct IP address (or something resolvable by the specified resolver in the Address). For logical/static DNS, it will be expected to be a hostname, resolved via DNS.
This is actually a bit more complicated thinking about it. See the wording at envoy/api/envoy/api/v2/core/address.proto Line 36 in 71c0fd6
Clusters. @mattklein123 WDYT on this? Seems a rough edge in the API.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I don't know. That's a good question. Should we just say that custom resolvers can't be used at all with DNS discovery types and block it? I don't think it really makes sense and is confusing. If we do that, I think we can have error checking / error messages to guide people in the right direction...
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would be reasonable. I think if we had the custom resolvers able to operate async, and some hooks to detect DNS changes, we could replace the hardcoded DNS cluster types with just resolver extensions. But, moving the resolver extension model to be expressive enough and then plumbing this would be a fair chunk of work. |
||||||
| // :ref:`STRICT_DNS<envoy_api_enum_value_Cluster.DiscoveryType.STRICT_DNS>` | ||||||
| // or :ref:`LOGICAL_DNS<envoy_api_enum_value_Cluster.DiscoveryType.LOGICAL_DNS>` clusters. | ||||||
| // This field supersedes :ref:`hosts<envoy_api_field_Cluster.hosts>` field. | ||||||
| // | ||||||
| // .. attention:: | ||||||
| // | ||||||
| // Setting this allows CDS static/DNS assignments to contain embedded EDS equivalent | ||||||
| // :ref:`endpoint assignments<envoy_api_msg_ClusterLoadAssignment>`. | ||||||
| // Setting this overrides :ref:`hosts<envoy_api_field_Cluster.hosts>` values. | ||||||
| ClusterLoadAssignment load_assignment = 33; | ||||||
|
|
||||||
| // Optional :ref:`active health checking <arch_overview_health_checking>` | ||||||
| // configuration for the cluster. If no | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ namespace Upstream { | |
| LogicalDnsCluster::LogicalDnsCluster(const envoy::api::v2::Cluster& cluster, | ||
| Runtime::Loader& runtime, Stats::Store& stats, | ||
| Ssl::ContextManager& ssl_context_manager, | ||
| const LocalInfo::LocalInfo& local_info, | ||
| Network::DnsResolverSharedPtr dns_resolver, | ||
| ThreadLocal::SlotAllocator& tls, ClusterManager& cm, | ||
| Event::Dispatcher& dispatcher, bool added_via_api) | ||
|
|
@@ -26,10 +27,15 @@ LogicalDnsCluster::LogicalDnsCluster(const envoy::api::v2::Cluster& cluster, | |
| dns_refresh_rate_ms_( | ||
| std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(cluster, dns_refresh_rate, 5000))), | ||
| tls_(tls.allocateSlot()), | ||
| resolve_timer_(dispatcher.createTimer([this]() -> void { startResolve(); })) { | ||
| const auto& hosts = cluster.hosts(); | ||
| if (hosts.size() != 1) { | ||
| throw EnvoyException("logical_dns clusters must have a single host"); | ||
| resolve_timer_(dispatcher.createTimer([this]() -> void { startResolve(); })), | ||
| priority_state_manager_(new PriorityStateManager(*this, local_info)), | ||
| load_assignment_(cluster.has_load_assignment() | ||
| ? cluster.load_assignment() | ||
| : Config::Utility::translateClusterHosts(cluster.hosts())) { | ||
| const auto& locality_lb_endpoints = load_assignment_.endpoints(); | ||
| if (locality_lb_endpoints.size() != 1 || locality_lb_endpoints[0].lb_endpoints().size() != 1) { | ||
| throw EnvoyException( | ||
| "LOGICAL_DNS clusters must have a single locality_lb_endpoint and a single lb_endpoint"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a test for this?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I were using the deprecated hosts I'd be really confused at Envoy throwing an error message about there needing to be a single lb_endpoint. I'd encourage switching on the configuration type so for we still have helpful error messages during the transition. |
||
| } | ||
|
|
||
| switch (cluster.dns_lookup_family()) { | ||
|
|
@@ -46,10 +52,12 @@ LogicalDnsCluster::LogicalDnsCluster(const envoy::api::v2::Cluster& cluster, | |
| NOT_REACHED; | ||
| } | ||
|
|
||
| const auto& socket_address = hosts[0].socket_address(); | ||
| const envoy::api::v2::core::SocketAddress& socket_address = | ||
| lbEndpoint().endpoint().address().socket_address(); | ||
| dns_url_ = fmt::format("tcp://{}:{}", socket_address.address(), socket_address.port_value()); | ||
| hostname_ = Network::Utility::hostFromTcpUrl(dns_url_); | ||
| Network::Utility::portFromTcpUrl(dns_url_); | ||
| priority_state_manager_->initializePerPriority(localityLbEndpoint()); | ||
|
|
||
| tls_->set([](Event::Dispatcher&) -> ThreadLocal::ThreadLocalObjectSharedPtr { | ||
| return std::make_shared<PerThreadCurrentHostData>(); | ||
|
|
@@ -87,7 +95,8 @@ void LogicalDnsCluster::startResolve() { | |
| current_resolved_address_ = new_address; | ||
| // Capture URL to avoid a race with another update. | ||
| tls_->runOnAllThreads([this, new_address]() -> void { | ||
| tls_->getTyped<PerThreadCurrentHostData>().current_resolved_address_ = new_address; | ||
| PerThreadCurrentHostData& data = tls_->getTyped<PerThreadCurrentHostData>(); | ||
| data.current_resolved_address_ = new_address; | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -106,14 +115,11 @@ void LogicalDnsCluster::startResolve() { | |
| new LogicalHost(info_, hostname_, Network::Utility::getIpv6AnyAddress(), *this)); | ||
| break; | ||
| } | ||
| HostVectorSharedPtr new_hosts(new HostVector()); | ||
| new_hosts->emplace_back(logical_host_); | ||
| // Given the current config, only EDS clusters support multiple priorities. | ||
| ASSERT(priority_set_.hostSetsPerPriority().size() == 1); | ||
| auto& first_host_set = priority_set_.getOrCreateHostSet(0); | ||
| first_host_set.updateHosts(new_hosts, createHealthyHostList(*new_hosts), | ||
| HostsPerLocalityImpl::empty(), HostsPerLocalityImpl::empty(), | ||
| {}, *new_hosts, {}); | ||
|
|
||
| const uint32_t priority = localityLbEndpoint().priority(); | ||
| priority_state_manager_->registerHostPerPriority(logical_host_, priority); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| priority_state_manager_->updateClusterPrioritySet(priority, absl::nullopt, | ||
| absl::nullopt, false); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -130,7 +136,8 @@ Upstream::Host::CreateConnectionData LogicalDnsCluster::LogicalHost::createConne | |
| return {HostImpl::createConnection(dispatcher, *parent_.info_, data.current_resolved_address_, | ||
| options), | ||
| HostDescriptionConstSharedPtr{ | ||
| new RealHostDescription(data.current_resolved_address_, shared_from_this())}}; | ||
| new RealHostDescription(data.current_resolved_address_, parent_.localityLbEndpoint(), | ||
| parent_.lbEndpoint(), shared_from_this())}}; | ||
| } | ||
|
|
||
| } // namespace Upstream | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,6 +30,7 @@ class LogicalDnsCluster : public ClusterImplBase { | |
| public: | ||
| LogicalDnsCluster(const envoy::api::v2::Cluster& cluster, Runtime::Loader& runtime, | ||
| Stats::Store& stats, Ssl::ContextManager& ssl_context_manager, | ||
| const LocalInfo::LocalInfo& local_info, | ||
| Network::DnsResolverSharedPtr dns_resolver, ThreadLocal::SlotAllocator& tls, | ||
| ClusterManager& cm, Event::Dispatcher& dispatcher, bool added_via_api); | ||
|
|
||
|
|
@@ -42,9 +43,10 @@ class LogicalDnsCluster : public ClusterImplBase { | |
| struct LogicalHost : public HostImpl { | ||
| LogicalHost(ClusterInfoConstSharedPtr cluster, const std::string& hostname, | ||
| Network::Address::InstanceConstSharedPtr address, LogicalDnsCluster& parent) | ||
| : HostImpl(cluster, hostname, address, envoy::api::v2::core::Metadata::default_instance(), | ||
| 1, envoy::api::v2::core::Locality().default_instance(), | ||
| envoy::api::v2::endpoint::Endpoint::HealthCheckConfig().default_instance()), | ||
| : HostImpl(cluster, hostname, address, parent.lbEndpoint().metadata(), | ||
| parent.lbEndpoint().load_balancing_weight().value(), | ||
| parent.localityLbEndpoint().locality(), | ||
| parent.lbEndpoint().endpoint().health_check_config()), | ||
| parent_(parent) {} | ||
|
|
||
| // Upstream::Host | ||
|
|
@@ -57,13 +59,22 @@ class LogicalDnsCluster : public ClusterImplBase { | |
|
|
||
| struct RealHostDescription : public HostDescription { | ||
| RealHostDescription(Network::Address::InstanceConstSharedPtr address, | ||
| const envoy::api::v2::endpoint::LocalityLbEndpoints& locality_lb_endpoint, | ||
| const envoy::api::v2::endpoint::LbEndpoint& lb_endpoint, | ||
| HostConstSharedPtr logical_host) | ||
| : address_(address), logical_host_(logical_host) {} | ||
| : address_(address), | ||
| health_check_address_( | ||
| lb_endpoint.endpoint().health_check_config().port_value() == 0 | ||
| ? address | ||
| : Network::Utility::getAddressWithPort( | ||
| *address, lb_endpoint.endpoint().health_check_config().port_value())), | ||
| locality_lb_endpoint_(locality_lb_endpoint), lb_endpoint_(lb_endpoint), | ||
| logical_host_(logical_host) {} | ||
|
|
||
| // Upstream:HostDescription | ||
| bool canary() const override { return false; } | ||
| const envoy::api::v2::core::Metadata& metadata() const override { | ||
| return envoy::api::v2::core::Metadata::default_instance(); | ||
| return lb_endpoint_.metadata(); | ||
| } | ||
| const ClusterInfo& cluster() const override { return logical_host_->cluster(); } | ||
| HealthCheckHostMonitor& healthChecker() const override { | ||
|
|
@@ -76,20 +87,33 @@ class LogicalDnsCluster : public ClusterImplBase { | |
| const std::string& hostname() const override { return logical_host_->hostname(); } | ||
| Network::Address::InstanceConstSharedPtr address() const override { return address_; } | ||
| const envoy::api::v2::core::Locality& locality() const override { | ||
| return envoy::api::v2::core::Locality().default_instance(); | ||
| return locality_lb_endpoint_.locality(); | ||
| } | ||
| // TODO(dio): To support different address port. | ||
| Network::Address::InstanceConstSharedPtr healthCheckAddress() const override { | ||
| return address_; | ||
| return health_check_address_; | ||
| } | ||
| uint32_t priority() const { return locality_lb_endpoint_.priority(); } | ||
| Network::Address::InstanceConstSharedPtr address_; | ||
| Network::Address::InstanceConstSharedPtr health_check_address_; | ||
| const envoy::api::v2::endpoint::LocalityLbEndpoints& locality_lb_endpoint_; | ||
| const envoy::api::v2::endpoint::LbEndpoint& lb_endpoint_; | ||
| HostConstSharedPtr logical_host_; | ||
| }; | ||
|
|
||
| struct PerThreadCurrentHostData : public ThreadLocal::ThreadLocalObject { | ||
| Network::Address::InstanceConstSharedPtr current_resolved_address_; | ||
| }; | ||
|
|
||
| const envoy::api::v2::endpoint::LocalityLbEndpoints& localityLbEndpoint() const { | ||
| ASSERT(load_assignment_.endpoints().size() == 1); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a comment explaining this is checked in the constructor (at config load time)? |
||
| return load_assignment_.endpoints()[0]; | ||
| } | ||
|
|
||
| const envoy::api::v2::endpoint::LbEndpoint& lbEndpoint() const { | ||
| ASSERT(localityLbEndpoint().lb_endpoints().size() == 1); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto. |
||
| return localityLbEndpoint().lb_endpoints()[0]; | ||
| } | ||
|
|
||
| void startResolve(); | ||
|
|
||
| // ClusterImplBase | ||
|
|
@@ -105,6 +129,8 @@ class LogicalDnsCluster : public ClusterImplBase { | |
| Network::Address::InstanceConstSharedPtr current_resolved_address_; | ||
| HostSharedPtr logical_host_; | ||
| Network::ActiveDnsQuery* active_dns_query_{}; | ||
| PriorityStateManagerPtr priority_state_manager_; | ||
| const envoy::api::v2::ClusterLoadAssignment load_assignment_; | ||
| }; | ||
|
|
||
| } // namespace Upstream | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@htuch what's our policy on docs and deprecation? I'd be inclined to say when we deprecate a field we should update the relevant docs (i.e. updating use of hosts in v2_overview.html) so folks cribbing configuration will be cribbing the new way of doing things and not the deprecated way of doing things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, yes.