infra
Platform

모듈 맵

[Kubernetes] Istio 서비스 메시가 제공하는 가시성과 mTLS 보안

0 / 29 완료

펼치기
0 / 29 완료0%

Kubernetes · 27 / 29

[Kubernetes] Istio 서비스 메시가 제공하는 가시성과 mTLS 보안

Envoy 사이드카 인젝션으로 mTLS 자동 적용, 트래픽 가중치 기반 카나리 배포를 구현합니다

🚨INCIDENT ALERT
HIGH

마이크로서비스가 늘어나자 어느 호출이 느린지, 어떤 서비스 간 통신을 암호화해야 하는지 파악하기 어려워졌습니다. 애플리케이션 코드마다 재시도와 인증을 넣으면 일관성이 깨집니다. Service Mesh는 트래픽 제어, 관측성, mTLS를 인프라 레이어에서 다루게 해줍니다.

Istio 서비스 메시

마이크로서비스 아키텍처를 운영하다 보면 서비스 간 통신 보안, 트래픽 관찰, 장애 격리라는 세 가지 문제가 동시에 찾아옵니다. 보안팀은 "서비스 간 통신도 암호화해야 한다"고 요구하는데 수십 개 서비스 각각에 TLS 코드를 추가하는 것은 현실적이지 않습니다. 트래픽 분배는 L7 라우팅 없이는 헤더 기반 카나리를 구현할 수 없고, 특정 서비스 장애가 연쇄 장애로 번지는 것을 막을 서킷브레이커가 없습니다. Istio는 애플리케이션 코드를 전혀 수정하지 않고 이 세 가지를 모두 해결합니다. 각 파드에 Envoy 프록시를 사이드카로 자동 주입해서 모든 트래픽이 Envoy를 통하도록 만들고, mTLS 인증서 발급과 갱신, 트래픽 라우팅, 메트릭 수집을 대신 처리합니다. 이 모듈을 마치면 Istio를 클러스터에 설치하고, 서비스 간 mTLS를 자동 적용하며, 트래픽 10%를 신규 버전으로 보내는 카나리 배포를 구성할 수 있습니다.

이번 챕터에서 배울 것
  • 1Istio 아키텍처: istiod Control Plane + Envoy 사이드카 Data Plane
  • 2사이드카 인젝션 메커니즘 — iptables, istio-init, istio-proxy
  • 3PeerAuthentication으로 mTLS 모드 설정 (PERMISSIVE → STRICT)
  • 4VirtualService로 트래픽 가중치 분배 (카나리 배포)
  • 5DestinationRule로 subset 정의 및 로드밸런싱 정책 설정
  • 6TroubleCase: mTLS STRICT 모드 전환 후 통신 실패 진단
실습 환경 준비
Istio 다운로드 및 CLI 설치
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.20.0 sh - && export PATH=$PWD/istio-1.20.0/bin:$PATH
클러스터 Istio 호환성 확인
istioctl x precheck
Istio 설치 (demo 프로파일)
istioctl install --set profile=demo -y
설치 확인
kubectl get pods -n istio-system
실습용 네임스페이스에 사이드카 인젝션 활성화
kubectl label namespace default istio-injection=enabled

Istio 아키텍처

┌─────────────────────────────────────────────────────────────┐
│                    Control Plane (istiod)                    │
│  ┌─────────────┐  ┌──────────────┐  ┌───────────────────┐  │
│  │  Pilot      │  │  Citadel     │  │  Galley           │  │
│  │(트래픽 규칙) │  │(인증서 관리)  │  │(설정 검증/배포)   │  │
│  └──────┬──────┘  └──────┬───────┘  └───────────────────┘  │
│         │ xDS API         │ 인증서                           │
└─────────┼─────────────────┼──────────────────────────────────┘
          │                 │
