개발 및 테스트 비용 절감이 절실하다
CPU, RAM 값이 천정부지로 올라가고 있는 요즘입니다. 그래서 노트북이나 PC를 사고 싶어도 망설여지게 됩니다. 그런 것들을 겨냥해서 최근에는 많은 모델이 나왔습니다. 최근에는 맥 미니와 같은 모델에 관심이 생기는 것 같습니다. 디스플레이와 I/O 장치들은 이미 있으니까요. 필요한 것은 CPU, RAM과 같은 자원 뿐인 저에게 최적입니다.

아쉽게도 아직 배우는 신분인 저는 이런 소비도 조금 과합니다. 그렇다고 AWS를 빌려서 쓰자니 달마다 펑펑 터지는 요금과, 계정 관리... 이런 것들이 좀 무겁게 생각됩니다. 이것저것 테스트를 해보고싶은데, 적어도 8개의 코어와 16GB의 램은 필요하지 않나? 생각이 드는데, 그것들을 AWS EC2로 대여하려고 하면 또 금액적인 부분을 생각하지 않을 수 없습니다. 물론 여러개의 컴퓨터가 있어야하는 순간부터는 AWS가 편하긴 할테지만요.
그래서 이런저런 생각을 하다가 문득 책상을 보니 놀고있는 데스크탑이 있는게 아니겠습니까?


정확하게 8코어와 16GB 램인 데스크탑을 놔두고 이런 고민을 하다니... 역시 등잔 밑이 어둡습니다. 아무튼 이런 자원을 십분 활용한다면, 제가 테스트를 아무리 돌려도 전기세 정도만 나올 것이고, 그럼 비용에 대한 부분은 일정 수준까지는 고려를 하지 않아도 된다는 결론이 납니다.
데스크탑을 이용한 서버 컴퓨터 구축
개인 PC를 서버 컴퓨터로 만들기 위해서는 다음과 같은 고민이 필요할 것 같습니다.
- 외부에서 어떻게 PC로 접근할 수 있게 할 것인가?
- 내부의 WAS는 어떤 식으로 띄울 것인가?
그럼 첫 번째 고민부터 차근차근 생각해보겠습니다.
누가, 어떤 주소로, 어떻게 접근할 것인가
서버 컴퓨터를 기준으로, 외부의 컴퓨터는 해당 컴퓨터에 유선으로 연결돼있지 않은 이상 네트워크를 타고 접근하게 됩니다. 그리고 컴퓨터에 도착하면, 내부적인 로직을 수행하고 다시 나가는 작업인 응답하는 과정을 거치게 되겠죠. 이런 것에서는 다양한 고려가 필요합니다.
DDNS (Dynamic DNS)
이것은 마치 AWS의 탄력적 IP와 유사한 것 같습니다. 데스크탑이 있는 가정의 경우에는 통신사의 정책에 따라서 계속해서 바뀔수 있습니다. 이런 상황에서 고지없이 바뀔 때마다 제가 직접 IP 값을 수정하는 것은 매우 번거로울 것입니다. DDNS는 클라이언트 소프트웨어가 주기적으로 자신의 공인 IP를 확인하고 DNS 서버의 Address Record를 API를 통해 실시간으로 갱신합니다. 이렇게 하면 단순히 도메인 이름으로만 설정해놓고 자동으로 바뀐 주소를 매핑할 수 있어서 편리합니다.
포트 포워딩 (Port Forwarding)
공유기의 공인 IP를 이용해서 요청을 열어놓고, 특정 포트를 제가 지정한 곳으로 향하게 하는, 즉 포트를 포워딩해놓는 방법입니다. 여기서는 NAT(Network Address Translation), PAT(Port Address Translation) 등이 사용됩니다. NAT은 하나의 공인 IP를 여러 사설 IP로 나누어 쓰는 기술입니다. 외부에서 들어오는 패킷은 목적지 사설 IP를 알 수 없기 때문에 포트 번호를 식별자로 사용하여 패킷의 목적지 IP를 변환하는 것입니다. PAT은 TCP/UDP 헤더의 포트 정보를 보고 IP 계층의 주소를 매핑하는 기술입니다.
공인 IP를 통해서 접근한다는 것은 PC에 직접 외부 패킷을 받아들인다는 것인데, 이는 WAS에 보안 취약점이 있다면 인바운드 공격 노출 지점이 될수 있습니다.
터널링 (Tunneling)
보안 이슈를 해결할 수 있다면 포트 포워딩을 사용해도 좋지만, 역시 보안이라는 점에는 완벽이라는 것은 없는 것 같습니다. 따라서 포트 포워딩을 사용하지 않고 사용할 수 있는 방식 중에 하나가 터널링입니다.
내 PC가 외부 서비스로 먼저 TCP/TLS 연결을 맺습니다. 이를 Outbound-only Connection이라고 합니다. 상태 기반 방화벽(Stateful Firewall)은 내부에서 나간 연결에 대한 응답은 허용하므로 외부에서 들어오는 인바운드 포트를 애초에 열 필요가 없습니다.
아래의 그림은 Cloudflare 서비스를 기준으로 내부와 외부의 PC를 연결하는 과정을 그린 그림입니다.

