Галоўная / Артыкулы

Развяроць NestJS на Bun і Prisma 7 у Cloud Run без памылак пад час будавання

Рабочыя пайплайны GitHub Actions для выкладкі прыемніка NestJS на Bun з Prisma 7 і Neon у Cloud Run, а таксама спасабіранне Docker і выправленні проблем з падзеямі з’ўязку, якія ствараюць труднасці для команд.

Выдавец: REACTAPP.TOP5 хв чытання1259 слоў

Крыніцы: Zero to Production: Deploying NestJS with Bun, Prisma 7, and Neon DB on Google Cloud Run

Рэндзінг NestJS на Bun, падтрымваны Prisma 7 і безсерверным Postgres у формате Neon, а таксама ўпаковка проекту і выкладчыцтва яго на Google Cloud Run через GitHub Actions — усё гэта вельмі шырока з усіх точаказоў. Але проблема тая, што такая комбінацыя ўсё яшчэ нова, таму проблэмы, якія можуць выйсці, незнайомы, і большасць команд траціць дзень на проблемы з будаваннем Docker-образа або наявнасцю проблем з падключэнням да Prisma перш чым удастся адбыць першую успешную разгрузку. Шэры рукаводзець прадаст вам рабочую схему адбывання задач і поясніць секрэты, якія стояць за ёю, тады вы зможаце адмахнуцца ад гэтага дня.

Як формуецца схема адбывання задач

Мета — автаматызаваны процес без участі чалавека: вы падаеце код у відпаведную галузь для разгрузкі, і аўтаматызацыя выканае міграціі базы дадзеных, створыць лёгкі Docker-образ, падае яго ў Artifact Registry і разгрузыць у безсерверную службу. Порядак выканання крокаў мае значэнне, а калькі з іх трэба выканаваць спецыяльна з-за таго, як Prisma і Bun працуюць унутры контейнера.

Успешна робота генератора Prisma падчас стварэння Docker-образа

Контейнерызацыя Prisma мае адна стойкая проблема. Калі запускаецца prisma generate, ён патрабуе стрычку з’яўлення да базы дадзенаў, каб могчыць аналізаваць схему і ствараць кліента. Падчас стварэння образа няма прычыны даваць яму рэальныя крэдытнаі данні для працы, а вбудовванне секрэтных дадзеных у шар образа — гэта тое, чаго трэба адхіляць, адколі все, што знаходзится ў даступным для распрасціравання образе, можа быць выявлена пазней.

Рашэнням є сынтаксічна правільны, але фальшывы URL, які існуе толькі падчас стварэння образа. Наведзены нижэй мульті-стадійны Dockerfile інсталюе залежнасці на аднойчы зафіксаванай файле з блокірацыяй, задае тымчасовую значэння DB__URL, якая вядуе на неіснуючую локальную базу дадзенаў, проста ў тым, каб генератор працаваў, стварае програму і копіюе толькі скомпільаваны выхід у лёгкі образ для запуску.

# Stage 1: Builder
FROM oven/bun:1 AS builder
WORKDIR /app

COPY package.json bun.lock ./
COPY prisma ./prisma
COPY prisma.config.ts ./

RUN bun install --frozen-lockfile

# The "Prisma Satisfaction" Trick:
# Prisma 7 needs to see a URL format during the build phase to generate the client.
ENV DB__URL="postgresql://dummy:dummy@localhost:5432/dummy"

RUN bunx prisma generate

COPY . .

RUN bun run build

# Stage 2: Runner (Slim)
FROM oven/bun:1-slim AS runner
WORKDIR /app

# Only copy what is strictly necessary to keep the image small
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/prisma.config.ts ./

ENV NODE_ENV=production
ENV PORT=8080

CMD ["bun", "run", "dist/main.js"]

Важны два аспекты. Будова змешанага формата здзейснюецца з выкарыстоўванням цэлага адпрацоўвальнага сераўера Bun і працюе на bun:1-slim, што дапамагае залічыць канечны файл маленькім і зменшае площу, яю можа атакавальнік выкарыстоўваць. А фіктывны строк задовольняе генератора, без таго, каб рэальныя даны прыйшлі ў які-небудзь слой, таму што генератору пад час будовы трэбна толькі структура URL для Postgres, а не доступны сервер.

Забезпечэнне безпекі счыту прызначанага для службы

Пайплайн аутентыфікуецца ў Google Cloud за дапамогою счыту прызначанага для службы, і ёсць спакуса надаць яму шырокія правыя для таго, каб падбіць бракі. Супротставляйцеся гэтаму. Адпаведна прынцыпу мінімальных прав, надаюце яму толькі тыя ролі, якія неабходны для развёртвання, таким чынам кількі, якая прасочылася, не зможа стаць інструментам для кантролю над усім проектам: толькі завантажэнне файлаў у Artifact Registry і развёртванне ў Cloud Run, нічога больш.

Процес GitHub Actions ад початку да канца

