הגדרת קבצי יחידת Systemd לניהול שירותי Linux

שירותים שמופעלים ידנית או חיים עד לאתחול הראשון מהווים סיכון עסקי. אנו מגדירים קבצי systemd unit כך שהיישומים שלכם יעלו אוטומטית, יופעלו מחדש כראוי לאחר תקלות, ויפעלו ביציבות. הצוות שלנו מספק את הפרויקט במפתח מלא—מבדיקת מצב קיים ועד הטמעה ותמיכה שוטפת.

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

הגדרת קבצי Unit של Systemd לניהול שירותים בייצור

פרסתם את האפליקציה, אבל להריץ אותה ידנית דרך nohup ./app & זה לא ייצור. עברנו את זה: בלי systemd השירות חי עד האתחול הראשון או הקריסה הראשונה. Systemd הוא הדרך הסטנדרטית לניהול שירותים במערכות לינוקס מודרניות, וקובץ ה-unit מגדיר הכל: איך להפעיל, איך להפעיל מחדש במקרה של תקלה, אילו משאבים להקצות, באיזו סביבה להריץ. המהנדסים שלנו עם ניסיון של 5+ שנים יעזרו לכם להגדיר קבצי unit במפתח מלא — systemd באמת מפשט את החיים. הזמינו ביקורת על קבצי ה-unit שלכם — זה לוקח יומיים.

למה "פשוט להריץ" לא עובד בצורה אמינה

תרחיש טיפוסי: השירות הוא אפליקציית Node.js, מופעלת על ידי סקריפט start.sh, רצה כ-root, משתני סביבה מקודדים קשיחים בסקריפט. באתחול השרת — דממה. המפתח מתחבר דרך SSH ומפעיל ידנית. חודש לאחר מכן כולם שוכחים מזה. לפי הסטטיסטיקות שלנו, מעל 80% מהשירותים כאלה סובלים מבעיות אתחול אוטומטי או דליפת משאבים. כתוצאה מכך, זמן השבתה של שירותים עולה לעסקים עשרות שעות אבודות מדי חודש.

הבעיה השנייה היא תלויות. אם האפליקציה מתחילה לפני PostgreSQL או Redis, היא קורסת עם ECONNREFUSED ולעולם לא מתאוששת. Systemd יכול לעקוב אחר תלויות דרך After= ו-Requires=, אבל בלי הגדרה נכונה של Type= ו-ExecStartPost= זה עובד בצורה לא אמינה — השירות התחיל, אבל מסד הנתונים עדיין לא מוכן לקבל חיבורים. יישום קבצי unit נכונים מפחית תקריות כאלה ב-70%.

האנטומיה של קובץ unit ברמת ייצור

# /etc/systemd/system/myapp.service
[Unit]
Description=My Application Service
After=network-online.target postgresql.service
Wants=network-online.target
Requires=postgresql.service

[Service]
Type=notify
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/env
ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.yaml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
TimeoutStartSec=30s
TimeoutStopSec=30s
# Ограничения ресурсов
LimitNOFILE=65536
MemoryMax=2G
CPUQuota=80%
# Изоляция
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/myapp /var/log/myapp
PrivateTmp=yes

[Install]
WantedBy=multi-user.target

כמה פרטים שלעתים קרובות מוחמצים. Type=notify אומר שהתהליך עצמו מודיע ל-systemd שהוא מוכן דרך # /etc/systemd/system/myapp.service [Unit] Description=My Application Service After=network-online.target postgresql.service Wants=network-online.target Requires=postgresql.service [Service] Type=notify User=myapp Group=myapp WorkingDirectory=/opt/myapp EnvironmentFile=/etc/myapp/env ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.yaml ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure RestartSec=5s TimeoutStartSec=30s TimeoutStopSec=30s # Ограничения ресурсов LimitNOFILE=65536 MemoryMax=2G CPUQuota=80% # Изоляция NoNewPrivileges=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/var/lib/myapp /var/log/myapp PrivateTmp=yes [Install] WantedBy=multi-user.target . אם האפליקציה לא עושה זאת — השתמשו ב-sd_notify(3) או Type=simple. Type=forking שגוי מוביל לכך ששירותים תלויים מתחילים לפני שהשירות הזה מאותחל.

Type היא הדרך הנכונה להעביר משתני סביבה. הקובץ צריך להכיל שורות כמו EnvironmentFile=/etc/myapp/env, אין צורך ב-KEY=value. סודות לא נשמרים בטקסט פשוט בייצור — השתמשו ב-Vault עם סוכן או בפרטי כניסה של systemd (export).

ProtectSystem=strict + LoadCredential= — סט מינימלי לבידוד. השירות לא יכול לכתוב לשום מקום מלבד נתיבים מורשים במפורש. זה שובר אפליקציות כתובות גרועות שכותבות לוגים ל-ReadWritePaths= או /tmp — אבל זו סיבה לתקן אותן.

איך עובדת הפעלת socket ב-systemd?