┌─────────┼─────────────────┼──────────────────────────────────┐
│         │   Data Plane     │                                  │
│  Pod A  │                 │  Pod B                            │
│  ┌──────▼──────────────┐  │  ┌──────────────────────────┐   │
│  │  [App Container]    │  │  │  [App Container]          │   │
│  │  ← 포트 8080        │  │  │  ← 포트 8080              │   │
│  │─────────────────────│  │  │──────────────────────────│   │
│  │  [istio-proxy]      │──┼─▶│  [istio-proxy]           │   │
│  │  Envoy :15001/:15006│  └  │  Envoy :15001/:15006     │   │
│  │  mTLS + 메트릭 수집  │     │  mTLS + 메트릭 수집       │   │
│  └──────────────────── ┘     └──────────────────────────┘   │
│  (iptables가 모든 트래픽을 Envoy로 리다이렉트)                │
└─────────────────────────────────────────────────────────────┘

istiod는 세 가지를 담당합니다:

  • Pilot: VirtualService, DestinationRule 등 트래픽 규칙을 Envoy에 xDS API로 배포
  • Citadel: 워크로드 인증서(SVID)를 자동 발급하고 24시간마다 갱신
  • Galley: Istio 설정 유효성 검증 및 istiod에 전달

사이드카 인젝션 이해

💡개념

iptables로 트래픽을 Envoy로 투명하게 리다이렉트하는 원리

Istio를 도입했는데 특정 파드의 트래픽이 Envoy를 통하지 않아 mTLS가 적용되지 않습니다. istioctl x check-inject를 실행해도 원인이 명확하지 않고, 애플리케이션 코드는 아무것도 바꾸지 않았는데 왜 어떤 파드는 메시에 포함되고 어떤 파드는 제외되는지 이해하기 어렵습니다. 사이드카 인젝션 메커니즘을 이해하지 못하면 인젝션 실패 원인을 찾지 못하고 무작정 파드를 재시작하게 됩니다. 이 CB에서는 istio-init init container가 iptables 규칙을 설정하는 원리와, 그 결과 앱 컨테이너가 투명하게 Envoy를 경유하는 구조를 다룹니다. 이것이 가능한 이유는 istio-init init container가 Pod 시작 전에 iptables 규칙을 설정하기 때문입니다.

iptables로 트래픽을 Envoy로 투명하게 리다이렉트하는 원리

Kubernetes
# 사이드카가 주입된 Pod 내부 컨테이너 확인
kubectl get pod payment-7d9b4c8f6-xkp2n -o jsonpath='{.spec.containers[*].name}'
# 출력: payment istio-proxy  ← 두 컨테이너

# istio-proxy 컨테이너 상세 정보
kubectl describe pod payment-7d9b4c8f6-xkp2n | grep -A20 "istio-proxy"

# iptables 규칙 확인 (nsenter로 Pod 네트워크 네임스페이스 진입)
# 실제 운영에서는 kubectl debug 사용
kubectl debug -n production \
  -it payment-7d9b4c8f6-xkp2n \
  --image=nicolaka/netshoot \
  -- iptables -t nat -L ISTIO_REDIRECT

iptables 규칙 구조:

Chain ISTIO_REDIRECT (인바운드)
  REDIRECT → TCP 15006  # 모든 인바운드 트래픽 → Envoy 15006

Chain ISTIO_OUTPUT (아웃바운드)
  RETURN   → uid 1337   # Envoy 자신의 트래픽은 제외 (루프 방지)
  REDIRECT → TCP 15001  # 나머지 아웃바운드 → Envoy 15001

결과: 앱 컨테이너는 localhost:8080으로 요청을 수신하고 http://other-service:80으로 요청을 보내지만, 실제로는 모두 Envoy를 경유합니다. Envoy가 mTLS 핸드쉐이크, 메트릭 수집, 트래픽 라우팅을 처리하고 실제 목적지로 전달합니다.

Kubernetes
# 사이드카 인젝션 활성화된 네임스페이스 확인
kubectl get namespace -L istio-injection

# 특정 Pod에 사이드카 인젝션 비활성화 (모니터링 에이전트 등)
# Pod annotations에 추가:
# sidecar.istio.io/inject: "false"

네임스페이스 레이블로 자동 인젝션 설정

위험 명령어모든 Pod가 순차 재시작되어 readinessProbe나 복제본 설정이 부족하면 일시 장애가 발생할 수 있습니다.