핵심적인 부분은 맨 윗 부분으로, 터널을 개통하는 부분입니다. 여기서 인바운드 커넥션은 막고 아웃바운드 커넥션만 사용한다는 점이 중요합니다.
여기서 먼저 개념적으로 잡고 가야할 부분이 있습니다. 바로 연결(Connection Initiation)과 데이터의 흐름(Traffic)입니다.
- 네트워크 연결의 방향
- TCP나 UDP 통신을 시작할 때, 누가 먼저 연결 요청을 보냈느냐가 방향을 결정합니다. 위 상황의 경우에는 내 PC의 cloudflared가 Cloudflare 서버로 먼저 패킷을 보내는 아웃바운드 커넥션입니다.
- 데이터의 양방향성 (Full-Duplex)
- 일단 아웃바운드로 파이프(터널)가 연결되고 나면, 그 파이프 안에서는 데이터가 양방향으로 자유롭게 오갈 수 있습니다. 외부의 HTTP 요청이 내 PC로 들어오는 것은 맞지만, 기존의 아웃바운드 커넥션으로 열린 파이프를 타고 돌아오는 응답 데이터로 취급됩니다.
따라서 포트 포워딩은 인바운드 개방으로, 외부에서 들어오는 연결을 포트와 매핑해 직접 개방해놓는 것이고, 터널링은 내 PC에서 클라우드플레어에게 요청을 해서 터널을 만들어놓고 외부에서 접근할 경우에는 클라우드 플레어 서버로 먼저 간뒤에 적당한 검사 후에 내 PC에서 미리 뚫어놓은 파이프로 데이터를 밀어넣습니다. 응답의 경우도 마찬가지로 터널을 통해서 클라우드 플레어 서버로 전송하고 사용자에게 전달됩니다. 즉, 인바운드 개방이 필요없고 미리 뚫어놓은 터널에서 데이터를 주고 받기만 하면 되는 것입니다.
인바운드와 아웃바운드의 개념이 혼동될 수 있는데, OSI Layer의 계층을 기준으로 생각하면 편합니다. 내 PC의 cloudflared가 밖으로 연결 요청을 보낸 행위는 L4 계층의 방화벽 입장에서는 이 파이프라인 전체를 아웃바운드 커넥션으로 취급하고 길을 열어줍니다. 밖에서 안으로 들어오는 연결 요청 (SYN)은 전부 차단합니다. L7 애플리케이션 계층, 즉 WAS와 도커의 관점에서는 터널을 타고 왔든말든 어쨌든 외부에서 자신에게 도달한 요청은 모두 인바운드 요청이고, 응답의 행위, 즉 나가는 패킷은 아웃바운드입니다.
내부의 실행 환경은 어떻게 할 것인가
네이티브 실행, Docker, WSL2 직접 구동 등의 방식이 있습니다. 지금 개발 환경과 서버 컴퓨터는 분리가 돼있는데, 이를 고려한다면 도커를 이용하는게 더 편할 것 같습니다. WSL2를 직접 세팅하는 방법은 리눅스의 직접적인 조작이 많이 필요할 경우에 리팩토링으로 고려해보겠습니다. 네이티브 실행은 좀 망설여지는게, Window에서 직접적으로 띄우면 튜닝과 같은 과정이 Mac과 Linux와 좀 많이 다를 것 같아 불편할것 같습니다. 따라서 Docker로 실행 환경을 조성하겠습니다.
서버 환경 구축
이제 WAS와 Tunneling에 필요한 Cloudflared, DB 등을 띄워놓기 위해서 데스크탑의 실행 환경을 조성하겠습니다.
Docker를 설치해야할텐데요, 이는 간단하므로 넘어가겠습니다.
Docker: Accelerated Container Application Development
Docker is a platform designed to help developers build, share, and run container applications. We handle the tedious setup, so you can focus on the code.
www.docker.com
Docker Hub에 제가 구현한 WAS는 이미 올려둔 상태입니다. 이제 클라우드 플레어를 이용해서 터널링을 하는 작업을 진행해보겠습니다.
Cloudflare Dashboard | Manage Your Account
dash.cloudflare.com
클라우드 플레어에 가입합니다. 50명 이하까지는 무료 플랜으로 커버가 가능합니다. 이후에 Zero Trust를 선택하고 터널을 생성합니다. Connectors에 들어가고 Add a tunnel을 클릭하면 됩니다.



