Linkerd란?
Linkerd는 Kubernetes 전용 경량 서비스 메시다. Istio와 같은 목적(mTLS, 트래픽 관리, 관측성)을 달성하지만, Rust 기반 마이크로프록시(linkerd2-proxy)로 구현되어 리소스 사용량이 극적으로 적다. Envoy 기반 Istio 대비 메모리 1/10, 레이턴시 오버헤드 sub-millisecond 수준이다.
CNCF Graduated 프로젝트로, “복잡한 설정 없이 바로 쓰는 서비스 메시”를 지향한다. 이 글에서는 설치부터 mTLS, 트래픽 분할, 관측성, 멀티클러스터까지 심화 정리한다.
Linkerd 설치
# CLI 설치
curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/install | sh
export PATH=$HOME/.linkerd2/bin:$PATH
# 클러스터 호환성 사전 검사
linkerd check --pre
# Control Plane 설치
linkerd install --crds | kubectl apply -f -
linkerd install | kubectl apply -f -
# 설치 확인
linkerd check
# Status check results are √
# Viz 확장 (대시보드 + Prometheus + Grafana)
linkerd viz install | kubectl apply -f -
linkerd viz check
# 대시보드 열기
linkerd viz dashboard &
Linkerd Control Plane은 destination(서비스 디스커버리), identity(mTLS 인증서), proxy-injector(사이드카 자동 주입) 세 컴포넌트로 구성된다.
사이드카 주입: 메시에 서비스 추가
# 방법 1: 네임스페이스 단위 자동 주입 (권장)
kubectl annotate namespace default linkerd.io/inject=enabled
# 이후 생성되는 모든 Pod에 linkerd-proxy 사이드카 자동 주입
kubectl rollout restart deployment -n default
# 방법 2: 개별 Deployment에 주입
kubectl get deployment my-app -o yaml | linkerd inject - | kubectl apply -f -
# 방법 3: YAML에 어노테이션 직접 추가
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
metadata:
annotations:
linkerd.io/inject: enabled
# 프록시 리소스 튜닝
config.linkerd.io/proxy-cpu-request: "100m"
config.linkerd.io/proxy-memory-request: "20Mi"
config.linkerd.io/proxy-cpu-limit: "500m"
config.linkerd.io/proxy-memory-limit: "128Mi"
# 주입 확인
kubectl get pods -o jsonpath='{.items[*].spec.containers[*].name}'
# my-app linkerd-proxy
Linkerd 프록시는 기본 메모리 약 20~30MB로, Envoy(50~100MB) 대비 매우 가볍다. 수백 개 Pod에 사이드카를 주입해도 클러스터 리소스 부담이 적다.
자동 mTLS
Linkerd의 가장 큰 장점은 설정 없이 자동 mTLS가 적용된다는 점이다. 메시에 주입된 서비스 간 통신은 자동으로 암호화된다.
# mTLS 상태 확인
linkerd viz edges deployment -n default
# SRC DST SRC_P DST_P SECURED
# frontend backend - - √
# backend database - - √
# 특정 Pod의 TLS 상태 확인
linkerd viz tap deployment/frontend --namespace default
# req id=0:0 proxy=out src=10.0.0.1:8080 dst=10.0.0.2:3000
# tls=true
# 인증서 자동 로테이션 확인
linkerd identity -n linkerd
# 기본 24시간마다 자동 갱신
별도의 PeerAuthentication이나 DestinationRule 설정이 필요 없다. 메시 안의 서비스끼리는 자동 mTLS, 메시 밖에서 오는 트래픽은 평문으로 수신한다.
트래픽 분할: 카나리 배포
# TrafficSplit CRD (SMI 스펙 호환)
apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
metadata:
name: backend-split
namespace: default
spec:
service: backend # 원본 Service
backends:
- service: backend-stable # 기존 버전
weight: 900 # 90%
- service: backend-canary # 새 버전
weight: 100 # 10%
# 카나리용 Service와 Deployment
apiVersion: v1
kind: Service
metadata:
name: backend-stable
spec:
selector:
app: backend
version: stable
ports:
- port: 3000
---
apiVersion: v1
kind: Service
metadata:
name: backend-canary
spec:
selector:
app: backend
version: canary
ports:
- port: 3000
---
# 점진적 트래픽 증가 스크립트
#!/bin/bash
for weight in 10 25 50 75 100; do
canary_weight=$weight
stable_weight=$((100 - weight))
kubectl patch trafficsplit backend-split --type merge -p
"{"spec":{"backends":[
{"service":"backend-stable","weight":${stable_weight}0},
{"service":"backend-canary","weight":${canary_weight}0}
]}}"
echo "Canary: ${weight}% — 5분 대기 후 메트릭 확인"
sleep 300
# 성공률 확인
success_rate=$(linkerd viz stat ts/backend-split
--from deployment/frontend -o json |
jq -r '.[] | select(.name=="backend-canary") | .success_rate')
if (( $(echo "$success_rate < 0.99" | bc -l) )); then
echo "카나리 실패! 롤백 중..."
kubectl patch trafficsplit backend-split --type merge -p
'{"spec":{"backends":[
{"service":"backend-stable","weight":1000},
{"service":"backend-canary","weight":0}
]}}'
exit 1
fi
done
Linkerd의 TrafficSplit은 SMI(Service Mesh Interface) 표준을 따른다. Istio의 VirtualService와 달리 별도의 CRD 문법을 배울 필요 없이, 표준 스펙으로 트래픽을 분할한다. Argo Rollouts와의 통합은 K8s Argo Rollouts 배포 전략 글에서 확인할 수 있다.
관측성: Golden Metrics 자동 수집
Linkerd 프록시는 모든 요청에서 성공률, 요청량, 레이턴시(p50/p95/p99)를 자동으로 수집한다. 애플리케이션 코드 수정이 필요 없다.
# 서비스별 골든 메트릭 조회
linkerd viz stat deployment -n default
# NAME MESHED SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
# frontend 1/1 100.00% 45.2 3ms 10ms 25ms
# backend 2/2 99.85% 89.1 5ms 18ms 45ms
# worker 1/1 100.00% 12.0 2ms 8ms 15ms
# 실시간 요청 모니터링 (tap)
linkerd viz tap deployment/backend -n default
# req id=0:1 proxy=in src=10.0.0.5:52341 dst=10.0.0.8:3000
# :method=GET :authority=backend:3000 :path=/api/users
# rsp id=0:1 proxy=in src=10.0.0.5:52341 dst=10.0.0.8:3000
# :status=200 latency=4321µs
# 서비스 간 의존성 확인
linkerd viz top deployment/frontend -n default
# SOURCE DESTINATION METHOD PATH COUNT BEST WORST LAST
# frontend backend GET /api/users 142 1ms 45ms 3ms
# frontend backend POST /api/orders 38 5ms 120ms 8ms
# Route별 메트릭 (ServiceProfile 필요)
linkerd viz routes deployment/backend -n default
# ROUTE SUCCESS RPS LATENCY_P50 LATENCY_P95
# GET /api/users 100.00% 32.1 4ms 12ms
# POST /api/orders 99.50% 8.2 15ms 85ms
# [DEFAULT] 100.00% 48.8 3ms 10ms
ServiceProfile: Route 수준 관측과 재시도
# ServiceProfile 자동 생성 (OpenAPI 스펙에서)
linkerd profile --open-api swagger.yaml backend -n default | kubectl apply -f -
# 수동 작성
apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: backend.default.svc.cluster.local
namespace: default
spec:
routes:
- name: GET /api/users
condition:
method: GET
pathRegex: /api/users
responseClasses:
- condition:
status:
min: 500
max: 599
isFailure: true
- name: GET /api/users/{id}
condition:
method: GET
pathRegex: /api/users/[^/]+
isRetryable: true # 이 Route에 자동 재시도 활성화
- name: POST /api/orders
condition:
method: POST
pathRegex: /api/orders
timeout: 10s # Route별 타임아웃
# 재시도 Budget 설정 (과도한 재시도 방지)
spec:
retryBudget:
retryRatio: 0.2 # 원래 요청의 20%까지만 재시도
minRetriesPerSecond: 10 # 초당 최소 재시도 허용
ttl: 10s # 재시도 예산 갱신 주기
isRetryable: true로 표시된 Route는 5xx 응답 시 자동 재시도된다. retryBudget으로 재시도 폭주(retry storm)를 방지한다. 이는 서킷 브레이커와 유사한 보호 메커니즘이다.
Linkerd vs Istio 비교
| 항목 | Linkerd | Istio |
|---|---|---|
| 프록시 | linkerd2-proxy (Rust) | Envoy (C++) |
| 사이드카 메모리 | ~20MB | ~50-100MB |
| 레이턴시 오버헤드 | <1ms | 2-5ms |
| mTLS | 자동 (설정 불필요) | 설정 필요 |
| 학습 곡선 | 낮음 | 높음 |
| L7 정책 | 기본적 | 매우 상세 |
| 프로토콜 지원 | HTTP, gRPC, TCP | HTTP, gRPC, TCP, Kafka 등 |
| 권장 환경 | 리소스 제약, 빠른 도입 | 복잡한 정책, 멀티프로토콜 |
Istio 서비스 메시에 대한 자세한 비교는 K8s Istio Service Mesh 심화 글을 참고하자.
Authorization Policy: 서비스 간 접근 제어
# Server: 포트 수준 정책 정의
apiVersion: policy.linkerd.io/v1beta3
kind: Server
metadata:
name: backend-http
namespace: default
spec:
podSelector:
matchLabels:
app: backend
port: 3000
proxyProtocol: HTTP/2
---
# AuthorizationPolicy: frontend만 backend 접근 허용
apiVersion: policy.linkerd.io/v1alpha1
kind: AuthorizationPolicy
metadata:
name: backend-authz
namespace: default
spec:
targetRef:
group: policy.linkerd.io
kind: Server
name: backend-http
requiredAuthenticationRefs:
- name: frontend-identity
kind: MeshTLSAuthentication
group: policy.linkerd.io
---
# MeshTLSAuthentication: 허용할 서비스 ID
apiVersion: policy.linkerd.io/v1alpha1
kind: MeshTLSAuthentication
metadata:
name: frontend-identity
namespace: default
spec:
identities:
- "frontend.default.serviceaccount.identity.linkerd.cluster.local"
mTLS 기반 서비스 ID로 접근을 제어하므로, NetworkPolicy(L3/L4)보다 정교한 L7 수준의 제로트러스트 보안을 구현할 수 있다.
운영 팁
# 1. 프록시 디버깅
linkerd viz tap deployment/backend --path /api/orders --method POST
linkerd diagnostics proxy-metrics -n default pod/backend-xxx
# 2. Control Plane 업그레이드
linkerd upgrade | kubectl apply --prune -l linkerd.io/control-plane-ns=linkerd -f -
linkerd check
# 3. 특정 Pod에서 사이드카 제외
metadata:
annotations:
linkerd.io/inject: disabled
# 4. HA 모드 설치 (프로덕션 권장)
linkerd install --ha | kubectl apply -f -
# → Control Plane 3 레플리카, PDB 자동 설정
마무리
Linkerd는 "설정보다 관습" 철학의 서비스 메시다. 설치 즉시 mTLS가 적용되고, 골든 메트릭이 자동 수집되며, TrafficSplit으로 카나리 배포를 선언적으로 관리한다. Rust 프록시의 극도로 낮은 오버헤드(메모리 20MB, 레이턴시 <1ms)는 사이드카 수백 개를 운영해도 부담이 없다. 복잡한 L7 정책이 필요하면 Istio, 빠르고 가벼운 메시가 필요하면 Linkerd가 정답이다.