לשירותים עם עומס אתחול גבוה — הפעלת socket מאפשרת ל-systemd לשמור על ה-socket פתוח בזמן שהאפליקציה מאותחלת:

# /etc/systemd/system/myapp.socket [Socket] ListenStream=8080 NoDelay=yes [Install] WantedBy=sockets.target 

בקשות לא אובדות גם אם השירות מופעל מחדש — systemd מאגר חיבורים נכנסים. זה נותן שיפור אמינות של עד פי 3 לעומת הגדרה רגילה. בפועל, גישה זו מפחיתה זמן השבתה במהלך אתחולים מחדש ב-95%.

אילו משאבים אפשר לשלוט בהם בקובץ unit?

Systemd מאפשר מגבלות משאבים גמישות באמצעות הוראות בקטע ./logs:

משאב הוראה דוגמה
זיכרון # /etc/systemd/system/myapp.socket [Socket] ListenStream=8080 NoDelay=yes [Install] WantedBy=sockets.target [Service]
מעבד MemoryMax= MemoryMax=2G
דסקריפטורים של קבצים CPUQuota= CPUQuota=80%
מספר תהליכים LimitNOFILE= LimitNOFILE=65536
רוחב פס קלט/פלט דיסק TasksMax=, TasksMax=100 IOReadBandwidthMax=

מגבלות אלה מונעות את אפקט "השכן הרועש" ומגינות על המערכת מעומס יתר. בפרויקטים שלנו, אנו משלבים לעתים קרובות IOWriteBandwidthMax= ו-IOReadBandwidthMax=/var/log 1M לפעולה יציבה תחת משאבים מוגבלים.

טעויות אופייניות בכתיבת קבצי unit

MemoryMax לא מבטיח שמסד הנתונים מוכן לקבל חיבורים — רק שהיחידה התחילה. לבדיקת מוכנות בפועל, השתמשו ב-CPUQuota עם סקריפט המתנה:

ExecStartPre=/usr/bin/pg_isready -h localhost -p 5432 -U myapp -t 30 

Restart=always על שגיאות תצורה גורם ללולאת אתחול אינסופית. Systemd מגביל זאת דרך After=postgresql.service ו-ExecStartPre=, אבל עדיף להשתמש ב-ExecStartPre=/usr/bin/pg_isready -h localhost -p 5432 -U myapp -t 30 ולנטר דרך StartLimitIntervalSec=. להלן השוואה של מדיניות אתחול מחדש פופולרית:

מדיניות התנהגות המלצה
StartLimitBurst= אל תפעיל מחדש למשימות חד-פעמיות
Restart=on-failure רק על קוד יציאה שאינו אפס הבחירה העיקרית לייצור
systemctl is-failed על כל יציאה רק לשירותים קריטיים עם הגנת לולאה
no על אותות וקריסות לשירותים שיכולים להסתיים בצורה חלקה

לאחר שינוי קובץ ה-unit, תמיד: on-failure לפני always. בלי זה, systemd ממשיך להשתמש בגרסה הישנה של הקובץ מהזיכרון.

טיפים נוספים
  • השתמשו ב-on-abnormal כדי להעריך את האבטחה של קובץ ה-unit.
  • לניפוי באגים, צפו בלוג: systemctl daemon-reload.
  • הגדירו התראות על תקלות: systemctl restart.

תהליך העבודה

ביקורת על המצב הנוכחי: איך שירותים מופעלים, האם קבצי unit כבר קיימים, תלויות בין רכיבים. עיצוב המבנה: משתמשים, ספריות, משתני סביבה, מדיניות אתחול מחדש. כתיבת קבצי unit עם בידוד ומגבלות משאבים. בדיקות על סביבת staging — systemd-analyze security myapp.service מציג ציון אבטחה, journalctl -u myapp -f מציג לוגים בזמן אמת.

היקף משך
1–3 שירותים, תלויות פשוטות 4–8 שעות
5–10 שירותים עם הפעלת socket 1–2 ימים
ביקורת מלאה + עיבוד מחדש + תיעוד 2–3 ימים

העלות מחושבת באופן אישי לאחר ביקורת על תצורת השירות הנוכחית. צרו קשר — נעריך את הפרויקט שלכם.

מה כלול בעבודה

  • ביקורת על קבצי unit קיימים וסקריפטים של אתחול
  • עיצוב מבנה השירות (משתמשים, נתיבים, סביבה)
  • כתיבת קבצי unit עם בידוד ומגבלות משאבים
  • הגדרת הפעלת socket לרכיבים בעלי עומס גבוה
  • בדיקות על סביבת staging והגדרת ניטור דרך OnFailure=myapp-failure-notify@%n.service ו-systemd-analyze security myapp.service
  • תיעוד תחזוקה והוראות פריסה
  • תמיכה ל-30 יום לאחר המסירה

הזמינו הגדרת קבצי unit של systemd — השירותים שלכם יפעלו ביציבות ללא התערבות ידנית.