본문으로 건너뛰기

Client 내부 구조

Client는 단순한 HTTP 클라이언트가 아닙니다. Tower 미들웨어 스택으로 구성된 레이어 아키텍처이며, Clone이 Arc 수준으로 가볍습니다. 내부 구조를 이해하면 타임아웃, 인증, 커스텀 미들웨어 같은 문제를 해결할 수 있습니다.

Client의 실체

kube-client/src/client/mod.rs (단순화)
pub struct Client {
inner: Buffer<Request<Body>, BoxFuture<'static, Result<Response<Body>, BoxError>>>,
default_ns: String,
valid_until: Option<Timestamp>,
}
  • tower::Buffer: ServiceArc로 감쌉니다. Client::clone()은 참조 카운트 증가에 불과하므로, 여러 Api<K> 핸들에서 같은 Client를 자유롭게 공유할 수 있습니다.
  • capacity 1024: Buffer의 in-flight 요청 용량입니다. 동시에 1024개까지 요청을 큐에 넣을 수 있습니다.
  • BoxFuture: 응답 future의 구체 타입이 지워져 있어서 내부 미들웨어 스택의 구체 타입이 외부에 노출되지 않습니다.
  • valid_until: credential 만료 시간입니다. 만료 후 Client를 재생성해야 합니다.

Tower 미들웨어 스택

요청은 위에서 아래로, 응답은 아래에서 위로 흐릅니다. 각 레이어는 tower::Layer trait을 구현합니다.

레이어역할
TraceLayerOpenTelemetry 호환 HTTP 스팬을 생성합니다. 요청/응답의 트레이싱 정보를 기록합니다.
extra_headers_layerimpersonation 등 커스텀 헤더를 추가합니다.
auth_layerBearer 토큰, exec 기반 인증, 토큰 자동 갱신을 처리합니다.
DecompressionLayergzip 응답을 해제합니다 (gzip feature 필요).
base_uri_layer모든 요청 URL에 cluster_url prefix를 추가합니다.
hyper ClientHTTP/1.1 + HTTP/2로 실제 전송합니다.
TimeoutConnectorconnect/read/write 각각의 타임아웃을 적용합니다.
TLS layerrustls-tls 또는 openssl-tls feature에 따라 TLS를 처리합니다.
ProxySOCKS5/HTTP 프록시를 통과합니다 (socks5/http-proxy feature).
HttpConnectorTCP 연결을 생성합니다.

Config 추론 체인

Client::try_default()는 내부적으로 Config::infer()를 호출합니다. 다음 순서로 설정을 탐색합니다.

  1. kubeconfig: $KUBECONFIG 환경변수가 가리키는 파일, 또는 ~/.kube/config
  2. in-cluster: /var/run/secrets/kubernetes.io/serviceaccount/의 토큰과 CA 인증서
  3. 둘 다 없으면 에러를 반환합니다.

기본 타임아웃

설정기본값용도
connect_timeout30초TCP 연결 수립
read_timeoutNone응답 대기 (무제한)
write_timeoutNone요청 전송 (무제한)
타임아웃 설계 — 계층별 분리

이전에는 read_timeout이 295초로 설정되어 watch long-polling과 일반 API 호출에 동일하게 적용되었습니다. 이로 인해 exec, attach, port-forward 같은 장기 연결이 유휴 295초 후 끊기는 문제가 있었습니다 (kube#1798).

현재는 Go client와 동일하게 global read_timeoutNone으로 설정하고, 각 계층이 자체 타임아웃을 관리합니다:

  • watcher: 서버 측 timeoutSeconds + 마진으로 idle timeout을 자체 관리. 네트워크 장애 시 자동 재연결
  • exec/attach/port-forward: 타임아웃 없음 (무기한 유휴 가능)
  • reconciler 내 API 호출: 필요 시 tokio::time::timeout으로 개별 감싸기
// reconciler 내부에서 느린 호출 방어
let pod = tokio::time::timeout(
Duration::from_secs(10),
pods.get("my-pod")
).await??;

인증 처리

auth_layer가 모든 인증을 담당합니다.

방식동작
정적 토큰Authorization: Bearer <token> 헤더를 추가합니다.
클라이언트 인증서TLS 레이어에서 mTLS로 인증합니다.
exec plugin외부 프로그램을 호출해 토큰을 얻습니다 (AWS EKS의 aws-iam-authenticator 등).
토큰 갱신토큰 만료 전 자동으로 refresh합니다.
장시간 실행 시 credential rotation

watcher가 장시간 실행되는 동안 credential이 rotate되고 연결이 끊기면, 재연결 시 stale credential을 사용해 영구적으로 실패할 수 있습니다.

대응: Client를 재생성하거나, exec plugin 기반 인증을 사용하면 매번 새 토큰을 발급받을 수 있습니다.

Client 커스텀

ClientBuilder를 사용하면 미들웨어 스택을 커스텀할 수 있습니다.

use kube::client::ClientBuilder;

let config = Config::infer().await?;
let client = ClientBuilder::try_from(config)?
// 커스텀 Tower 레이어 추가 가능
.build();

Client 커스텀 타임아웃

global read_timeoutNone이므로 용도별 Client 분리는 더 이상 필수가 아닙니다. 필요한 경우 개별 호출에 tokio::time::timeout을 적용하거나, 특정 용도로 짧은 타임아웃 Client를 만들 수 있습니다.

// 기본 Client — 타임아웃 없음 (watcher, exec 등 모두 사용 가능)
let client = Client::try_default().await?;

// 특정 용도로 짧은 타임아웃이 필요한 경우
let mut config = Config::infer().await?;
config.read_timeout = Some(Duration::from_secs(30));
let short_timeout_client = Client::try_from(config)?;