Skip to content

Kubernetes Service 통신 완벽 가이드 + Tailscale DNS 문제 해결 ​

목차 ​

  1. 문제 상황 분석
  2. Kubernetes Service 통신 흐름
  3. CoreDNS 동작 원리
  4. Tailscale이 DNS에 미치는 영향
  5. 완전한 패킷 흐름 분석
  6. 문제 해결 방법
  7. 트러블슈팅 가이드
  8. 모니터링 및 디버깅

문제 상황 분석 ​

실제 발생한 문제 ​

요청:
  Pod → frigate.video-management-system.svc.cluster.local

CoreDNS가 받은 쿼리:
  frigate.video-management-system.svc.cluster.local.tailb45a71.ts.net
                                                    ^^^^^^^^^^^^^^^^
                                                    Tailscale 도메인 추가됨!

결과:
  [ERROR] Failed to resolve (Caused by NameResolutionError)

로그 분석 ​

CoreDNS 에러:
[ERROR] plugin/errors: 2 frigate.video-management-system.svc.cluster.local.tailb45a71.ts.net. A: read udp 10.42.0.162:51979->8.8.8.8:53: i/o timeout

분석:
1. 원래 도메인: frigate.video-management-system.svc.cluster.local
2. 실제 쿼리: frigate.video-management-system.svc.cluster.local.tailb45a71.ts.net
3. CoreDNS가 8.8.8.8 (외부 DNS)에 쿼리 → 실패
4. 클러스터 내부 도메인인데 외부로 쿼리를 보냄 (잘못됨!)

Kubernetes Service 통신 흐름 ​

Pod to Service 통신 전체 그림 ​

┌─────────────────────────────────────────────────────────────────┐
│                    Kubernetes Cluster                           │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ Pod A (Airflow Worker)                                   │  │
│  │ IP: 10.42.0.100                                          │  │
│  │ Namespace: workflow-engine                               │  │
│  │                                                          │  │
│  │  Application Code:                                       │  │
│  │  requests.get('http://frigate.video-management-system    │  │
│  │                     .svc.cluster.local:5000/api/...')   │  │
│  └────────────────────────┬─────────────────────────────────┘  │
│                           │                                     │
│                           │ 1. DNS Query                        │
│                           │    "frigate.video-management-system │
│                           │     .svc.cluster.local"             │
│                           ▼                                     │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ /etc/resolv.conf (Pod 내부)                              │  │
│  ├──────────────────────────────────────────────────────────┤  │
│  │ nameserver 10.43.0.10  ← CoreDNS Service IP              │  │
│  │ search workflow-engine.svc.cluster.local                 │  │
│  │        svc.cluster.local                                 │  │
│  │        cluster.local                                     │  │
│  │ options ndots:5                                          │  │
│  └────────────────────────┬─────────────────────────────────┘  │
│                           │                                     │
│                           │ 2. DNS Query 전송                   │
│                           ▼                                     │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ CoreDNS Pod (kube-system namespace)                      │  │
│  │ IP: 10.42.1.5                                            │  │
│  │                                                          │  │
│  │ Corefile:                                                │  │
│  │   cluster.local {                                        │  │
│  │     kubernetes cluster.local in-addr.arpa ip6.arpa {    │  │
│  │       pods insecure                                      │  │
│  │       fallthrough in-addr.arpa ip6.arpa                 │  │
│  │     }                                                    │  │
│  │   }                                                      │  │
│  └────────────────────────┬─────────────────────────────────┘  │
│                           │                                     │
│                           │ 3. Kubernetes API 조회              │
│                           │    "frigate" Service 정보 요청      │
│                           ▼                                     │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ Kubernetes API Server                                    │  │
│  │                                                          │  │
│  │ Services:                                                │  │
│  │   - frigate.video-management-system                      │  │
│  │     ClusterIP: 10.43.123.45                             │  │
│  │     Port: 5000                                          │  │
│  │     Selector: app=frigate                               │  │
│  └────────────────────────┬─────────────────────────────────┘  │
│                           │                                     │
│                           │ 4. DNS Response                     │
│                           │    A record: 10.43.123.45           │
│                           ▼                                     │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ Pod A                                                    │  │
│  │ DNS Response 수신: 10.43.123.45                          │  │
│  └────────────────────────┬─────────────────────────────────┘  │
│                           │                                     │
│                           │ 5. HTTP Request                     │
│                           │    Dst: 10.43.123.45:5000           │
│                           ▼                                     │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ kube-proxy (iptables or IPVS)                            │  │
│  │                                                          │  │
│  │ iptables rules:                                          │  │
│  │   -A KUBE-SERVICES -d 10.43.123.45/32 -p tcp            │  │
│  │    --dport 5000 -j KUBE-SVC-XXX                         │  │
│  │                                                          │  │
│  │   -A KUBE-SVC-XXX -j KUBE-SEP-AAA (50% probability)     │  │
│  │   -A KUBE-SVC-XXX -j KUBE-SEP-BBB (50% probability)     │  │
│  │                                                          │  │
│  │   -A KUBE-SEP-AAA -p tcp -j DNAT                        │  │
│  │    --to-destination 10.42.2.10:5000  ← Frigate Pod 1    │  │
│  │   -A KUBE-SEP-BBB -p tcp -j DNAT                        │  │
│  │    --to-destination 10.42.2.11:5000  ← Frigate Pod 2    │  │
│  └────────────────────────┬─────────────────────────────────┘  │
│                           │                                     │
│                           │ 6. DNAT 적용                        │
│                           │    10.43.123.45:5000               │
│                           │    → 10.42.2.10:5000 (실제 Pod)    │
│                           ▼                                     │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ Frigate Pod                                              │  │
│  │ IP: 10.42.2.10                                           │  │
│  │ Port: 5000                                               │  │
│  │                                                          │  │
│  │ Application 처리                                          │  │
│  └────────────────────────┬─────────────────────────────────┘  │
│                           │                                     │
│                           │ 7. HTTP Response                    │
│                           ▼                                     │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ Pod A                                                    │  │
│  │ Response 수신                                             │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

CoreDNS 동작 원리 ​

CoreDNS 설정 파일 (Corefile) ​

yaml
# kubectl -n kube-system get cm coredns -o yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready

        # Kubernetes plugin: 클러스터 내부 DNS
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }

        # Prometheus metrics
        prometheus :9153

        # Forward plugin: 외부 DNS
        forward . /etc/resolv.conf {
           max_concurrent 1000
        }

        # Cache
        cache 30

        # Loop detection
        loop

        # Reload
        reload

        # Load balance
        loadbalance
    }

