【翻譯】使用 Systemd 將 Flask 應用程序做爲服務運行

翻譯
Running a Flask Application as a Service with Systemdweb

在服務器上部署應用程序時,須要確保應用程序不間斷地運行。若是應用程序崩潰,則但願它自動重啓,若是服務器斷電,則但願該應用程序在恢復電源後當即啓動。 基本上,您須要的是監視應用程序並在發現再也不運行時將其重啓。 ​sql

在之前的教程中,我向你展現瞭如何使用supervisord(一種用Python編寫的第三方實用程序)實現此功能。 今天,我將向您展現基於 systemd 的相似解決方案,它是許多 Linux發行版中的本地組件,包括Debian衍生產品(如Ubuntu)和 RedHat 衍生產品(如Fedora和CentOS)。 ​flask

使用 Systemd 配置服務

Systemd 經過稱爲 unit 的實體進行配置。 有幾種類型的 unit,包括服務,套接字,設備,計時器等。 對於服務,unit 配置文件必須具備.service擴展名。 在下面,你能夠看到服務 unit 配置文件的基本結構:ubuntu

[Unit]
Description=<a description of your application>
After=network.target

[Service]
User=<username>
WorkingDirectory=<path to your app>
ExecStart=<app start command>
Restart=always

[Install]
WantedBy=multi-user.target
複製代碼

[Unit]部分是全部類型的 unit 配置文件的公共部分。它用於配置關於 unit 和任何依賴項的通常信息,這些信息有助於系統肯定啓動順序。在個人模板中,我添加了服務的描述,而且我還指定我但願個人應用程序在網絡子系統初始化後啓動,由於它是一個 web 應用程序。 ​bash

[Service]部分包含了特定於你的應用程序的詳細信息。我使用最多見的選項來定義運行服務的用戶、起始目錄和執行命令。Restart 選項告訴 systemd,除了在系統啓動時啓動服務外,若是應用程序退出,我還但願從新啓動它。這樣能夠解決崩潰或其餘可能致使進程結束的意外問題。 ​服務器

最後,[Install]部分將配置啓用該 unit 的方式和時間。經過添加 WantedBy=multi-user.target行我告訴 systemd 在系統以多用戶模式運行時激活這個 unit,這是 Unix 服務器在運行時的正常模式。若是你想了解更多關於多用戶模式的細節,請參閱關於 Unix runlevels的討論。 ​markdown

unit 配置文件添加到 /etc/systemd/system 目錄中,供 systemd 查看。每次添加或修改單元文件時,必須告訴 systemd 刷新其配置:網絡

$ sudo systemctl daemon-reload
複製代碼

而後,您可使用 systemctl <action> <service-name> 命令啓動、中止、從新啓動或得到服務狀態:app

$ sudo systemctl start <service-name>
$ sudo systemctl stop <service-name>
$ sudo systemctl restart <service-name>
$ sudo systemctl status <service-name>
複製代碼

注意: 可使用 service 命令來管理服務,而不是使用 systemctl。在大多數發行版中,service 命令映射到 systemctl 並給出相同的結果。 ​ide

爲 Flask 應用程序編寫系統配置文件

若是你想爲你本身的應用程序建立一個 systemd 服務文件,只須要使用上面的模板並填寫 DescriptionUserWorkingDirectory 和 ExecStart 便可。 ​

做爲一個例子,假設我想在 Linux 服務器上部署 Flask Mega-Tutorial 中提到的 microblog 應用程序,可是我想使用 systemd 來監視這個 process,而不是使用 supervisord。 ​

做爲你的參考,這裏是我在教程中使用的 supervisord  配置文件:

[program:microblog]
command=/home/ubuntu/microblog/venv/bin/gunicorn -b localhost:8000 -w 4 microblog:app
directory=/home/ubuntu/microblog
user=ubuntu
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
複製代碼

systemd的等效單元配置文件將寫入/etc/systemd/system/microblog.service中,並將具備如下內容:

[Unit]
Description=Microblog web application
After=network.target

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/microblog
ExecStart=/home/ubuntu/microblog/venv/bin/gunicorn -b localhost:8000 -w 4 microblog:app
Restart=always

[Install]
WantedBy=multi-user.target
複製代碼

請注意,啓動命令如何到達虛擬環境內部以獲取可執行gunicorn。 這等效於激活虛擬環境,而後在沒有路徑的狀況下運行 gunicorn,可是這樣作的好處是能夠在單個命令中完成。 ​

將這個文件添加到你的系統後,你可使用如下命令啓動服務:

$ sudo systemctl daemon-reload
$ sudo systemctl start microblog
複製代碼

環境變量

若是 Flask 應用程序但願提早設置一個或多個環境變量,那麼能夠將它們添加到服務文件中。例如,若是須要設置 FLASK_CONFIGDATABASE_URL 變量,可使用 Environment 選項定義它們以下:

[Unit]
Description=Microblog web application
After=network.target

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/microblog
Environment=FLASK_CONFIG=production
Environment=DATABASE_URL=sqlite:////path/to/the/database.sqlite
ExecStart=/home/ubuntu/microblog/venv/bin/gunicorn -b localhost:8000 -w 4 microblog:app
Restart=always

[Install]
WantedBy=multi-user.target
複製代碼

請注意,若是你遵循個人教程風格,併爲環境變量使用.env文件,則無需經過 systemd 服務文件添加它們。 實際上,我更喜歡經過.env文件處理環境,由於這是一種適用於開發和生產的統一方法。 ​

訪問日誌

Systemd 有一個稱爲 journal 的日誌記錄子系統,由 journald 守護進程實現,它收集全部正在運行的 Systemd 單元的日誌。可使用 journalctl 實用工具查看日記的內容。下面是一些常見日誌訪問命令的示例。 ​

