یک برنامهی جدید عالی در Node نوشتهاید و آمادهی انتشار آن به طور عمومی هستید. به بیان دیگر، بیش از این نمیخواهید آن را روی لپتاپ خود اجرا کنید، بلکه قصد دارید آن را روی یک سرور (Server) و در ارتباط با اینترنت واقعی قرار دهید.
راههای مختلف زیادی برای اجرای یک برنامه در محصول وجود دارد. در این مطلب به اجرای یک برنامه روی یک سرور لینوکس “استاندارد” که از systemd استفاده میکند، میپردازیم. پس ما نمیخواهیم در مورد استفاده از Docker، AWS Lambda، Heroku و یا هر نوع دیگر از محیطهای مدیریت شده صحبت کنیم. فقط شما هستید، کد شما و یک ترمینال با یک نشست ssh.
قبل از آنکه شروع کنیم، اجازه دهید به صورت مختصر در مورد systemd و اینکه چرا به آن اهمیت میدهیم صحبت کنیم.
به طور کلی systemd چیست؟
پاسخ کامل به این پرسش خیلی گسترده است. پس قصد نداریم که به طور کامل به آن پاسخ دهیم چرا که ما به قسمتی از آن نیاز داریم که بتوانیم برنامهی خود را اجرا نماییم. لازم است بدانید که systemd در سرورهای لینوکس نسبتا جدید (new-ish) اجرا میشود و مسئولیت شروع / پایان دادن / اجرای مجدد (starting / stopping / restarting) برنامههای شما را بر عهده دارد. به عنوان مثال اگر شما mysql را نصب کرده باشید، هر زمان که سرور را reboot میکنید، خواهید دید که mysql همچنان در حال اجرا است، علت این موضوع آنست که systemd میداند که mysql را هنگام بالا آمدن سیستم، باید اجرا نماید.
این مکانیزم systemd در سیستمهای لینوکس جدید، جایگزین سیستمهای قدیمیتر همچون init و upstart شده است. به طور خاص منظور از لینوکس های جدید چیست؟ اگر شما از هر کدام از نسخههایی که در ادامه میآید استفاده میکنید، در حال استفاده از systemd هستید:
- CentOS 7 / RHEL 7
- Fedora 15 or newer
- Debian Jessie or newer
- Ubuntu Xenial or newer
اجرای برنامه به صورت دستی
فرض میکنیم شما به تازگی نسخهی Ubuntu Xenial را نصب کردهاید و نام کاربری ubuntu را با دسترسیهای sudo ایجاد کردهاید.
با استفاده از ssh با کاربر ubuntu به سرور خود متصل شده و Node را نصب نمایید.
$ sudo apt-get -y install curl $ curl -sL https://deb.nodesource.com/setup_6.x | sudo bash - $ sudo apt-get -y install nodejs
در ادامه اجازه دهید که یک برنامه را ایجاد کرده و به صورت دستی اجرا کنیم. در اینجا برنامهی کوچکی مینویسیم که متغیرهای محیطی کاربر را چاپ میکند.
const http = require('http'); const hostname = '0.0.0.0'; const port = process.env.NODE_PORT || 3000; const env = process.env; const server = http.createServer((req, res) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); for (var k in env) { res.write(k + ": " + env[k] + "\n"); } res.end(); }); server.listen(port, hostname, () => { console.log("Server running at http://" + hostname + ":" + port + "/"); });
با استفاده از ویرایشگر متن دلخواه خود، متن فوق را در یک فایل به نام hello_env.js و در مسیر پوشهی home/ubuntu/ ذخیره کنید. سپس با دستور زیر آن را اجرا نمایید.
$ /usr/bin/node /home/ubuntu/hello_env.js
حالا در مرورگر خود باید بتوانید به مسیر زیر بروید:
http://11.22.33.44:3000
توجه داشته باشید که ۱۱٫۲۲٫۳۳٫۴۴ را با آدرس IP سرور خود جایگزین نمایید، و متغیرهای محیطی کاربر ubuntu را مشاهده خواهید کرد. اگر چنین چیزی را میبینید، عالی است! میدانیم که برنامه در حال اجرا است و میدانیم که برای اجرای آن به یک command احتیاج داریم. خب حالا Ctrl + c را بزنید و برنامه را متوقف نمایید. حالا میخواهیم به بخش systemd برویم.
ایجاد فایل سرویس system
جادویی که برای کار کردن systemd مورد نیاز است، یک فایل متنی است که فایل service نامیده میشود. به این دلیل از لفظ “جادو” استفاده میکنم که بسیاری از افراد وقتی به این بخش از فرایند میرسند، دچار مشکل میشوند. اما خوشبختانه، به آن سختی که فکر میکنید نیست.
میخواهیم یک فایل در system area که هر چیزی تحت مالکیت کاربر root است، ایجاد کنیم. بنابراین مجموعهای از دستورات را با استفاده از sudo اجرا میکنیم. مجدد تاکید میکنم که نگران نباشید، این کار خیلی سرراست است.
فایلهای سرویس که توسط systemd کنترل میشوند در مسیر زیر هستند:
/lib/systemd/system
بنابراین یک فایل جدید را همانجا ایجاد میکنیم. اگر از Nano به عنوان ویرایشگر استفاده میکنید، یک فایل جدید با دستور زیر ایجاد نمایید:
sudo nano /lib/systemd/system/hello_env.service
و محتوای زیر را درون آن قرار دهید:
[Unit] Description=hello_env.js - making your environment variables rad Documentation=https://example.com After=network.target [Service] Environment=NODE_PORT=3001 Type=simple User=ubuntu ExecStart=/usr/bin/node /home/ubuntu/hello_env.js Restart=on-failure [Install] WantedBy=multi-user.target
حالا اجازه دهید در مورد محتوای فایل توضیح دهیم. در بخش [Unit] همانطور که میبینید متغیرهای Description و Documentation واضح هستند. آنچه که نیاز به توضیح بیشتری دارد بخش
After=network.target
است که به systemd میگوید که زمانی که دستگاه روشن میشود، برنامهی ما زمانی شروع به کار میکند که عملکرد اصلی شبکهی سرور فعال شده باشد. ما به این موضوع نیاز داریم، چرا که برنامهی ما تا زمانی که شبکه بالا نیامده و اجرا نشده است نمیتواند NODE_PORT را اختصاص دهد.
به بخش [Service] میرویم و مغز پروژه امروز را مشاهده میکنیم. ما میتوانیم متغیرهای محیطی را در اینجا تعریف کنیم، بنابراین اولین خط را قرار دادیم:
Environment=NODE_PORT=3001
به این ترتیب، زمانی که برنامهی ما شروع به کار میکند، به پورت ۳۰۰۱ گوش میدهد. این از پورت پیشفرض ۳۰۰۰ که زمان اجرای برنامه به صورت دستی مشاهده کردیم، متفاوت است. اگر چند متغیر محیطی نیاز دارید، میتوانید چندین بار Environment را مشخص کنید. متغیر بعدی عبارتست از
Type=simple
که به systemd میگوید که برنامهی ما چگونه خودش را راهاندازی میکند. به طور مشخص، به systemd اطلاع میدهد که برنامه تلاشی برای از بین بردن دسترسیهای کاربر یا چیزی شبیه به آن نخواهد کرد. برنامه تنها راهاندازی و اجرا خواهد شد. بعد از آن خواهیم دید
User=ubuntu
که به systemd میگوید که برنامه با کاربر (unprivileged) غیر ممتاز ubuntu اجرا خواهد شد. شما قطعا میخواهید که برنامهی شما تحت یک کاربر غیر ممتاز اجرا شود تا مهاجمین (attackers) نتوانند چیزی که با کاربر root اجرا میشود را مورد هدف قرار دهند.
دو بخش بعدی احتمالا بیشتر مورد توجه ما هستند:
ExecStart=/usr/bin/node /home/ubuntu/hello_env.js Restart=on-failure
اولین متغیر، ExecStart به systemd میگوید که کدام دستور باید اجرا شود تا برنامهی ما راهاندازی شود. پس از آن Restart به systemd میگوید زمانی که برنامه متوقف شده است، در چه شرایطی باید برنامه را مجددا راه اندازی کند. عبارت on-failure همان چیزی است که به احتمال زیاد شما میخواهید. با استفاده از این، اگر برنامه به شکلی “تمیز” (cleanly) متوقف شده باشد، در نتیجه مجددا راهاندازی نخواهد شد. منظور از توقف برنامه به شکل تمیز این است که برنامه خودش را با یک خروجی ۰ متوقف کرده است یا با یک سیگنال “تمیز” مانند سیگنال پیش فرضی که توسط دستور kill ارسال میشود، متوقف شده است. اساسا، اگر برنامه طبق خواستهی خودمان متوقف شود، systemd آن را رها کرده و اجازه میدهد متوقف باقی بماند. با این حال، اگر به هر دلیل دیگری (به عنوان مثال یک exception کنترل نشده) برنامه متوقف شود، پس از آن systemd بلافاصله برنامه را مجدد راه اندازی میکند. اگر میخواهید بدون توجه به آنچه که موجب توقف برنامه شده است، آن را مجددا راهاندازی کنید، مقدار on-failure را به always تغییر دهید.
در نهایت به بخش [Install] میرسیم. ما این قسمت را خیلی توضیح نمیدهیم. در این بخش به systemd میگوییم که اگر ما میخواهیم برنامه را در زمان boot سیستم اجرا کنیم، چگونه دستورالعملها را مدیریت کند. احتمالا شما باید از همان مقدار نشان داده شده، در اکثر مواقع استفاده کنید مگر آنکه یک کاربر حرفهای systemd باشید.
چگونه از systemctl برای کنترل برنامهی خود استفاده کنیم
قسمت دشوار کار به پایان رسید! حالا یاد خواهیم گرفت که چگونه از ابزارهای فراهم شده در سیستم برای کنترل برنامهی خود استفاده کنیم. برای شروع، دستور زیر را اجرا کنید:
$ sudo systemctl daemon-reload
شما باید این دستور را هر زمان که یکی از فایلهای service تغییر کرد اجرا نمایید، به این ترتیب systemd اطلاعات جدید را دریافت میکند.
در ادامه، برنامهی خود را اجرا میکنیم:
$ sudo systemctl start hello_env
بعد از آن شما باید قادر باشید با رفتن به آدرس زیر (توجه داشته باشید که ۱۱٫۲۲٫۳۳٫۴۴ را با آدرس IP سرور خود جایگزین نمایید) در مرورگر خود، خروجی را مشاهده نمایید.
http://11.22.33.44:3001
اگر این طور است، تبریک میگوییم! شما برنامهی خود را با استفاده از systemd اجرا کردهاید. اگر خروجی برنامه با زمانی که برنامه را به صورت دستی اجرا کردید، خیلی متفاوت است، نگران نباشید، این موضوع عادی است. وقتی systemd یک برنامه را اجرا میکند، نسبت به زمانی که شما به یک ماشین از طریق ssh متصل شده و برنامه را اجرا کردهاید، از محیط اجرای بسیار مختصرتری استفاده میکند. به طور خاص، متغیرهای محیطی HOME$ ممکن است به طور پیشفرض تنظیم نشده باشند. بنابراین به این نکته توجه داشته باشید اگر برنامهی شما از هر کدام از متغیرهای محیطی استفاده میکند، زمانی که از systemd استفاده میکنید، شما احتمالا باید خودتان آنها را تنظیم نمایید.
اگر بخواهید از وضعیتی که systemd در مورد یک برنامه میداند، مطلع شوید، میتوانید از دستور زیر استفاده کنید.
$ sudo systemctl status hello_env
برای متوقف کردن برنامهی خود، به راحتی دستور زیر را اجرا کنید:
$ sudo systemctl stop hello_env
و خیلی عالی است که دستور زیر، برنامهی ما را به صورت مجدد (restart) راهاندازی میکند:
$ sudo systemctl restart hello_env
اگر میخواهید برنامه، زمانی که ماشین boot میشود، راهاندازی گردد، با دستور زیر آن را فعال (enable) کنید:
$ sudo systemctl enable hello_env
در نهایت، اگر قبلا یک برنامه را فعال کردهاید، و حالا نظر شما تغییر کرده و میخواهید از راهاندازی برنامه هنگام boot شدن ماشین، جلوگیری نمایید، با دستور زیر آن را غیرفعال (disable) نمایید:
$ sudo systemctl disable hello_env
منبع: Running Your Node.js App With Systemd
ترجمه: سیدمحمدحسین طباطبایی بالا