DNS 쿼리 처리 과정 ​

┌────────────────────────────────────────────────────────────┐
│           CoreDNS Query Processing                         │
└────────────────────────────────────────────────────────────┘

쿼리: frigate.video-management-system.svc.cluster.local

┌─────────────────────────────────────────────────────────────┐
│ Step 1: Query 수신                                          │
├─────────────────────────────────────────────────────────────┤
│ - Client: 10.42.0.100 (Pod A)                               │
│ - Query: frigate.video-management-system.svc.cluster.local │
│ - Type: A (IPv4 주소 조회)                                  │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ Step 2: Plugin Chain 시작                                   │
├─────────────────────────────────────────────────────────────┤
│ 1. errors plugin                                            │
│    → 에러 로깅 준비                                          │
│                                                             │
│ 2. health plugin                                            │
│    → 헬스체크 (pass)                                        │
│                                                             │
│ 3. ready plugin                                             │
│    → Readiness check (pass)                                 │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ Step 3: kubernetes plugin                                   │
├─────────────────────────────────────────────────────────────┤
│ Zone 확인: cluster.local?                                   │
│   YES → Kubernetes API 조회                                 │
│                                                             │
│ Query Kubernetes API:                                       │
│   GET /api/v1/namespaces/video-management-system/          │
│       services/frigate                                      │
│                                                             │
│ Response:                                                   │
│   {                                                         │
│     "metadata": {                                           │
│       "name": "frigate",                                    │
│       "namespace": "video-management-system"                │
│     },                                                      │
│     "spec": {                                               │
│       "clusterIP": "10.43.123.45",                          │
│       "ports": [{                                           │
│         "port": 5000,                                       │
│         "targetPort": 5000                                  │
│       }]                                                    │
│     }                                                       │
│   }                                                         │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ Step 4: DNS Response 생성                                   │
├─────────────────────────────────────────────────────────────┤
│ Answer:                                                     │
│   frigate.video-management-system.svc.cluster.local.        │
│   30 IN A 10.43.123.45                                      │
│                                                             │
│ Authority: (none)                                           │
│ Additional: (none)                                          │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ Step 5: cache plugin                                        │
├─────────────────────────────────────────────────────────────┤
│ Cache에 저장 (TTL: 30초)                                    │
│   Key: frigate.video-management-system.svc.cluster.local    │
│   Value: 10.43.123.45                                       │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ Step 6: Response 전송                                       │
├─────────────────────────────────────────────────────────────┤
│ UDP Packet:                                                 │
│   Src: 10.43.0.10:53 (CoreDNS)                             │
│   Dst: 10.42.0.100:random (Pod A)                          │
│   Answer: 10.43.123.45                                      │
└─────────────────────────────────────────────────────────────┘

