Как настроить связь между Docker, Nest.js, Prisma и PostgreSQL?

Как можно решить проблему с тем, что команда `yarn build` выполняется раньше, чем запускается база данных в моем `docker-compose.yaml`, что приводит к ошибке `PrismaClientInitializationError: Can't reach database server at 'postgres':'5432'`? Я уже пробовал использовать скрипты ожидания, но они не помогают. 

Вот содержимое моего `docker-compose.yaml` и `Dockerfile`, а также конфигурация `.env` файла:

**docker-compose.yaml:**
```yaml
version: '3.8'

services:
  postgres:
    image: postgres:latest
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: 123456
      POSTGRES_DB: mydb
    restart: always
    ports:
      - '5432:5432'
    networks:
      - kinonetwork

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    env_file:
      - ./backend/.env
    ports:
      - '4200:4200'
    networks:
      - kinonetwork
    depends_on:
      - postgres
```

**Dockerfile:**
```dockerfile
# Build stage
FROM node:22.2.0 as build

WORKDIR /app

COPY package*.json yarn.lock ./

RUN yarn install --frozen-lockfile
COPY . .

RUN yarn prisma db push
RUN yarn prisma generate
# Build the application
RUN yarn build

# Production stage
FROM node:22.2.0

WORKDIR /app

COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/package*.json ./
COPY --from=build /app/dist ./dist

EXPOSE 4200

CMD [ "yarn", "start:prod" ]
```

**.env файл:**
```bash
DATABASE_URL="postgresql://postgres:123456@postgres:5432/mydb?schema=public"
```
  • 6 января 2025 г. 0:03
Ответы на вопрос 2
Ваша проблема заключается в том, что команда `yarn build`, которая выполняется на этапе сборки Docker образа, запускается до того, как база данных PostgreSQL успевает подняться и быть доступной. Это приводит к ошибке, так как Prisma не может соединиться с базой данных во время сборки.

Для решения этой проблемы можно изменить подход к сборке образов и запуску базы данных. Вот несколько вариантов, как это можно сделать:

### Вариант 1: Используйте `docker-compose up` для локальной разработки

Во время разработки оставьте использование `docker-compose up` для запуска всего стека. Это позволит вам всего лишь запускать контейнеры и дожидаться того, чтобы база данных была готова к работе, а затем запускать ваши миграции и сборку приложения. Например, вы можете сделать так:

1. Убедитесь, что у вас есть скрипт, который запускает Prisma миграции, например, в `package.json`:

   ```json
   "scripts": {
     "migrate": "prisma migrate deploy",
     "build": "tsc",
     "docker:dev": "yarn migrate && yarn build && yarn start:prod"
   }
   ```

2. В `docker-compose.yaml` уберите `RUN yarn prisma db push` и `RUN yarn prisma generate` из `Dockerfile`.

3. Запустите ваш стек с помощью:

   ```bash
   docker-compose up
   ```

4. В другом терминале выполните:

   ```bash
   docker-compose exec backend yarn migrate
   ```

5. Теперь ваше приложение можно запускать через:

   ```bash
   docker-compose exec backend yarn start:prod
   ```

### Вариант 2: Используйте `wait-for-it` или аналогичный скрипт

Если вы хотите, чтобы команда `yarn build` выполнялась автоматически во время сборки образа, то вы можете использовать скрипт ожидания, такой как `wait-for-it.sh`. Вот как вы можете это сделать:

1. Скачайте `wait-for-it.sh` и добавьте его в папку вашего проекта (можно создать папку `scripts` и положить туда `wait-for-it.sh`).

2. В `Dockerfile` перед тем, как делать `yarn prisma db push`, добавьте вызов `wait-for-it.sh`:

```dockerfile
# Build stage
FROM node:22.2.0 as build

WORKDIR /app

COPY package*.json yarn.lock ./

RUN yarn install --frozen-lockfile
COPY . .

# Добавляем wait-for-it
COPY scripts/wait-for-it.sh ./

# Ожидание базы данных перед выполнением миграций
RUN ./wait-for-it.sh postgres:5432 -- yarn prisma db push

RUN yarn prisma generate
# Build the application
RUN yarn build

# Production stage
FROM node:22.2.0

WORKDIR /app

COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/package*.json ./
COPY --from=build /app/dist ./dist

EXPOSE 4200

CMD [ "yarn", "start:prod" ]
```

Таким образом, `wait-for-it.sh` будет ожидать, пока база данных станет доступной, прежде чем выполнять команды Prisma.

### Вариант 3: Используйте `depends_on` с условием

`depends_on` в `docker-compose` позволяет установить порядок запуска контейнеров, но не ожидает, пока сервис будет готов. Однако можно использовать дополнительные инструменты для управления состоянием, такие как `wait-for-it.sh`.

### Заключение

Лучший подход зависит от ваших предпочтений и конечных целей. Если вы настраиваете окружение для разработки, то запуск миграций и сборки после `docker-compose up` может быть более простым решением. Если же вы хотите автоматизировать процесс сборки, использование `wait-for-it.sh` будет хорошей альтернативой.
Можно перенести yarn prisma db push и yarn prisma generate в Entrypoint контейнера node.js и запускать их перед стартом приложения. 
Но вообще это "yarn build возникает раньше, чем запускается бд" врядли. У вас же есть
depends_on:
- postgres
Похожие вопросы