查看 microblog 服務的日誌:

$ journalctl -u microblog
複製代碼

查看 microblog 服務的最後25個日誌條目:

$ journalctl -u microblog -n 25
複製代碼

跟蹤 microblog 服務的日誌:

$ journalctl -u microblog -f
複製代碼

還有更多的選項可用。運行 journalctl --help 查看更完整的選項摘要。

高級用法: 使用 Systemd 的運行 Worker Pools

若是你使用 Celery 運行後臺進程,則將上述解決方案擴展到適用於你的 workers 是很簡單,由於 Celery容許你使用單個命令啓動 worker 進程池。 這實際上與處理帶有多個 worker 的gunicorn的方式相同,所以您要作的就是建立第二個.service文件來管理 Celery 主進程,該文件又將管理 worker。 ​

可是,若是你讀到了個人 Flask Mega-Tutorial 的最後幾章,你就會知道我已經引入了一個基於RQ的任務隊列來執行後臺任務。 使用RQ時,您必須單獨啓動 workers,沒有主流程能夠爲你管理 workers pool。 這是我在教程中使用supervisor 管理 RQ workers 的方法:

[program:microblog-tasks]
command=/home/ubuntu/microblog/venv/bin/rq worker microblog-tasks
numprocs=1
directory=/home/ubuntu/microblog
user=ubuntu
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
複製代碼

在這裏,numprocs 參數使你能夠根據須要啓動任意數量的 worker。 經過此參數,supervisor 將從單個配置文件啓動並監視指定數量的實例。​ ​

不幸的是,在 systemd 中沒有 numprocs 選項,所以這種類型的服務須要不一樣的解決方案。 最簡單的方法是爲每一個工做實例建立一個單獨的服務文件,可是這樣作會很麻煩。 相反,我要作的是將服務文件建立爲模板,可用於啓動全部這些相同的實例:

[Unit]
Description=Microblog task worker %I
After=network.target

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/microblog
ExecStart=/home/ubuntu/microblog/venv/bin/rq worker microblog-tasks
Restart=always

[Install]
WantedBy=multi-user.target
複製代碼

你可能在此文件中注意到的奇怪的事情是,我在服務描述中添加了%I。這是服務參數,一個要傳遞給每一個實例的數字。在描述中包含這個 %I 將幫助我識別實例,由於來自 systemd 命令的全部輸出都將替換爲實例號。對於這種特定的狀況,我實際上並不須要使用此參數,可是在其餘字段中包含 %I 是很常見的,好比必要時使用 start 命令。 ​

與常規服務文件的另外一個區別是,我將使用/etc/systemd/system/microblog-tasks@.service 這個名稱來編寫此服務文件。 文件名中的@表示這是一個模板,所以在它後面將有一個參數來標識從中衍生出的每一個實例。 我將使用實例編號做爲參數,所以該服務的不一樣實例將在 systemd 中被稱爲 microblog-tasks@1microblog-tasks@2 等。 ​

如今,我能夠在bash中使用大括號擴展來啓動四個 worker:

$ sudo systemctl daemon-reload
$ sudo systemctl start microblog-tasks@{1..4}
$ sudo systemctl status microblog-tasks@{1..4}
複製代碼

若是你想單獨處理一個實例,你也能夠這樣作:

$ sudo systemctl restart microblog-tasks@3
複製代碼

這幾乎和單個 supervisord 配置同樣方便,可是有一個缺點,當你想對全部工做程序執行操做時,必須在命令中包括{1..4} 範圍。 ​

要將整個 worker  pool 真正視爲一個實體,我能夠建立一個新的systemd target,這是另外一種類型的 unit。 而後,我能夠將全部實例映射到該目標,當我要對組的全部成員執行操做時,這將容許我引用該目標。 讓咱們重新目標的 unit 配置文件開始,我將其命名爲 /etc/systemd/system/microblog-tasks.target:

[Unit]
Description=Microblog RQ worker pool

[Install]
WantedBy=multi-user.target
複製代碼

除了描述以外,惟一須要的定義是對 multi-user.target 的依賴,就像你記得的那樣,multi-user.target 是定義上上述全部單元文件的目標。 ​

如今,我能夠更新服務文件模板以引用新目標,因爲對原始 multi-user.target 的可傳遞引用,最終等同於新目標。

[Unit]
Description=Microblog task worker %I
After=network.target

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/microblog
ExecStart=/home/ubuntu/microblog/venv/bin/rq worker microblog-tasks
Restart=always

[Install]
WantedBy=microblog-tasks.target
複製代碼

如今系統可使用如下命令從新配置,使用新的設置:

$ sudo systemctl daemon-reload
$ sudo systemctl disable microblog-tasks@{1..4}
$ sudo systemctl enable microblog-tasks@{1..4}
複製代碼

必須使用 disable 和 enable 命令,以強制 systemd 爲 worker 任務刪除舊目標並應用新目標。 如今 woker pool 可使用 target 來處理:

$ sudo systemctl restart microblog-tasks.target
複製代碼

若是以後你決定增長第5個 worker,你能夠這樣作:

$ sudo systemctl enable microblog-tasks@5
$ sudo systemctl start microblog-tasks.target
複製代碼

固然,你也能夠減小 worker。下面是如何減小 worker 4 和 5:

$ sudo systemctl stop microblog-tasks@{4..5}
$ sudo systemctl disable microblog-tasks@{4..5}
複製代碼

在這一點上,我認爲這個解決方案在方便性和功能性方面超過了 supervisor 的 numprocs 命令,由於我不只能夠控制整個 worker 進程,並且能夠添加和刪除 worker,而沒必要編輯任何配置文件! ​

相關文章
相關標籤/搜索