Files
mono/Dockerfile
coopops 223dd00f91 fix(mono-base): install ca-certificates in runtime stage
@livekit/rtc-node (Rust + rustls + rustls-native-certs) reads root CAs
only from the system trust store. node:22-slim ships without
ca-certificates, so without /etc/ssl/certs/ca-certificates.crt every TLS
handshake from native bindings fails with
"invalid peer certificate: UnknownIssuer" — even for a valid LE chain.
Node.js itself is unaffected (own embedded CA bundle).

Incident 2026-05-21: secretary in dicoop/coopback:v2026.5.21-2 could not
connect to wss://chatcooprtc.coopenomics.world (controller logs full of
SecretaryAgentService UnknownIssuer; nginx on api-prod never saw the
handshake — broken before HTTP upgrade). Hot-fix on
voskhod-coopback-blue (docker cp ca-certificates.crt + restart)
restored secretary connectivity; this commit makes the fix permanent
in the base image so every consumer of dicoop/mono-base
(coopback / desktop / cooparser / notifications / boot) inherits it
on the next release tag.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 13:21:26 +00:00

119 lines
6.7 KiB
Docker
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Multi-stage build для `dicoop/mono-base`.
#
# Стадии:
# 1. builder — full toolchain: pnpm + lerna + apt dev-headers (gcc,
# libcairo2-dev, ...) + WeasyPrint в venv. Здесь делаются
# `pnpm install` (с devDeps), `lerna run build`, потом
# `pnpm prune --prod` (devDeps выкидываются из workspace
# node_modules — экономит ~1.5GB).
# 2. runtime — slim образ без dev-headers и без gcc. Только runtime
# libs WeasyPrint + готовый /app + venv. Глобальные pnpm/lerna
# ставятся отдельно для удобного запуска `pnpm -F <pkg> run start`.
#
# Финальный размер: ~1-1.5GB (вместо 7.36GB в одностадийной сборке).
# Все потребители mono-base (controller/desktop/parser/notifications/
# notificator/boot) пользуются одним и тем же тонким runtime-образом
# через `FROM dicoop/mono-base:<tag>` + `CMD`.
# ── Stage 1: builder ──────────────────────────────────────────────────
FROM node:22-slim AS builder
WORKDIR /app
# Build-time системные deps. Тяжёлые `-dev`-пакеты (для компиляции
# native-bindings + WeasyPrint native deps) сюда, в runtime их не
# тащим. `--no-install-recommends` экономит ещё ~150MB.
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv \
build-essential gcc g++ python3-dev \
libcairo2-dev libffi-dev libjpeg-dev libopenjp2-7-dev zlib1g-dev \
libpango-1.0-0 libpangoft2-1.0-0 libpangocairo-1.0-0 libcairo2 \
shared-mime-info \
&& rm -rf /var/lib/apt/lists/*
# WeasyPrint в venv — переедет в runtime-stage целиком через COPY.
RUN python3 -m venv /venv \
&& /venv/bin/pip install --no-cache-dir WeasyPrint==67
# pnpm пинится через `packageManager` поле в корневом package.json
# (corepack-стандарт). Без пина CI каждый раз тянет latest, а pnpm 10
# ужесточил политику build-скриптов и валит `--frozen-lockfile` с
# ERR_PNPM_IGNORED_BUILDS на native-пакетах (electron / esbuild / …).
RUN corepack enable && npm install -g lerna --no-fund --no-audit
# Сначала манифесты (для кэш-friendly install). `.dockerignore` уже
# выкидывает node_modules/dist/.git, поэтому `COPY .` лёгкий.
COPY . .
# Используем существующий lockfile. Если он не совпадает с workspace —
# падать сразу, не дрейфовать незаметно.
RUN pnpm install --frozen-lockfile
# Сборка всех пакетов через lerna-граф (зависимости собираются в
# правильном порядке). Никаких --filter — пусть собирается всё, чтобы
# образ годился любому потребителю mono-base.
RUN lerna run build
# Удалить devDeps из всех workspace node_modules. Это и есть основная
# оптимизация размера — typescript, vitest, eslint, unbuild, ts-node-cli,
# desktop-сборщики и пр. удаляются из финального дерева.
#
# - `CI=true` отключает интерактивный prompt («вы уверены что снести
# modules?») — без TTY pnpm иначе абортится с
# ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY.
# - `--ignore-scripts` подавляет lifecycle hooks (prepare/postinstall)
# после prune. Иначе pnpm дёргает `quasar prepare` в desktop'е,
# а quasar (devDep) уже снесён → ELIFECYCLE.
#
# Все production-сервисы переведены на runtime без devDeps:
# cooparser → `node ./dist/index.cjs`
# notifications → `node ./dist/sync/sync-runner.cjs` (entry добавлен в unbuild)
# coopback → `ts-node` + `tsconfig-paths` (оба в deps)
# desktop → `node dist/ssr/index.js`
# boot:remote → `node /app/components/boot/dist/index.cjs boot:remote`
RUN CI=true pnpm prune --prod --ignore-scripts
# ── Stage 2: runtime ──────────────────────────────────────────────────
FROM node:22-slim AS runtime
WORKDIR /app
# Только runtime-библиотеки WeasyPrint (без `-dev` headers и без gcc).
# `python3` нужен потому что venv в /venv/bin/python — символическая
# ссылка на системный интерпретатор; без него `weasyprint` падает с
# `python: not found`. `procps`/`wget` — для дебага/healthcheck'ов
# из docker-compose.
#
# `ca-certificates` обязателен: node:22-slim его не содержит, а
# native-биндинги типа @livekit/rtc-node (Rust + rustls + rustls-native-certs)
# читают корни ТОЛЬКО из системного store. Без файла
# /etc/ssl/certs/ca-certificates.crt любой TLS handshake из такого
# биндинга падает с "invalid peer certificate: UnknownIssuer" —
# даже для валидной LE-цепочки (Node-стек при этом работает,
# у него свои вшитые корни). Инцидент 2026-05-21: secretary в
# coopback не мог подключиться к wss://chatcooprtc.coopenomics.world.
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
python3 \
libpango-1.0-0 libpangoft2-1.0-0 libpangocairo-1.0-0 libcairo2 \
libffi8 libjpeg62-turbo libopenjp2-7 zlib1g shared-mime-info \
procps wget \
&& rm -rf /var/lib/apt/lists/* \
&& update-ca-certificates
# Python venv с WeasyPrint, готовый к использованию.
COPY --from=builder /venv /venv
ENV PATH="/venv/bin:$PATH"
# Приложение целиком (с уже урезанными до prod node_modules).
COPY --from=builder /app /app
# Глобальные pnpm/lerna — нужны чтобы потребители mono-base могли
# делать `CMD ["pnpm","-F","<pkg>","run","start"]` в production.
# pnpm активируется через corepack (версия из `packageManager` корневого
# package.json — синхронно со builder-стадией).
RUN corepack enable && npm install -g lerna --no-fund --no-audit
# Sanity-check: WeasyPrint работает и виден через PATH.
RUN weasyprint --version