اجرای API به عنوان یک سرویس پسزمینه
اکنون که میدانیم فایل اجرایی API ما روی droplet تولیدی به خوبی کار میکند، مرحله بعدی پیکربندی آن برای اجرا به عنوان یک سرویس پسزمینه است، از جمله شروع خودکار هنگام ریبوت شدن droplet.
ابزارهای مختلفی وجود دارد که میتوانیم برای این کار استفاده کنیم، اما در این کتاب از systemd استفاده خواهیم کرد — مجموعهای از ابزارها برای مدیریت سرویسها که با Ubuntu (و بسیاری از توزیعهای دیگر Linux) عرضه میشود.
برای اجرای برنامه API ما به عنوان یک سرویس پسزمینه، اولین کاری که باید انجام دهیم ایجاد یک فایل واحد است که به systemd میگوید چگونه و چه زمانی سرویس را اجرا کند.
اگر در حال دنبال کردن هستید، به پنجره ترمینال در ماشین محلی خود بازگردید و یک فایل جدید remote/production/api.service در پروژه خود ایجاد کنید:
$ mkdir remote/production $ touch remote/production/api.service
و سپس کد زیر را اضافه کنید:
[Unit] # Description is a human-readable name for the service. Description=Greenlight API service # Wait until PostgreSQL is running and the network is "up" before starting the service. After=postgresql.service After=network-online.target Wants=network-online.target # Configure service start rate limiting. If the service is (re)started more than 5 times # in 600 seconds then don't permit it to start anymore. StartLimitIntervalSec=600 StartLimitBurst=5 [Service] # Execute the API binary as the greenlight user, loading the environment variables from # /etc/environment and using the working directory /home/greenlight. Type=exec User=greenlight Group=greenlight EnvironmentFile=/etc/environment WorkingDirectory=/home/greenlight ExecStart=/home/greenlight/api -port=4000 -db-dsn=${GREENLIGHT_DB_DSN} -env=production # Automatically restart the service after a 5-second wait if it exits with a non-zero # exit code. If it restarts more than 5 times in 600 seconds, then the rate limit we # configured above will be hit and it won't be restarted anymore. Restart=on-failure RestartSec=5 [Install] # Start the service automatically at boot time (the 'multi-user.target' describes a boot # state when the system will accept logins). WantedBy=multi-user.target
اکنون که فایل واحد را تنظیم کردهایم، مرحله بعدی نصب این فایل واحد روی droplet و راهاندازی سرویس است. در اصل باید سه کار انجام دهیم:
- برای نصب فایل واحد، باید آن را به پوشه
/etc/systemd/system/در droplet خود کپی کنیم. از آنجا که این پوشه توسط کاربرrootدر droplet ما مالکیت شده است، باید این کار را به دو مرحله تقسیم کنیم: اول فایل واحد را به پوشه خانه کاربرgreenlightکپی کنیم و دوم از دستورsudo mvبرای انتقال آن به مکان نهایی استفاده کنیم. - سپس باید دستور
systemctl enable apiرا روی droplet خود اجرا کنیم تاsystemdاز فایل واحد جدید مطلع شود و سرویس را به طور خودکار هنگام ریبوت شدن droplet فعال کند. - در نهایت، باید
systemctl restart apiرا اجرا کنیم تا سرویس را شروع (یا ریاستارت) کنیم.
همه این سه کار باید با sudo اجرا شوند.
بیایید قاعده makefile production/deploy/api را به روز کنیم تا این کارها را برای ما خودکار کند. به این صورت:
... # ==================================================================================== # # PRODUCTION # ==================================================================================== # production_host_ip = '161.35.71.158' ... ## production/deploy/api: deploy the api to production .PHONY: production/deploy/api production/deploy/api: rsync -P ./bin/linux_amd64/api greenlight@${production_host_ip}:~ rsync -rP --delete ./migrations greenlight@${production_host_ip}:~ rsync -P ./remote/production/api.service greenlight@${production_host_ip}:~ ssh -t greenlight@${production_host_ip} '\ migrate -path ~/migrations -database $$GREENLIGHT_DB_DSN up \ && sudo mv ~/api.service /etc/systemd/system/ \ && sudo systemctl enable api \ && sudo systemctl restart api \ '
فایل makefile را ذخیره کنید، سپس قاعده production/deploy/api را دوباره اجرا کنید. هنگام اجرا باید رمز عبور کاربر greenlight را وارد کنید و قاعده باید بدون خطا تکمیل شود. خروجی که میبینید باید شبیه به این باشد:
$ make production/deploy/api
rsync -P ./bin/linux_amd64/api greenlight@"161.35.71.158":~
api
7,700,480 100% 2.39GB/s 0:00:00 (xfr#1, to-chk=0/1)
rsync -rP --delete ./migrations greenlight@"161.35.71.158":~
sending incremental file list
migrations/000001_create_movies_table.down.sql
28 100% 0.00kB/s 0:00:00 (xfr#1, to-chk=11/13)
migrations/000001_create_movies_table.up.sql
286 100% 279.30kB/s 0:00:00 (xfr#2, to-chk=10/13)
migrations/000002_add_movies_check_constraints.down.sql
198 100% 193.36kB/s 0:00:00 (xfr#3, to-chk=9/13)
migrations/000002_add_movies_check_constraints.up.sql
289 100% 282.23kB/s 0:00:00 (xfr#4, to-chk=8/13)
migrations/000003_add_movies_indexes.down.sql
78 100% 76.17kB/s 0:00:00 (xfr#5, to-chk=7/13)
migrations/000003_add_movies_indexes.up.sql
170 100% 166.02kB/s 0:00:00 (xfr#6, to-chk=6/13)
migrations/000004_create_users_table.down.sql
27 100% 26.37kB/s 0:00:00 (xfr#7, to-chk=5/13)
migrations/000004_create_users_table.up.sql
294 100% 287.11kB/s 0:00:00 (xfr#8, to-chk=4/13)
migrations/000005_create_tokens_table.down.sql
28 100% 27.34kB/s 0:00:00 (xfr#9, to-chk=3/13)
migrations/000005_create_tokens_table.up.sql
203 100% 198.24kB/s 0:00:00 (xfr#10, to-chk=2/13)
migrations/000006_add_permissions.down.sql
73 100% 71.29kB/s 0:00:00 (xfr#11, to-chk=1/13)
migrations/000006_add_permissions.up.sql
452 100% 441.41kB/s 0:00:00 (xfr#12, to-chk=0/13)
rsync -P ./remote/production/api.service greenlight@"161.35.71.158":~
api.service
1,266 100% 0.00kB/s 0:00:00 (xfr#1, to-chk=0/1)
ssh -t greenlight@"161.35.71.158" '\
migrate -path ~/migrations -database $GREENLIGHT_DB_DSN up \
&& sudo mv ~/api.service /etc/systemd/system/ \
&& sudo systemctl enable api \
&& sudo systemctl restart api \
'
no change
[sudo] password for greenlight:
Created symlink /etc/systemd/system/multi-user.target.wants/api.service → /etc/systemd/system/api.service.
Connection to 161.35.71.158 closed.
در مرحله بعد، به droplet متصل شوید و وضعیت سرویس جدید api را با استفاده از دستور sudo systemctl status api بررسی کنید:
$ make production/connect
greenlight@greenlight-production:~$ sudo systemctl status api
● api.service - Greenlight API service
Loaded: loaded (/etc/systemd/system/api.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2023-02-23 17:28:18 CET; 1min 7s ago
Main PID: 2717 (api)
Tasks: 5 (limit: 512)
Memory: 2.9M
CPU: 10ms
CGroup: /system.slice/api.service
└─2717 /home/greenlight/api -port=4000 -db-dsn=postgres://greenlight:pa55word1234@localhost/greenlight -env=production
Feb 23 17:28:18 greenlight-production systemd[1]: Starting Greenlight API service...
Feb 23 17:28:18 greenlight-production systemd[1]: Started Greenlight API service.
Feb 23 17:28:18 greenlight-production api[2717]: time=2023-02-23T17:28:18.722+02:00 level=INFO msg="database connection pool established"
Feb 23 17:28:18 greenlight-production api[2717]: time=2023-02-23T17:28:18.722+02:00 level=INFO msg="starting server" ...
عالی! این تأیید میکند که سرویس api ما با موفقیت در پسزمینه در حال اجرا است و در مورد من، شناسه فرآیند (PID) 2717 را دارد.
برای کنجکاوی، بیایید فرآیندهای در حال اجرا برای کاربر greenlight خود را به سرعت لیست کنیم:
greenlight@greenlight-production:~$ ps -U greenlight PID TTY TIME CMD 2717 ? 00:00:00 api 2749 ? 00:00:00 systemd 2750 ? 00:00:00 (sd-pam) 2807 ? 00:00:00 sshd 2808 pts/0 00:00:00 bash 2859 pts/0 00:00:00 ps
این با هم همخوانی دارد — میتوانیم فرآیند api را با شناسه فرآیند 2717 اینجا ببینیم، که تأیید میکند کاربر greenlight ما در حال اجرای فایل اجرایی api است.
در نهایت، از دیدگاه خارجی بررسی کنیم و دوباره درخواستی به endpoint healthcheck خود بفرستیم، چه در مرورگر و چه با curl. باید پاسخ موفقی مانند این دریافت کنید:
$ curl 161.35.71.158:4000/v1/healthcheck
{
"status": "available",
"system_info": {
"environment": "production",
"version": "1c9b6ff48ea800acdf4f5c6f5c3b62b98baf2bd7"
}
}
ریاستارت پس از ریبوت
با اجرای دستور systemctl enable api در فایل makefile پس از کپی کردن فایل واحد systemd، سرویس API ما باید به طور خودکار پس از ریبوت شدن droplet شروع شود.
اگر میخواهید، میتوانید خودتان این را تأیید کنید. در حالی که از طریق SSH به droplet متصل هستید، آن را ریبوت کنید (مطمئن شوید که به droplet متصل هستید و به طور تصادفی ماشین محلی خود را ریبوت نکنید!).
greenlight@greenlight-production:~$ sudo reboot greenlight@greenlight-production:~$ Connection to 161.35.71.158 closed by remote host. Connection to 161.35.71.158 closed. make: *** [Makefile:91: production/connect] Error 255
یک دقیقه صبر کنید تا ریبوت کامل شود و droplet دوباره آنلاین شود. سپس باید بتوانید دوباره متصل شوید و از systemctl status استفاده کنید تا بررسی کنید سرویس دوباره در حال اجرا است. به این صورت:
$ make production/connect
greenlight@greenlight-production:~$ sudo systemctl status api.service
[sudo] password for greenlight:
● api.service - Greenlight API service
Loaded: loaded (/etc/systemd/system/api.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2023-02-23 17:34:55 CET; 1min 33s ago
Main PID: 895 (api)
Tasks: 5 (limit: 512)
Memory: 8.8M
CPU: 15ms
CGroup: /system.slice/api.service
└─895 /home/greenlight/api -port=4000 -db-dsn=postgres://greenlight:pa55word1234@localhost/greenlight -env=production
Feb 23 17:28:18 greenlight-production systemd[1]: Starting Greenlight API service...
Feb 23 17:28:18 greenlight-production systemd[1]: Started Greenlight API service.
Feb 23 17:28:18 greenlight-production api[2717]: time=2023-02-23T17:28:18.722+02:00 level=INFO msg="database connection pool established"
Feb 23 17:28:18 greenlight-production api[2717]: time=2023-02-23T17:28:18.722+02:00 level=INFO msg="starting server" ...
غیرفعال کردن پورت 4000
اگر مراحل این بخش از کتاب را دنبال کردهاید، بیایید تغییر موقت فایروال را که قبلاً ایجاد کردیم برگردانیم و ترافیک روی پورت 4000 را دوباره غیرفعال کنیم.
greenlight@greenlight-production:~$ sudo ufw delete allow 4000/tcp Rule deleted Rule deleted (v6) greenlight@greenlight-production:~$ sudo ufw status Status: active To Action From -- ------ ---- 22 ALLOW Anywhere 80/tcp ALLOW Anywhere 443/tcp ALLOW Anywhere 22 (v6) ALLOW Anywhere (v6) 80/tcp (v6) ALLOW Anywhere (v6) 443/tcp (v6) ALLOW Anywhere (v6)
اطلاعات تکمیلی
گوش دادن روی یک پورت محدود
اگر قصد ندارید برنامه خود را پشت یک reverse proxy اجرا کنید و میخواهید مستقیماً روی پورت 80 یا 443 درخواستها را گوش دهید، باید فایل واحد خود را طوری تنظیم کنید که سرویس قابلیت CAP_NET_BIND_SERVICE داشته باشد (که به آن اجازه اتصال به یک پورت محدود را میدهد). به عنوان مثال:
[Unit] Description=Greenlight API service After=postgresql.service After=network-online.target Wants=network-online.target StartLimitIntervalSec=600 StartLimitBurst=5 [Service] Type=exec User=greenlight Group=greenlight CapabilityBoundingSet=CAP_NET_BIND_SERVICE AmbientCapabilities=CAP_NET_BIND_SERVICE EnvironmentFile=/etc/environment WorkingDirectory=/home/greenlight ExecStart=/home/greenlight/api -port=80 -db-dsn=${GREENLIGHT_DB_DSN} -env=production Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target
مشاهده لاگها
امکان مشاهده لاگهای سرویس پسزمینه شما با استفاده از دستور journalctl وجود دارد، به این صورت:
$ sudo journalctl -u api -- Logs begin at Sun 2021-04-18 14:55:45 EDT, end at Mon 2021-04-19 03:39:55 EDT. -- Apr 19 03:24:35 greenlight-production systemd[1]: Starting Greenlight API service... Apr 19 03:24:35 greenlight-production systemd[1]: Started Greenlight API service. Apr 19 03:24:35 greenlight-production api[6997]: time=2023-02-23T17:28:18.722+02:00 level=INFO msg="database connection pool established" Apr 19 03:24:35 greenlight-production api[6997]: time=2023-02-23T17:28:18.722+02:00 level=INFO msg="starting server" ...
دستور journalctl واقعاً قدرتمند است و طیف گستردهای از پارامترها را ارائه میدهد که میتوانید از آنها برای فیلتر کردن پیامهای لاگ و سفارشی کردن قالببندی استفاده کنید. این مقاله مقدمه عالی برای جزئیات journalctl ارائه میدهد و ارزش خواندن دارد.
پیکربندی ارائهدهنده SMTP
فایل واحد ما در حال حاضر برای شروع API ما با دستور زیر تنظیم شده است:
ExecStart=/home/greenlight/api -port=4000 -db-dsn=${GREENLIGHT_DB_DSN} -env=production
مهم است که به یاد داشته باشید که به جز پرچمهای port، db-dsn و env که اینجا مشخص میکنیم، برنامه ما همچنان از مقادیر پیشفرض برای تنظیمات دیگر استفاده میکند که در فایل cmd/api/main.go کدنویسی شده است — از جمله اعتبارنامههای SMTP برای صندوق Mailtrap شما. در شرایط عادی، باید اعتبارنامههای SMTP تولیدی خود را به عنوان بخشی از این دستور در فایل واحد تنظیم کنید.
گزینههای اضافی فایل واحد
فایلهای واحد Systemd طیف گستردهای از گزینههای پیکربندی را ارائه میدهند و ما فقط سطح را در این فصل خراش دادهایم. برای اطلاعات بیشتر، مقاله فهم واحدها و فایلهای واحد Systemd یک مرور واقعاً خوب است و میتوانید اطلاعات جامع (هرچند متراکم) را در صفحات راهنما پیدا کنید.
همچنین این gist وجود دارد که گزینههای امنیتی مختلفی را که میتوانید برای تقویت سرویس خود استفاده کنید، توضیح میدهد.