Search Domain과 ndots ​

bash
# Pod 내부 /etc/resolv.conf
nameserver 10.43.0.10
search workflow-engine.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

ndots:5의 의미:

  • 도메인에 점(.)이 5개 미만이면 search domain을 붙여서 시도
  • frigate.video-management-system.svc.cluster.local
    • 점이 4개 → search domain 시도!

실제 DNS 쿼리 순서:

Original query: frigate.video-management-system.svc.cluster.local

ndots=5 → 점이 4개 → Search domain 적용!

1st try: frigate.video-management-system.svc.cluster.local.workflow-engine.svc.cluster.local
   → NXDOMAIN (없음)

2nd try: frigate.video-management-system.svc.cluster.local.svc.cluster.local
   → NXDOMAIN (없음)

3rd try: frigate.video-management-system.svc.cluster.local.cluster.local
   → NXDOMAIN (없음)

4th try: frigate.video-management-system.svc.cluster.local (원본 그대로)
   → SUCCESS! 10.43.123.45

성능 문제: 불필요한 DNS 쿼리 3번 발생!

해결 방법:

python
# 방법 1: FQDN 사용 (마지막에 점 추가)
url = "http://frigate.video-management-system.svc.cluster.local.:5000"
#                                                            ↑
#                                                            점 추가!

# 방법 2: ndots 조정 (Pod spec)
dnsConfig:
  options:
  - name: ndots
    value: "1"

Tailscale이 DNS에 미치는 영향 ​

Tailscale 기본 동작 ​

┌─────────────────────────────────────────────────────────────┐
│                  Tailscale Network                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Node A (Kubernetes Node)                                   │
│  ┌──────────────────────────────────────────────────────┐  │
│  │ tailscaled                                           │  │
│  │ Tailscale IP: 100.64.1.10                            │  │
│  │ Hostname: k8s-node-1.tailb45a71.ts.net               │  │
│  │                                                      │  │
│  │ /etc/resolv.conf (Host):                            │  │
│  │   nameserver 100.100.100.100  ← Tailscale DNS       │  │
│  │   search tailb45a71.ts.net                          │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  Pod (inside Node A)                                        │
│  ┌──────────────────────────────────────────────────────┐  │
│  │ /etc/resolv.conf:                                    │  │
│  │   nameserver 10.43.0.10  ← CoreDNS                   │  │
│  │   search namespace.svc.cluster.local                 │  │
│  │          svc.cluster.local                           │  │
│  │          cluster.local                               │  │
│  │          tailb45a71.ts.net  ← Tailscale domain 추가! │  │
│  │   options ndots:5                                    │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

문제 발생 메커니즘 ​

┌────────────────────────────────────────────────────────────┐
│ Problem: Tailscale Search Domain 간섭                       │
└────────────────────────────────────────────────────────────┘

Query: frigate.video-management-system.svc.cluster.local

Pod의 /etc/resolv.conf:
  nameserver 10.43.0.10
  search workflow-engine.svc.cluster.local
         svc.cluster.local
         cluster.local
         tailb45a71.ts.net  ← 문제!
  options ndots:5

ndots=5 → 점이 4개 → Search domain 시도!

1st: frigate.video-management-system.svc.cluster.local
     .workflow-engine.svc.cluster.local
   → CoreDNS → NXDOMAIN

2nd: frigate.video-management-system.svc.cluster.local
     .svc.cluster.local
   → CoreDNS → NXDOMAIN

3rd: frigate.video-management-system.svc.cluster.local
     .cluster.local
   → CoreDNS → NXDOMAIN

