From a4541cf26635f7956378968b8dc459e33a369f35 Mon Sep 17 00:00:00 2001 From: shop Date: Sun, 17 May 2026 09:41:03 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20Docker=20Compose=20=E2=80=94=20app,=20P?= =?UTF-8?q?ostgreSQL=2017,=20=D0=BE=D0=BF=D1=86=D0=B8=D0=BE=D0=BD=D0=B0?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20Caddy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .dockerignore | 12 +++++++ .env.docker.example | 12 +++++++ Dockerfile | 22 ++++++++++++ README.md | 64 ++++++++++++++++++++++++++++----- caddy/Caddyfile.docker.example | 19 ++++++++++ docker-compose.dev.yml | 22 ++++++++++++ docker-compose.yml | 65 ++++++++++++++++++++++++++++++---- 7 files changed, 200 insertions(+), 16 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.docker.example create mode 100644 Dockerfile create mode 100644 caddy/Caddyfile.docker.example create mode 100644 docker-compose.dev.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9fe0179 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +node_modules +npm-debug.log +data +.env +.env.* +!.env.example +.git +.gitignore +README.md +*.md +.dockerignore +docker-compose*.yml diff --git a/.env.docker.example b/.env.docker.example new file mode 100644 index 0000000..8af8c16 --- /dev/null +++ b/.env.docker.example @@ -0,0 +1,12 @@ +# Скопируйте: cp .env.docker.example .env +# Используется docker compose (переменные подставляются в compose) + +POSTGRES_USER=shop +POSTGRES_PASSWORD=shop +POSTGRES_DB=shop + +APP_PORT=3000 +SESSION_SECRET=change-me-to-a-long-random-string +TRUST_PROXY=0 + +# С профилем proxy (Caddy): TRUST_PROXY=1 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..84df2b6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM node:20-alpine + +WORKDIR /app + +RUN apk add --no-cache tini curl + +COPY package.json ./ +RUN npm install --omit=dev && npm cache clean --force + +COPY src ./src +COPY postgres ./postgres + +ENV NODE_ENV=production +ENV HOST=0.0.0.0 +ENV PORT=3000 + +EXPOSE 3000 + +USER node + +ENTRYPOINT ["/sbin/tini", "--"] +CMD ["node", "src/server.js"] diff --git a/README.md b/README.md index f71f0d8..109d4ba 100644 --- a/README.md +++ b/README.md @@ -11,21 +11,64 @@ ## Требования -- Node.js 18+ -- PostgreSQL 17 -- npm +- Node.js 18+ и PostgreSQL 17 — **или** Docker / Docker Compose --- -## PostgreSQL 17 +## Docker Compose (рекомендуется для теста) -### Docker (разработка / тест) +Полный стек: **PostgreSQL 17** + **приложение Node.js**. ```bash -docker compose up -d -# БД: postgresql://shop:shop@127.0.0.1:5432/shop +cp .env.docker.example .env +# Отредактируйте SESSION_SECRET в .env + +docker compose up -d --build +docker compose ps +curl -s http://127.0.0.1:3000/health ``` +Сайт: **http://localhost:3000** + +| Команда | Описание | +|---------|----------| +| `docker compose up -d --build` | Сборка и запуск | +| `docker compose logs -f app` | Логи приложения | +| `docker compose down` | Остановка | +| `docker compose down -v` | Остановка + удаление БД | + +### Caddy в Docker (HTTPS, опционально) + +```bash +# В .env: TRUST_PROXY=1 +# Отредактируйте caddy/Caddyfile.docker.example (домен) +docker compose --profile proxy up -d --build +``` + +Порты: **80**, **443** (Caddy) → `app:3000`. + +### Только PostgreSQL (разработка на хосте) + +```bash +docker compose -f docker-compose.dev.yml up -d +cp .env.example .env +# DATABASE_URL=postgresql://shop:shop@127.0.0.1:5432/shop +npm install && npm run dev +``` + +### Файлы + +| Файл | Назначение | +|------|------------| +| `Dockerfile` | Образ приложения | +| `docker-compose.yml` | app + postgres (+ caddy с профилем `proxy`) | +| `docker-compose.dev.yml` | только postgres для `npm run dev` | +| `.env.docker.example` | переменные для compose | + +--- + +## PostgreSQL 17 (без Docker) + ### Ubuntu (сервер) ```bash @@ -301,7 +344,7 @@ journalctl -u shop -n 50 --no-pager ## Локальная разработка ```bash -docker compose up -d +docker compose -f docker-compose.dev.yml up -d cp .env.example .env npm install npm run dev @@ -318,9 +361,12 @@ npm run dev ## Структура ``` +Dockerfile +docker-compose.yml +docker-compose.dev.yml postgres/init/01_schema.sql -docker-compose.yml — PostgreSQL 17 локально caddy/Caddyfile.example +caddy/Caddyfile.docker.example deploy/shop.service scripts/ setup-postgres-ubuntu.sh diff --git a/caddy/Caddyfile.docker.example b/caddy/Caddyfile.docker.example new file mode 100644 index 0000000..c3f2724 --- /dev/null +++ b/caddy/Caddyfile.docker.example @@ -0,0 +1,19 @@ +# Для docker compose --profile proxy +# Скопируйте и отредактируйте домен перед запуском: +# cp caddy/Caddyfile.docker.example caddy/Caddyfile.docker +# Укажите volume в compose на свой файл + +{ + email admin@example.com +} + +:80 { + encode gzip zstd + reverse_proxy app:3000 +} + +# С доменом и HTTPS (раскомментируйте, закомментируйте :80): +# shop.example.com { +# encode gzip zstd +# reverse_proxy app:3000 +# } diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..832ac7d --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,22 @@ +# Локальная разработка: только PostgreSQL, приложение на хосте (npm run dev) +services: + postgres: + image: postgres:17-alpine + container_name: shop-postgres-dev + ports: + - '5432:5432' + environment: + POSTGRES_USER: shop + POSTGRES_PASSWORD: shop + POSTGRES_DB: shop + volumes: + - shop_pg_dev:/var/lib/postgresql/data + - ./postgres/init:/docker-entrypoint-initdb.d:ro + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U shop -d shop'] + interval: 3s + timeout: 3s + retries: 5 + +volumes: + shop_pg_dev: diff --git a/docker-compose.yml b/docker-compose.yml index 16b5322..f3cc5a5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,22 +1,73 @@ services: postgres: - image: postgres:17 + image: postgres:17-alpine container_name: shop-postgres restart: unless-stopped environment: - POSTGRES_USER: shop - POSTGRES_PASSWORD: shop - POSTGRES_DB: shop - ports: - - '5432:5432' + POSTGRES_USER: ${POSTGRES_USER:-shop} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-shop} + POSTGRES_DB: ${POSTGRES_DB:-shop} volumes: - shop_pg_data:/var/lib/postgresql/data - ./postgres/init:/docker-entrypoint-initdb.d:ro healthcheck: - test: ['CMD-SHELL', 'pg_isready -U shop -d shop'] + test: ['CMD-SHELL', 'pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB'] interval: 5s timeout: 5s + retries: 10 + start_period: 10s + networks: + - shop + + app: + build: . + container_name: shop-app + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + NODE_ENV: production + HOST: 0.0.0.0 + PORT: 3000 + TRUST_PROXY: ${TRUST_PROXY:-0} + SESSION_SECRET: ${SESSION_SECRET:-change-me-in-docker-compose-env} + DATABASE_URL: postgresql://${POSTGRES_USER:-shop}:${POSTGRES_PASSWORD:-shop}@postgres:5432/${POSTGRES_DB:-shop} + ports: + - '${APP_PORT:-3000}:3000' + healthcheck: + test: ['CMD', 'curl', '-f', 'http://127.0.0.1:3000/health'] + interval: 10s + timeout: 5s retries: 5 + start_period: 15s + networks: + - shop + + caddy: + image: caddy:2-alpine + container_name: shop-caddy + profiles: + - proxy + restart: unless-stopped + depends_on: + app: + condition: service_healthy + ports: + - '80:80' + - '443:443' + volumes: + - ./caddy/Caddyfile.docker.example:/etc/caddy/Caddyfile:ro + - caddy_data:/data + - caddy_config:/config + networks: + - shop + +networks: + shop: + driver: bridge volumes: shop_pg_data: + caddy_data: + caddy_config: