[Kubernetes/트러블슈팅] Calico + Firewalld 환경에서 pod와 외부노드 간 통신

문제 상황

기존에 Ubuntu에서만 서비스하던 pod를 Rocky로 확장하니, Rocky에 띄운 pod에서 DB에 연결할 수 없어 pod가 죽는 문제가 발생했다.
DB는 클러스터 외부에 위치하며, Rocky 노드가 아닌 외부 노드에서 실행중이다.
즉, pod와 타 노드 간 통신이 불가한 상황.

  • 클러스터 환경: 온프레미스 Only 클러스터 (홈네트워크)
  • CNI: calico
  • DB 노드 IP: 10.10.10.51 (home51)
  • pod가 배치된 문제 노드 IP: 10.10.10.53 (home53)
  • DB 포트: 3306
  • 에러 메시지: Host Unreachable

원인 확인

Q1. home53에서 home51으로의 통신 자체가 불가능한가

노드 <-> 노드 간 통신에 문제가 없는지 ping 명령으로 확인했다.

# home53에서 실행
$ ping -c 3 10.10.10.51

# 정상

Q2. home51에 패킷이 도착했는데 응답을 못받았나

home51에서 tcpdump 명령으로 패킷 캡쳐를 시도해보았다.

# home51에서 실행
$ sudo tcpdump -i any port 3306 -n

# 아무것도 안찍힘

이를 통해 home53에서 발생한 패킷이 home51에 도달하지 못했다고 판단했다.

Q3. home53에서 패킷이 정상적으로 내보내졌나

Q1에서 노드 간 통신에 문제 없음을 확인했고, 이는 home53 -> home51의 라우팅 경로가 잘 잡혀있다는 말..일 수도 있지만 문제 패킷은 출발지가 pod ip이므로 정확하게 다시 확인해본다.

$ ip route
default via 10.10.10.1 dev enp2s0 proto dhcp src 10.10.10.53 metric 100 
10.10.10.0/24 dev enp2s0 proto kernel scope link src 10.10.10.53 metric 100 
blackhole 192.168.14.64/26 proto bird 
192.168.14.77 dev calid33c80c10e7 scope link 
192.168.75.0/26 via 10.10.10.52 dev tunl0 proto bird onlink 
192.168.239.0/26 via 10.10.10.51 dev tunl0 proto bird onlink

192.168.14.77 IP는 문제 pod의 IP이며, calico가 잡아놓은 IP 대역에 맞게 다른 노드로의 경로도 잘 잡혀있다.
192.168.14.61/26 대역에 대한 blackhole은 아직 pod가 할당되지 않은 IP에 대해 엄한 라우팅이 수행되는걸 방지하기 위해 calico가 추가해놓은 규칙이다.
192.168.14.77 IP가 속한 대역이지만 192.168.14.77/32 IP에 대한 규칙이 서브넷마스크 길이가 더 길기 때문에(26 vs 32), LPM(Longest Prefix Match) 규칙에 의해 192.168.14.77 주소로 가는 패킷은 블랙홀에 빠지지 않는다.


라우팅 테이블 내용은 이상 없어 보이지만, pod에서 출발하는 라우팅이 정상적으로 수행되는지는 확인되지 않았다.


home53에서 나가는 패킷도 tcpdump로 캡쳐해보기로 한다.

$ sudo tcpdump -i any port 3306

# (결과 생략)

출발지IP가 pod ip인 패킷만 반복해서 캡쳐되었다.
즉, 출발하는 패킷만 캡쳐되고 다시 들어오는 패킷은 캡쳐되지 않았으며, NAT(SNAT)도 일어나지 않았다.


home53의 입장에서는 source ip = pod ip via caliXXXX 인터페이스, destination ip = 10.10.10.51이므로 패킷 포워딩이 일어나야 하며, Netfilter가 PREROUTING -> FORWARD -> POSTROUTING 체인을 실행했을 것이다.


pod ip는 home53 내부의 사설IP이므로, home51로 패킷이 발사되기 전에 POSTROUTING 체인에서 NAT가 동작했어야 했다.
따라서 앞 단계인 FORWARD 체인에서 필터 당했다고 판단했다.


iptables 명령으로 filter 테이블의 FORWARD 체인을 확인해보았지만, calico와 kubernetes 관련 체인뿐이었다.

$ sudo iptables -t filter -L FORWARD
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
cali-FORWARD  all  --  0.0.0.0/0            0.0.0.0/0            /* cali:wUHhoiAYhphO9Mso */
KUBE-PROXY-FIREWALL  all  --  0.0.0.0/0            0.0.0.0/0            ctstate NEW /* kubernetes load balancer firewall */
KUBE-FORWARD  all  --  0.0.0.0/0            0.0.0.0/0            /* kubernetes forwarding rules */
KUBE-SERVICES  all  --  0.0.0.0/0            0.0.0.0/0            ctstate NEW /* kubernetes service portals */
KUBE-EXTERNAL-SERVICES  all  --  0.0.0.0/0            0.0.0.0/0            ctstate NEW /* kubernetes externally-visible service portals */
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            /* cali:S93hcgKJrXEqnTfs */ /* Policy explicitly accepted packet. */ mark match 0x10000/0x10000
MARK       all  --  0.0.0.0/0            0.0.0.0/0            /* cali:mp77cMpurHhyjLrM */ MARK or 0x10000

위 명령의 결과로 출력된 체인은 빙산의 일각이지, 타고 들어가면 중첩된 커스텀 체인들이 무지 많다.
이 체인들을 다 뒤져봐야 할지 고민하던 중 firewalld가 동작중인게 떠올랐다.


내 기억에 firewalld에 pod ip 대역도 calico의 가상 네트워크 인터페이스도 허용한 적이 없다.
눈으로 확인해보자.

$ sudo firewall-cmd --get-active-zones
public
  interfaces: enp2s0
  sources: 10.10.10.24/24

역시 허용되지 않았다.
범인을 찾았다.

해결

Q1. firewalld를 계속 유지할 것인가 Stop할 것인가

속편하게 Stop + Disable 해버리면 당장의 통신에 문제는 없겠다 싶었다.
그래도 방화벽이 꺼진 OS가 얼마나 취약할지는 모르지만, 모르기에 더더욱 방화벽은 켜두는게 좋다고 생각했다.


firewalld는 계속 실행한다.

firewalld가 패킷을 필터하지 않도록 조치

pod ip 대역은 calico가 동적으로 결정하고 있으니, 모든 Rocky 노드마다 source ip 대역을 firewalld에 등록하는 것은 번거롭다.
대신 calico가 추가하는 모든 네트워크 인터페이스를 firewalld에 등록함으로써 firewalld의 패킷 필터를 피한다.

$ sudo firewall-cmd --permanent --zone=trusted --add-interface=cali+
$ sudo firewall-cmd --reload

이후 home53의 pod에서 home51의 DB에 정상적으로 연결되는 것을 확인했고,
워커노드 세팅 자동화를 위해 작성했던 Ansible 플레이북에도 커밋하고 마무리했다.


[🔗 링크 - Ansible 플레이북 GitHub]

'Kubernetes' 카테고리의 다른 글

[트러블슈팅] 다른 노드에 POD 재생성  (0) 2025.11.23