4th: frigate.video-management-system.svc.cluster.local
     .tailb45a71.ts.net  ← 여기서 문제!
   → CoreDNS → kubernetes plugin (cluster.local이 아님)
   → forward plugin (외부 DNS로 전달)
   → 8.8.8.8 or 172.27.150.2 (upstream DNS)
   → Timeout! (존재하지 않는 도메인)

5th: frigate.video-management-system.svc.cluster.local
     (원본 그대로)
   → CoreDNS → kubernetes plugin → SUCCESS!

CoreDNS 로그에서 확인 ​

[ERROR] plugin/errors: 2 frigate.video-management-system.svc.cluster.local.tailb45a71.ts.net. A: read udp 10.42.0.162:51979->8.8.8.8:53: i/o timeout

분석:
- 쿼리: frigate.video-management-system.svc.cluster.local.tailb45a71.ts.net
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
         Kubernetes 도메인                                 Tailscale 도메인

- CoreDNS가 이 쿼리를 받음
- kubernetes plugin: "cluster.local"이 아니므로 pass
- forward plugin: 외부 DNS(8.8.8.8)로 전달
- 8.8.8.8: 이런 도메인 없음 → Timeout

완전한 패킷 흐름 분석 ​

정상 케이스 (Tailscale 없음) ​

┌─────────────────────────────────────────────────────────────┐
│ Step 1: Application → DNS Query                             │
└─────────────────────────────────────────────────────────────┘

Application (Python):
  requests.get('http://frigate.video-management-system.svc.cluster.local:5000')

↓ getaddrinfo() system call

glibc resolver:
  /etc/resolv.conf 읽기
  nameserver 10.43.0.10
  search workflow-engine.svc.cluster.local svc.cluster.local cluster.local
  options ndots:5

ndots 체크:
  "frigate.video-management-system.svc.cluster.local" → 점 4개
  → Search domain 시도!

DNS Query 1:
  frigate.video-management-system.svc.cluster.local.workflow-engine.svc.cluster.local

┌─────────────────────────────────────────────────────────────┐
│ Step 2: DNS Query → CoreDNS                                 │
└─────────────────────────────────────────────────────────────┘

UDP Packet:
  Src: 10.42.0.100:random
  Dst: 10.43.0.10:53 (CoreDNS Service IP)

L2: Ethernet
  Src MAC: Pod A veth MAC
  Dst MAC: cni0 bridge MAC

L3: IP
  Src IP: 10.42.0.100
  Dst IP: 10.43.0.10

L4: UDP
  Src Port: 54321 (random)
  Dst Port: 53

L7: DNS Query
  Transaction ID: 0x1234
  Flags: Standard query
  Questions: 1
    Name: frigate.video-management-system.svc.cluster.local.workflow-engine.svc.cluster.local
    Type: A
    Class: IN

┌─────────────────────────────────────────────────────────────┐
│ Step 3: CoreDNS Processing                                  │
└─────────────────────────────────────────────────────────────┘

CoreDNS receives query

kubernetes plugin:
  Zone match? "cluster.local"
    frigate.video-management-system.svc.cluster.local.workflow-engine.svc.cluster.local
    → 끝이 "cluster.local"? YES!

  Parse query:
    Service: frigate
    Namespace: video-management-system
    But... ".workflow-engine.svc.cluster.local"이 추가로 붙음
    → Kubernetes API 조회 실패 → NXDOMAIN

DNS Response:
  RCODE: NXDOMAIN (No such domain)

┌─────────────────────────────────────────────────────────────┐
│ Step 4: Retry (Search domain 계속)                          │
└─────────────────────────────────────────────────────────────┘

DNS Query 2:
  frigate.video-management-system.svc.cluster.local.svc.cluster.local
  → NXDOMAIN

DNS Query 3:
  frigate.video-management-system.svc.cluster.local.cluster.local
  → NXDOMAIN

DNS Query 4:
  frigate.video-management-system.svc.cluster.local (원본)
  → SUCCESS! A 10.43.123.45

┌─────────────────────────────────────────────────────────────┐
│ Step 5: HTTP Request → Service                              │
└─────────────────────────────────────────────────────────────┘

TCP Handshake:
  SYN: 10.42.0.100:54322 → 10.43.123.45:5000

iptables DNAT (kube-proxy):
  -A KUBE-SERVICES -d 10.43.123.45/32 -p tcp --dport 5000
     -j KUBE-SVC-FRIGATE

  -A KUBE-SVC-FRIGATE -m statistic --mode random --probability 0.5
     -j KUBE-SEP-FRIGATE-POD1

  -A KUBE-SEP-FRIGATE-POD1 -p tcp
     -j DNAT --to-destination 10.42.2.10:5000

