Troubleshooting 2026.08.08 5 min read

RKE2·Tailscale·OCI LPG 중첩 라우팅 장애 해결기

Ready로 보이는 RKE2 노드에서 통신과 부하 문제가 발생했다. OCI LPG와 Tailscale 경로 중첩을 확인하고 목적지별 Policy Routing을 조정했다.

장애 증상

RKE2 5노드 Cluster를 운영하던 중 Kubernetes Node는 모두 Ready로 보였지만 Control Plane 통신과 Rancher 운영 화면이 불안정해졌다.

  • 2 vCPU인 core-a의 Load Average가 2.9~3.6
  • tailscaled CPU 약 69%
  • cilium-agent CPU 약 640m
  • Tailscale 연결 추적 실패가 10초마다 700~1,200건 누적
  • Tailscale TX Queue Drop 약 4.78억 건
  • etcd peer 2380, RKE2 supervisor 9345, Cilium health 4240 Timeout
  • Rancher 502와 Webhook Probe 실패

Disk 사용률은 18~20%, 가용 Memory는 약 9GiB였고 실패한 Systemd Service도 없었다. 이 상태를 보고 Network Path부터 확인하기로 했다.

환경 구성

Cloud Node를 OCI의 두 Subnet에 배치하고 LPG로 연결했다. On-premise Node는 Tailscale Subnet Router를 통해 Cloud Node와 통신하도록 구성해 둔 상태였다.

OCI private network: 10.0.0.0/16
On-premise LAN:      192.168.0.0/24
Tailscale:           100.64.0.0/10

Cloud 내부 통신에는 LPG를 사용하고, Cloud와 On-premise 간 통신에는 Tailscale을 사용하도록 구성했다.

실제 Packet이 선택한 경로

core-a에서 다른 Cloud Node의 사설 IP 경로를 확인했다.

ip route get 10.0.0.2
ip route get 10.0.1.3

OCI Interface를 사용할 것으로 예상했는데 조회 결과는 아래와 달랐다.

10.0.0.2 dev tailscale0 table 52 src 100.123.123.1
10.0.1.3 dev tailscale0 table 52 src 100.123.123.1

On-premise에서 Cloud 대역으로 접근할 수 있도록 edge-a에는 Cloud 대역 /16을 Subnet Route로 광고하게 했고, Cloud Node에는 --accept-routes=true를 설정해 Route를 수락하도록 구성해놨다. 그 결과 Tailscale Table 52에는 10.0.0.0/16, 10.0.0.0/24, 10.0.1.0/24가 함께 설치됐다.

확인해 보니 Tailscale Policy Rule이 일반 Main Routing Table보다 먼저 평가됐다. LPG로 보내려던 Cloud 내부 Packet도 tailscale0로 나가고 있었다.

근본 원인

원인은 OCI LPG와 Tailscale Subnet Route의 목적지 대역이 겹친 상태에서 Tailscale Policy Routing이 먼저 선택된 것으로 확인했다.

core-a → core-b 사설 IP
       → Tailscale table 52
       → tailscale0
       → edge-a subnet route
       → Timeout과 재시도 폭주

노드마다 자신이 광고한 Route의 설치 상태가 다를 수 있어 응답 경로도 비대칭이 됐다. core-a에만 임시 예외를 넣었을 때는 복구되지 않았고 core-b에도 같은 규칙을 적용하자 2380, 9345와 4240 연결이 즉시 정상화됐다.

해결 방법

Cloud Node에서 10.0.0.0/16 목적지만 Tailscale보다 Main Table을 먼저 보도록 Policy Rule을 추가했다.

ip rule add to 10.0.0.0/16 priority 2500 lookup main

Priority를 Tailscale Policy Rule보다 낮은 2500으로 지정해 먼저 평가되게 했다. On-premise 192.168.0.0/24에는 예외를 추가하지 않아 기존 Tailscale 경로를 유지했다.

최종 경로는 다음처럼 분리됐다.

목적지 경로
Cloud 10.0.0.0/16 OCI Main Table → LPG
On-premise 192.168.0.0/24 Tailscale → edge-b
Tailscale 100.64.0.0/10 Tailscale 직접 연결

재부팅 후에도 유지하기

네 Cloud Node에 oci-lpg-route-policy.service를 만들었다.

[Unit]
Description=Prefer OCI LPG routes for the cloud private network
Wants=network-online.target
After=network-online.target tailscaled.service
Before=rke2-server.service rke2-agent.service

[Service]
Type=oneshot
ExecStartPre=-/usr/sbin/ip rule del to 10.0.0.0/16 priority 2500 lookup main
ExecStart=/usr/sbin/ip rule add to 10.0.0.0/16 priority 2500 lookup main
ExecStop=-/usr/sbin/ip rule del to 10.0.0.0/16 priority 2500 lookup main
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

ExecStartPre로 기존 Rule을 먼저 지워 재실행 시 중복되지 않게 했다. RKE2보다 먼저 적용되도록 Unit 순서도 지정했다.

검증 결과

조치 후 다음을 확인했다.

  • etcd 3개 Member 모두 Healthy
  • Cilium Cluster Health 5/5 Reachable
  • RKE2 supervisor와 kubelet Port 연결 정상
  • etcd peer Timeout과 Tailscale open-conn-track Timeout 재발 없음
  • core-a 1분 Load Average 약 3.6 → 0.47
  • Cloud 내부는 OCI Interface, On-premise는 Tailscale 경로 선택

--accept-routes=false를 사용하지 않은 이유

Cloud Node가 Subnet Route 수락을 완전히 끄면 On-premise Node의 Internal IP에도 접근할 수 없다. Cloud와 On-premise 간 통신을 유지하려면 기존 Tailscale Route 광고를 그대로 둬야 했다.

LPG가 담당하는 Cloud 대역에만 Main Table 우선 규칙을 적용하고 Tailscale과 Route 광고는 유지했다. 두 Network 경로를 함께 쓰면서 같은 CIDR의 우선순위를 정하지 않은 설정을 수정한 것이다.

재발 방지

  • 신규 Subnet Route 승인 전 OCI와 LAN CIDR 중첩 확인
  • ip route get으로 Cloud·On-premise 경로를 각각 확인
  • etcd peer와 Tailscale timeout Log 모니터링
  • RKE2 또는 Tailscale Upgrade 후 Policy Rule과 Systemd Unit 확인
  • LPG Route Table과 NSG에서 etcd·RKE2·Cilium Port 양방향 확인

Node가 Ready인 상태에서도 Control Plane 통신은 실패하고 있었다. 실제 Routing Table과 Policy Rule을 확인하고 양쪽 노드의 경로를 맞춘 뒤 포트 연결과 부하가 정상화되는 것을 확인했다.

SEARCH JOURNAL

기록 검색

글 제목, 요약, 태그에서 찾습니다.

검색을 준비하고 있어요.

검색어를 입력해 주세요.

    Ctrl/⌘ K 검색 Esc 닫기 ↑ ↓ 결과 이동