logo

Blog

홈서버 구축하기

#home server#raspberry pi#aws#dns#port forwarding
ryxxn profileryxxn
2025.02.05
thumbnail

지난 학기에 보안네트워크프로그래밍이라는 강의를 들었다.
멀티스레드 서버에서 암호화 통신을 구현하기 위해 라즈베리파이를 나누어줬다.
심지어 그냥 가져도 된다고 하셨다.

그래서 바로 든 생각이 "홈서버로 써야겠다" 였다.


1. 초기 세팅

우선 내 방에 랜선을 하나 더 끌어와 라즈베리파이와 연결했다.

그리고 ssh로 원격 접속 후 기본 세팅을 해줬다.

홈서버 구축 후 가장 먼저 하려고 했던 일은
AWS에 있는 wepp store백엔드 서버DB를 이전하는 것이었다.

우선 AWS RDS에서 DB 파일을 가져와야 했다.

그래서 맥북에서 디비 백업 덤프 파일을 생성했다.

bash
pg_dump -h <AWS_RDS_호스트> -U <사용자명> -d <DB명> -p <포트번호> -F c -b -v -f ~/backup.dump

디비가 제대로 백업됐는지 확인 후 scp 명령어를 이용해서 라즈베리파이로 디비 백업 파일을 옮겼다.

bash
scp ./backup.sql <서버이름>@192.168.x.x:<파일경로>

2. DDNS 등록

이제 외부에서 작업할 일을 대비해서 외부에서 홈서버로 접근하기 위해 SSH 포트를 뚫을 차례다.

현재 공유기 IP를 동적 IP로 설정해뒀기 때문에 DDNS를 등록했다.

iptime에서 제공하는 DDNS를 등록하고
weppstore-api.<DDNS 주소>를 API 주소로 사용하려 했다.
그래서 https를 적용하기 위한 SSL 인증서를 설치했다.

나는 웹서버로 nginx를 사용하고 있었기 때문에 certbotnginx를 설치했다.

bash
sudo apt install certbot python3-certbot-nginx -y

이후 SSL 인증서 발급을 시도했다.

bash
sudo certbot --nginx -d <DDNS 주소>

그런데,
iptime에서 제공하는 DDNSiptime.org 도메인은 사람들이 너무 많이 SSL 인증서를 발급하여
CAA record 오류로 더이상 인증서가 발급되지 않았다.

그래서 DuckDNS라는 DDNS 서비스에서 DDNS를 발급받고 시도했지만,
DuckDNS는 개인이 운영하는 서비스였기에 안정성이 낮았다.
그래서 오류가 잦았다. 하필 내가 발급받았을 때 서버가 이상했다.

일단 내가 원하는 DDNS 사용 요건은 공유기의 동적 IP와 DDNS와 동기화돼야 하는 것이었다.

그래서 cloudflare를 사용해서 DDNS를 사용하기로 했다.

우선 내 도메인인 ryxxn.com을 현재 구매한 공급 업체에서
네임서버를 cloudflare로 바꿨다.

이후 공유기의 동적 IP와 동기화 셸 파일을 작성했다.

bash
#!/bin/bash
# Cloudflare API 정보CLOUDFLARE_API_TOKEN="-------cloudflare API 토큰-------"ZONE_ID="-------cloudflare zone id-------"RECORD_ID="-------cloudflare record id-------"DOMAIN="-------내 도메인-------"LOG_FILE="./cloudflare-ddns.log"SAVED_IP_FILE="./current_ip.txt"
# 다중 IP 조회 서비스 설정 (순차적으로 시도)get_public_ip() {    for SERVICE in \        "http://ipv4.icanhazip.com" \        "http://checkip.amazonaws.com"; do        IP=$(curl -s "$SERVICE" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+')        if [[ -n "$IP" ]]; then            echo "$IP"            return        fi    done    echo "ERROR" # 모든 서비스 실패 시}
# 현재 공인 IP 확인CURRENT_IP=$(get_public_ip)
# 오류 발생 시 로그 남기고 종료if [[ "$CURRENT_IP" == "ERROR" ]]; then    TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")    echo "[$TIMESTAMP] 공인 IP 확인 실패" >> "$LOG_FILE"    exit 1fi
# 이전에 저장된 IP 확인if [ -f "$SAVED_IP_FILE" ]; then    OLD_IP=$(cat "$SAVED_IP_FILE")else    OLD_IP=""fi
# IP가 변경된 경우에만 Cloudflare 업데이트 실행if [ "$CURRENT_IP" != "$OLD_IP" ]; then    RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \        -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \        -H "Content-Type: application/json" \        --data "{\"type\":\"A\",\"name\":\"$DOMAIN\",\"content\":\"$CURRENT_IP\",\"ttl\":1,\"proxied\":false}\"")
    echo "$CURRENT_IP" > "$SAVED_IP_FILE"    TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")    echo "[$TIMESTAMP] IP 변경됨: $OLD_IP$CURRENT_IP" >> "$LOG_FILE"

위와 같이 동기화 셸 코드를 작성했다.
이후 crontab을 이용해서 5분마다 동기화 셸 코드를 실행했다.
API 제한에 걸리지 않도록 IP가 변경된 경우에만 API를 호출하도록 코드를 짰다.

이후 서버 DDNS 주소로 SSL 인증서 발급에 성공했다.


3. 백엔드 서버 이전

이제 DB도 옮겼고 ssl 인증서도 발급받았으니 백엔드 서버를 옮길 차례다.

처음엔 weppstore-api.<DDNS 주소>과 같이 서브도메인으로 하려 했으나, DDNS를 사용하므로 IP 동기화도 매번 해줘야 하는 등 번거로움이 있어 nginx의 경로 기반 리버스프록시로 세팅했다.

그리고 DB 접근 권한에 백엔드 서버를 허용해야 했다.
백엔드 서버는 docker로 배포했기 때문에 docker network 게이트웨이 IP 주소를 허용해주었다.

이후 nodejs를 설치하고 이전한 DB 정보와 연결하여 백엔드 코드를 빌드했다.

그런데 cloudflare로 변경한 네임서버가 아직 DNS 서버에 동기화되지 않아
프론트와 연결이 불가능했다.

구글cloudflare DNS 서버에는 금방 동기화됐는데,
정작 중요한 공유기와 연결된 KT DNS 서버에 동기화되는데 이틀이 걸렸다..

그렇게 동기화되자마자 프론트와 연결 후
AWS EC2 인스턴스RDS를 삭제했다.


4. 서비스 아키텍처 비교

기존 wepp store의 아키텍처는 다음과 같다.

architecture1

홈서버로 이전 후 아키텍처는 다음과 같다.

architecture1

드디어 나의 홈서버를 구축했고 놀이터를 만들어야겠다.

Related Articles