RKE2·Tailscale·OCI LPG 중첩 라우팅 장애 해결기
OCI LPG로 가야 할 RKE2 노드 간 트래픽이 Tailscale 정책 라우팅으로 우회하면서 etcd와 Cilium 연결이 무너진 원인과 복구 과정을 정리했습니다.
장애 증상
RKE2 5노드 Cluster를 운영하던 중 Kubernetes Node는 모두 Ready로 보였지만 Control Plane 통신과 Rancher 운영 화면이 불안정해졌다.
- 2 vCPU인
core-a의 Load Average가 2.9~3.6 tailscaledCPU 약 69%cilium-agentCPU 약 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도 없었다. 자원 부족이나 OS 장애보다 Network Path를 먼저 확인했다.
환경 구성
Cloud Node는 OCI의 두 Subnet에 나눠 배치하고, 두 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가 함께 설치됐다.
Linux의 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
Tailscale Policy Rule보다 낮은 Priority 값인 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-a1분 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 광고를 그대로 둬야 했다.
그래서 Tailscale을 끄거나 Route를 철회하지 않고, LPG가 담당하는 Cloud 대역에만 Main Table 우선 규칙을 적용했다. 문제는 Tailscale 자체가 아니라 두 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 Network가 정상이라고 판단할 수 없다는 점을 다시 확인했다. Resource보다 실제 Routing Table과 Policy Rule을 먼저 확인한 것이 해결의 핵심이었다.