Packet 변환:
  Before DNAT: 10.42.0.100 → 10.43.123.45:5000
  After DNAT:  10.42.0.100 → 10.42.2.10:5000

┌─────────────────────────────────────────────────────────────┐
│ Step 6: Packet Routing to Pod                               │
└─────────────────────────────────────────────────────────────┘

Routing decision:
  Dst: 10.42.2.10
  Route table: 10.42.2.0/24 via flannel.1 (VXLAN)

VXLAN Encapsulation:
  Outer IP: Node A IP → Node B IP
  Outer UDP: Port 4789
  VXLAN: VNI 1
  Inner IP: 10.42.0.100 → 10.42.2.10
  Inner TCP: 54322 → 5000

┌─────────────────────────────────────────────────────────────┐
│ Step 7: Frigate Pod Receives Request                        │
└─────────────────────────────────────────────────────────────┘

VXLAN Decapsulation at Node B

TCP Connection:
  10.42.0.100:54322 ←→ 10.42.2.10:5000

HTTP Request:
  GET /api/115/recordings?after=... HTTP/1.1
  Host: frigate.video-management-system.svc.cluster.local:5000

Frigate Application processes request

HTTP Response:
  HTTP/1.1 200 OK
  Content-Type: application/json
  ...

문제 케이스 (Tailscale 있음) ​

┌─────────────────────────────────────────────────────────────┐
│ Step 1-3: 동일 (Search domain 시도)                         │
└─────────────────────────────────────────────────────────────┘

Query 1-3: NXDOMAIN (동일)

┌─────────────────────────────────────────────────────────────┐
│ Step 4: Tailscale Search Domain 시도 (문제!)                │
└─────────────────────────────────────────────────────────────┘

DNS Query 4:
  frigate.video-management-system.svc.cluster.local.tailb45a71.ts.net
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
  Kubernetes 도메인                                  Tailscale 도메인

CoreDNS receives query:

kubernetes plugin:
  Zone match? "cluster.local"
    frigate...cluster.local.tailb45a71.ts.net
    → 끝이 "cluster.local"? NO! (끝이 "ts.net")
    → Pass (처리 안 함)

forward plugin:
  외부 DNS로 전달
  forward . /etc/resolv.conf

/etc/resolv.conf (CoreDNS Pod):
  nameserver 8.8.8.8
  nameserver 172.27.150.2

UDP Query:
  Src: CoreDNS Pod (10.42.0.162)
  Dst: 8.8.8.8:53

  Question:
    frigate.video-management-system.svc.cluster.local.tailb45a71.ts.net

8.8.8.8 (Google DNS):
  "그런 도메인 모름"
  → No response (timeout)

CoreDNS:
  [ERROR] plugin/errors: 2 frigate.video-management-system.svc.cluster.local.tailb45a71.ts.net. A: read udp 10.42.0.162:51979->8.8.8.8:53: i/o timeout

Retry with 172.27.150.2:
  → Timeout again

┌─────────────────────────────────────────────────────────────┐
│ Step 5: 최종적으로 원본 도메인 시도                           │
└─────────────────────────────────────────────────────────────┘

DNS Query 5:
  frigate.video-management-system.svc.cluster.local (원본)

CoreDNS kubernetes plugin:
  → SUCCESS! 10.43.123.45

BUT: 이미 많은 시간 소비 (5초 timeout × 여러 번)
     → Application timeout 발생 가능!

문제 해결 방법 ​

방법 1: CoreDNS Rewrite Plugin (권장) ​

Tailscale search domain을 무시하도록 설정:

yaml
# ConfigMap 수정
kubectl -n kube-system edit cm coredns

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready

        # ========================================
        # Tailscale search domain 제거 (추가!)
        # ========================================
        rewrite stop {
            name regex (.*)\.tailb45a71\.ts\.net\.cluster\.local {1}.cluster.local
            answer name (.*)\.cluster\.local {1}.tailb45a71.ts.net.cluster.local
        }

        rewrite stop {
            name regex (.*)\.tailb45a71\.ts\.net {1}
            answer auto
        }

        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }

        prometheus :9153
        forward . /etc/resolv.conf {
           max_concurrent 1000
        }
        cache 30
        loop
        reload
        loadbalance
    }