운영 Deployment 재시작

안전한 실행 조건: 무중단 배포 설정과 모니터링을 확인한 뒤 변경 창구에서 실행하세요.

실행 전 반드시 확인

  • 현재 컨텍스트와 Namespace가 의도한 대상인지 확인했는가
  • 운영 트래픽이나 상태 저장 데이터에 미치는 영향을 확인했는가
  • 되돌릴 매니페스트, 백업, 또는 복구 절차가 준비되어 있는가
kubectl rollout restart deployment -n production

위 항목을 모두 확인한 후 복사할 수 있습니다

Kubernetes
# 네임스페이스에 자동 인젝션 활성화
kubectl label namespace production istio-injection=enabled

# 기존 Pod에 적용하려면 재배포 필요
kubectl rollout restart deployment -n production

# 사이드카 주입 확인
kubectl get pod -n production -o yaml | grep -c "istio-proxy"
# 숫자가 Pod 수와 일치해야 함

mTLS 설정

PeerAuthentication으로 mTLS 모드 설정

YAML
# PERMISSIVE 모드: mTLS와 평문 트래픽 모두 허용 (기본값, 마이그레이션 단계)
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: production
spec:
  mtls:
    mode: PERMISSIVE
YAML
# STRICT 모드: mTLS 인증서 없는 트래픽은 거부
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: production
spec:
  mtls:
    mode: STRICT
Kubernetes
# 적용
kubectl apply -f peer-auth-strict.yaml

# mTLS 상태 확인
istioctl x check-inject -n production
istioctl authn tls-check payment.production.svc.cluster.local

# 출력 예시:
# HOST:PORT                                    STATUS     SERVER     CLIENT
# payment.production.svc.cluster.local:8080   OK         STRICT     STRICT

DestinationRule로 아웃바운드 mTLS 설정

YAML
# mTLS를 클라이언트 측에서도 강제
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: payment-mtls
  namespace: production
spec:
  host: payment.production.svc.cluster.local
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL     # Istio 발급 인증서로 mTLS
  connectionPool:
    tcp:
      maxConnections: 100
    http:
      h2UpgradePolicy: UPGRADE
  outlierDetection:          # 서킷브레이커
    consecutiveErrors: 5
    interval: 30s
    baseEjectionTime: 30s
    maxEjectionPercent: 50
Kubernetes
kubectl apply -f destination-rule.yaml

# DestinationRule 적용 확인
kubectl get destinationrule -n production
istioctl proxy-config cluster payment-7d9b4c8f6-xkp2n.production | grep payment

트래픽 가중치 기반 카나리 배포

실습: v1 → v2 점진적 트래픽 전환

YAML
# 1. 두 버전의 Deployment 준비
# v1 Deployment (기존)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-v1
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: payment
      version: v1
  template:
    metadata:
      labels:
        app: payment
        version: v1        # 버전 레이블 필수
    spec:
      containers:
        - name: payment
          image: payment:1.0.0
          ports:
            - containerPort: 8080
---
# v2 Deployment (신규)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-v2
  namespace: production
spec:
  replicas: 1              # 처음엔 적게
  selector:
    matchLabels:
      app: payment
      version: v2
  template:
    metadata:
      labels:
        app: payment
        version: v2
    spec:
      containers:
        - name: payment
          image: payment:2.0.0
          ports:
            - containerPort: 8080
YAML
# 2. DestinationRule로 v1/v2 subset 정의
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: payment-dr
  namespace: production
spec:
  host: payment           # Service 이름
  subsets:
    - name: v1
      labels:
        version: v1       # 이 레이블을 가진 Pod를 v1 subset으로 그룹화
    - name: v2
      labels:
        version: v2
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
YAML
# 3. VirtualService로 트래픽 분배 (처음: v1 90% / v2 10%)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: payment-vs
  namespace: production
spec:
  hosts:
    - payment             # Service 이름과 일치
  http:
    - route:
        - destination:
            host: payment
            subset: v1
          weight: 90      # v1으로 90%
        - destination:
            host: payment
            subset: v2
          weight: 10      # v2으로 10%
      timeout: 5s         # 타임아웃 설정
      retries:
        attempts: 3
        perTryTimeout: 2s
        retryOn: gateway-error,connect-failure,retriable-4xx
