本節將從service單元文件的編寫(基礎選項、起停及重載選項、依賴性處理選項,資源限制選項等),對服務進程基本操做,進程和進程組的查看和如何更好的殺死服務進程 四個方面介紹systemd下服務進程的管理。php
systemd 的單元文件是受 XDG Desktop Entry .desktop 文件啓發而產生,而最初起源是 Windows 下的 .ini 文件。 .service 文件是systemd的本地配置文件,相似於 sysvinit 中的 /etc/init.d 裏腳本的做用。html
一個.service 文件必定包含三個Sections,分別是[Unit]、[Service]、[Install]。每一個Section有不一樣的Key,例如Unit常見 有Description、Documentation、Requires等,Service常見有Type等,Install常見有Alias、 WantedBy=、RequiredBy等,具體查看 man systemd.unit
,以及 man systemd.service
node
systemd單元文件的加載路徑(優先級從上到下依次下降):linux
系統模式nginx
/etc/systemd/system/* /run/systemd/system/* /usr/lib/systemd/system/* ...
注:debian 下,關注:/lib/systemd/systemd/*
目錄。通常來講,本身定義的.service 放在/etc/systemd/system
下。git
用戶模式github
$XDG_CONFIG_HOME/systemd/user/* $HOME/.config/systemd/user/* /etc/systemd/user/* /run/systemd/user/* /usr/lib/systemd/user/* ...
先看個例子:web
nginx.service
apache
[Unit] Description=A high performance web server and a reverse proxy server After=network.target [Service] Type=forking PIDFile=/run/nginx.pid ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;' ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;' ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload ExecStop=/usr/sbin/nginx -s quit [Install] WantedBy=multi-user.target
經過[Unit] section能夠知道這是HTTP服務器與反向代理服務器--nginx的單元文件(Description,關於服務的說明),此單元需在某些 target(network.target)以後纔可啓動(After),關於target的介紹請查看Target一節。api
經過[Server] section,能夠知道服務的啓動方式爲forking,PIDFile文件的路徑爲/run/nginx.pid
(需 與nginx.conf中指定的PID文件路徑相同,讓systemd追蹤服務的主進程,若未指定或不一致將沒法啓動服務),ExecStartPre用 於指定在服務啓動以前須要執行的命令,ExecStart即服務啓動調用的命令。ExecReload和ExecStop分別指定服務進程重載和中止的命 令。更多配置選項介紹見下文。
[Install] section 中的WantedBy表示此服務被multi-user.target
wants,在對應.wants/子目錄中爲服務創建相應的連接,當systemd啓用此target,本服務也會啓動。
service單元文件中的[Unit]和[Install]同其餘單元文件相似,其特有的seciton是[Service]。單元文件中空行和 以#和;開始的行會被忽略,因此能夠用來寫註釋。以反斜槓(\)鏈接在一塊兒的行,處理時會將反斜槓替換成空格,因此能夠用這個來把太長的行分紅多行。若是 文件是空的,或連接到/dev/null,它將不被加載,這樣能夠用來禁用服務,讓它即便手工也沒法啓動。
Description= :一些描述,顯示給用戶界面看的,能夠是任何字符串,通常是關於服務的說明。
Documentation= :指定參考文檔的列表,以空格分開的 URI 形式,如http://, https://, file:, info:, man:,這是有順序的,最好是先解釋這個服務的目的是什麼,而後是它是如何配置的,再而後是其它文件,這個選項能夠屢次指定,會將多行的合併,若是指定 了一個空的,那麼會重置此項,前面的配置再也不起做用。
Requires= :指定此服務依賴的其它服務,若是本服務被激活,那麼 Requires 後面的服務也會被激活,反之,若是 Requires 後面的服務被中止或沒法啓動,則本服務也會中止。這個選項能夠指定屢次,那麼就要求全部指定的服務都被激活。須要注意的是這個選項不影響啓動或中止的順 序,啓動順序使用單句的 After= 和 Before= 來配置。例如,若是 foo.service 依賴 bar.serivce,可是隻配置了 Requires= 而沒有 After= 或 Before=,那麼 foo.service 啓動時會同時激活 foo.service 和 bar.service。一般使用 Wants= 代替 Requires= 是更好的選擇,由於系統會更好的處理服務失敗的狀況。注意,這種依賴關係,也能夠在文件以外來處理,即便用 .requires/ 目錄,能夠參看上面的說明。
RequiresOverridable= :相似上面的 Requires= ,不過這種狀況下,只要用戶明確要求它啓動,纔會影響到被依賴的服務,否則服務出錯什麼的,不會影響被依賴服務的啓動。
Requisite=, RequisiteOverridable= :分別相似上面的兩個,不過若是是這個指定服務沒有啓動,被依賴的服務會不啓動,當即失敗。
Wants= :相對弱化的 Requires= ,這裏列出的服務會被啓動,但若是沒法啓動或沒法添加到事務處理,並不影響本服務作爲一個總體的啓動。這是推薦的兩個服務關聯的方式。這種依賴也能夠配置 文件外,經過 .wants/ 目錄添加,具體能夠看上面的說明。
BindsTo= :和 Requires= 很像,可是這種狀況,若是他後面列出的服務中止運行或崩潰之類的,本服務也會同時中止。
PartOf= :又一個相似 Requires= 的選項,可是限制在中止或重啓動服務,若是這裏列出的服務被中止或重啓動,那麼本服務也會中止或重啓動,注意這個依賴是意向,即本服務中止或重啓動,不會影響到這裏列出服務的運行狀態。
Conflicts= :配置一個依賴衝突,若是配置了些項,那麼,當一個服務啓動時,或中止此處列出的服務,反過來,若是這裏列出的服務啓動,那 麼本服務就會中止,即後啓動的才起做用。注意,此設置和 After= 和 Before= 是互相獨立的。若是服務 A 和 B 衝突,且在 B 啓動的時候同時啓動,那麼有可能會啓動失敗(兩都都是必需的)或修改以修復它(二者之一或兩都都不是必需的),後一種狀況,會將不須要的依賴刪除,或中止 衝突。
Before=, After= :配置服務間的啓動順序,好比一個 foo.service 包含了一行 Before=bar.service,那麼當他們同時啓動時,bar.service 會等待 foo.service 啓動完成後才啓動。注意這個設置和 Requires= 的相互獨立的,同時包含 After= 和 Requires= 也是常見的。此選項能夠指定一次以上,這時是按順序所有啓動。
OnFailure= :列出一個或更多的服務,當本服務啓動狀態是 failed 的時候,激活這些服務。
PropagatesReloadTo=, ReloadPropagatedFrom= :這兩個是列出一些服務,當其它服務 reload 時同時 reload 這個服務,或者反之。
RequiresMountsFor= :用空格分開的絕對路徑列表,是 Requires= 和 After= 添加的依賴中的 mount 文件須要訪問的指定的路徑。
OnFailureIsolate= :是一個布爾值,若是是真,那麼 OnFailure= 後面的服務會進入隔離模式,即全部不是它依賴的服務都會中止。若是隻設置一個服務,能夠放在 OnFailure= 後,默認值是假。
IgnoreOnIsolate= :一個布爾值.若是是真則當隔離其它服務時本服務不會中止(不明白隔離是什麼意思,大概在後面)。默認是假。
IgnoreOnSnapshot= :一個布爾值.若是是真則本服務不包含快照(snapshots)。對 device 和 snapshot 服務默認爲真,其它服務默認爲假。
StopWhenUnneeded= :一個布爾值。若是是真則當本服務不使用時會中止。 注意,爲了儘可能減小 systemd 的工做,默認狀況下是不會中止不使用的服務的,除非和其它服務衝突,或用戶明確要求中止。若是設置了這個選項,那麼若是沒有其它活動的服務須要此服務,它 會自動中止。默認值是假。
RefuseManualStart=, RefuseManualStop= :布爾值。若是設爲真值,則此服務只能間接的激活或中止。這種狀況下,用戶直接啓動或中止此服務會被拒絕,只有作 爲其它的服務依賴關係,由其它服務進行啓動或中止才能夠。這主要是爲了中止用戶誤操做。默認值是假。
AllowIsolate= :布爾值。若是是真值,則此服務可使用 systemctl isolate 命令進行操做。不然會拒絕此操做。最好的辦法是不要動這處選項,除非目標服務的行爲相似於 SysV 啓動系統中的 runlevels。只是一種預防措施,避免系統沒法使用的狀態。默認值是假。
DefaultDependencies= :布爾值。若是是真(默認值),一些本服務默認的依賴會隱式的創建,具體是哪些依賴,則於服務的類型 決定。好比,對於普通的服務(.service類型),它會確保在系統基本服務啓動後才啓動本服務,會在系統關機前確保本服務已關閉。通常來講,只有早期 開機服務和後期的關機服務,才須要把這個設成假。強烈對大多數普通服務,讓這個選項啓用便可。若是設成假,也不會禁用全部的隱式依賴,只是禁用那些非必要 的。
JobTimeoutSec= :當一個客戶端等待本服務的某個 Job 完成時,所指定的超時時間。若是達到了限制的時間,此 Job 會取消運行,但服務不會更改狀態,包括進入「failed」狀態。除了設備服務(即.device類型),其它的默認值是0(即沒有超時設置)。注意,這 個是獨立於特定服務所設置的超時設置的(好比對 .service 類型所設置的 Timeout=),它對服務自己沒有影響,但特定服務的設置是有影響的(能用來更改服務狀態)。《-這段不明白究竟是什麼意思,因此翻譯的也是亂七八 糟,真對不起)。
ConditionPathExists=, ConditionPathExistsGlob=, ConditionPathIsDirectory=, ConditionPathIsSymbolicLink=, ConditionPathIsMountPoint=, ConditionPathIsReadWrite=, ConditionDirectoryNotEmpty=,ConditionFileNotEmpty=, ConditionFileIsExecutable=, ConditionKernelCommandLine=, ConditionVirtualization=, ConditionSecurity=, ConditionCapability=, ConditionHost=, ConditionACPower=,ConditionNull= :這是一組相似的東西。檢測特定的條件是否是真值,若是不是真值,服務會略過啓 動,可是它依賴的服務仍是會正常運行的。這個條件測試失敗不會讓服務進入失敗狀態。條件是在服務開始運行時檢查的。
ConditionPathExists= 是指定在服務啓動時檢查指定文件的存在狀態。若是指定的絕對路徑名不存在,這個條件的結果就是失敗。若是絕對路徑的帶有!前綴,則條件反轉,即只有路徑不存在時服務才啓動。
ConditionPathExistsGlob= 相似上面的選項,但支持通配符。
ConditionPathIsDirectory= 判斷指定路徑是否是目錄。
ConditionPathIsSymbolicLink= 判斷指定路徑是否是連接。
ConditionPathIsMountPoint= 判斷指定路徑是否是一個掛載點。
ConditionPathIsReadWrite= 多年指定路徑是否可讀寫(即不是作爲只讀系統掛載的)
ConditionDirectoryNotEmpty= 判斷指定目錄是否存在且不爲空。
ConditionFileNotEmpty= 判斷指定文件是不是常規文件且不爲空(即大小不是0)。
ConditionFileIsExecutable= 判斷指定文件是不是常規文件且可執行。
相似的,ConditionKernelCommandLine=是判斷有沒有指定的內核命令行啓動參數(或帶有!反之),這個參數必須是一個單詞或用=分開的兩個單詞,前一種狀況下,會尋找內核參數是否有此單詞或是賦值的左邊。後一種狀況則必須是賦值的左右同時符合。
ConditionVirtualization= 是判斷是否是在虛擬化環境下執行的服務。這能夠是個布爾值以判斷是否是任意的虛擬化環境,或者 下列的字符串之一: qemu, kvm, vmware, microsoft, oracle, xen, bochs, chroot, openvz, lxc, lxc-libvirt, systemd-nspawn,以判斷是否是特定的虛擬化環境,多重嵌套的虛擬化環境,只判斷最後一層。可使用!進行反轉判斷。
ConditionSecurity= 是判斷系統是否啓用了安全環境,當前僅能識別selinux, apparmor, 和 smack。可使用!進行反轉判斷。
ConditionCapability= 是判斷服務管理器綁定的 capability 是否存在。(能夠查看其它部分的詳細信息。)設置爲 capability 的名字,好比 CAP_MKNOD。能夠經過在前面加!反轉判斷。
ConditionHost= 是判斷主機名 (hostname)或機器ID(machine ID)是否匹配。能夠加!反轉。
ConditionACPower= 是判斷機器是否在使用交流電源。若是設成 true,而只有至少鏈接一個交流電源時結果才爲真,反過來,設成 false,則不鏈接全部交流電源時才爲真。
ConditionNull= 是一個常量性質的判斷條件,它應該是布爾值,若是設成 false ,則條件永遠失敗,反過來則永遠成立。
若是指定多個條件,則全部條件都須要成立(即條件之間是 AND 的關係)。條件前面能夠加上 | 符號,這時條件變成一個觸發條件,服務定義了觸發條件,那麼在知足其它非觸發條件和這個觸發條件的狀況下,服務會至少執行一次。同時指定|和!前綴時,先 處理|,後處理!。除了ConditionPathIsSymbolicLink=,其它條件均跟隨連接。若是這些條件指定爲空,則至關於重置,前面的任 何設置都再也不起做用。
SourcePath= :這個服務生成的配置文件所在的路徑,這主要是用在生成工具從外部配置文件的格式轉換到本地服務的配置格式中。所以,對通常的服務不要使用此選項。
介紹部分常見選項,其他選項請查看本節參考連接2。
Type= :
PIDFile= :指定PID 文件絕對路徑,當Type=forking時,必須設置此項。
ExecStart= :服務啓動命令,可帶參數。!!關於此選項有大量注意事項與細節(例如:命令中不可使用重定向符號,管道,後臺運行&
以及其餘特殊符號等),請查看用戶手冊。
ExecStartPre=, ExecStartPost= :以前ExecStart以前或者以後執行的命令。注意語法格式要求同ExecStart選項。若包含多條此選項,將按順序串行執行。
ExecReload= :重載服務時候觸發的命令。
ExecStop= :服務中止命令。
ExecStopPost= :中止服務以後執行的命令。語法格式要求同ExecStart選項。
Restart= :指定服務進程自動重啓的條件。
SuccessExitStatus=
選項指定的退出信號)。關於啓動、中止的超時設置請參考用戶手冊。
PrivateNetwork=[BOOL] :若服務不須要網絡鏈接可開啓本選項,更加安全。
PrivateTmp=[BOOl] :因爲傳統/tmp
目錄是全部本地用戶和服務共用,會帶來不少安全性問題,開啓本選項後,服務將有一個私有的tmp,可防止攻擊。
InaccessibleDirectories= :限制服務進程訪問某些目錄。
ReadOnlyDirectories= :設置服務進程對某些目錄只讀,保證目錄下數據不被服務意外撰改。
OOMScoreAdjust= :調整服務OOM值,從-1000(對該服務進程關閉OOM)到1000(嚴格)。
IOSchedulingClass= :IO調度類型,可設置爲0,1,2,3中的某個數值,分配對應none,realtime,betst-effort和idle。
IOSchedulingPriority= :IO調度優先級,0~7(高到低)。
CPUSchedulingPriority= :CPU調度優先級,99~1(高到低)
Nice= :進程調度等級。
-(更多介紹請查看本節參考連接7,8)
關於服務進程的權限限制、資源限制、安全性管理將在進階部分中進一步介紹。
Alias= :在安裝使用應該使用的額外名字(即別名)。名字必須和服務自己有一樣的後綴(即一樣的類型)。這個選項能夠指定屢次,全部的名字都起做用,當執行 systemctl enable 命令時,會創建至關的連接
WantedBy=, RequiredBy= :在 .wants/ 或 .requires/ 子目錄中爲服務創建相應的連接。這樣作的效果是當列表中的服務啓動,本服務也會啓動。 在 bar.service 中的 WantedBy=foo.service 和 Alias=foo.service.wants/bar.service 基本是一個意思。
Also= :當此服務安裝時同時須要安裝的附加服務。 若是用戶請求安裝的服務中配置了此項,則 systemctl enable 命令執行時會自動安裝本項所指定的服務。
在 [Install] 段使用這些字符串有特定含義: %n, %N, %p, %i, %U, %u, %m, %H, %b。請查看查看本節參考連接1。
注:修改service單元文件以後,需使用命令systemctl daemon-reload
從新載入systemd,掃描新的或有變更的單元。
服務進程執行環境配置:
更多關於服務進程執行環境的配置請參考:systemd.exec
systemd的主要命令行工具是systemctl,可用於管理單元文件,不只僅是service。
注:若只給出名稱,而爲給出擴展名,將默認爲service單元,例:systemctl start foo
與systemctl start foo.service
等價。
在sysv下,經常使用的管理命令是service,在systemd中此命令可用(參考本文檔「SysV兼容性」一節),更建議使用systemctl命令,如下爲兩者的命令基礎操做對照表:
使用systemctl能夠查看服務進程的狀態。
查看服務進程運行狀態(同時查看到其下屬進程們的PID)
# systemctl status nginx.service
查看系統全部service及其狀態,將會打印4列信息,分別爲UNIT、LOAD、ACTIVE、SUB、DESCRIPTION
# systemctl list-units --type service
查看特定服務的依賴關係
# systemctl list-dependencies nginx
更多關於systemctl在服務進程查看方面的用法請查看systemctl用戶手冊,另外也可經過 pstree
命令查看系統進程樹。
Systemd 採用 Linux 的 Cgroup 特性跟蹤和管理進程的生命週期。CGroup 提供了相似文件系統的接口,使用方便。當進程建立子進程時,子進程會繼承父進程的 CGroup。所以不管服務如何啓動新的子進程,全部的這些相關進程都會屬於同一個 CGroup,systemd 只須要簡單地遍歷指定的 CGroup 便可正確地找到全部的相關進程,將它們一一中止便可。 例如:一個CGI程序派生兩次,脫離和Apache的父子關係,當apache進程被中止後,該CGI程序還在繼續運行的狀況,在systemd的管理下 將不會存在。
中止服務進程建議使用systemctl工具。
殺死一個服務的全部進程(傳遞信號到指定服務的全部進程):
$ sudo systemctl kill crond.service #指定信號類型 $ sudo systemctl kill -s SIGKILL crond.service 或 $ sudo systemctl kill -s 9 crond.service #不管服務進程通過多少層fork,使用以上命令便可殺死全部進程。 # 發送指定信號到服務的主進程 $ sudo systemctl kill -s HUP --kill-who=main crond.service
另外在單元文件中,能夠指定KillMode,KillSignal,SengSIGHUP,SendSIGKILL,請查看本節參考連接4。
fork.c
編譯生成二進制程序fork
,編寫fork.service
,調用fork程序。運行fork.service
,使用pstree查看進程樹:
kill 掉某個fork出的子進程,使該子進程的子進程們脫離主進程。
查看fork.service 的狀態,發現脫離父進程的進程們依然在其crgoup中,未脫離。
kill掉fork.service,發現該服務的全部進程,包括脫離主進程的進程都被中止,無一例外。
絕大多數的服務依賴是套接字依賴。好比服務 A 經過一個套接字端口 S1 提供本身的服務,其餘的服務若是須要服務 A,則須要鏈接 S1。所以若是服務 A 還沒有啓動,S1 就不存在,其餘的服務就會獲得啓動錯誤。因此傳統地,人們須要先啓動服務 A,等待它進入就緒狀態,再啓動其餘須要它的服務。Systemd 認爲,只要咱們預先把 S1 創建好,那麼其餘全部的服務就能夠同時啓動而無需等待服務 A 來建立 S1 了。若是服務 A 還沒有啓動,那麼其餘進程向 S1 發送的服務請求實際上會被 Linux 操做系統緩存,其餘進程會在這個請求的地方等待。一旦服務 A 啓動就緒,就能夠當即處理緩存的請求,一切都開始正常運行。
Linux 操做系統有一個特性,當進程調用 fork 或者 exec 建立子進程以後,全部在父進程中被打開的文件句柄 (file descriptor) 都被子進程所繼承。套接字也是一種文件句柄,進程 A 能夠建立一個套接字,此後當進程 A 調用 exec 啓動一個新的子進程時,只要確保該套接字的 close_on_exec 標誌位被清空,那麼新的子進程就能夠繼承這個套接字。子進程看到的套接字和父進程建立的套接字是同一個系統套接字,就彷彿這個套接字是子進程本身建立的一 樣,沒有任何區別。
這個特性之前被一個叫作 inetd 的系統服務所利用。Inetd 進程會負責監控一些經常使用套接字端口,好比 Telnet,當該端口有鏈接請求時,inetd 才啓動 telnetd 進程,並把有鏈接的套接字傳遞給新的 telnetd 進程進行處理。這樣,當系統沒有 telnet 客戶端鏈接時,就不須要啓動 telnetd 進程。Inetd 能夠代理不少的網絡服務,這樣就能夠節約不少的系統負載和內存資源,只有當有真正的鏈接請求時才啓動相應服務,並把套接字傳遞給相應的服務進程。
和 inetd 相似,systemd 是全部其餘進程的父進程,它能夠先創建全部須要的套接字,而後在調用 exec 的時候將該套接字傳遞給新的服務進程,而新進程直接使用該套接字進行服務便可。
systemd中這個並行化Socket服務極大的加快了系統的啓動速度(固然,不全是它的功勞,此處不贅述)。
systemd的並行啓動能力(與sysv、upstart對比)
Socket Activation 帶來的益處(摘自Systemd主做者Blog):
上文提到inetd能夠代理不少網絡服務以此節約系統資源,Systemd 也能夠提供按需啓動的能力,只有在某個服務被真正請求的時候才啓動它。空閒時使該服務結束,等待下次須要時再次啓動它。例:
服務器上的SSHD服務,只有當系統管理員或者用戶登陸時候,此服務才被使用到,而後它卻一直駐留在後臺,極大的浪費系統資源。而在systemd下,咱們能夠作得按需啓動,即有人經過SSH訪問服務時,SSHD服務纔會被啓動。(具體實現請查看
/lib/systemd/system
下的ssh.service
與ssh.socket
文件)
更多例子:Socket Activation with Popular Daemons
注:systemd的socket單元負責監控端口時,當外部鏈接帶來,而systmed沒法將socket轉移給對應服務單元時候(若服務進程崩 潰、中止退出、沒法重啓等),對應socket單元將進入failed狀態,外部鏈接將不通(telnet ip pot 測試對應服務端口將不通)。
按需啓動實例:
使用systemd部署Node.js應用(myservice.service),見圖myservice.service:
$ sudo systemctl enable myservice.service # 設置服務隨機啓動 $ sudo systemctl start myservice.service #啓動服務
在這種配置下,沒有使用socket激活,systemd運行且監控着服務的守護進程,Node.js監聽TCP端口和服務器請求,進程是時刻運行着,即便沒有外部訪問,極大的浪費了系統的資源。
系統運行原理,見圖no-socket-activation:
須要將其改成socket激活。空閒時,systemd監控着TCP端口,當有請求來到時,systemd激活Node.js服務並將socket 移教給Node.js處理,而後Nojde.js接管並處理所有的客戶端請求,systemd從新變成監控Node.js的角色。 當 Node.js 處理完任務後,它自動中止。而再由 systemd 來監控 TCP 端口,直至下次客戶端請求的到來並再啓動 Node.js 的應用,循環往復,見圖socket-activation1,2
要達到以上目的,Node.js須要安裝systemd相關模塊,同時安裝node-autoquit模塊在空閒時關閉應用,以及作好應用數據的保存工做,具體此處不贅述,請參考本節參考連接。
systemd的相關配置:須要修改以前的service單元文件(去除自動重啓以及自動啓動),並增長一個同名socket單元文件讓systemd監聽TCP端口,見圖socket-activation3
$ sudo systemctl enable myservice.socket #使socket單元自啓動 $ sudo systemctl start myservice.socket #啓動socket
部分服務例如syslog、apache等,在系統上通常是隻有單進程在運行。而另一些服務須要多實例化,例如Dovecot-IMAP服務會有 多個實例進程運行在不一樣的IP端口或者本地IP地址上。 在systemd中,咱們不須要爲每一個實例化進程編寫一份配置文件,只須要編寫一個模板文件,而後在啓用時候調用模板文件便可,模板文件能夠經過匹配模板 時傳入的字符串實例化進程,例如咱們系統上有3塊網卡,分別爲eth0、eth一、eth2,咱們但願經過每塊網卡都經過dhcpd配置動態ip。利用 systemd的模板\實例機制,咱們只須要寫一份dhcpd的模板文件,便可爲3塊網卡所用。
dhcpd@.service
[Unit] Description=dhcpcd on %I Wants=network.target Before=network.target BindsTo=sys-subsystem-net-devices-%i.device After=sys-subsystem-net-devices-%i.device [Service] Type=forking PIDFile=/run/dhcpcd-%I.pid ExecStart=/usr/bin/dhcpcd -4qb %I ExecStop=/usr/bin/dhcpcd -x %I [Install] WantedBy=multi-user.target
經過如下命令啓用:
# systemctl start dhcpcd@eth0.service # systemctl start dhcpcd@eth1.service # systemctl start dhcpcd@eth2.service
命令中的字符串ethN將在service文件中被匹配展開:
dhcp@eth0.service
[Unit] Description=dhcpcd on eth0 Wants=network.target Before=network.target BindsTo=sys-subsystem-net-devices-eth0.device After=sys-subsystem-net-devices-eth0.device [Service] Type=forking PIDFile=/run/dhcpcd-eth0.pid ExecStart=/usr/bin/dhcpcd -4qb eth0 ExecStop=/usr/bin/dhcpcd -x eth0 [Install] WantedBy=multi-user.target
模板文件中的%I
、%i
被展開爲命令中@
以後.service
以前的字符串,即ethN
。%I
與%i
的區別在此例中看不出來,實際上%I
展開後是不轉義的字符串,而%i
展開後是轉移的字符串。例如若是咱們調用串口tty的模板文件實例化某個接口的tty服務, systemctl start 'serial-getty@serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0.service'
,實際上接口地址爲 「serial/by-path/pci-0000:00:1d.0-usb-0:1.4:1.1-port0」 ,以上命令是通過轉義的, /
被替換爲-
,特殊符號被替換爲16進制符號等。service文件中的%I
將展開爲轉義前的「serial/by-path/pci-0000:00:1d.0-usb-0:1.4:1.1-port0」, 而%i
將展開爲serial-getty@serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0.service
。
單元文件的特殊符號更多解釋請查看 systemd.unit#Specifiers
另一個例子: systemd-fsck@.service
[Unit] Description=File System Check on %f Documentation=man:systemd-fsck@.service(8) DefaultDependencies=no BindsTo=%i.device After=systemd-readahead-collect.service systemd-readahead-replay.service %i.device Before=shutdown.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/lib/systemd/systemd-fsck %f StandardOutput=journal+console TimeoutSec=0
systemd-fsck是systemd的磁盤檢查工具,經過此模板文件能夠對多塊磁盤設備進行實例化後檢查(%f
表示未轉義的文件名)。
systemd內部使用cgroups對其下的單元進行資源管理,包括CPU、BlcokIO以及MEM方面。systemd的資源管理主要基於三個單元service、scope以及slice。
Slice=slicename
,可將服務可添加到特定slice。每個用戶登陸時候會自動建立一個此用戶的slice單元,同時用戶的每一個對話會自動建立一個scope用於管理對話下的進程。 每個模板service單元文件實例化時會自動建立一個對應的slcie,例如getty@.service進行實例化爲一個 getty@tty1.service時會自動建立一個systemd-getty.slice用於管理此模板下的全部實例化的進程,關於service 模板文件的介紹請查看上文的基礎--進程管理-instandtiatied serivices一節。
可以使用systemd-cgls
( Recursively show control group contents)命令查看系統中以上三種單元的層級管理,見圖systemd-cgls。
systemd另外提供了一個工具systemd-cgtop
( Show top control groups by their resource usage)用於監控各個單元實時、動態的資源佔用狀況,見圖systemd-cgtop。
本小節僅簡單介紹關於CPU、MEM、BlockIO常見的資源管理選項,更多配置選項詳情請查看本節參考連接四、6等。
/etc/systemd/system/httpd.service
:
.include /usr/lib/systemd/system/httpd.service [Service] CPUShares=1500
而後重載systemd deamon,重啓http.service便可。
#systemctl daemon-reload #systemctl restart httpd.service
/etc/systemd/system/httpd.service
.include /usr/lib/systemd/system/httpd.service [Service] MemoryLimit=1G
在debian testing 上沒法完成內存限制的實驗,緣由不明。正常配置,service單元與slice單元可正常啓動,可是slice單元中限制的內存無效,日誌正常。更換到fedora 20上實驗成功。 注意若在service單元中指定了slice單元,而限制選項主要在slice單元中,最好slice單元比service單元先啓動,不然可能報錯。 若服務程序內存超過MemoryLimit的限制,服務單元將退出進入failed狀態。
上文提到的這些設置選項其實都是與cgroup對應的控制組相關,例如MemoryLimit選項對應memory.limit_in_bytes
控制組。systemd的單元文件中能夠經過ControlGroupAttribute配置對應cgroup的控制組,例如:controlGroupAttribute=memory.swappiness 70
。更多介紹請查看本節參考連接9,10。
ControlGroupAttribute= Set a specific control group attribute for executed processes, and(if needed) add the executed processes to a cgroup in the hierarchy of the controller the attribute belongs to.
咱們將上文關於CPU,IO,BlockIO等相關的設置整合在一塊,可獲得以下的service文件:
/etc/systemd/system/httpd.service
.include /usr/lib/systemd/system/httpd.service [Service] CPUShares=1500 MemoryLimit=1G BlockIOWeight=500 BlockIOReadBandwith=/var/log 5M ControlGroupAttribute=memory.swappiness 70
咱們知道service單元文件中service 段能夠經過Slice=選項指定所屬的slice單元,因此除了將資源管理配置寫在service單元文件中,還能夠將服務進程的資源限制寫在特定slice單元中。
例:
/etc/systemd/system/limits.slice
:
[Unit] Description=Limited resources Slice DefaultDependencies=no Before=slices.target [Slice] CPUShares=512 MemoryLimit=1G
/lib/systemd/system/nginx.service
:
[Unit] Description=A high performance web server and a reverse proxy server After=network.target [Service] Type=forking PIDFile=/run/nginx.pid ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;' ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;' ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload ExecStop=/usr/sbin/nginx -s quit Slice=limits.slice [Install] WantedBy=multi-user.target
而後執行如下命令:
# systemctl daemon-reload # systemctl restart nginx.service
使用systemd-cgls查看,結果如圖 nginx-slice。
另外也可經過systemctl永久性或者臨時性的對正在運行的service、slice、scope單元進行即時的資源控制。
例如:
#systemctl set-property nginx.service CPUShares=500 MemoryLimit=500M
會發現/etc/systemd/system
出現了nginx.service.d目錄,其中包含着與nginx.service相關的資源控制配置文件:90-CPUShare.conf
,90-MemoryLimit.conf
。 這種更改是永久性的,即下次重啓依然有效。若是須要臨時性的更改使用選項--runtime
替代set-property
便可。
關於本節內容建議閱讀本節參考連接2:《Securing Your Services》。
service單元中可使用 RootDirectory= 選項來達到chroot的效果。
RootDirectory=
Takes an absolute directory path. Sets the root directory for executed processes, with the chroot(2) system call. If this is used, it must be ensured that the process and all its auxiliary files are available in the chroot() jail.
例:
[Unit] Description=A chroot()ed Service [Service] RootDirectory=/srv/chroot/foobar ExecStartPre=/usr/local/bin/setup-foobar-chroot.sh ExecStart=/usr/bin/foobard RootDirectoryStartOnly=yes
如上文服務單元,對於foobard來講,根目錄爲/srv/chroot/foobar,注意若指定了RootDirectory,則ExecStart(ExecStop等等)選項的路徑需以新指定的RootDirectory爲準, 例如本例中ExecStart實際指向爲/srv/chroot/foobar/usr/bin/foobard,但在service單元中需寫成相對於RootDirectory的路徑:/usr/bin/foorbad。
注:systemd中提供了一個方便的輕量級的容器管理工具systemd-naspawn,具體請查看container(systemd-nspawn)一節。
另外若是想控制服務單元對特定目錄的訪問,systemd的service單元提供瞭如下選項(詳細解釋請查看本節參考連接1對應條目):
例(/home目錄對於此服務單元不可訪問,而/var目錄對於此服務單元只讀):
... [Service] ExecStart=... InaccessibleDirectories=/home ReadOnlyDirectories=/var ...
若服務單元無需網絡服務,可將其與網絡隔離,以保證安全性。
PrivateNetwork= :
Takes a boolean argument. If true, sets up a new network namespace for the executed processes and configures only the loopback network device "lo" inside it. No other network devices will be available to the executed process. This is useful to securely turn off network access by the executed process. Defaults to false. It is possible to run two or more units within the same private network namespace by using the JoinsNamespaceOf= directive, see systemd.unit(5) for details. Note that this option will disconnect all socket families from the host, this includes AF_NETLINK and AF_UNIX. The latter has the effect that AF_UNIX sockets in the abstract socket namespace will become unavailable to the processes (however, those located in the file system will continue to be accessible).
例:
... [Service] ExecStart=... PrivateNetwork=yes ...
因爲/tmp目錄的權限問題,容易帶來一些安全問題,systemd中能夠在服務單元中設置私有/tmp目錄,可以使服務進程更加安全。
PrivateTmp=
Takes a boolean argument. If true, sets up a new file system namespace for the executed processes and mounts private /tmp and /var/tmp directories inside it that is not shared by processes outside of the namespace. This is useful to secure access to temporary files of the process, but makes sharing between processes via /tmp or /var/tmp impossible. If this is enabled, all temporary files created by a service in these directories will be removed after the service is stopped. Defaults to false. It is possible to run two or more units within the same private /tmp and /var/tmp namespace by using the JoinsNamespaceOf= directive, see systemd.unit(5) for details. Note that using this setting will disconnect propagation of mounts from the service to the host (propagation in the opposite direction continues to work). This means that this setting may not be used for services which shall be able to install mount points in the main mount namespace.
注意:使用privatetmp以後,基於/tmp或/var/tmp目錄進行進程間通信(IPC)將不可用。
/privatetmp目錄位於/tmp目錄下,如圖privatetmp (在nginx.service服務單元中啓用了PrivateTmp):
例:
... [Service] ExecStart=... PrivateTmp=yes ...
systemd還可對服務進程的Capabilities(能力)進行控制,相關的選項有Capabilities=,CapabilityBoundingSet=,SecureBits=。 具體請查看本節參考連接1與 capabilities 用戶手冊。
注:此部分選項未實驗。
LimitCPU=, LimitFSIZE=, LimitDATA=, LimitSTACK=, LimitCORE=, LimitRSS=, LimitNOFILE=, LimitAS=, LimitNPROC=, LimitMEMLOCK=, LimitLOCKS=, LimitSIGPENDING=, LimitMSGQUEUE=, LimitNICE=, LimitRTPRIO=, LimitRTTIME=
以上選項可用於服務進程運行時的資源限制,選項具體意義請參考setrlimit。
例:
... [Service] ExecStart=... LimitNPROC=1 LimitFSIZE=0 ...
查看setrlimit手冊,以下:
可知以上單元文件限制了此服務進程最多隻能有一個進程,即禁止了其fork,且此服務進程能建立的最大文件大小爲0。
DeviceAllow選項可指定服務進程可訪問的設備節點以及相關權限,
例如:
... [Service] ExecStart=... DeviceAllow=/dev/null rw ...
該服務單元只對/dev/null有訪問權限,且權限爲讀寫。
另外還有PrivateDevices=,ProtectSystem=,ProtectHome=等等選項,具體用法請參考本節參考連接1。
思路:
systemctl status name.service
,除了輸出服務進程基礎狀態,還可輸出關於此服務進程最近的10條日誌記錄。經過服務單元中指定ExecStopPost等命令,在服務單元異常退出時候,經過ExecStopPost指定的程序檢測對應日誌可即時報警,須要注意目前若ExecStart執行失敗,EcecStopPost將不會執行(這多是一個bug,不知後續是否會修復)!
ExecStopPost以及ExecStartPost等命令中能夠傳入Environment=等選項配置的環境變量以及Unit單元中內置的特殊變量。前者請man systemd.exec
查看Exviroment相關條目,後者請man systemd.unit
查看Specifiers。 因此在ExecStopPost中,除了經過檢測日誌獲得服務進程信息做出報警,還能夠經過服務進程中設置的環境變量做爲參數傳入ExecStopPost,從而獲得主服務進程的相關信息而執行對應操做。
例:/etc/default/ssh
文件中指定了須要的環境變量($SSHD_OPTS),而$MAINPID是sshd daemon中設置的。
[Unit] Description=OpenBSD Secure Shell server After=network.target auditd.service ConditionPathExists=!/etc/ssh/sshd_not_to_be_run [Service] EnvironmentFile=-/etc/default/ssh ExecStart=/usr/sbin/sshd -D $SSHD_OPTS ExecReload=/bin/kill -HUP $MAINPID KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target Alias=sshd.service
另外經過後臺程序輪詢查看過濾分析各個服務單元的日誌也可作監控與報警,但因爲是輪詢可能存在必定的延時性。