공공기관에서 MLOps 기반 AI 플랫폼을 구축하는 일은 단순히 소프트웨어를 설치하는 데 그치지 않습니다. 내부망과 외부망이 철저히 분리된 환경에서도 안전하고 효율적으로 운영할 수 있는 네트워크 구조와 보안 정책까지 함께 고려해야 합니다. 특히 폐쇄망(air-gapped) 환경에서는 아무리 AI 기술이 준비돼 있더라도 외부에서 서비스가 연결되지 않으면 운영에 큰 제약이 발생할 수밖에 없습니다.
이번 포스팅에서는 공공기관 고객이 제시한 ‘외부 웹 서비스 접속과 내부망 보호’라는 구체적인 요구사항을 충족시키기 위한 네트워크 설계 방식과 WireGuard와 HAProxy를 통해 공공기관의 보안 정책을 만족시키면서도 외부에서 안전하게 접속할 수 있는 구조를 완성한 과정을 공유합니다. 이를 통해 마키나락스의 MLOps 플랫폼 Runway가 어떻게 공공기관의 까다로운 요구사항을 충족할 수 있었는지 보여드리고자 합니다.
고객 요구사항: 외부 웹 서비스 접속 + 내부망 보호
“AI 플랫폼 화면을 웹으로 확인할 수 있어야 합니다. 웹 서버는 DMZ에 노출되어 있어야 하고요. 하지만 내부망은 절대로 외부와 직접 연결되면 안 됩니다.”
처음 고객이 제시한 요구사항은 명확했습니다. 이 요구를 만족시키기 위해서는 여러 가지 기술적 조건을 충족해야 했습니다.
- 모든 서비스는 내부망 K8s 클러스터에서 구동해야 함
- 외부망과는 분리된 전용선 또는 VPN만 허용
- 망 연계 시스템을 통해 특정 네트워크만 연결 가능하게 설정
- 프론트엔드는 k8s내부 서비스가 아닌 외부 DMZ에 web 서버 별도 설치

