목차
[k8s] GKE WireGuard 네트워크 장애: 디버깅 및 해결 사례 연구
안녕하세요, 13년차 서버실 지킴이입니다. 오늘은 제가 최근 GKE (Google Kubernetes Engine) 환경에서 WireGuard를 쓰다가 겪었던 네트워크 장애 경험과, 그걸 해결하기 위해 삽질했던 과정을 솔직하게 공유해볼게요. 😅 아마 인프라 엔지니어 분들이라면 누구나 한 번쯤은 복잡한 네트워크 문제로 밤샘 디버깅을 해본 경험이 있으실 거더라고요. 특히 쿠버네티스(Kubernetes) 같은 동적인 환경에서는 예측 불가능한 변수가 많아 정말 골치 아플 때가 많습니다. 저도 이번에 제대로 당했는데, 결국 해결하고 나니 또 하나 배우는 게 있네요. 💡
이번 글에서는 GKE에서 WireGuard를 운영하면서 발생했던 특정 네트워크 통신 문제를 어떻게 발견하고, 어떤 도구들을 사용해서 원인을 분석했으며, 최종적으로 어떤 방법으로 해결했는지 자세히 이야기해 드릴게요. 혹시 비슷한 문제로 고민하고 계신 분들에게 작은 도움이 되었으면 좋겠습니다.
GKE 클러스터와 WireGuard VPN 터널이 연결된 전체 아키텍처의 개념도입니다.
GKE와 WireGuard, 왜 함께 쓰려 했을까요?
먼저, 왜 제가 GKE 클러스터에 WireGuard를 도입하려고 했는지부터 말씀드려야겠네요. 사실 GKE 자체적으로도 훌륭한 네트워크 기능들을 제공합니다. VPC Native 클러스터는 Private IP를 사용하고, Cloud VPN이나 Interconnect를 통해 온프레미스(On-Premise) 환경과도 쉽게 연결할 수 있거든요. 그럼에도 불구하고 제가 WireGuard를 선택한 이유는 크게 두 가지였습니다.
- 성능과 간결함 (Performance & Simplicity): WireGuard는 다른 VPN 솔루션들에 비해 오버헤드(Overhead)가 정말 적고, 설정도 간결하더라고요. 홈랩에서 다양한 테스트를 해봤는데 성능이 정말 만족스러웠거든요. 복잡한 IPsec 설정에 비하면 정말 '혁명' 같았습니다.
- 특정 서비스의 보안 강화 및 멀티 클러스터 네트워킹: 특정 마이크로서비스(Microservice) 간의 통신을 더욱 안전하게 암호화하고, 나아가 다른 클라우드 환경이나 온프레미스에 있는 또 다른 쿠버네티스 클러스터와 안전하게 연결하고 싶었습니다. GKE의 기본 네트워킹을 보완하는 개념으로 접근한 거죠.
쉽게 말해, GKE의 기본 네트워크 인프라는 유지하되, 그 위에 특정 트래픽에 대해서만 WireGuard 터널(Tunnel)을 만들어서 보안과 유연성을 동시에 잡으려는 시도였습니다. 🚀
GKE에 WireGuard 실전 구현 (그리고 문제의 시작)
GKE에 WireGuard를 배포하는 방법은 여러 가지가 있겠지만, 저는 각 노드(Node)에 WireGuard 인터페이스를 생성하고 설정을 유지하기 위해 DaemonSet을 활용했습니다. DaemonSet은 모든 노드에 파드(Pod)를 하나씩 배포해주니까, WireGuard를 인프라 레벨에서 관리하기에 딱 맞겠다 싶었거든요.
간단한 흐름은 이렇습니다.
- WireGuard 커널 모듈이 설치된 베이스 이미지 준비 (또는
initContainer활용). - 각 노드의 파드에서 WireGuard 인터페이스(
wg0) 생성 및 설정. - 다른 피어(Peer)들과의 연결 설정 (
PublicKey,Endpoint,AllowedIPs). - 필요한 라우팅 테이블(Routing Table) 및
iptables규칙 추가.
초기 배포는 나름 순조로웠습니다. DaemonSet으로 WireGuard 파드를 띄우고, 각 노드에 wg0 인터페이스가 잘 생성되는 것을 확인했거든요. kubectl exec -it [wireguard-pod] -- wg show 명령어로 상태를 확인해보니 피어들과의 핸드셰이크(Handshake)도 정상적으로 이루어지는 것처럼 보였어요. 🎉
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: wireguard-node
namespace: kube-system
spec:
selector:
matchLabels:
app: wireguard-node
template:
metadata:
labels:
app: wireguard-node
spec:
hostNetwork: true # 노드 네트워크 직접 사용
hostPID: true # 프로세스 네임스페이스 공유
containers:
- name: wireguard
image: <your-wireguard-image>
securityContext:
privileged: true # 특권 모드 활성화
capabilities:
add:
- NET_ADMIN
- SYS_MODULE
volumeMounts:
- name: lib-modules
mountPath: /lib/modules
readOnly: true
- name: wg-config
mountPath: /etc/wireguard
command: ["sh", "-c"]
args:
- |-
# WireGuard 커널 모듈 로드
modprobe wireguard
# WireGuard 인터페이스 및 라우팅 설정 스크립트 실행
/usr/local/bin/setup_wireguard.sh
volumes:
- name: lib-modules
hostPath:
path: /lib/modules
- name: wg-config
configMap:
name: wireguard-config
items:
- key: wg0.conf
path: wg0.conf
이렇게 설정하고, ConfigMap에 각 노드의 WireGuard 설정 파일(wg0.conf)을 넣어서 배포했습니다. 초기에는 내부 서비스 간 통신이나 외부 WireGuard 피어와의 통신도 잘 되는 듯했어요. 그런데….
WireGuard 설정을 위한 DaemonSet YAML과 wg0.conf 파일의 구성 예시입니다.
⚠️ 삽질의 시작: 네트워크 장애 발생 및 쿠버네티스 트러블슈팅
문제는 GKE 노드가 재시작되거나, 특정 네트워크 이벤트를 겪은 후에 발생했습니다. 갑자기 WireGuard 터널을 통해 통신해야 하는 파드들이 서로 연결되지 않는 현상이 나타나기 시작한 겁니다. 처음엔 '이게 뭔가?' 싶었죠. 🤯
증상
- WireGuard 터널을 사용하는 파드(Pod) 간의 통신 불능.
wg show명령으로는 터널이up상태이고 피어들도 연결된 것처럼 보임.- 하지만
ping이나curl같은 기본적인 네트워크 명령어조차 실패.
디버깅 과정
저는 다음과 같은 단계로 디버깅을 시작했습니다.
wg show확인: 가장 먼저 WireGuard 자체의 상태를 확인했습니다. 아까 말씀드렸듯이, 터널은 정상적으로 보였거든요.latest handshake시간도 최근으로 업데이트되고 있었고요.ip a,ip route확인: WireGuard 인터페이스(wg0)가 정상적으로 IP를 가지고 있는지, 그리고 WireGuard 서브넷(Subnet)으로 가는 라우팅 테이블이 제대로 설정되어 있는지 확인했습니다. 여기서 이상한 점을 발견했습니다. 특정 노드에서는 WireGuard 서브넷으로 가는 라우팅 규칙이 사라져 있거나, GKE의 자체 네트워킹과 충돌하는 듯한 규칙이 혼재되어 있었습니다.iptables -L -n -v확인: 라우팅 문제가 아니라iptables규칙 문제일 수도 있겠다 싶어서 확인해봤거든요. GKE는 자체적으로 복잡한iptables규칙들을 관리하는데, 제가 추가한 WireGuard 관련 규칙들이 제대로 적용되지 않거나, GKE의 규칙에 의해 덮어씌워지는 경우가 있었습니다. 특히FORWARD체인에서 문제가 발생할 가능성이 높다고 판단했어요.tcpdump활용: 실제 패킷(Packet)이 어디까지 도달하는지 확인하기 위해tcpdump를 사용했습니다. WireGuard 인터페이스(wg0)와 물리 인터페이스(eth0등)에서 동시에 패킷을 캡처해보니, 파드에서 WireGuard 서브넷으로 나가는 패킷은wg0으로 들어오지만, 실제 암호화되어 외부로 나가는 트래픽이 없거나, 혹은 돌아오는 응답이 중간에 사라지는 것을 확인했습니다.- GKE 노드 재시작 테스트: 가장 결정적인 단서는 GKE 노드를 재시작할 때마다 문제가 발생한다는 것이었어요. 노드가 재시작되면 WireGuard DaemonSet 파드도 다시 시작되지만, 그 과정에서 네트워크 설정의 영속성(Persistence)이 깨지는 것이 분명했거든요.
원인 분석: GKE의 자체 네트워킹과 WireGuard의 미묘한 충돌
결론적으로 원인은 GKE의 자체 네트워킹 시스템과 WireGuard의 라우팅 및 iptables 설정이 충돌하거나, 혹은 부팅 시점의 Race Condition 때문이라는 것을 알게 되었습니다. GKE는 각 노드에 파드 IP를 할당하고, 복잡한 라우팅과 iptables 규칙을 관리합니다. 여기에 WireGuard가 별도의 터널 인터페이스를 만들고 라우팅 규칙을 추가하면서, 특정 상황에서 GKE의 기존 규칙이 WireGuard 규칙을 덮어쓰거나, WireGuard 규칙이 GKE 트래픽을 예상치 못한 곳으로 보내버리는 문제가 발생했던 거였거든요.
특히 노드 재시작 시, GKE의 네트워킹이 먼저 설정을 완료하고 나서 WireGuard DaemonSet이 동작하면서, WireGuard가 추가하는 규칙들이 제대로 자리 잡지 못하는 경우가 있었습니다. 😩
✅ 해결책: 네트워크 설정의 영속성 확보
이 문제를 해결하기 위해 여러 방법을 시도해봤는데, 가장 효과적이었던 방법은 네트워크 설정의 영속성을 강화하고, WireGuard 설정 스크립트가 GKE의 네트워킹 초기화 이후에 안정적으로 실행되도록 하는 것이었어요.
1. PostUp/PostDown 스크립트 활용 및 영속성 강화
WireGuard는 wg0.conf 파일 내에 PostUp 및 PostDown 명령어를 정의할 수 있습니다. 저는 여기에 필요한 라우팅 규칙과 iptables 규칙을 명시적으로 추가하고, wg-quick up wg0 명령어를 통해 이 스크립트들이 실행되도록 했습니다. DaemonSet의 command 섹션에서 이 부분을 좀 더 견고하게 만들었거든요.
# /usr/local/bin/setup_wireguard.sh 내용 예시
#!/bin/bash
# WireGuard 커널 모듈 로드
modprobe wireguard
# GKE CNI 초기화를 위해 잠시 대기 (노드마다 다를 수 있음)
sleep 10
# /etc/wireguard/wg0.conf 파일에 PostUp/PostDown 설정 포함
# 예시: PostUp = ip route add 10.10.0.0/16 dev wg0; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
# WireGuard 인터페이스 활성화
wg-quick up wg0
# 설정이 유실되지 않도록 주기적으로 모니터링
while true; do
sleep 300
# 필요시 wg0 상태 체크 및 재설정 로직
done
또한, WireGuard DaemonSet 파드가 시작될 때마다 이 setup_wireguard.sh 스크립트가 실행되도록 하고, 스크립트 내부에서 GKE 네트워킹이 완전히 준비될 때까지 일정 시간 대기하는 로직을 추가했습니다.
2. AllowedIPs 정확한 설정
wg0.conf 파일의 각 피어(Peer)에 대한 AllowedIPs 설정을 더욱 정확하게 지정했어요. 예를 들어, 특정 피어의 WireGuard IP만 허용하는 것이 아니라, 해당 피어 뒤에 있는 서브넷 전체를 AllowedIPs에 포함시켜 라우팅이 명확하게 이루어지도록 했습니다. 이건 WireGuard가 패킷을 어디로 포워딩할지 결정하는 정말 중요한 요소거든요.
[Peer]
PublicKey = <peer-public-key>
Endpoint = <peer-endpoint>:51820
AllowedIPs = 10.10.0.0/16, 192.168.10.0/24 # 정확한 서브넷 명시
PersistentKeepalive = 25
이 두 가지 방법을 적용하고 GKE 노드를 재부팅해보니, 드디어 문제가 해결되었습니다! 🎉 재부팅 후에도 WireGuard 터널을 통한 통신이 정상적으로 이루어졌고, 파드 간 연결도 원활했습니다. 정말이지 며칠 밤낮으로 씨름했던 문제가 해결되니 얼마나 기뻤는지 모릅니다.
WireGuard 터널의 정상 작동을 확인하는 wg show 명령어 출력과 정상적인 네트워크 트래픽 그래프입니다.
마무리하며: GKE와 커스텀 네트워크 솔루션 통합 시 교훈
이번 GKE WireGuard 네트워크 장애 해결 사례를 통해 제가 얻은 교훈은 다음과 같습니다.
- 관리형 서비스와 커스텀 솔루션의 경계 이해: GKE 같은 관리형 쿠버네티스 서비스는 자체적인 네트워크 관리 로직을 가지고 있습니다. 여기에 WireGuard 같은 커스텀 네트워크 솔루션을 통합할 때는 GKE의 기본 네트워킹 동작 방식을 정확히 이해하고, 충돌이 발생하지 않도록 주의해야 합니다. 특히 라우팅 테이블과
iptables규칙은 가장 민감한 부분이거든요. - 네트워크 설정의 영속성 확보: 쿠버네티스 노드는 언제든 재시작될 수 있습니다. 이때 수동으로 설정했던 네트워크 규칙들이 유실되지 않도록 DaemonSet의
command나initContainer를 통해 영속적인 스크립트 실행 환경을 구축하는 것이 정말 중요합니다. - 꼼꼼한 네트워크 디버깅 도구 활용:
ip route,iptables,tcpdump,wg show등 기본적인 네트워크 디버깅 도구들은 아무리 강조해도 지나치지 않습니다. 눈에 보이는 문제가 아니라, 실제 패킷의 흐름을 추적하는 것이 문제 해결의 핵심이거든요. - 문서화 및 철저한 테스트: 복잡한 네트워크 설정을 적용할 때는 반드시 상세한 문서화를 해두고, 노드 재시작 등 다양한 시나리오에서 충분한 테스트를 거쳐야 합니다. 저도 이번에 테스트 시나리오를 좀 더 촘촘히 짰어야 했는데, 초기 검증이 미흡했던 점이 아쉬웠어요.
GKE 운영 환경에서 커스텀 네트워크 솔루션을 도입하는 것은 분명 도전적인 일이지만, 잘만 활용하면 서비스의 유연성과 보안을 크게 향상시킬 수 있습니다. 이번 삽질 경험이 여러분의 GKE 운영에 작은 도움이 되기를 바라며, 다음번에는 또 다른 홈랩 삽질기를 들고 찾아오겠습니다! 😊
GKE와 WireGuard를 통합할 때 고려해야 할 핵심 사항들을 요약한 체크리스트 인포그래픽입니다.
'IT > k8s' 카테고리의 다른 글
| [k8s] GKE WireGuard 보안 취약점 분석: 매니지드 쿠버네티스 보안 강화 전략 (0) | 2026.05.31 |
|---|---|
| [k8s] K3s/MicroK8s로 구축하는 경량 엣지 쿠버네티스: 실제 운영 사례 분석 (0) | 2026.05.31 |
| [k8s] Rancher vs OpenShift: 엔터프라이즈 쿠버네티스 플랫폼 비용 비교 분석 (0) | 2026.05.28 |
| [Kubernetes] Cilium CNI 성능 벤치마크: Calico, Flannel과 직접 비교 (0) | 2026.05.27 |
| [k8s] 쿠버네티스 Secret 관리 베스트 프랙티스: 민감 데이터 보안 강화 전략 (0) | 2026.05.26 |
| [k8s] K3s와 AWX 통합 사례 연구: 경량 Kubernetes 자동화 워크플로우 구축 (0) | 2026.05.24 |