동작 원리:

Query: frigate.video-management-system.svc.cluster.local.tailb45a71.ts.net

rewrite plugin:
  Pattern match: (.*)\.tailb45a71\.ts\.net
  Capture: frigate.video-management-system.svc.cluster.local
  Rewrite to: frigate.video-management-system.svc.cluster.local

kubernetes plugin:
  Query: frigate.video-management-system.svc.cluster.local
  → SUCCESS!

방법 2: Tailscale DNS 설정 수정 ​

Tailscale이 Kubernetes에 search domain을 주입하지 않도록:

bash
# Tailscale 설정 확인
tailscale status
tailscale netcheck

# Node에서 Tailscale 설정 수정
# /etc/systemd/system/tailscaled.service.d/override.conf
[Service]
Environment="TS_ACCEPT_DNS=false"

# 재시작
sudo systemctl daemon-reload
sudo systemctl restart tailscaled

또는 Tailscale Admin Console에서:

DNS → Global nameservers → Override local DNS
→ Disable "Add search domains to local DNS"

방법 3: Pod DNS Policy 설정 ​

특정 Pod가 Tailscale DNS를 우회하도록:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: airflow-worker
spec:
  dnsPolicy: "None"  # 기본 DNS 설정 무시
  dnsConfig:
    nameservers:
    - 10.43.0.10  # CoreDNS 직접 지정
    searches:
    - workflow-engine.svc.cluster.local
    - svc.cluster.local
    - cluster.local
    # tailb45a71.ts.net 제외!
    options:
    - name: ndots
      value: "2"  # 성능 최적화
    - name: timeout
      value: "2"
    - name: attempts
      value: "2"

방법 4: FQDN 사용 (임시 해결) ​

애플리케이션 코드 수정:

python
# Before (문제)
url = "http://frigate.video-management-system.svc.cluster.local:5000"

# After (해결)
url = "http://frigate.video-management-system.svc.cluster.local.:5000"
#                                                             ↑
#                                                             점 추가!

왜 동작하는가?:

  • 마지막 점(.) = FQDN (Fully Qualified Domain Name)
  • ndots 체크 무시 → search domain 시도 안 함
  • 바로 원본 도메인으로 쿼리

방법 5: NodeLocal DNSCache 사용 ​

DNS 성능 최적화 + Tailscale 문제 우회:

bash
# NodeLocal DNSCache 설치
kubectl apply -f https://k8s.io/examples/admin/dns/nodelocaldns.yaml

동작:

Pod → 169.254.20.10 (Node-local DNS cache)
      → Cache hit? Return
      → Cache miss? CoreDNS (10.43.0.10)

장점:

  • DNS 쿼리 속도 향상
  • CoreDNS 부하 감소
  • Tailscale search domain 영향 최소화

트러블슈팅 가이드 ​

증상 1: DNS 해석 실패 ​

bash
# Pod 내부에서
kubectl exec -it <pod> -- nslookup frigate.video-management-system.svc.cluster.local

# 실패 시 출력:
Server:         10.43.0.10
Address:        10.43.0.10#53

** server can't find frigate.video-management-system.svc.cluster.local: NXDOMAIN

진단 단계:

bash
# 1. Pod DNS 설정 확인
kubectl exec -it <pod> -- cat /etc/resolv.conf

출력:
nameserver 10.43.0.10
search namespace.svc.cluster.local svc.cluster.local cluster.local tailb45a71.ts.net
options ndots:5

문제: tailb45a71.ts.net이 포함됨!

# 2. CoreDNS 로그 확인
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=100

문제 로그:
[ERROR] plugin/errors: 2 frigate...cluster.local.tailb45a71.ts.net. A: read udp ... i/o timeout

# 3. CoreDNS ConfigMap 확인
kubectl -n kube-system get cm coredns -o yaml

확인 사항:
- rewrite plugin 설정 여부
- kubernetes plugin zone 설정

# 4. Service 존재 확인
kubectl -n video-management-system get svc frigate

출력:
NAME      TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
frigate   ClusterIP   10.43.123.45   <none>        5000/TCP   30d

# 5. CoreDNS에서 직접 쿼리
kubectl run -it --rm debug --image=busybox --restart=Never -- \
  nslookup frigate.video-management-system.svc.cluster.local 10.43.0.10

성공하면: CoreDNS는 정상, Pod DNS 설정 문제
실패하면: CoreDNS 설정 문제