외부 웹 서비스 접속 + 내부망 보호 구성도
이러한 구조는 보안 측면에서는 이상적이지만, 실제로 외부에서 웹 서비스에 접근하려고 하면 곧바로 장애물에 부딪힙니다. 내부망을 안전하게 보호하면서도 외부에서 웹 서비스를 문제없이 연결하는 것이 이번 프로젝트의 핵심 과제였습니다.
Air-gapped 환경에서 K8s를 구축한다는 것의 현실
외부 인터넷과 완전히 분리된 Air-gapped 환경에서 Kubernetes 클러스터를 구축하는 작업은 일반적인 클라우드 기반 설치와는 차원이 다릅니다. 이 환경에서는 ‘클릭 한 번으로 설치’는 없습니다. 오히려 모든 패키지와 이미지, 의존성 하나하나를 직접 수집하고 이식해야 하는 수작업의 연속이었습니다. 구체적으로 현장에서 직면한 문제들은 다음과 같습니다.
- 이미지 다운로드가 불가능한 환경
- Docker Hub, PyPI, GitHub 등 모든 외부 저장소에 접근 불가
- 필요한 컨테이너 이미지를 외부에서 미리 받아와 저장소를 구성해야 했음
- 패키지와 바이너리도 일일이 오프라인 준비
- OS 패키지(
dnf
,yum
)조차 설치 불가 - 필요한 설치 파일들을 외부망에서 미리 수집 → 내부망에 USB나 외장 HDD로 옮김
- Helm 차트, Operator 등도 마찬가지
- 단순한 K8s YAML만으로 되는 게 아님
- 의존성까지 포함된 Helm 저장소 자체를 오프라인에 복제해야 함
우리가 내린 선택과 대처
이러한 제한 속에서 우리는 단순한 기술적 접근을 넘어, 내부망 환경에 안전하게 구축할 수 있는 실질적인 해법을 찾기 위해 고심했습니다. 그 결과 다음과 같은 단계로 문제를 풀어 나갔습니다.
- 외부에서 패키징된 파일을 만들어 내부로 반입
- 필요한 모든 이미지, 바이너리, Helm 차트, 패키지 등을 외부 서버에서 사전 수집
- 내부로 옮길 때는 버전 충돌 없이 작동하도록 디펜던시를 맞춰서 구성필요
- 내부망 전용 프라이빗 레지스트리 구축
- containerd와 함께 사용할 수 있도록 내부망에 프라이빗 이미지 레지스트리 구축
- 외부에서 받은 이미지들을
docker save
로 tar 압축 후 옮겨docker load
로 등록
- Air-gapped 배포 자동화 스크립트 작성
- 수동 설치의 반복을 줄이기 위해 shell 스크립트로 설치/구성 자동화(Ansible)
- Kubeadm을 사용 가능한 모든 옵션 테스트하며 최적화
문제 분석과 구조 개선 흐름
이러한 준비와 선택 덕분에 내부망 환경을 안정적으로 구축할 수 있었습니다. 고객의 명확한 요구사항을 만족시키기 위해 내부망의 보안을 유지하면서도 외부에서 웹 서비스에 안전하게 접속할 수 있도록 네트워크 구조를 단계별로 개선해 나간 과정을 소개합니다.
- frontend 분리 및 외부망 배포
- 기존에는 frontend가 K8s 내부에 존재
- 이를 외부망으로 분리 배포하여, 사용자 브라우저에서 직접 접속 가능하게 구성
- 중계서버 구축
- 외부 frontend ↔ 내부 backend 간 통신이 가능해야 했기 때문에, DMZ에 중계 서버를 구성
- 이 중계 서버에 HAProxy를 설치해 트래픽을 내부 API로 전달
- 내부-외부 안전한 통신 채널 구축
- 내부망과 외부망 간 터널링을 위해 WireGuard를 채택
- VPN보다 가볍고, 성능 좋고, 설정도 간단한 WireGuard는 폐쇄망 연동에 적합
WireGuard 설정: 간단 가이드
실제 환경에서 WireGuard를 설치하고 구성한 방법을 단계별로 보여드리겠습니다. 내부-외부 간 안전한 통신을 위해 WireGuard를 사용했는데, 아래 코드는 그 설정 과정을 담았습니다.
📦 기본 설치 (모든 서버 공통)
🔥 방화벽 및 SELinux 비활성화
SELINUX=disabled
SELINUXTYPE=targeted
EOF
🔁 커널 설정 (IP 포워딩)
sudo sysctl -p
🔑 키 생성
서버에서:
cat server_privatekey | wg pubkey > server_publickey
클라이언트에서:
cat client_privatekey | wg pubkey > client_publickey
🧭 서버 설정
🖥 클라이언트 설정
[Interface]
Address = 10.0.0.2/24
PrivateKey = <클라이언트 비공개 키>[Peer]
PublicKey = <서버 공개 키>
Endpoint = <서버 IP>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
EOF
HAProxy 구성: 외부에서 내부 API 연결
중계서버에는 WireGuard로 내부망에 터널이 연결된 상태입니다. 여기에 HAProxy를 설정해 외부 요청을 내부의 Backend(K8s Cluster API)로 연결합니다.
예시:
bind *:80
default_backend k8s_backend
backend k8s_backend
server k8s-api 10.0.0.100:8000 check
이렇게 구성함으로써 외부에서 접속한 사용자의 요청은 중계 서버를 거쳐 내부 백엔드 서비스로 안전하게 전달됩니다.
최종 결과: 고객 요구를 충족한 아키텍처
최종적으로 고객의 요구사항을 충족시키는 안전하고 효율적인 아키텍처를 구축할 수 있었습니다.
✅ 외부에서 웹 대시보드 접근 가능
✅ 내부망 자산은 외부에 직접 노출되지 않음
✅ 중계서버를 통해 접근 제어 및 로깅 가능
✅ WireGuard를 통한 안전한 터널링
✅ 고객의 보안 정책과 네트워크 제약 모두 충족
이번 프로젝트는 단순한 시스템 설치가 아닌, 고객 요구사항에 맞춘 실질적 문제 해결이 핵심이었습니다. 완전히 격리된 환경에서 보안 정책을 위반하지 않고, Runway를 온전히 구동시키며 외부에서 접근 가능하도록 설계한 복합적인 문제 해결의 연속이었습니다.
기술적으로는 K8s, HAProxy, WireGuard를 조합했지만, 가장 중요한 건 고객이 원하는 방식으로 서비스가 동작하고 있다는 점입니다. 이 방안은 비슷한 환경의 기관이나 보안 규제가 강한 기업에서도 실용적인 대안이 될 수 있을 것입니다.
이 경험에서 얻은 가장 큰 교훈
‘진짜 기술력은 오프라인에서 검증된다.’
이 프로젝트를 통해 가장 뼈저리게 느낀 점입니다. Public 클라우드 환경에서는 수많은 컴포넌트가 알아서 동작합니다. Helm 차트 하나만 설치해도 필요한 이미지와 패키지가 자동으로 내려받아지고, 인증서도 자동 발급되며, 기본 구성은 대부분 손쉽게 끝나버립니다. 하지만 폐쇄망, 특히 Air-gapped 환경에서는 모든 게 정반대였습니다.
helm install
은 단순 명령이 아닙니다. 의존성 차트와 관련된 CRD, 이미지, 스크립트 등 수십 개의 외부 파일을 사전에 모두 파악하고 직접 준비해야 합니다.- Runway는 Prometheus, Grafana, MLflow, ArgoCD 같은 오픈소스 툴조차 수십 개의 컨테이너 이미지와 구성 요소로 이루어져 있습니다.
- 인증서, DNS, NTP, 레지스트리, 패키지 관리자 등 운영체제 레벨부터 애플리케이션까지 모든 계층을 직접 구성해야 했습니다.
폐쇄망 환경에서 가장 힘들었던 점은 문제가 터졌을 때 검색해서 해결할 수 없다는 것입니다. 인터넷이 안 되는 환경에선, 구글링 대신 직감과 예습이 전부입니다. 모든 걸 미리 알아야 하는 환경이라 사전에 시뮬레이션을 몇 번이고 반복하며 장애 가능성을 검토했습니다. 또한, 각종 에러 로그를 외부 환경에서 재현하고 분석하는 사전 디버깅
까지 필요했습니다.
이 프로젝트는 단순히 ‘구축을 잘했다’는 수준이 아니라, 기술의 구조를 실제로 이해하지 않으면 결코 성공할 수 없는 환경이었습니다. 모든 컴포넌트를 직접 손으로 하나하나 맞춰 넣으며, 기술 전체를 시스템 단위로 바라보고 작동 원리를 체득하는 학습의 과정이자 우리 팀 기술력의 밀도를 한층 끌어올린 기회였습니다.