SRE / DevOps 시작하기 (4) - 네트워킹과 관측성
SRE / DevOps 시작하기 (4) - 네트워킹과 관측성
VPN & Networking
일반적으로 하나의 인프라 네트워크를 구축 할 때에 커스텀으로 포트 터널링을 통해 Network Mesh를 구축 할 일은 거의 존재하지 않는다. 대부분의 초기 서비스들은 동일한 인프라 제공자 망 위에서 구현되고, 배포되며, 관리되기 때문에 VCN을 동일한 서브넷 하위로 두는 것 만으로도 서비스 간 내부 통신은 충분히 해결된다.
그러나 인프라 제공자가 둘 이상으로 나뉘는 순간, 이야기가 달라진다. AWS의 Lightsail 인스턴스와 OCI의 ARM 인스턴스가 서로의 내부 서비스에 접근해야 할 때, 퍼블릭 IP를 열어두고 포트 화이트리스트로 관리하는 방법도 있다. 실제로 초기에는 그렇게 운영하는 경우가 많다.
이와는 별개로 Zero Trust 관점에서 시스템을 접근 할 때, 퍼블릭 IP 기반의 화이트리스트 자체가 신뢰 모델로서 불충분하다. IP는 스푸핑될 수 있고, 클라우드 인스턴스의 IP는 재할당될 수 있으며, “이 IP에서 오는 트래픽은 안전하다”는 가정 자체가 Zero Trust가 거부하는 전제다. VPN은 네트워크 레벨에서 암호화된 터널을 통해 피어 인증을 강제하기 때문에, 단순한 편의 도구가 아니라 Zero Trust 네트워크의 기본 빌딩 블록이 된다.
하지만 서비스가 늘어나고, 노드 간 통신 경로가 복잡해질수록 이 방식은 보안적으로도, 운영적으로도 한계에 부딪힌다. 퍼블릭 IP에 포트를 열 때마다 공격 표면이 넓어지고, 방화벽 규칙은 점점 스파게티가 된다.
사내 고정아이피들과, 컨테이너 각각의 고정아이피 (설령 reserved public IP를 사용한다 하더라도, 여러번 재할당 되는 과정에서의 휴먼에러), 의존중인 외부 서비스들의 고정아이피, 수십개의 public IP와, 수십개의 하위 route 별로 분기되는 서비스 네트워크를 방화벽 규칙만으로 관리하는 건, 결국 사람이 실수하느냐 마느냐의 문제로 귀결된다. 그리고 인프라에서 “사람이 실수하지 않으면 된다”는 전제는 장애 보고서의 단골 첫 줄이다.
VPN mesh는 이 문제를 근본적으로 해결한다. 모든 노드를 하나의 사설 네트워크(10.0.0.0/24)로 묶으면, 서비스 간 통신은 내부 IP로 이루어지고 퍼블릭 IP에는 VPN 포트(51820) 하나만 열면 된다. 노드가 어느 클라우드에 있든, 물리적으로 어디에 있든 동일한 서브넷에 있는 것처럼 동작한다.
멀티클라우드 VPN을 구축할 때 선택지는 크게 세 갈래로 나뉜다.
OpenVPN
OpenVPN은 가장 오래된 선택지다. SSL/TLS 기반으로 동작하며 거의 모든 환경에서 지원된다. 하지만 유저스페이스에서 동작하기 때문에 성능 오버헤드가 크고, 설정이 장황하다. 인증서 기반 PKI를 직접 관리해야 하는 점도 소규모 인프라에서는 부담이다. 레거시 환경이나 TCP 폴백이 반드시 필요한 상황이 아니라면 2026년 시점에서 신규 구축에 추천하기는 어렵다.
WireGuard
WireGuard는 Linux 커널 모듈로 동작하는 현대적인 VPN이다. 코드베이스가 약 4,000줄로 극도로 작고, Noise Protocol Framework 기반의 고정된 암호화 스위트(ChaCha20, Curve25519, BLAKE2s)를 사용한다. 설정이 단순하고 성능이 빠르다. 다만 WireGuard 자체는 순수한 터널이기 때문에 피어 관리, 키 배포, 웹 UI 같은 운영 레이어는 별도 도구가 필요하다.
WireGuard Easy와 WireGuard Portal는 이 운영 레이어를 제공하는 대표적인 오픈소스 도구다. WireGuard Easy는 이름 그대로 단순함에 초점을 맞춘다. Docker 컨테이너 하나로 웹 UI + WireGuard 서버가 올라가고, 피어 추가/삭제를 브라우저에서 할 수 있다. 소규모 팀이나 개인 VPN 용도로는 충분하지만, 멀티사이트 구성이나 피어별 세밀한 라우팅 제어에는 한계가 있다.
WireGuard Portal(wg-portal v2)은 좀 더 운영 지향적이다. LDAP/OIDC 연동, 피어별 설정 관리, REST API, Prometheus 메트릭 엔드포인트(:8787)를 제공한다. Hub-and-spoke든 full mesh든 토폴로지를 유연하게 구성할 수 있고, 웹 UI도 피어 상태 모니터링까지 지원한다. 다만 설정 복잡도는 Easy보다 확실히 높기 때문에, 노드가 3대 이상이거나 기존 인증 체계(Keycloak 등)와 연동이 필요한 경우에 선택하는 게 맞다.
Headscale
Headscale은 Tailscale의 오픈소스 컨트롤 플레인 구현체다. WireGuard를 전송 레이어로 쓰되, 피어 디스커버리와 NAT traversal, ACL 관리를 자동화해준다. Tailscale의 SaaS에 의존하지 않고 셀프호스팅할 수 있다는 게 핵심이다. 노드 수가 많아지거나 NAT 뒤의 피어가 많을 때 유리하지만, 3노드 규모에서는 WireGuard 직접 구성 대비 얻는 이점이 크지 않다.
libp2p
libp2p는 VPN이 아니다. IPFS에서 파생된 P2P 네트워킹 라이브러리로, 피어 디스커버리와 NAT traversal, 멀티플렉싱을 애플리케이션 레벨에서 제공한다. 서비스 메시나 분산 시스템의 통신 레이어로는 의미가 있지만, 인프라 레벨의 VPN 대체제로 쓰기에는 목적 자체가 다르다. 다만 “노드 간 통신을 어떻게 할 것인가”라는 큰 질문 안에서 알아둘 가치는 있다.
Observability
관측성(Observability)은 모니터링과 다르다. 모니터링은 “CPU가 90%를 넘으면 알림을 보내라”처럼 미리 정의한 조건을 감시하는 것이고, 관측성은 “왜 이 요청이 느린가”처럼 사전에 예측하지 못한 질문에 시스템 스스로가 답할 수 있는 능력이다. 이 구분이 중요한 이유는, 모니터링만으로는 장애의 증상은 잡을 수 있어도 원인을 추적하기 어렵기 때문이다.
ELK, PLG
우선 ELK와 PLG는 하나의 기술 스택을 말하는 것이 아니라는 것과, 이 과정에서 왜 인프라 네트워크를 사전에 구축해야 하는지를 먼저 짚어야 한다. ELK와 PLG는 각각 독립적인 도구들의 조합이다. 이 도구들이 서로 다른 노드에 분산되어 동작하기 때문에, 노드 간 안전한 내부 통신 경로가 전제되어야 한다. Promtail이 원격 노드에서 Loki로 로그를 전송하고, Prometheus가 각 노드의 /metrics를 스크래핑하려면, 그 경로가 퍼블릭 IP 위에 놓여 있어서는 안 된다. 관측성 스택이 VPN 네트워크 위에서 동작해야 하는 이유가 여기에 있다.
관측성 스택이 왜 필요한지는 로그 관리의 역사를 잠깐 돌아보면 명확해진다.
과거 불과 몇십년 전만 하더라도, /var/logs/**/*.log 파일 수십 기가바이트를 직접 뒤져가며 장애 원인을 추적하는 것이 일상이었다. grep, tail -f, awk로 로그를 필터링하고, 터미널을 여러 개 띄워놓고 노드마다 SSH로 접속해서 같은 작업을 반복했다. 서버가 한두 대일 때는 이것으로 충분했다. 하지만 노드가 3대로 늘어나는 순간, 같은 시간대의 로그를 세 노드에서 각각 뒤지고 머릿속에서 타임라인을 합쳐야 하는 작업이 된다.
여기서 조금 발전하여, pm2와 같은 프로세스 매니저가 로그 관리를 일부 해결해주었다. pm2는 Node.js 프로세스의 stdout/stderr를 자동으로 파일에 기록하고, pm2 logs로 실시간 스트리밍하고, pm2-logrotate로 로테이션까지 해준다. 단일 노드에서 여러 프로세스를 관리할 때는 확실히 편하다. 하지만 pm2의 로그는 결국 그 노드의 로컬 파일시스템에 머문다.
멀티노드 환경에서는 여전히 “어떤 노드에 SSH 접속해서 pm2 logs를 쳐야 하지?“라는 문제가 남는다. 그리고 pm2는 Node.js 생태계의 도구다. .NET 백엔드, C++ CDN 서비스, Java 기반 Jenkins까지 포함한 이기종 스택에서는 통합 로그 관리의 답이 되지 못한다.
이 지점에서 중앙집중식 로그 수집의 필요성이 대두된다. 모든 노드의 로그를 하나의 장소로 모으고, 하나의 인터페이스에서 검색하고, 메트릭과 함께 상관관계를 분석할 수 있는 스택이 필요해진다.
ELK (Elasticsearch + Logstash + Kibana)는 이 문제를 풀어온 가장 오래된 조합이다. Logstash가 각 노드에서 로그를 수집·파싱하고, Elasticsearch에 저장하며, Kibana에서 시각화한다. Elasticsearch의 풀텍스트 검색은 강력하고, 비정형 로그에서 패턴을 찾아내는 데 탁월하다. 하지만 Elasticsearch가 JVM 기반으로 메모리를 많이 먹는다.
단일 노드에서 최소 2~4GB는 잡아야 안정적으로 돌아가고, 인덱스가 쌓이면 디스크와 메모리 관리가 운영 부담이 된다. 대규모 로그 파이프라인에서는 여전히 강력한 선택이지만, 소규모 인프라에서는 ELK 스택 자체를 모니터링해야 하는 아이러니가 생긴다.
PLG (Prometheus + Loki + Grafana)는 경량화에 초점을 맞춘 조합이다. Prometheus는 pull 기반 메트릭 수집기로, 각 서비스의 /metrics 엔드포인트를 주기적으로 스크래핑한다. Loki는 Grafana Labs가 만든 로그 수집기인데, Elasticsearch와 결정적으로 다른 점은 로그 본문을 인덱싱하지 않는다는 것이다. 라벨(서비스명, 노드, 로그 레벨)만 인덱싱하고 본문은 압축 저장하기 때문에 리소스 사용량이 극적으로 낮다.
풀텍스트 검색 능력은 Elasticsearch에 미치지 못하지만, 대부분의 운영 상황에서는 “이 시간대에 이 서비스에서 에러 레벨 로그를 보여줘”가 전부이므로 라벨 기반 필터링으로 충분하다. Grafana가 Prometheus 메트릭과 Loki 로그를 하나의 대시보드에서 통합해주기 때문에, “이 시점에 메트릭이 튀었는데 같은 시간대 로그에 뭐가 찍혔지?“라는 상관관계 분석이 쉽게 가능해진다.
그래서?
사실 ELK/PLG 모두가 개인 프로젝트에서는 오버엔지니어링이다. btop, npm/pm2, nginx로 그냥 저냥 nodejs(nextjs/nestjs)로 만들어서 배포해도 문제가 생기지 않는다. 솔직히 말하면 요새는 vercel이나 aws amplify에 github를 연동해서 딸깍 배포하고 신경쓰지 않는 경우가 더 많은 것 같다.
그럼에도 불구하고, 대규모 어플리케이션 환경에서, 대부분의 업무환경에서는 이 스택이 선택이 아니라 전제가 된다. 노드가 3대를 넘어가고, 서비스가 이기종 스택으로 구성되고, 장애가 새벽 3시에 터지는 순간, “어느 노드에 접속해서 무슨 명령어를 쳐야 하지?“라는 질문 자체가 장애 복구 시간을 결정한다. 관측성 스택은 이 질문을 “Grafana 열어서 이 시간대 필터 걸면 된다”로 바꿔주는 도구다.
그리고 이 관측성이 VPN 네트워크 위에서 돌아가야 한다는 점은 다시 강조할 가치가 있다. Prometheus의 스크래핑 트래픽, Promtail의 로그 전송, Grafana의 데이터소스 쿼리 — 이 모든 경로가 퍼블릭 IP 위에 올라간 순간, 메트릭과 로그 자체가 공격 벡터가 된다.
“관측성을 구축했더니 공격 표면이 넓어졌다”는 건 아이러니를 넘어 인프라 설계의 실패다.정리하자면, 네트워킹과 관측성은 별개의 주제처럼 보이지만 실제로는 하나의 레이어다. VPN mesh는 노드들을 하나의 신뢰 경계 안으로 묶는 작업이고, 관측성 스택은 그 경계 안에서 무슨 일이 벌어지는지 읽어내는 작업이다.
전자가 없으면 후자는 안전하게 동작할 수 없고, 후자가 없으면 전자가 제대로 동작하는지조차 확인할 수 없다.다음 글에서는 이 위에 얹히는 Secret 관리와 CI/CD 파이프라인을 다룬다. Vault, Jenkins, 그리고 이 둘이 어떻게 서로를 신뢰하게 만드는가에 대한 이야기다.
Comments ()