Kubernetes
# 적용
kubectl apply -f destination-rule.yaml
kubectl apply -f virtual-service.yaml

# 트래픽 분배 확인 (부하 테스트)
for i in $(seq 1 100); do
  kubectl exec -n production \
    $(kubectl get pod -n production -l app=frontend -o name | head -1) \
    -- curl -s http://payment:8080/version
done | sort | uniq -c
# 약 90개: "version: v1"
# 약 10개: "version: v2"

# v2 에러율 모니터링 (Grafana 또는 istioctl)
istioctl dashboard kiali   # Kiali 트래픽 시각화 대시보드

헤더 기반 카나리 (특정 사용자만 v2 체험)

YAML
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: payment-vs
  namespace: production
spec:
  hosts:
    - payment
  http:
    # QA 팀은 X-Canary: true 헤더로 항상 v2로 라우팅
    - match:
        - headers:
            x-canary:
              exact: "true"
      route:
        - destination:
            host: payment
            subset: v2
          weight: 100
    # 나머지는 v1
    - route:
        - destination:
            host: payment
            subset: v1
          weight: 100
위험 명령어Git이나 매니페스트와 다른 임시 상태가 생기고 잘못된 패치가 즉시 운영 트래픽에 영향을 줄 수 있습니다.

운영 리소스 직접 패치

안전한 실행 조건: 변경 내용을 코드에 반영할 계획이 있고 영향 범위를 검토했을 때만 실행하세요.

실행 전 반드시 확인

  • 현재 컨텍스트와 Namespace가 의도한 대상인지 확인했는가
  • 운영 트래픽이나 상태 저장 데이터에 미치는 영향을 확인했는가
  • 되돌릴 매니페스트, 백업, 또는 복구 절차가 준비되어 있는가
kubectl patch virtualservice payment-vs -n production

위 항목을 모두 확인한 후 복사할 수 있습니다

로컬 또는 서버
# v2 테스트 (헤더 포함)
curl -H "x-canary: true" http://payment.production.svc.cluster.local/api/pay

