Skip to content

Commit cd6da0f

Browse files
authored
control: Build the identity client each time its used (#1021)
We currently build the identity client at startup and hold it to be used fairly infrequently: the client is used once per day by default, and even in extremely aggressive scenarios is unlikely to be used more frequently than once every few minutes. As such, there's very little benefit to holding the client (and buffers, DNS resolutions, etc) open continually. Instead, it seems preferable to instantiate the identity client only as it's needed. Practically, we see issues like linkerd/linkerd2#6184 where the identity client may try to reconnect to stale endpoints when the identity deployment is rescheduled (because there aren't a steady stream of requests on this client). This change makes the controller stack a `NewService<()>` so that clients can be instantiated lazily. The identity module now creates a new connection for each identity request. Other controller clients are unaffacted, continuing to use long-live clients.
1 parent 99e3c06 commit cd6da0f

File tree

4 files changed

+44
-37
lines changed

4 files changed

+44
-37
lines changed

linkerd/app/core/src/control.rs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
use crate::{
2-
classify, config, control, dns, metrics,
3-
proxy::http,
4-
reconnect,
5-
svc::{self, NewService, Param},
6-
tls,
7-
transport::ConnectTcp,
8-
Addr, Error,
2+
classify, config, control, dns, metrics, proxy::http, reconnect, svc, tls,
3+
transport::ConnectTcp, Addr, Error,
94
};
105
use futures::future::Either;
116
use std::fmt;
@@ -26,7 +21,7 @@ pub struct ControlAddr {
2621
pub identity: tls::ConditionalClientTls,
2722
}
2823

29-
impl Param<Addr> for ControlAddr {
24+
impl svc::Param<Addr> for ControlAddr {
3025
fn param(&self) -> Addr {
3126
self.addr.clone()
3227
}
@@ -51,13 +46,15 @@ impl Config {
5146
dns: dns::Resolver,
5247
metrics: metrics::ControlHttp,
5348
identity: Option<L>,
54-
) -> Client<B>
49+
) -> svc::BoxNewService<(), Client<B>>
5550
where
5651
B: http::HttpBody + Send + 'static,
5752
B::Data: Send,
5853
B::Error: Into<Error> + Send + Sync + 'static,
59-
L: Clone + Param<tls::client::Config> + Send + 'static,
54+
L: Clone + svc::Param<tls::client::Config> + Send + Sync + 'static,
6055
{
56+
let addr = self.addr;
57+
6158
let connect_backoff = {
6259
let backoff = self.connect.backoff;
6360
move |_| Ok(backoff.stream())
@@ -102,7 +99,9 @@ impl Config {
10299
.push(metrics.to_layer::<classify::Response, _>())
103100
.push(self::add_origin::layer())
104101
.push_on_response(svc::layers().push_spawn_buffer(self.buffer_capacity))
105-
.new_service(self.addr)
102+
.push_map_target(move |()| addr.clone())
103+
.push(svc::BoxNewService::layer())
104+
.into_inner()
106105
}
107106
}
108107

@@ -234,8 +233,7 @@ mod balance {
234233
mod client {
235234
use crate::{
236235
proxy::http,
237-
svc::{self, Param},
238-
tls,
236+
svc, tls,
239237
transport::{Remote, ServerAddr},
240238
};
241239
use linkerd_proxy_http::h2::Settings as H2Settings;
@@ -263,19 +261,19 @@ mod client {
263261

264262
// === impl Target ===
265263

266-
impl Param<Remote<ServerAddr>> for Target {
264+
impl svc::Param<Remote<ServerAddr>> for Target {
267265
fn param(&self) -> Remote<ServerAddr> {
268266
Remote(ServerAddr(self.addr))
269267
}
270268
}
271269

272-
impl Param<SocketAddr> for Target {
270+
impl svc::Param<SocketAddr> for Target {
273271
fn param(&self) -> SocketAddr {
274272
self.addr
275273
}
276274
}
277275

278-
impl Param<tls::ConditionalClientTls> for Target {
276+
impl svc::Param<tls::ConditionalClientTls> for Target {
279277
fn param(&self) -> tls::ConditionalClientTls {
280278
self.server_id.clone()
281279
}

linkerd/app/src/dst.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use linkerd_app_core::{
44
metrics,
55
profiles::{self, DiscoveryRejected},
66
proxy::{api_resolve as api, identity::LocalCrtKey, resolve::recover},
7+
svc::NewService,
78
Error, Recover,
89
};
910
use tonic::body::BoxBody;
@@ -41,7 +42,7 @@ impl Config {
4142
) -> Result<Dst, Error> {
4243
let addr = self.control.addr.clone();
4344
let backoff = BackoffUnlessInvalidArgument(self.control.connect.backoff);
44-
let svc = self.control.build(dns, metrics, identity);
45+
let svc = self.control.build(dns, metrics, identity).new_service(());
4546

4647
Ok(Dst {
4748
addr,

linkerd/app/src/oc_collector.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
use crate::{dns, identity::LocalCrtKey};
2-
use linkerd_app_core::{control, metrics::ControlHttp as HttpMetrics, Error};
2+
use linkerd_app_core::{control, metrics::ControlHttp as HttpMetrics, svc::NewService, Error};
33
use linkerd_opencensus::{self as opencensus, metrics, proto};
4-
use std::future::Future;
5-
use std::pin::Pin;
6-
use std::{collections::HashMap, time::SystemTime};
4+
use std::{collections::HashMap, future::Future, pin::Pin, time::SystemTime};
75
use tokio::sync::mpsc;
86
use tokio_stream::wrappers::ReceiverStream;
97
use tracing::Instrument;
@@ -51,7 +49,10 @@ impl Config {
5149
Config::Disabled => Ok(OcCollector::Disabled),
5250
Config::Enabled(inner) => {
5351
let addr = inner.control.addr.clone();
54-
let svc = inner.control.build(dns, client_metrics, identity);
52+
let svc = inner
53+
.control
54+
.build(dns, client_metrics, identity)
55+
.new_service(());
5556

5657
let (span_sink, spans_rx) = mpsc::channel(Self::SPAN_BUFFER_CAPACITY);
5758
let spans_rx = ReceiverStream::new(spans_rx);

linkerd/proxy/identity/src/certify.rs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use http_body::Body as HttpBody;
2-
use linkerd2_proxy_api::identity as api;
2+
use linkerd2_proxy_api::identity::{self as api, identity_client::IdentityClient};
33
use linkerd_error::Error;
44
use linkerd_identity as id;
55
use linkerd_metrics::Counter;
6-
use linkerd_stack::Param;
6+
use linkerd_stack::{NewService, Param};
77
use linkerd_tls as tls;
88
use pin_project::pin_project;
99
use std::convert::TryFrom;
@@ -84,12 +84,13 @@ impl Config {
8484
// === impl Daemon ===
8585

8686
impl Daemon {
87-
pub async fn run<T>(self, client: T)
87+
pub async fn run<N, S>(self, mut new_client: N)
8888
where
89-
T: GrpcService<BoxBody>,
90-
T::ResponseBody: Send + 'static,
91-
<T::ResponseBody as Body>::Data: Send,
92-
<T::ResponseBody as HttpBody>::Error: Into<Error> + Send,
89+
N: NewService<(), Service = S>,
90+
S: GrpcService<BoxBody>,
91+
S::ResponseBody: Send + 'static,
92+
<S::ResponseBody as Body>::Data: Send,
93+
<S::ResponseBody as HttpBody>::Error: Into<Error> + Send,
9394
{
9495
let Self {
9596
crt_key_watch,
@@ -99,18 +100,24 @@ impl Daemon {
99100

100101
debug!("Identity daemon running");
101102
let mut curr_expiry = UNIX_EPOCH;
102-
let mut client = api::identity_client::IdentityClient::new(client);
103103

104104
loop {
105105
match config.token.load() {
106106
Ok(token) => {
107-
let req = grpc::Request::new(api::CertifyRequest {
108-
token,
109-
identity: config.local_id.to_string(),
110-
certificate_signing_request: config.csr.to_vec(),
111-
});
112-
trace!("daemon certifying");
113-
let rsp = client.certify(req).await;
107+
let rsp = {
108+
// The client is used for infrequent communication with the identity controller;
109+
// so clients are instantiated on-demand rather than held.
110+
let mut client = IdentityClient::new(new_client.new_service(()));
111+
112+
trace!("daemon certifying");
113+
let req = grpc::Request::new(api::CertifyRequest {
114+
token,
115+
identity: config.local_id.to_string(),
116+
certificate_signing_request: config.csr.to_vec(),
117+
});
118+
client.certify(req).await
119+
};
120+
114121
match rsp {
115122
Err(e) => error!("Failed to certify identity: {}", e),
116123
Ok(rsp) => {

0 commit comments

Comments
 (0)