Cloudflared를 선택하고 이후에 자신의 PC 환경에 맞는 cloudflared를 설치합니다. 저는 Docker로 띄울 것이기 때문에 도커 명령어가 가이드로 나왔습니다. 도커는 참 편리한 것 같습니다. 다만 Docker VM으로 차지하는 메모리량이 2GB 정도로 되는 것으로 알고 있기 때문에 메모리가 부족하면 로컬로 돌려야합니다.
이제 도커로 필요한 이미지들을 띄우겠습니다. 다음은 docker-compose.yml 의 내용입니다.
services:
# 1. DB
mysql:
image: mysql:8.0
# 2. Message broker
redis:
image: redis:latest
# 3. 메인 서버
main-was:
# 4. 알림 서버
noti-was:
# 5. 로드 밸런서: Nginx
api-gateway:
image: nginx:latest
# 6. 외부 접속: Cloudflare Tunnel
tunnel:
image: cloudflare/cloudflared:latest
기존 WAS가 분산 아키텍처를 채택하고 있었어서 도커에도 3개의 WAS 서버를 띄웠습니다. 사용자가 많아지는 상황을 연출할 것은 아니기 때문에 로컬 호스트 환경에서 서버를 많이 띄워도 상관은 없습니다. 또한 클라우드 플레어도 네트워크 사용량 제약을 제외하면 크게 제약이 없기 때문에 이 정도 세팅으로도 서버 구축에는 충분합니다.
worker_processes auto;
events {
worker_connections 2048;
}
http {
# 대상 그룹 1: TG-Main (메인 서버 1대)
upstream tg_main {
server main-was:8080;
}
# 대상 그룹 2: TG-Notification (알림 서버 2대)
upstream tg_notification {
server noti-was:8080;
}
server {
listen 80;
location /api/noti/subscribe {
proxy_pass http://tg_notification;
proxy_http_version 1.1;
proxy_set_header Connection '';
chunked_transfer_encoding on;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
proxy_pass http://tg_main;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
기존에 AWS의 ALB가 수행하던 로드 밸런싱 처리를 Nginx애 위임해줍니다. 이렇게 설정하고 도커 컴포즈를 실행해보겠습니다.


이제 Tunnel이 켜졌습니다. 이제 여기서 application을 연결해줘야 합니다. tunnel을 통해서 사용자가 접근하면 연결된 어플리케이션이 있어야하니까요. 그런데 도메인을 준비를 안했습니다. 도메인을 먼저 등록했어야 했는데... 그냥 편한대로 사서 진행해보겠습니다.


저는 가비아에서 구매했구요, 클라우드 플레어에서 제공하는 도메인을 네임서버 설정에다가 넣어주면 됩니다.

이렇게 설정해놓고 터널에 도메인을 연결하면 끝입니다!

이제 외부 컴퓨터에서 집에 있는 데스크탑으로 요청 송수신이 가능하게 됐습니다. 프로메테우스 모니터링 환경 세팅하는 것은 생각보다 쉬우니 넘어가도록 하겠습니다!
공인 IP 개방이 더 편할 것 같기는 한데...
공인 IP 포트 포워딩으로 개방하면 물론 과정이 더 줄어들긴 했겠지만, 이렇게 보안 설정을 해놓고 보안 사고가 발생하는 것과, 그냥 완전 개방 해놓고 보안 사고가 생기는 것은 천지 차이일 것입니다. 그리고 별다른 인증서 없이도 https 요청이 가능하니 이건 덤으로 더 좋은 것 같습니다. 해보고 나니 여러모로 설정하는게 이점이 많았고, 터널도 토큰값과 함께 도커로만 띄우면 되니까 다른 컴퓨터에서 사용하는 재사용성도 뛰어날 것 같습니다!