# 단계별 트래픽 전환 스크립트
for weight in 10 25 50 75 100; do
  echo "v2 트래픽: $weight%"
  kubectl patch virtualservice payment-vs -n production \
    --type=json \
    -p="[
      {\"op\": \"replace\", \"path\": \"/spec/http/0/route/0/weight\", \"value\": $((100-weight))},
      {\"op\": \"replace\", \"path\": \"/spec/http/0/route/1/weight\", \"value\": $weight}
    ]"
  echo "에러율 모니터링 중... (5분 대기)"
  sleep 300
done

트러블슈팅

PeerAuthentication을 STRICT 모드로 변경한 직후 일부 서비스에서 RBAC: access denied 또는 upstream connect error 오류가 발생합니다.

1단계: 사이드카 인젝션 여부 확인

Kubernetes
# 통신 실패 Pod에 istio-proxy 사이드카가 있는지 확인
kubectl get pod -n production -o json | \
  jq '.items[] | select(.spec.containers[].name == "istio-proxy") | .metadata.name'

# 사이드카가 없는 Pod 찾기
kubectl get pod -n production -o yaml | \
  grep -B5 "istio-injection: false"

# 특정 Pod의 사이드카 상태 한 번에 확인
kubectl describe pod <pod-name> -n production | \
  grep -E "istio-proxy|istio-init|Containers:"

2단계: mTLS 연결 상태 진단

로컬 터미널
# 서비스 간 mTLS 상태 확인
istioctl authn tls-check <pod-name>.<namespace> <service-name>.<namespace>.svc.cluster.local

# 예시:
istioctl authn tls-check payment-7d9b4c8f6-xkp2n.production \
  order.production.svc.cluster.local

# 출력:
# HOST:PORT                                  STATUS    SERVER    CLIENT
# order.production.svc.cluster.local:8080   OK        STRICT    STRICT    ← 정상
# order.production.svc.cluster.local:8080   CONFLICT  STRICT    DISABLE   ← 문제!
# CONFLICT: 서버는 STRICT를 요구하지만 클라이언트는 mTLS 없이 연결 시도

3단계: Envoy 접근 로그로 실제 에러 확인

Kubernetes
# Envoy 접근 로그 활성화
kubectl apply -f - << 'EOF'
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: enable-access-log
  namespace: production
spec:
  accessLogging:
    - providers:
        - name: envoy
EOF

# 실패하는 Pod의 Envoy 로그 확인
kubectl logs -n production <pod-name> -c istio-proxy | \
  grep -E "response_code=403|UF|URX|NR"

# 주요 에러 코드 해석:
# UF: Upstream connection failure
# URX: Upstream connection reset (mTLS 핸드쉐이크 실패)
# NR: No route found
# UAEX: Upstream application exception (AuthorizationPolicy 거부)

4단계: 단계적 해결

Kubernetes
# 해결 1: 사이드카가 없는 네임스페이스에 인젝션 활성화
kubectl label namespace <namespace> istio-injection=enabled
kubectl rollout restart deployment -n <namespace>

# 해결 2: 특정 Pod만 PERMISSIVE 예외 처리 (임시)
kubectl apply -f - << 'EOF'
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: legacy-service-exception
  namespace: production
spec:
  selector:
    matchLabels:
      app: legacy-batch    # 사이드카 없는 레거시 앱
  mtls:
    mode: PERMISSIVE       # 이 Pod만 예외
EOF

# 해결 3: DestinationRule의 tls 모드 확인
kubectl get destinationrule -n production -o yaml | \
  grep -A3 "tls:"
# mode: DISABLE 라면 ISTIO_MUTUAL로 변경

# 해결 4: AuthorizationPolicy 규칙 확인 (403 에러인 경우)
kubectl get authorizationpolicy -n production
kubectl describe authorizationpolicy <name> -n production

5단계: 프록시 설정 덤프로 최종 확인

로컬 터미널
# Envoy가 실제로 받은 라우팅 설정 확인
istioctl proxy-config route <pod-name>.production
istioctl proxy-config cluster <pod-name>.production | grep payment
istioctl proxy-config endpoint <pod-name>.production | grep payment

# 전체 설정 덤프 (고급 분석용)
istioctl proxy-config all <pod-name>.production -o json > /tmp/proxy-config.json
💼
실무 맥락
현업 패턴

시나리오: v2 결제 모듈 카나리 배포 — 에러 감지 시 즉시 롤백

새로운 결제 로직이 포함된 payment:2.0.0을 프로덕션에 배포합니다. 장애 없이 점진적으로 트래픽을 전환하고, 문제 감지 시 즉시 롤백하는 절차를 준비했습니다.

위험 명령어Git이나 매니페스트와 다른 임시 상태가 생기고 잘못된 패치가 즉시 운영 트래픽에 영향을 줄 수 있습니다.

운영 리소스 직접 패치

안전한 실행 조건: 변경 내용을 코드에 반영할 계획이 있고 영향 범위를 검토했을 때만 실행하세요.

실행 전 반드시 확인

  • 현재 컨텍스트와 Namespace가 의도한 대상인지 확인했는가
  • 운영 트래픽이나 상태 저장 데이터에 미치는 영향을 확인했는가
  • 되돌릴 매니페스트, 백업, 또는 복구 절차가 준비되어 있는가
kubectl patch virtualservice payment-vs -n production

위 항목을 모두 확인한 후 복사할 수 있습니다

로컬 터미널
# 배포 전: 기준선 에러율 측정
istioctl dashboard prometheus
# PromQL: sum(rate(istio_requests_total{destination_service="payment.production.svc.cluster.local", response_code=~"5.."}[5m])) / sum(rate(istio_requests_total{destination_service="payment.production.svc.cluster.local"}[5m]))
# 기준값: 0.2% (0.002)

# Step 1: v2 Deployment 배포 (0% 트래픽)
kubectl apply -f payment-v2-deployment.yaml

# Step 2: 10% 트래픽 전환
kubectl apply -f - << 'EOF'
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: payment-vs
  namespace: production
spec:
  hosts:
    - payment
  http:
    - route:
        - destination:
            host: payment
            subset: v1
          weight: 90
        - destination:
            host: payment
            subset: v2
          weight: 10
EOF

# Step 3: 5분간 v2 에러율 모니터링
watch -n 10 'kubectl exec -n monitoring \
  $(kubectl get pod -n monitoring -l app=prometheus -o name | head -1) \
  -- curl -sg "http://localhost:9090/api/v1/query" \
  --data-urlencode "query=sum(rate(istio_requests_total{destination_workload=\"payment-v2\",response_code=~\"5..\"}[2m])) / sum(rate(istio_requests_total{destination_workload=\"payment-v2\"}[2m]))" \
  | jq ".data.result[0].value[1]"'

# v2 에러율이 1%를 초과하면 즉시 롤백
V2_ERROR_RATE=$(kubectl exec -n monitoring ... | jq -r ...)
if (( $(echo "$V2_ERROR_RATE > 0.01" | bc -l) )); then
  echo "에러율 임계값 초과, 롤백 시작"
  # 롤백: 100% v1으로 되돌리기
  kubectl patch virtualservice payment-vs -n production \
    --type=json \
    -p='[{"op":"replace","path":"/spec/http/0/route/0/weight","value":100},
         {"op":"replace","path":"/spec/http/0/route/1/weight","value":0}]'
