Вылечыце адчыненную на Альпіне Docker памятку пра Prisma's libssl.so.1.1
Канрэйтар Prisma зламваецца на адпрацоўкіх Alpine Docker з-за абсэнцыі файла libssl.so.1.1. Што на самай працэ практыкуецца з OpenSSL і як яго назаўсёды вылечыць у Node.js.
Крыніцы: Prisma Docker Error: `libssl.so.1.1` Not Found on Alpine
Вы напісалі Dockerfile, запусцілі процес будовы, і все выглядала нормальна. Потым вы запусцілі docker compose up, стараліся стежыць за потокам логаў, і самэ тады, калі прыстанок павінен быў з’яўіцца на звязку з базай дадзенаў, ён перастаў працаваць.
Сімптамы
Якщо што-небудзь з гэтага здаецца вам знайомым, адпаведныя пояснення і способы выправлення прыведзены нижэй:
- Звестка пра збой, у якай паведамляецца, што спільную бібліятэку пад назвай
libssl.so.1.1не можна было знайсці нідзе ў файлавой системе - Праця Prisma не вдалася, пры чым паведамляецца
PrismaClientInitializationError: Unable to require libquery_engine-linux-musl.so.node - Паведамленне пра тое, што Prisma не могла выявіць, якая версія libssl/openssl выкорыстоўваецца
- Контейнер, запусканы за дапамогою
docker compose up, практычна адразу ж завершае свою працę
Полныя паведамленні пра адказ зазвычай выглядаюць так:
Details: Error loading shared library libssl.so.1.1: No such file or directory
(needed by /app/node_modules/.prisma/client/libquery_engine-linux-musl.so.node)
Стак
Сцэнарый, у яком гэта выявілася, абыў такі: API на базе TypeScript у Node.js/Express, Prisma як шар ORM, а PostgreSQL як база дадзеных. Файл Dockerfile быў створаны на базе node:20-alpine — гэта разумны стандарт, адколькі образы на базе Alpine значна легшыя за адпаведныя Debian-образы: яны шырэй падключаюцца, маюць меншую площу для вразламоў і ў цэлым лепей падходзяць для роботы ў працэсе вырабніцтва.
Сам процес збіркі працав без проблем. Це была багатоэтапная настройка: TypeScript скомпілювався у папку dist/, а кліент Prisma был правильна сгенераван. Падэнняў пад час збіркі не было. Проблемы з’явіліся толькі тады, калі команда docker compose up фактычна запусціла контейнер.
Што на самай працоўвае
Alpine Linux спецыяльна створана з мінімумам элементаў. У гэтым і ёе прыемнасць — самэ гэта прычына таго, што ствараныя іміджы такія маленькія. Але самэ мінімалізм означае, што Alpine не мае багато пакетаў, якія іміджы на базе Debian чы Ubuntu включаюць за замовчаннем.
Механізм запитаў Prisma поставляецца у вигляді заздалега скомпільванага бінарнага файлу — файла .so (shared object), які ўключаецца ў ваш проект і пазначаецца, напрыклад, як libquery_engine-linux-musl.so.node. Гэты бінарны файл адпавядае за фактычную взаімнасвязь з вашай базой дадзеных і для сваёй роботы безпосередна завісіць ад OpenSSL.
Важліва тут тое, што гэты бінарны файл механізма запитаў прыўязаны да аднай конкрэтной версіі OpenSSL, а не да OpenSSL як такога. Версія, якая ў яго выкарыстоўваецца, залежыць ад версіі Prisma і версіі Alpine:
- У болей старыях версіях механізма запитаў Prisma ён прыўязаны да OpenSSL 1.1, якая выдае
libssl.so.1.1 - У новыях версіях Prisma ён скомпільваны пад OpenSSL 3, якая выдае
libssl.so.3 - У версіях Alpine да 3.16 як стандарт выкарыстоўвалася OpenSSL 1.1
З-за гэтага точныя прычыны аднойшання залежнаць ад таго, якія версіі Prisma і Alpine вы вжываеце. Аккуратная перачытка тэксту аднойшання паказвае назву конкрэтнай бібліятэкі, якая не ўсёродзе, і libssl.so.1.1 проты libssl.so.3 апяроцьнюе неабходнасць іншага пакета та іншага способу адраджэння.
Прычына, чаму гэта не практыкуецца ўчасьці кампілявання, а выяўляецца толькі пад час запуску, заключаецца у тым, што prisma generate — які выканаўляецца на стадыі кампілявання — ніколі не запускае сам бінарны файл двойчыка. Ён толькі стварае код кліента на TypeScript. Сам бінарны файл двойчыка запускаецца толькі тады, калі запускаецца прыемленае і Prisma прабуе адкрыць з’ёеднанне з базай дадзеных. У гэты момент вы вялікі час знаходзіцеся ў запускаемым контейнеры з несумісной або вялікай бібліятэкай OpenSSL, і немае ўжо жадных паведамленняў прытым, калі ўсё ламаецца.
Рашэння
Рашэннем ёсць явна установка OpenSSL у вашым Dockerfile через менеджар пакетаў Alpine, apk. Каторы пакет вам патрэбен, залежыць абоўсюдзе ад таго, што паведаміла васе спіска праблем.
Якщо ў спісцы праблем упамінана libssl.so.1.1 (OpenSSL 1.1):
У новыяй версіі Alpine (3.17 і вышэ) OpenSSL 1.1 больш не ўстановліваецца за замовчаннем, таму вам патрэбен пакет для сумеснасці:
RUN apk add --no-cache openssl1.1-compat
Якщо ў спісцы праблем упамінана libssl.so.3 (OpenSSL 3), або вы запускаеце няўзрачную версію Prisma на новай версіі Alpine:
RUN apk add --no-cache openssl
Этот крок запускае установку таго варыянту OpenSSL, які ў наявнасці ў рэпазітарыі пакетаў Alpine для тай версіі — у Alpine 3.17+ гэтым ёсць OpenSSL 3.
Найбезпечнейшая стратэгія на далекую перспектыву — це свядома фіксацыя версый вашых компанентоў. Выправіце версію Alpine за дапамой тага base image, а таксама закрепіце версію Prisma ў файле package.json, каб трэбаванні до OpenSSL заставаліся сталявымі і не зменяліся неспадзевана ў розных сэрвісах. У сабе пры звястках пра выданні Prisma пераказваецца, пад якой версіяю OpenSSL скомпільаваны кожны бінарны файл двайчы.
Якщо ваша наладка викорыстоўвае багатастадійную компіляцыю, вам трэба дадаць гэтыя рэквізіты у обох стадіях, адколі кожная з іх працюе на Alpine незалежна:
# Stage 1: Builder
FROM node:20-alpine AS builder
RUN apk add --no-cache openssl
WORKDIR /app
COPY package*.json ./
COPY prisma ./prisma
RUN npm install
COPY . .
RUN npx prisma generate
RUN npm run build
# Stage 2: Production
FROM node:20-alpine AS production
RUN apk add --no-cache openssl
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
COPY prisma ./prisma
RUN npm install --omit=dev
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma
COPY --from=builder /app/dist ./dist
EXPOSE 4000
CMD ["node", "dist/server.js"]
Флаг --no-cache не дазволяе apk зберагаць локальную копію індэксу пакетаў, што дапамагае заставіць розмер гэтага аб’екта малым — што ў паўнай меры адпавядае прычыне, за якой вы ўзагалі выбралі Alpine.
Чаму проста не перайсці на node:slim?
Это справедлівы вопыт. node:20-slim створаны на базе Debian, а не Alpine, і Debian поставляе OpenSSL без дадзейнаўкі, таму гэты конкрэтны спосаб абяцелі здзейснення пераважна зникае. Багато команд проста рабяць гэты перакід і продаваліся далей.
Проблема крыўцяеся ў размерзе адпраўленых зображэнняў. Тыповае адпраўленне Node на базе Alpine мае размер прыблізна 50–70 МБ, тады як адпраўленне Debian slim мае размер ближэй да 150–200 МБ. Якщо падсумаваць гэты разлік для дзесяткаў адпраўленняў, калькі служб і пастойчых запрашэнняў ад усіх членаў команды, размер быстра зрастае. Alpine таксама мае меншую плошчу для атак з самага пачатку, што ў значным ступені мае значэнне, якщо вы працуеце ў средзе з высокімі вакантамі безпекі.
Установка неяўнае пакета за дапамою apk add — это шырока вжываны спосаб, прычыму практычна напісана ў інструкцыях Prisma, і яго вже можна знайсці ў безлічы файлаў Dockerfile для працы. Пераход на образ на базе Debian, каб ухіліцца ад проблем з падборам версій, — цэлком правядзямы выбар, але такім чынам заўсёды важна зрозумець, чаму ўзагалі выступаюць такія несувязі, незалежна ад таго, які базовы образ вы выберазе.
Прымечанне пра будову з калькамі
Якщо два рядкі FROM і синтаксіс --from=builder у пакульнай версіі Dockerfile здаваліся незнайомымі, то тут кароткая адпаведна паслявыяснення.
Dockerfile з адзінам этапам, які кампілюе TypeScript, ў падсумку перадае сам кампілятор TypeScript, ts-node, усі devDependency-ы і вашы необработаныя вучорнікі працыўнае адображэнне. Нічога з гэтага не павінна там быць — гэта збялёжвае адображэнне і расширвае плошчу, выкладзеную на рызык уразлівых момантов.
Будова з калькама этапам правільна раздзеляе элементы: этап будавання мае ў сабе все, што трэба для кампіляцыі проекту, тады як этап працыўнага адображэння запускаецца з чыстай базы і берае толькі тое, што насправды патрэбна ў часе выканання — скомпільаваны выход dist/, залежнасці, неабяжныя толькі для працыўнага адображэння, і створаны Prisma Client. Усе непатрэбнае з этапу будавання автаматычна застаёцца позаду, калі Docker стварае фінальнае адображэнне.
У падсумку у вас будзе лёгкае, гатовае да прыменення адображэнне, якое не носіць з сабою свойяй інструментальная лінейка для будавання.
Пасля выправлення
Праз установана правильная версія OpenSSL, запуск docker compose up нарэшце дае той результат, якога вы спакоўваліся:
✅ Connected to PostgreSQL
🚀 Server listening on port 4000
Як толькі ваша настройка Prisma запускаецца через Docker Compose, наступны крок — апляваць міграцыі да базы дадзеных, якая знаходзіцца ў контейнере, а не да той, якая працуе локальна. Іншымі словамі, трэба запусціць docker compose exec app npx prisma migrate deploy, што выконвае команду міграцыі знутры працюючага контейнера аплікацыі — дзе DATABASE_URL наведзены на вашу службу Postgres, керованую Compose, а не на які-небудзь ресурсы на хоставанай машыне.
Калека OpenSSL выклікае страх праз першы раз, калі з’являецца – густы блакат выходных дадзенняў у бінарным формате, які заканчваецца ENOENT. Але асляпленая прычына є простаю і конкрэтной: двойчаковы моцар Prisma ўскладнены на адной конкрэтной версіі OpenSSL, а Alpine не гарантуе, што гэтая версія справды ўстановлена. Акружна прачытайце паведамленне пра калеку, з’ясавайце, які libssl ён запрашвае, установіце яго вярным чынам і зафіксавайце версіі, каб парадокс заступалася прыгадным незалежна ад таго, дзе запускаецца контейнер.