Google Cloud Platform (GCP) free tier로 Notion 웹 사이트를 호스팅하면 어떨까?컨테이너 이미지 만들기배포 하기VM swap 설정도메인 구입 및 연결하기결론
Google Cloud Platform (GCP) free tier로 Notion 웹 사이트를 호스팅하면 어떨까?
처음에는 단순히 Notion으로 개인 블로그 사이트를 만들려고 했었다. 그런데 커스텀 도메인을 지원하지 않는다고 해서 알아보니 Next.js로 Notion 정적 사이트를 만들 수 있는 도구 nextjs-notion-starter-kit 를 찾게되었다. 물론 직접 구축하지 않더라도 유료 서비스 (예: super.so)를 사용해서 만들 수도 있다. 그렇지만 이참에 Next.js도 조금 배워보자는 마음으로 직접 웹 사이트를 구축해보기로 했다.
서버를 호스팅 할 클라우드로 GCP를 선택했던 건 단순히 도메인 때문이었다. 어차피 무료 티어는 AWS, GCP 둘다 가능했기 때문에 무엇을 선택해도 상관 없었다. 다만 .dev 도메인을 쓰고 싶었는데 AWS에서는 판매를 하지 않는다는 점 때문에 GCP를 선택했다.
컨테이너 이미지 만들기
배포는 컨테이너 이미지로 하기로 결정했다. VM에서 빌드하는 것도 해보았는데 직접 접속해서 하려니 번거롭기도 하고 인스턴스도 사양이 낮아서 빌드가 쉽지는 않았다. 그보다는 컨테이너 이미지로 만드는게 PC에서 빌드하는 것도 가능하고 결과물도 관리 할 수 있어서 좋다고 생각했다. GCP에서는 Compute Engine에서 컨테이너 이미지로 VM을 구동하는 방식이 가능해서 이 방식으로 배포하기로 했다.
컨테이너 이미지를 만드려면 nextjs-notion-starter-kit의 렌더링 방식을 수정할 필요가 있다. nextjs-notion-starter-kit은 Server Site Generation (SSG) 기반으로 되어있어서 미리 입력된 사이트 설정 소스 파일에 따라 페이지를 생성하는 방식이다. 물론 사이트 설정을 바꿔서 커밋하면서 빌드 및 관리하는 것도 가능하다. 하지만 그보다 실행 시점에 사이트 설정을 명시할 수 있는 형태로 하면 같은 이미지로 설정만 바꿔가면서 사이트를 만들 수 있기 때문에 더 좋을 것이라 생각했다. 이를 위해서 Server Side Rendering (SSR)로 렌더링하도록 변경하고 관련된 props를 서버에서 클라이언트로 올려줄 수 있도록 수정했다.
물론 기존에도 NEXT_PUBLIC_SITE_CONFIG 환경 변수로 사이트 설정을 실행 시점에 설정할 수 있게 되어있지만 기본적으로는 사이트 설정 소스 파일에 지정된 값을 기준으로 번들이 생성된다. 따라서 처음 페이지를 로딩할 때 번들에 포함된 페이지가 먼저 표시되는 문제가 있다. 그리고 UI의 일부분은 번들에 있는 값을 기준으로 렌더링 되도록 고정되어있어서 환경 변수로 수정이 되지 않는다.
그리고 .dev 도메인을 사용하려면 https 사용이 필수이기 때문에 커스텀 서버를 구현해야 한다. Next.js는 기본적으로 https를 지원하지 않으므로 별도의 커스텀 서버를 작성할 필요가 있다. 서버 인증서는 Let’s encrypt를 사용할 것이라서 (1) 도메인 소유 검증 기능, (2) 인증서가 갱신되었을 때 다시 로드할 수 있는 기능도 추가했다.
마지막으로 별도의 작업을 구동하지 않고도 이미지 내에서 인증서를 갱신할 수 있게 만들고 싶었다. 이를 위해서 certbot client + cron daemon을 이미지 내부에서 구동하는 방식을 사용했다. 일정 주기로 스크립트를 실행하여 Let’s encrypt에 요청, 자동으로 인증서를 갱신할 수 있도록 구현하였다.
Docker file은 alpine 이미지를 기반으로 구성하였고 런타임에 설정된 환경 변수에 인증서 갱신과 서버 구동이 가능하도록 했다.
배포 하기
이렇게 만들어진 이미지를 Artifact Registry에 업로드하고 VM에 배포한다. 리전은 무료 티어가 제공되는 미국 아이오와 (us-central1-a)로 선택했다. Artifact Registry에서 저장소를 생성한 뒤 podman을 사용하여 이미지를 빌드 및 업로드했다.
Artifact Registry에 업로드 할 때는 컨테이너 이미지 URL 형식에 유의해야 한다. 저장소의 URL이ZONE-docker.pkg.dev/USER/REPOSITORY
라고 할 때ZONE-docker.pkg.dev/USER/REPOSITORY/SUB_REPOSITORY:TAG
와 같이 depth가 하나 더 존재한다.SUB_REPOSITORY
부분을 지정하지 않으면 오류가 발생한다.
Compute Engine에서 VM 생성시 유형을 e2-micro를 설정하고 업로드한 컨테이너 이미지와 환경 변수 설정을 지정하는 것으로 VM 구동이 완료된다.
만일 새로운 이미지를 업로드 했을 경우에는 수동으로 VM을 재시작 할 필요가 있는데 이 부분은 추후에 자동화할 수 있을 것이라 생각한다.
VM swap 설정
메모리 확보를 위해 swap을 설정해야 한다. VM 구동 후 일정 시간이 경과하면 Next.js가 죽는 현상이 있어서 살펴보니 메모리가 부족한 상황이었다. VM은 기본적으로 swap이 설정되어있지 않기 때문에 별도로 설정이 필요하다.
# swap file (2GiB) 생성 $ dd if=/dev/zero of=/mnt/stateful_partition/swapfile bs=1MiB count=2048 $ chmod 600 /mnt/stateful_partition/swapfile $ mkswap /mnt/stateful_partition/swapfile
생성된 swap file을 매 부팅시 마다 사용할 수 있도록 VM 설정에서 아래와 같이 시작 스크립트를 지정해야 한다.
도메인 구입 및 연결하기
Cloud Domains에서 새로운 도메인을 구입하여 Cloud DNS에서 VM IP로 연결한다. Compute Engine에서 VM 구동 시 외부 IP가 부여되므로 해당 IP를 Cloud DNS의 A record에 연결한다. 어차피 접속하는 사람이 거의 없고 안정성이 별로 중요하지 않기 때문에 VM에 바로 연결하기로 하였다.
결론
도메인 구입 비용을 제외하면 적은 비용으로 Notion 웹사이트 호스팅이 가능하다. 아래는 지난 11월, 12월 두 달간 서버를 구동한 뒤 청구된 요금의 그래프이다. 도메인 구입을 제외한 총 비용은 평균 월 330원 정도이며 83% 가량이 Cloud DNS 비용이고, 나머지 17% 정도가 네트워크 비용 및 컨테이너 이미지 보관 비용에 해당된다 (물론 트래픽에 따라서는 비용이 달라질 수는 있겠다).
작업 결과물은 아래 Github 저장소에서 받아볼 수 있다.