由於網路伺服器可能會因各種天災人禍或是因為需要進行系統更新而必須重新開機,因此在部署網路相關的服務時,我們應該要確保網路服務可以在伺服器重新開機之後也跟著自動啟動。再來,網路伺服器可能會遭遇到各種意外狀況,而導致程式執行出錯,造成程式崩潰(crash),一旦提供服務的應用程式崩潰了,就無法再繼續處理後續進來的請求,因此有個能夠保證服務能夠在崩潰或是被關閉之後自動重啟的機制也是很重要的。在Linux作業系統上,我們可以直接透過Linux作業系統內建的init系統,來將任意應用程式變成開機可自動執行,且保證能夠在崩潰或是被關閉之後自動重啟的服務。
Linux內建的init系統,從過去主流的SysV-init,到過渡的Upstart,再到成為新主流的Systemd,都可以用來處理上述問題。本篇文章只會介紹Systemd的作法。
Systemd Unit
Systemd Unit是Systemd設定檔的類型,常用的類型有處理服務的.service
、處理runlevel的.target
、處理檔案系統掛載的.mount
。
Debian或是其衍生的Linux發行版會有三個來存放不同優先等級的Systemd Unit。第一個是優先權最低的系統目錄/lib/systemd/system
(已安裝套件通常會將Systemd Unit檔案放置在此目錄,如果是紅帽系的Linux發行版的話,此目錄的路徑是/usr/lib/systemd/system
),第二個是優先權中等的執行階段目錄/run/systemd/system
,第三個是優先權最高的管理員自訂目錄/etc/systemd/system
。如果要查看Systemd Unit是否有發生覆寫,可以在終端機使用systemd-delta
指令,有興趣者可以自行試試,這篇文章就不細談這部份了。
撰寫Systemd Service Unit的設定檔
若我們想要利用Systemd Service Unit來啟動某個與作業系統本身運作沒什麼關聯的應用程式,可以將Systemd Service Unit的設定檔寫在管理員自訂目錄/etc/systemd/system
下。
直接在/etc/systemd/system
下建立一個副檔名為.service
的檔案,並先使用以下文字作為檔案內容:
[Unit]
Description=A description for this service unit.
After=network.target
[Service]
# User=user
# Group=group
# WorkingDirectory=/path/to/folder
ExecStart=/path/to/program [arguments]
Restart=always
RestartSec=3s
# Environment="VARIABLE_1=VALUE_1" "VARIABLE_2=VALUE_2"
# Environment="VARIABLE_3=VALUE_3" "VARIABLE_4=VALUE_4"
[Install]
WantedBy=multi-user.target
[Unit]
區塊中,我們撰寫了兩個項目。Description
用來設定這個Systemd Service Unit設定檔案的描述,可以讓管理員在日後查看時還能想起這個Systemd Service Unit的目的是什麼。另一個After
項目可以用來指定一個Systemd Target Unit,可以確保目前正在設定的這個Unit,會在Systemd Target Unit運行完後再運行。如果是用來提供Web服務的應用程式,那麼通常就必須要在作業系統環境的網路功能被啟用之後才能夠正常啟動,所以After
項目設定為network.target
可以確保該程式在第一次執行時候就順利啟動。
[Service]
區塊中有五個使用井字號#
註解掉的項目,這些先不要管它,稍候會再提到。ExecStart
項目可以用來設定當這個Systemd Service Unit項目啟動時要執行的應用程式(需使用完整絕對路徑),並且如果需要的話,可以利用空白字元來分隔多個不同的引數。若是這個程式需要搭配shell的命令列功能(例如變數、管線等)來執行,那就使用如以下的方式先執行shell之後,再運行shell的命令吧!
ExecStart=/bin/bash -c 'shell命令'
Restart
項目可以設定ExecStart
所執行的程式要在什麼樣的情況被關閉時,以相同的指令再重新執行一次。有效的設定值如下:
no
:不重啟。(預設值)always
:不管什麼原因都會重啟。on-success
:只在成功運行結束後(也就是行程回傳的Exit Status為0
的時候)才進行重啟。(這蠻少用)on-failure
:在運行失敗後(行程回傳的Exit Status非為0
、被使用Kill信號強制關閉、逾時或是餵狗沒反應時)才進行重啟。on-abnormal
:在意外地運行失敗後(被使用Kill信號強制關閉、逾時或是餵狗沒反應時)才進行重啟。on-abort
:被使用Kill信號強制關閉時才進行重啟。on-watchdog
:餵狗沒反應時才進行重啟。
上述提到的「逾時」,可以在[Service]
區塊中用TimeoutSec
項目來設定程式在執行多久後算是逾時,一般我們是用不到這個機制啦!而「餵狗」則是使用Systemd提供的看門狗(Watchdog)機制,使程式在固定時間內(可以在[Service]
區塊中用WatchdogSec
項目來設定),必須主動呼叫Systemd函式庫的sd_notify
函數,以利Systemd判斷該程式是否還是正常運作的狀態,一般我們也是用不到這個機制的啦!
至於RestartSec
這個項目,就是設定程式在被關閉後,到下次自動重啟的間隔時間,預設是100ms
(毫秒)。
最後的[Install]
區塊中,只有WantedBy
這一個設定項目,它可以用來指定一個Systemd Target Unit,讓目前正在設定的這個Unit,會在運行該Systemd Target Unit時,也跟著被運行。
接著來談談被註解掉的項目,首先是User
和Group
這兩個項目,它們可以分別用來設定要運行ExecStart
項目所指定的程式時所使用的使用者名稱和群組名稱。另外如果程式的運行必須仰賴工作目錄的支援的話,就要使用WorkingDirectory
項目來手動指定程式工作目錄的路徑。
而如果要替執行的程式設定環境變數的話,可以透過一個或是數個Environment
項目來設定,一個Environment
項目可以設定多個環境變數。
套用Systemd Unit的設定檔變更
在新增、或是修改Systemd Unit的設定檔後,要在終端機執行以下指令才會重新載入:
這個指令並不會導致正常運行中的服務被重啟。(換句話說,正在運行中的服務還是會使用原來的Systemd Unit設定檔,除非重啟手動它。)
啟動、重新啟動、停止指定的Systemd Service Unit
systemctl
指令除了可以讓Systemd重新載入新的Systemd Unit的設定檔外,當然也可以用來進行其它的管理操作。
例如要啟動magiclen.org.service
這個Systemd Service Unit,可以在終端機執行以下指令:
Systemd Unit檔案的副檔名可以省略掉,所以以上的指令也可以簡寫成:
如果是要「重新啟動」正在運行的服務,就把指令中的start
換成restart
即可。
如果是要「停止」正在運行的服務,就把指令中的start
換成stop
即可。
查看指定Systemd Service Unit的狀態
使用systemctl start
指令來啟動服務,是屬於非阻塞(non-blocking)的操作,我們無法直接得知服務到底有沒有執行成功。因此在手動使用systemctl start
指令執行服務之後,通常還會再使用systemctl status
指令來查看指定的Systemd Service Unit的狀態。
如果是想要查看Systemd Service Unit所執行的程式所輸出的Log,可以使用journalctl
指令,基本用法如下:
如果要同時查看多個Systemd Service Unit,可以再使用多個-u
參數進行串接。
在預設情況下journalctl
會把所有時間以及所有層級(如Debug、Warning、Error)的Log通通顯示出來,如果要進行過濾的話,可以將指令寫成:
Log的層級列表如下:
- 0: emerg
- 1: alert
- 2: crit
- 3: err
- 4: warning
- 5: notice
- 6: info
- 7: debug
層級數字或是層級名稱都可以與-p
參數搭配使用。
時間點的寫法最基本的就是使用以下格式:
例如:2019-06-19
、2019-06-19 12:00
或2019-06-19 12:00:00
。
或是:
例如:12:00
表示今天正中午。
另外也可以用yesterday
、today
和tomorrow
來分別表示昨天、今天和明天(未來日記?)。
也可以用1 hour ago
、1 hours ago
、2 week ago
、2 weeks ago
等來表示距離現在時間多久前的時間點。
開機自動啟動服務
如果要將Systemd Service Unit在開機時自動運行的話,首先要確定設定檔中的[Install]
區塊有撰寫正確,接著要在終端機上執行以下指令:
如果要關閉自動執行的功能,則在終端機上執行以下指令:
牛刀小試
利用這篇文章中所介紹的mprober來架設出可開機自動執行,且保證能夠在崩潰或是被關閉之後自動重啟的系統狀態監看之Web服務。該Web服務必須監聽TCP的9958
連接埠。
參考答案
按照mprober文章的方法將mprober
安裝在/usr/local/bin
目錄中。然後新增一個/etc/systemd/system/mprober.service
檔案,內容如下:
[Unit]
Description=M Prober
After=network.target
[Service]
ExecStart=/usr/local/bin/mprober web -p 9958
Restart=always
RestartSec=3s
[Install]
WantedBy=multi-user.target
在終端機執行以下指令:
sudo systemctl daemon-reload
sudo systemctl start mprober
sudo systemctl status mprober
若mprober
有成功運行,再執行以下指令:
sudo systemctl enable mprober