fi

# 안정적이면 25% → 50% → 100% 순으로 단계적 전환
# 각 단계 5분 관찰 후 이상 없으면 다음 단계 진행

Istio 없이 카나리 배포를 구현하려면 Deployment 레플리카 수로 트래픽을 조절해야 합니다(10%를 위해 v1 9개, v2 1개 유지). Istio의 weight 기반 라우팅은 각 Deployment의 레플리카 수와 무관하게 정확한 비율을 유지할 수 있어, 적은 v2 레플리카로도 세밀한 트래픽 분배가 가능합니다.

핵심 요약

리소스역할주요 설정
PeerAuthentication인바운드 mTLS 정책PERMISSIVE / STRICT
DestinationRule아웃바운드 정책 + subset 정의tls.mode, subsets, outlierDetection
VirtualService라우팅 규칙weight, match, timeout, retries
AuthorizationPolicyL7 접근 제어principals, methods, paths

마이그레이션 안전 순서: 전체 PERMISSIVE → 각 서비스 사이드카 확인 → 서비스별 STRICT 전환 → 네임스페이스 STRICT 전환

다음 단계

  • Kiali 대시보드로 서비스 메시 토폴로지 시각화
  • Jaeger/Zipkin 트레이싱 연동으로 요청 추적
  • AuthorizationPolicy로 서비스 간 접근 제어 (L7 RBAC)
  • Istio Ingress Gateway로 외부 트래픽 진입 제어

지식 확인

퀴즈 — 4문제

Q1

Istio의 사이드카 인젝션 방식에서 Envoy 프록시가 모든 트래픽을 가로채는 메커니즘은?

Q2

VirtualService와 DestinationRule의 역할 구분으로 올바른 것은?

Q3

Istio mTLS STRICT 모드로 전환 후 일부 서비스 간 통신이 실패하는 상황에서 가장 가능성 높은 원인은?

Q4

카나리 배포에서 VirtualService의 weight: 10을 v2에 설정했을 때 실제 동작은?

0 / 4 답변

🧪 실습으로 확인하기

K8s 기초 — Pod/Deployment/Service 생성

초급

kubectl로 nginx Pod를 생성하고 Deployment와 Service를 차례로 만들어 클러스터 외부에서 접근 가능한 상태까지 구성한다. K8s 3대 리소스의 역할과 관계를 직접 손으로 익힌다.

40📋 5단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

kubernetes고급 · 70
[Kubernetes] Kubernetes 장애 원인을 빠르게 격리하는 트러블슈팅 가이드
Kubernetes 트랙 계속
docker입문 · 30
[Docker] 백엔드 개발자에게 Docker와 컨테이너 가상화가 필수인 이유
Docker 트랙 시작점