Перад першым запускам зберагаюцься значэнні, на якія пасылаецца рабочы процес, такія як ключ адналога службы і URL базы дадзэння, як заштыфраваныя секреты хранілішча. Задача завантажвае код, насталявает Bun, аутентыфікуецца ў Google Cloud, запускае міграцыю Prisma, аутентыфікуецца Docker у Artifact Registry, стварае і выкладзець адпраўку, размешчае яе ў Cloud Run і адмахвае старыя адпраўкі. Ў выхідным коде гэты фрагмент пазначаны як SQL; цэў ёсць рабочы процес у формате YAML для GitHub Actions, а падказка нижэй правільна яго пазначае.

name: Deploy Server to Cloud Run

on:
  push:
    branches: ['main']

env:
  PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
  REGION: asia-south1
  REPO_NAME: cloud-run-source-deploy
  SERVICE_NAME: server

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Bun
        uses: oven-sh/setup-bun@v1
        with:
          bun-version: latest

      - name: Google Auth
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2

      # STEP: Database Migration
      # Run migrations BEFORE deployment to ensure the DB is ready for new code.
      - name: Run Prisma Migrations
        run: |
          bun install --frozen-lockfile
          bunx prisma generate
          bunx prisma migrate deploy
        env:
          DB__URL: ${{ secrets.DB__URL }}

      - name: Docker Auth
        run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev

      - name: Build and Push
        run: |
          IMAGE_URI=${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPO_NAME }}/${{ env.SERVICE_NAME }}:latest
          docker build -t $IMAGE_URI .
          docker push $IMAGE_URI
          echo "IMAGE_URI=$IMAGE_URI" >> $GITHUB_ENV

      - name: Deploy to Cloud Run
        uses: google-github-actions/deploy-cloudrun@v2
        with:
          service: ${{ env.SERVICE_NAME }}
          region: ${{ env.REGION }}
          image: ${{ env.IMAGE_URI }}
          flags: '--port=8080 --allow-unauthenticated'
          env_vars: |
            DB__URL=${{ secrets.DB__URL }}
            # Add placeholders for other secrets below:
            # SMTP__HOST=${{ secrets.SMTP__HOST }}
            # AUTH__TOKEN_SECRET=${{ secrets.AUTH__TOKEN_SECRET }}

      # STEP: Housekeeping (Optional)
      # Deletes old image digests to save on storage costs.
      - name: Delete Old Images
        shell: bash
        run: |
          IMAGE_PATH="${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPO_NAME }}/${{ env.SERVICE_NAME }}"
          gcloud artifacts docker images list "$IMAGE_PATH" --sort-by=~create_time > all_images.txt
          awk 'NR>2 {print $2}' all_images.txt > digests_to_delete.txt

          if [ -s digests_to_delete.txt ]; then
            while read digest; do
              if [[ "$digest" == sha256:* ]]; then
                echo "Deleting old image: $digest"
                gcloud artifacts docker images delete "$IMAGE_PATH@$digest" --delete-tags --quiet || true
              fi
            done < digests_to_delete.txt
          fi

Чаму складныя крокі маюць свое месца

Выкананне команды prisma migrate deploy як окольніцы, прытаму, перш чым новы образ будзе запусканы, ў дзiesяць разоў безпечней, чым міграцыя з усероўня контейнера пад час запуску. Якщо міграцыя не выйдзе, запуск зупініцца, а чырвоныя версіі будуць і даляй обслужваць запиты; якщо ж міграцыя выканацца пад час запуску контейнера, некалькія проблемы з міграцыяю можу выключыць службу, калі новы код вясно запускаецца.

Пасляпэльны крок – выдаленне старых хешаў образаў, таму што ў актывах средовішчах накапліваюцца збераганыя файлы, і расходы на схованне растуць. Гэта сапраўды необавязкова: якщо вы пакладаецеся на можлівасць вярнуцца да кожнага паказванага ранейя версіі, можаце прыміті гэты крок і платыць за схованне.

Пасляпоследній спосаб няўдачы — це выкаранне канектацый. Cloud Run швытка нарашчвае колькасць інстансаў, і кожны з яных стварае сваія канектацыі, таму адсылка да прымітнага адресу базы дадзеных можа прывести да перасічэння ўстановленага ліміту канектацый. У Neon трэба выкарыстоўваць стрынг канектацый з пулам у DB__URL, ў результате чаго большасць тымчасовых інстансаў дзеліцца адным обмежаным пулам. Гэта праўда для будзь-якай наладкі з серверлессу і Postgres.

Ключовыя моманты

  • У час будовы да генератора Prisma неабходна дадаць фальшывы, правільна сформаваны адрес базы дадзеных, каб жодныя рэальныя секреты не потрапілі ў шар зображэнняў.
  • Будуйце проектаў з повным образам Bun і запускайце ў легкай версіі, каб зменшыць размер зображэнняў.
  • Выконвайце міграцыі як окремы крок перад развяртаннем, так ў случае няўдачы будзе абаранена публікацыя, а не зупінены жывы сервіс.
  • Для серверлесс-цэльва выкарыстоўвайце стрынг канектацый з пулам, а аккаунт службы развяртання неабходна налаштаваць з мінімальнымі правамі.
  • Bun у стадії розробкі, а Prisma 7 на момент написання тэкста залічваецца апошнім досягненням у гэтай сферы; пераканацеся ў працэ выкарыстоўвання інструменту на адповідачных документах.