증상 2: DNS 응답 느림 ​

bash
# DNS 쿼리 시간 측정
kubectl exec -it <pod> -- time nslookup frigate.video-management-system.svc.cluster.local

출력:
real    0m 5.023s  ← 너무 느림!
user    0m 0.001s
sys     0m 0.002s

원인 분석:

bash
# tcpdump로 DNS 쿼리 캡처
kubectl exec -it <pod> -- tcpdump -i any -nn port 53

출력 분석:
10:30:01.000 A? frigate...workflow-engine.svc.cluster.local
10:30:01.100 NXDOMAIN
10:30:01.200 A? frigate...svc.cluster.local
10:30:01.300 NXDOMAIN
10:30:01.400 A? frigate...cluster.local
10:30:01.500 NXDOMAIN
10:30:01.600 A? frigate...tailb45a71.ts.net  ← 여기서 timeout!
10:30:06.600 Timeout
10:30:06.700 A? frigate...cluster.local (원본)
10:30:06.800 A 10.43.123.45  ← 성공

총 5초 이상 소요!

해결:

bash
# 방법 1: ndots 조정
kubectl edit deployment <deployment>

spec:
  template:
    spec:
      dnsConfig:
        options:
        - name: ndots
          value: "2"  # 5 → 2로 변경

# 방법 2: FQDN 사용 (코드 수정)
# 방법 3: CoreDNS rewrite plugin 추가

증상 3: 특정 도메인만 실패 ​

bash
# 증상
kubectl exec -it <pod> -- curl http://frigate.video-management-system.svc.cluster.local:5000
# → 실패

kubectl exec -it <pod> -- curl http://google.com
# → 성공

진단:

bash
# CoreDNS forward 설정 확인
kubectl -n kube-system get cm coredns -o yaml | grep -A 5 forward

출력:
forward . /etc/resolv.conf {
   max_concurrent 1000
}

# CoreDNS Pod의 /etc/resolv.conf 확인
kubectl -n kube-system exec -it <coredns-pod> -- cat /etc/resolv.conf

출력:
nameserver 8.8.8.8
nameserver 172.27.150.2
search tailb45a71.ts.net  ← 문제!

# 외부 도메인은 성공 (8.8.8.8이 응답)
# 클러스터 도메인 + Tailscale suffix는 실패

모니터링 및 디버깅 ​

CoreDNS 메트릭 확인 ​

bash
# Prometheus metrics
kubectl -n kube-system port-forward svc/kube-dns 9153:9153

# 메트릭 확인
curl http://localhost:9153/metrics

주요 메트릭:
# coredns_dns_request_duration_seconds: DNS 쿼리 시간
# coredns_dns_requests_total: 총 DNS 요청 수
# coredns_dns_responses_total: DNS 응답 (NOERROR, NXDOMAIN 등)
# coredns_forward_requests_total: Forward된 쿼리 수

Grafana Dashboard:

yaml
# Prometheus Query 예시
# 실패한 DNS 쿼리 비율
sum(rate(coredns_dns_responses_total{rcode="NXDOMAIN"}[5m]))
/
sum(rate(coredns_dns_responses_total[5m])) * 100

# Tailscale 도메인 쿼리 수
sum(rate(coredns_forward_requests_total{to=~".*tailb45a71.ts.net.*"}[5m]))

# DNS 쿼리 지연 시간 (p99)
histogram_quantile(0.99,
  sum(rate(coredns_dns_request_duration_seconds_bucket[5m])) by (le)
)

패킷 캡처로 분석 ​

bash
# CoreDNS Pod에서 패킷 캡처
kubectl -n kube-system exec -it <coredns-pod> -- \
  tcpdump -i any -nn -s 0 -w /tmp/coredns.pcap port 53

# 파일 다운로드
kubectl -n kube-system cp <coredns-pod>:/tmp/coredns.pcap ./coredns.pcap

# Wireshark로 분석
wireshark coredns.pcap

분석 포인트:

Filter: dns

정상 쿼리:
  Query: frigate.video-management-system.svc.cluster.local
  Response time: 0.001s
  Answer: 10.43.123.45

문제 쿼리:
  Query: frigate.video-management-system.svc.cluster.local.tailb45a71.ts.net
  Response time: 5.000s (timeout)
  Answer: (none)

DNS 쿼리 추적 (dig) ​

