근묵자흑
DefaultBuildHandlerChain 깊게 파헤치기 본문
웹 서버가 HTTP 요청을 받았을 때, 단순히 "요청 받고 → 처리하고 → 응답 보내기"만 하면 될까요?
실제로는 훨씬 복잡합니다. 특히 Kubernetes처럼 수많은 컨테이너와 애플리케이션을 관리하는 시스템의 API 서버라면 더욱 그렇습니다. 인증은 누가 하고, 권한 검사는 언제 하며, 동시에 몰려오는 수천 개의 요청은 어떻게 처리할까요?
이 글에서는 Kubernetes API 서버의 핵심인 DefaultBuildHandlerChain
함수를 통해, 하나의 HTTP 요청이 어떤 여정을 거쳐 처리되는지 상세히 살펴보겠습니다. Go 언어와 Kubernetes를 처음 접하는 분들도 이해할 수 있도록, 기초부터 차근차근 설명하겠습니다.
목차
- Go 언어의 HTTP 처리 기초
- 미들웨어 패턴이란?
- Kubernetes API 서버 아키텍처 개요
- DefaultBuildHandlerChain 함수 분석
- 주요 필터들의 역할과 구현
- filterlatency - 성능 추적의 비밀
- 실제 요청 처리 흐름 따라가기
- 마치며
1. Go 언어의 HTTP 처리 기초
HTTP 핸들러란?
Go에서 웹 서버를 만들 때 가장 기본이 되는 개념은 http.Handler
인터페이스입니다. 이 인터페이스는 단 하나의 메서드만 가지고 있습니다:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
이게 뭔지 쉽게 설명하면, "HTTP 요청이 왔을 때 어떻게 처리할지를 정의하는 규칙"입니다.
간단한 예제를 보겠습니다:
package main
import (
"fmt"
"net/http"
)
// HelloHandler는 http.Handler 인터페이스를 구현합니다
type HelloHandler struct{}
func (h HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "안녕하세요!")
}
func main() {
handler := HelloHandler{}
http.ListenAndServe(":8080", handler)
}
위 코드를 실행하고 브라우저에서 http://localhost:8080
에 접속하면 "안녕하세요!"가 출력됩니다.
http.HandlerFunc - 함수를 핸들러로
매번 구조체를 만들기 번거롭죠? Go는 일반 함수를 핸들러로 변환하는 편리한 방법을 제공합니다:
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "안녕하세요!")
}
func main() {
// http.HandlerFunc는 함수를 Handler 인터페이스로 변환합니다
handler := http.HandlerFunc(helloHandler)
http.ListenAndServe(":8080", handler)
}
2. 미들웨어 패턴이란?
양파 껍질 같은 구조
미들웨어(Middleware)는 요청과 응답 사이에 끼어들어 특정 작업을 수행하는 소프트웨어 컴포넌트입니다. 양파 껍질처럼 여러 겹으로 감싸는 구조를 상상하면 됩니다.
요청 → [로깅] → [인증] → [권한검사] → [실제 처리] → [권한검사] → [인증] → [로깅] → 응답
↓ ↓ ↓ ↓ ↑ ↑ ↑ ↑
Go에서의 미들웨어 구현
Go에서 미들웨어는 보통 이런 패턴으로 구현합니다:
// 미들웨어 함수의 기본 형태
func MyMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. 요청 전처리 (Before)
fmt.Println("요청을 받았습니다:", r.URL.Path)
// 2. 다음 핸들러 호출
next.ServeHTTP(w, r)
// 3. 응답 후처리 (After)
fmt.Println("응답을 보냈습니다")
})
}
미들웨어 체인 만들기
여러 미들웨어를 연결하는 예제를 보겠습니다:
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("[로그] %s %s\n", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
})
}
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "인증이 필요합니다")
return // 여기서 중단! next를 호출하지 않음
}
next.ServeHTTP(w, r)
})
}
func main() {
// 실제 비즈니스 로직을 처리하는 핸들러
finalHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "비밀 데이터입니다!")
})
// 미들웨어 체인 구성 (역순으로 감싸기)
handler := LoggingMiddleware(AuthMiddleware(finalHandler))
http.ListenAndServe(":8080", handler)
}
실행 순서:
- LoggingMiddleware가 요청을 받음
- 로그를 출력하고 AuthMiddleware 호출
- AuthMiddleware가 인증 토큰 확인
- 토큰이 있으면 finalHandler 호출
- 응답이 역순으로 돌아감
3. Kubernetes API 서버 아키텍처 개요
Kubernetes란?
Kubernetes(쿠버네티스, 줄여서 k8s)는 컨테이너화된 애플리케이션을 자동으로 배포, 확장 및 관리하는 오픈소스 플랫폼입니다.
예를 들어, 온라인 쇼핑몰을 운영한다고 가정해봅시다:
- 평소에는 서버 3대로 충분하지만
- 블랙프라이데이에는 30대가 필요할 수 있습니다
- Kubernetes는 이런 확장/축소를 자동으로 처리합니다
API 서버의 역할
Kubernetes API 서버는 Kubernetes의 모든 구성 요소와 사용자가 통신하는 중앙 허브입니다.
비유하자면:
- 경찰서의 신고접수대: 모든 요청이 여기를 거칩니다
- 은행의 금고: 중요한 데이터(etcd)에 대한 접근을 통제합니다
- 공항의 보안검색대: 모든 요청의 신원과 권한을 확인합니다
API 서버가 처리해야 할 것들
- 인증(Authentication): "당신은 누구입니까?"
- 인가(Authorization): "당신은 이 작업을 할 권한이 있습니까?"
- 입력 검증: "요청이 올바른 형식입니까?"
- 감사(Audit): "누가 언제 무엇을 했는지 기록"
- 속도 제한: "너무 많은 요청을 보내지 않도록 제한"
- 변경 사항 적용: "실제로 요청된 작업 수행"
4. DefaultBuildHandlerChain 함수 분석
이제 본격적으로 Kubernetes API 서버의 핵심인 DefaultBuildHandlerChain
함수를 살펴보겠습니다.
함수의 구조
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
handler := apiHandler
// 여기서부터 역순으로 핸들러를 감쌉니다
handler = filterlatency.TrackCompleted(handler)
handler = genericapifilters.WithAuthorization(handler, c.Authorization.Authorizer, c.Serializer)
handler = filterlatency.TrackStarted(handler, c.TracerProvider, "authorization")
// ... 더 많은 핸들러들 ...
return handler
}
왜 역순으로 구성하는가?
코드에서는 아래에서 위로 핸들러를 추가하지만, 실제 실행은 위에서 아래로 됩니다. 이것은 양파 껍질을 만드는 것과 같습니다:
코드 작성 순서: 실제 실행 순서:
3. 인증 추가 → 1. 인증 확인
2. 로깅 추가 → 2. 로그 기록
1. 핵심 로직 → 3. 핵심 로직 실행
필터 체인의 전체 구조
DefaultBuildHandlerChain은 약 25개의 필터를 순서대로 연결합니다. 주요 필터들을 실행 순서대로 정리하면:
- 감사 초기화 (WithAuditInit)
- 패닉 복구 (WithPanicRecovery)
- 서버 준비 상태 확인 (WithMuxAndDiscoveryComplete)
- 요청 시간 기록 (WithRequestReceivedTimestamp)
- 요청 정보 파싱 (WithRequestInfo)
- 지연 시간 추적 (WithLatencyTrackers)
- HTTP 로깅 (WithHTTPLogging)
- 캐시 제어 (WithCacheControl)
- 타임아웃 설정 (WithTimeoutForNonLongRunningRequests)
- CORS 처리 (WithCORS)
- 인증 (WithAuthentication)
- 감사 로깅 (WithAudit)
- 사용자 가장 (WithImpersonation)
- 속도 제한 (WithPriorityAndFairness)
- 권한 검사 (WithAuthorization)
5. 주요 필터들의 역할과 구현
5.1 요청 정보 파싱 (WithRequestInfo)
모든 처리의 시작점입니다. URL을 분석해서 어떤 리소스에 대한 어떤 작업인지 파악합니다.
func WithRequestInfo(handler http.Handler, resolver request.RequestInfoResolver) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// URL 분석: /api/v1/namespaces/default/pods/nginx
// → namespace: default, resource: pods, name: nginx
info, err := resolver.NewRequestInfo(req)
if err != nil {
// 파싱 실패시 500 에러
responsewriters.InternalError(w, req, err)
return
}
// 파싱된 정보를 Context에 저장
req = req.WithContext(request.WithRequestInfo(req.Context(), info))
handler.ServeHTTP(w, req)
})
}
예시:
GET /api/v1/pods
→ 모든 Pod 조회POST /api/v1/namespaces/prod/deployments
→ prod 네임스페이스에 Deployment 생성DELETE /api/v1/nodes/worker-1
→ worker-1 노드 삭제
5.2 인증 (WithAuthentication)
"당신은 누구입니까?"를 확인하는 단계입니다.
func WithAuthentication(handler http.Handler, auth authenticator.Request, failed http.Handler, ...) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// 다양한 인증 방법 시도 (토큰, 인증서, 등)
resp, ok, err := auth.AuthenticateRequest(req)
if err != nil || !ok {
// 인증 실패 → 실패 핸들러로 처리
failed.ServeHTTP(w, req)
return
}
// 인증 성공: Authorization 헤더 제거 (보안상)
req.Header.Del("Authorization")
// 사용자 정보를 Context에 저장
req = req.WithContext(genericapirequest.WithUser(req.Context(), resp.User))
handler.ServeHTTP(w, req)
})
}
지원하는 인증 방법:
- Bearer Token:
Authorization: Bearer <token>
- Client Certificate: TLS 클라이언트 인증서
- Basic Auth:
Authorization: Basic <base64(username:password)>
- Service Account Token: Kubernetes 내부 서비스용
5.3 권한 검사 (WithAuthorization)
"당신은 이 작업을 할 권한이 있습니까?"를 확인합니다.
func WithAuthorization(handler http.Handler, a authorizer.Authorizer, s runtime.NegotiatedSerializer) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// Context에서 요청 정보와 사용자 정보 추출
attributes, err := GetAuthorizerAttributes(req.Context())
if err != nil {
responsewriters.InternalError(w, req, err)
return
}
// 권한 확인
authorized, reason, err := a.Authorize(req.Context(), attributes)
if authorized == authorizer.DecisionAllow {
// 권한 있음 → 계속 진행
handler.ServeHTTP(w, req)
return
}
// 권한 없음 → 403 Forbidden
responsewriters.Forbidden(req.Context(), attributes, w, req, reason, s)
})
}
권한 검사 예시:
- user1이 namespace1의 Pod를 생성할 수 있는가?
- serviceaccount1이 모든 Secret을 조회할 수 있는가?
- admin 그룹이 Node를 삭제할 수 있는가?
5.4 감사 로깅 (WithAudit)
모든 API 호출을 기록합니다. 보안과 디버깅에 필수적입니다.
func WithAudit(handler http.Handler, sink audit.Sink, policy audit.PolicyRuleEvaluator, ...) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// 감사 이벤트 생성
ac, err := evaluatePolicyAndCreateAuditEvent(req, policy, sink)
// ResponseWriter를 감싸서 응답도 기록
respWriter := &auditResponseWriter{
ResponseWriter: w,
event: ac,
}
// 패닉이 발생해도 감사 로그는 남김
defer func() {
if r := recover(); r != nil {
ac.ProcessEventStage(ctx, auditinternal.StagePanic)
panic(r) // 다시 패닉 발생
}
ac.ProcessEventStage(ctx, auditinternal.StageResponseComplete)
}()
handler.ServeHTTP(respWriter, req)
})
}
감사 로그에 기록되는 정보:
- 누가 (User)
- 언제 (Timestamp)
- 무엇을 (Resource)
- 어떻게 (Verb: get, create, delete 등)
- 결과 (성공/실패)
- 응답 코드
5.5 속도 제한 (WithPriorityAndFairness)
API 서버가 과부하되지 않도록 요청 속도를 제한합니다.
func WithPriorityAndFairness(handler http.Handler, ...) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 요청 분류 (우선순위 결정)
classification := classifyRequest(r)
// 대기열에 추가
queued := fc.QueueRequest(classification)
if !queued {
// 대기열이 가득 참 → 429 Too Many Requests
tooManyRequests(r, w, retryAfter)
return
}
// 순서가 되면 실행
handler.ServeHTTP(w, r)
})
}
우선순위 예시:
- system:masters: 최고 우선순위 (관리자)
- system:nodes: 높은 우선순위 (노드 하트비트)
- 일반 사용자: 보통 우선순위
- 익명 사용자: 낮은 우선순위
5.6 패닉 복구 (WithPanicRecovery)
Go 프로그램에서 panic이 발생하면 프로그램이 종료됩니다. 이를 방지합니다.
func WithPanicRecovery(handler http.Handler, resolver request.RequestInfoResolver) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
defer func() {
if r := recover(); r != nil {
// 패닉 발생! 하지만 서버는 계속 동작
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
// 스택 트레이스 로깅
logStackTrace(r)
}
}()
handler.ServeHTTP(w, req)
})
}
5.7 타임아웃 처리 (WithTimeoutForNonLongRunningRequests)
요청이 너무 오래 걸리지 않도록 제한합니다.
func WithTimeoutForNonLongRunningRequests(handler http.Handler, longRunning LongRunningCheck) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if longRunning(r) {
// Watch, Proxy 등은 타임아웃 없음
handler.ServeHTTP(w, r)
return
}
// 일반 요청: 60초 타임아웃
ctx, cancel := context.WithTimeout(r.Context(), 60*time.Second)
defer cancel()
done := make(chan bool)
go func() {
handler.ServeHTTP(w, r.WithContext(ctx))
done <- true
}()
select {
case <-done:
// 정상 완료
case <-ctx.Done():
// 타임아웃!
http.Error(w, "Gateway Timeout", http.StatusGatewayTimeout)
}
})
}
6. filterlatency - 성능 추적의 비밀
filterlatency 패키지의 역할
filterlatency
패키지는 각 필터의 실행 시간을 정밀하게 측정합니다. 이는 성능 병목 현상을 찾고 최적화하는 데 필수적입니다.
구현 원리
// TrackStarted: 필터 실행 시작 시점 기록
func TrackStarted(handler http.Handler, tp trace.TracerProvider, name string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// OpenTelemetry span 시작
ctx, span := tracer.Start(r.Context(), name)
defer span.End()
// 시작 시간 기록
fr := &requestFilterRecord{
name: name,
startedTimestamp: clock.Now(),
}
r = r.WithContext(withRequestFilterRecord(ctx, fr))
handler.ServeHTTP(w, r)
})
}
// TrackCompleted: 이전 필터의 완료 시점 측정
func TrackCompleted(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 이전 필터가 막 완료된 시점
completedAt := clock.Now()
// 다음 핸들러 실행 (defer로 먼저 측정)
defer handler.ServeHTTP(w, r)
// 지연시간 계산 및 메트릭 기록
if fr := requestFilterRecordFrom(r.Context()); fr != nil {
latency := completedAt.Sub(fr.startedTimestamp)
metrics.RecordFilterLatency(r.Context(), fr.name, latency)
// 100ms 이상이면 로그에 기록
if latency > minFilterLatencyToLog {
httplog.AddKeyValue(r.Context(), fmt.Sprintf("fl_%s", fr.name), latency.String())
}
}
})
}
측정 패턴 이해하기
// DefaultBuildHandlerChain에서의 사용 예
handler = filterlatency.TrackCompleted(handler)
handler = genericapifilters.WithAuthorization(handler, ...)
handler = filterlatency.TrackStarted(handler, tp, "authorization")
실행 순서:
TrackStarted("authorization")
- 시작 시간 기록TrackCompleted
- 이전 필터 완료 시간 측정WithAuthorization
- 실제 인가 처리- 다음 필터로 진행...
이렇게 각 필터를 TrackStarted
와 TrackCompleted
로 감싸서 정확한 실행 시간을 측정합니다.
7. 실제 요청 처리 흐름 따라가기
예시: Pod 생성 요청
사용자가 nginx Pod를 생성하는 요청을 보낸다고 가정해봅시다:
kubectl create -f nginx-pod.yaml
이 명령은 다음과 같은 HTTP 요청으로 변환됩니다:
POST /api/v1/namespaces/default/pods
Authorization: Bearer <token>
Content-Type: application/json
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "nginx"
},
"spec": {
"containers": [{
"name": "nginx",
"image": "nginx:latest"
}]
}
}
단계별 처리 과정
1단계: WithAuditInit
[감사 초기화] 새로운 감사 이벤트 ID 생성: audit-12345
2단계: WithPanicRecovery
[패닉 복구] 준비 완료. 이후 패닉 발생시 복구 예정
3단계: WithRequestReceivedTimestamp
[시간 기록] 요청 수신 시각: 2024-01-15 10:30:45.123
4단계: WithRequestInfo
[요청 파싱]
- Verb: create
- Resource: pods
- Namespace: default
- APIVersion: v1
5단계: WithHTTPLogging
[HTTP 로그] POST /api/v1/namespaces/default/pods (from 192.168.1.100)
6단계: WithAuthentication
[인증] Bearer 토큰 확인...
[인증] 사용자 확인됨: user1 (groups: [developers])
7단계: WithAudit (요청 단계)
[감사] RequestReceived 단계 기록
- User: user1
- Verb: create
- Resource: pods
- Namespace: default
8단계: WithImpersonation
[사용자 가장] 가장 요청 없음. 원래 사용자로 진행
9단계: WithPriorityAndFairness
[속도 제한] 요청 분류: workload-low (일반 사용자)
[속도 제한] 대기열 위치: 3번째
[속도 제한] 예상 대기 시간: 50ms
... 50ms 후 ...
[속도 제한] 처리 시작
10단계: WithAuthorization
[권한 검사] user1이 default 네임스페이스에 pods를 create할 수 있는가?
[권한 검사] RBAC 규칙 확인 중...
[권한 검사] 허용됨 (Role: pod-creator)
11단계: 실제 API 처리
[API 핸들러] Pod 생성 시작
[API 핸들러] 유효성 검사 통과
[API 핸들러] etcd에 저장
[API 핸들러] Pod 생성 완료: nginx
응답 단계 (역순으로)
[감사] ResponseComplete 단계 기록 (StatusCode: 201)
[지연시간] authorization: 5ms
[지연시간] audit: 2ms
[지연시간] authentication: 10ms
[지연시간] 전체: 120ms
최종 응답
HTTP/1.1 201 Created
Content-Type: application/json
Audit-ID: audit-12345
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "nginx",
"namespace": "default",
"uid": "550e8400-e29b-41d4-a716-446655440000",
"creationTimestamp": "2024-01-15T10:30:45Z"
},
"spec": { ... },
"status": {
"phase": "Pending"
}
}
에러 시나리오
인증 실패 시:
[인증] Bearer 토큰 확인...
[인증] 토큰이 만료됨
[감사] 인증 실패 기록
→ HTTP 401 Unauthorized
권한 부족 시:
[권한 검사] user2가 kube-system 네임스페이스에 pods를 create할 수 있는가?
[권한 검사] 거부됨 (권한 없음)
[감사] 권한 거부 기록
→ HTTP 403 Forbidden
속도 제한 시:
[속도 제한] 대기열이 가득 참 (최대: 100)
→ HTTP 429 Too Many Requests
→ Retry-After: 5 (5초 후 재시도)
필터 체인의 실행 흐름 다이어그램
클라이언트 요청
│
▼
┌─────────────────────────┐
│ WithAuditInit │ ← 감사 이벤트 초기화
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ WithPanicRecovery │ ← 패닉 복구 준비
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ WithRequestInfo │ ← URL 파싱, 리소스 정보 추출
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ TrackStarted │ ← "authentication" 시작 시간 기록
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ TrackCompleted │ ← 이전 필터 완료 시간 측정
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ WithAuthentication │ ← 사용자 인증
└─────────────────────────┘
│ (인증 실패시 401 반환)
▼
┌─────────────────────────┐
│ TrackStarted │ ← "authorization" 시작
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ TrackCompleted │ ← "authentication" 완료 시간 측정
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ WithAuthorization │ ← 권한 검사
└─────────────────────────┘
│ (권한 없으면 403 반환)
▼
┌─────────────────────────┐
│ API Handler │ ← 실제 비즈니스 로직
└─────────────────────────┘
│
▼
응답 반환
8. 마치며
핵심 정리
Kubernetes API 서버의 요청 처리 파이프라인은:
- 계층적 구조: 각 필터가 특정 책임을 가지고 독립적으로 동작
- 조기 종료: 문제가 발견되면 즉시 중단하여 리소스 절약
- 포괄적 로깅: 모든 단계가 추적되어 디버깅과 보안 감사 가능
- 확장 가능: 새로운 필터를 쉽게 추가 가능
이런 복잡한 구조가 필요한 이유
- 보안: 여러 단계의 검증으로 안전성 확보
- 안정성: 패닉 복구, 타임아웃 등으로 서버 안정성 보장
- 성능: 속도 제한과 우선순위로 과부하 방지
- 관찰 가능성: 상세한 로깅과 메트릭으로 문제 추적 용이
DefaultBuildHandlerChain의 설계 철학
- 관심사의 분리: 각 필터는 하나의 책임만 가짐
- 조합 가능성: 필터들을 자유롭게 조합하고 순서 변경 가능
- 투명성: 각 단계의 성능과 동작을 측정 가능
- 실패 격리: 한 필터의 실패가 전체 시스템을 중단시키지 않음
실무에서의 시사점
이러한 설계 패턴은 Kubernetes뿐만 아니라 다른 시스템에서도 활용할 수 있습니다:
- 마이크로서비스 API 게이트웨이
- 엔터프라이즈 애플리케이션 서버
- IoT 플랫폼의 디바이스 관리 시스템
- 금융 거래 시스템의 요청 처리
더 알아보기
- Go의 미들웨어 패턴에 대해 더 깊이 공부하고 싶다면
net/http
패키지 문서를 참고하세요 - Kubernetes의 RBAC(역할 기반 접근 제어)에 대해 알아보면 권한 시스템을 더 잘 이해할 수 있습니다
- OpenTelemetry를 통한 분산 추적에 대해 공부하면
filterlatency
패키지의 역할을 더 잘 이해할 수 있습니다
이 글이 Kubernetes API 서버의 내부 동작을 이해하는 데 도움이 되었기를 바랍니다!
'k8s' 카테고리의 다른 글
Kubernetes API Server : 코드 레벨에서 이해하는 내부 동작 원리 (0) | 2025.07.03 |
---|---|
Kubernetes 모니터링 (0) | 2025.06.22 |
쿠버네티스 파드를 사용하는 주요 오브젝트들 (2) | 2025.06.15 |
커스텀 리소스와 컨트롤러 - CR & CRD (1) | 2025.06.08 |
Kubernetes Admission Controller (1) | 2025.06.01 |