התהליך הוא המכולה
בשרת, האפליקציה שלכם היא תהליך עם PID. כל מה שמסביב — systemd, Docker, PM2 — קיים כדי להחזיר אותו לחיים אם הוא מת, ולהעביר אליו בקשות.
להאזין נכון
app.listen(port, "0.0.0.0", () => log(`listening on ${port}`));
0.0.0.0 = כל הממשקים. 127.0.0.1 = רק המכונה עצמה (מתאים כשפרוקסי יושב על אותה מכונה). אם האפליקציה מאזינה ל-127.0.0.1 ומגיעה בקשה מבחוץ — היא תתקבל בידי הפרוקסי ולא תמצא את מי לענות.
SIGTERM: לכבות יפה
כשמעלים גרסה חדשה, שולחים לתהליך SIGTERM. אם אתם לא מטפלים בו, הבקשות באמצע נחתכות.
process.on("SIGTERM", async () => {
server.close(async () => {
await db.close();
process.exit(0);
});
setTimeout(() => process.exit(1), 10_000).unref(); // לא להיתקע לנצח
});
זה מה שהופך פריסה ל"אפס השבתה" בפועל: השרת הישן מפסיק לקבל בקשות חדשות, מסיים את מה שהתחיל, ורק אז מת.
PID 1
במכולה, התהליך הראשון הוא PID 1. ל-PID 1 בלינוקס אין טיפול ברירת מחדל באותות, ולכן CMD ["npm","start"] הוא בעיה: npm הוא PID 1 ומקבל את האות במקום Node. לכן:
CMD ["node", "server.js"]
או docker run --init, שמוסיף תהליך init קטן שמטפל באותות ובזומבים.
הרשאות
- לא להריץ כ-root. משתמש ייעודי עם הרשאה רק לתיקיית האפליקציה.
- קבצים מוגנים:
.envבהרשאות600. - פורטים מתחת ל-1024 דורשים הרשאה מיוחדת — עדיף פרוקסי (nginx/Caddy/Traefik) שמאזין ל-443 ומעביר ל-3000.
משאבים: להגביל כדי לא ליפול
docker run --memory=512m --cpus=1 --restart=unless-stopped myapp
בלי הגבלה, אפליקציה עם דליפת זיכרון תאכל את כל השרת ותפיל גם את בסיס הנתונים. עם הגבלה — היא נופלת לבד, ו-restart מחזיר אותה, והשרת נשאר בחיים.