Morty's Blog | Web developer

在 VPS(VULTR) 上部屬 NestJS + PostgreSQL

在 VPS(VULTR) 上部屬 NestJS + PostgreSQL

本文詳細介紹如何將 NestJS + PostgreSQL 的 side project 從 GCP 移至 VPS 虛擬主機 - Vultr,並探討 Docker 的使用和部署策略。從 Dockerizing 專案、網域設定,到在 Vultr VPS 上的部署步驟,提供完整的指南和參考資源,助您輕鬆完成部署。

原由

最近寫了一個 NestJS + PostgreSQLside project,原先使用 GCP 上的 Cloud Run + Cloud SQL,但後來發現 Cloud SQL 的計價是以小時的,不是像其他 App Engine 等服務是照用量計價,導致我才開一兩天價格就爆表,因此最終決定將專案移至 VPS 虛擬主機上,經比較及調查最終決定使用 Vultr,一個月只要 6 美金就很夠用。

Dockerizing 專案

  1. 新增 .dockerignore 檔案
# Git
.git
.gitignore

# Docker
.docker
Dockerfile
.dockerignore

# Node
node_modules
npm-debug.log
dist
.env.*
  1. 新增 Dockerfile
FROM node:lts-alpine
# Create app directory
WORKDIR /app
EXPOSE 3000


# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package.json ./
COPY pnpm-lock.yaml ./

# Install app dependencies
RUN npm i -g pnpm
RUN pnpm install

# Bundle app source
COPY . .

# Creates a "dist" folder with the production build
RUN pnpm run build
  1. 新增 docker-compose.yml 裡面有多個容器, 以下簡易說明
  • app 為我們的 NestJS 服務,因專案使用 googl 登入 故有 GOOGLE_CLIENT_ID 等環境變數
  • db 為 postgres 資料庫服務
  • dbGUI 為 pgadmin 的 web 介面,詳細可參考 pgadmin-Container Deployment
  • nginx 主要進行反向代理及 https 相關設定,使用 docker-nginx-certbot 自動申請證書

需要注意的是 環境變數相關建議使用 env_file,避免在 git 上留下任何紀錄,此文章方便紀錄先將大部分環境變數寫在 environment

此外,透過 volumes 可以持久化我們的 db 數據及 證書。

version: '3.9'

services:
  app:
    build:
      context: .
    restart: always
    ports:
      - 3000:3000
    volumes:
      - ./src:/app/src
    command: >
      sh -c "pnpm run start:prod"
    environment:
      - DB_HOST=your_host
      - DB_NAME=your_db_name
      - DB_USERNAME=your_user_name
      - DB_PASSWORD=your_db_password
      - GOOGLE_CLIENT_ID=your_google_client_id
      - GOOGLE_CLIENT_SECRET=your_google_client_secret
    depends_on:
      - db

  db:
    image: postgres:13-alpine
    restart: always
    volumes:
      - dev-db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=your_db_name
      - POSTGRES_USER=your_user_name
      - POSTGRES_PASSWORD=your_db_password

  dbGUI:
    container_name: 'pgadmin'
    image: dpage/pgadmin4
    restart: always
    environment:
      PGADMIN_DEFAULT_EMAIL: your_email
      PGADMIN_DEFAULT_PASSWORD: your_password
      PGADMIN_LISTEN_PORT: 16543
    ports:
      - '16543:16543'
    depends_on:
      - db

  nginx:
    image: jonasal/nginx-certbot:latest
    restart: unless-stopped
    env_file:
      - ./nginx-certbot.env
    ports:
      - 80:80
      - 443:443
    links:
      - app
      - dbGUI
    volumes:
      - nginx_secrets:/etc/letsencrypt
      - ./user_conf.d:/etc/nginx/user_conf.d

volumes:
  dev-db-data:
  nginx_secrets:
  1. 新增 user_conf.d/example_server.conf 由於要搭配 docker-nginx-certbot 申請證書,需要新增 user_conf.d 資料夾及 example_server.conf 檔案,需將以下內容的 yourdomain.example 替換成自己的 domain,透過反向代理將後端 api 及 pgadmin 導至正確的 port,需要注意 upstream 內的 appdbGUIdocker-compose.yml 中的 nginx links 內進行設定,其餘就依照需求進行 nginx 的設定
upstream backend {
    server app:3000;
}

upstream dbGUI {
    server dbGUI:16543;
}

server {
    # Listen to port 443 on both IPv4 and IPv6.
    listen 443 ssl default_server reuseport;
    listen [::]:443 ssl default_server reuseport;

    # Domain names this server should respond to.
    server_name yourdomain.example;

    # Load the certificate files.
    ssl_certificate         /etc/letsencrypt/live/yourdomain.example/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/yourdomain.example/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.example/chain.pem;

    # Load the Diffie-Hellman parameter.
    ssl_dhparam /etc/letsencrypt/dhparams/dhparam.pem;

    location ^~ /admin/ {
        proxy_set_header X-Script-Name /admin;
        proxy_set_header Host $host;
        proxy_pass http://dbGUI/;
        proxy_redirect off;
    }

    location / {
        proxy_pass http://backend/;
    }
}

申請及設定 Vultr VPS

  1. 註冊 Vultr 帳號
  2. 右上角點選藍色 + -> Deploy New Server
Vultr new service
  1. 選擇 Cloud Compute 類型,其餘設定依據需求,這邊主機位置選擇 SingaporeTokyo 都差不多,依據個人測試為主 4. 完成之後透過 ssh 連進主機,可在專案 Overview 上看到 ip 及密碼
ssh root@yourip
  1. 連進後進行防火牆設定
sudo ufw reset
sudo ufw allow ssh
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enable
  1. 參考 docker-install 安裝 docker

  2. 在 Github 中的 Settings -> Deploy keys 新增 ssh key (方便主機 pull 專案)

  3. 在主機中 clone 專案並執行

git clone your_repo
cd your_repo
docker compose up -d

基本步驟就差不多完成了,之後就可以把相關流程寫進 Actions,方便快速部屬

參考文件