bash
# Pod 내부에서
kubectl exec -it <pod> -- sh

# dig 설치 (Alpine 기준)
apk add bind-tools

# DNS 쿼리 추적
dig +trace frigate.video-management-system.svc.cluster.local @10.43.0.10

출력:
; <<>> DiG 9.16.1 <<>> +trace frigate...
;; global options: +cmd
.                       518400  IN      NS      a.root-servers.net.
...

; 최종 응답
frigate.video-management-system.svc.cluster.local. 30 IN A 10.43.123.45

;; Query time: 1 msec
;; SERVER: 10.43.0.10#53(10.43.0.10)

로그 기반 모니터링 ​

bash
# CoreDNS 에러 로그 실시간 모니터링
kubectl -n kube-system logs -f -l k8s-app=kube-dns | grep ERROR

# Tailscale 관련 에러만
kubectl -n kube-system logs -f -l k8s-app=kube-dns | grep tailb45a71

# 특정 도메인 쿼리만
kubectl -n kube-system logs -f -l k8s-app=kube-dns | grep frigate

정리 ​

문제 원인 요약 ​

1. Tailscale이 Node에 설치됨
   └→ /etc/resolv.conf에 search domain 추가
       search tailb45a71.ts.net

2. Kubernetes가 Pod 생성 시 resolv.conf 상속
   └→ Pod의 /etc/resolv.conf에도 tailb45a71.ts.net 추가

3. Pod에서 Service 호출
   └→ frigate.video-management-system.svc.cluster.local

4. glibc resolver의 ndots=5 체크
   └→ 점이 4개 → Search domain 시도!

5. 4번째 시도에서 Tailscale domain 추가
   └→ frigate...cluster.local.tailb45a71.ts.net

6. CoreDNS가 이 쿼리를 받음
   └→ kubernetes plugin: "cluster.local"로 끝나지 않음 → Pass
   └→ forward plugin: 외부 DNS(8.8.8.8)로 전달

7. 외부 DNS가 응답 못함 (존재하지 않는 도메인)
   └→ Timeout (5초)

8. 최종적으로 원본 도메인으로 재시도
   └→ 성공하지만 이미 많은 시간 소비

최적 해결 방법 ​

┌─────────────────────────────────────────────────────────────┐
│               권장 해결 방법 우선순위                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│ 1순위: CoreDNS rewrite plugin (클러스터 레벨)                │
│   - 모든 Pod에 적용                                         │
│   - 애플리케이션 코드 수정 불필요                            │
│   - 성능 영향 최소                                           │
│                                                             │
│ 2순위: Pod dnsPolicy/dnsConfig (Pod 레벨)                   │
│   - 특정 워크로드만 조정                                     │
│   - ndots 최적화 가능                                        │
│   - 세밀한 제어 가능                                         │
│                                                             │
│ 3순위: Tailscale DNS 설정 변경 (Node 레벨)                  │
│   - Tailscale 전체에 영향                                    │
│   - 다른 Tailscale 기능 영향 가능                            │
│                                                             │
│ 4순위: FQDN 사용 (코드 레벨)                                 │
│   - 임시 해결책                                              │
│   - 모든 도메인에 점(.) 추가 필요                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

성능 최적화 ​

yaml
# 최적화된 DNS 설정
apiVersion: v1
kind: Pod
metadata:
  name: optimized-pod
spec:
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
    - 10.43.0.10
    searches:
    - video-management-system.svc.cluster.local  # 자주 쓰는 namespace
    - svc.cluster.local
    - cluster.local
    options:
    - name: ndots
      value: "2"  # 성능 향상 (5 → 2)
    - name: timeout
      value: "2"  # 빠른 실패
    - name: attempts
      value: "2"  # 재시도 횟수
    - name: single-request-reopen  # A와 AAAA 쿼리 병렬화
    - name: use-vc  # TCP 사용 (대용량 응답)

성능 개선 효과:

Before (ndots=5, Tailscale domain 포함):
  - DNS 쿼리 시간: 5초+
  - 불필요한 쿼리: 4번

After (ndots=2, Tailscale domain 제거):
  - DNS 쿼리 시간: 0.001초
  - 불필요한 쿼리: 0번

→ 5000배 성능 향상!

이제 Kubernetes Service DNS 통신과 Tailscale DNS 간섭 문제를 완벽히 이해하고 해결할 수 있습니다!