Centos7 Systemd 使用詳解

[toc]

一、Systemd 是什麼?

systemd 是一個屬於用戶空間的系統服務管理程序,在紅帽 RHEL7 上採用,替代了原來 RHEL6 上的 systemVinit。 其作用是,在內核啓動完成後,初始化用戶空間的進程,進程號爲 1 、管理操作系統的運行級別,系統的服務啓動和關閉,系統的掛載點。

systemd 的特性:

  • 支持服務並行啓動,實現快速開機
  • 按需啓動守護進程
  • 支持快照和系統恢復
  • 各服務間依賴關係管理
  • 同時採用socket式與D-Bus總線式激活服務
  • 維護掛載點和自動掛載點
  • 可以通過 Linux 的 cgroups 監控運行的進程

二、Systemd 管理系統的運行級別

查看當前默認的啓動級別
systemctl get-default
切換到多用戶級別
systemctl isolate multi-user.target
或
systemctl isolate runlevel3.target
設置系統默認啓動的運行級別
systemctl set-default multi-user.target
運行級別對應表
systemd target 解釋
runlevel0.target, poweroff.target 關閉系統 —> 0
runlevel1.target, rescue.target 單用戶模式 —> 1
runlevel2.target, runlevel4.target, multi-user.target 用戶定義/域特定運行級別。默認等同於 3 —> 2,4
runlevel3.target, multi-user.target 最常用模式,非圖形化多用戶 —> 3
runlevel5.target, graphical.target 多用戶圖形化 —> 5
runlevel6.target, reboot.target 重啓 —> 6
systemd 運行級別配置目錄
/etc/systemd/system/

三、Systemd 管理系統服務

認識 Systemd 下的 unit 文件

在 Systemd 下 unit 文件用於配置對資源的控制,每種資源對應不同的 unit 文件類型,比如後臺服務(service)、套接字(socket)、設備(device)、掛載點(mount) 等。 所有 unit 文件都包含兩個通用段 [Unit] 、[Install] 和一個資源類型段,如 [Service] 就表示後臺服務。

unit 類型如下:

  • service :守護進程的啓動、停止、重啓和重載是此類 unit 中最爲明顯的幾個類型。

  • socket :此類 unit 封裝系統和互聯網中的一個socket。當下,systemd支持流式, 數據報和連續包的AF_INET,AF_INET6,AF_UNIXsocket 。也支持傳統的 FIFOs 傳輸模式。每一個 socket unit 都有一個相應的服務 unit 。相應的服務在第一個“連接”進入 socket 或 FIFO 時就會啓動(例如:nscd.socket 在有新連接後便啓動 nscd.service)。

  • device :此類 unit 封裝一個存在於 Linux設備樹中的設備。每一個使用 udev 規則標記的設備都將會在 systemd 中作爲一個設備 unit 出現。udev 的屬性設置可以作爲配置設備 unit 依賴關係的配置源。

  • mount:此類 unit 封裝系統結構層次中的一個掛載點。

  • automount :此類 unit 封裝系統結構層次中的一個自掛載點。每一個自掛載 unit 對應一個已掛載的掛載 unit (需要在自掛載目錄可以存取的情況下儘早掛載)。

  • target :此類 unit 爲其他 unit 進行邏輯分組。它們本身實際上並不做什麼,只是引用其他 unit 而已。這樣便可以對 unit 做一個統一的控制。(例如:multi-user.target 相當於在傳統使用 SysV 的系統中運行級別5);bluetooth.target 只有在藍牙適配器可用的情況下才調用與藍牙相關的服務,如:bluetooth 守護進程、obex 守護進程等)

  • snapshot :與 targetunit 相似,快照本身不做什麼,唯一的目的就是引用其他 unit 。

編寫後臺服務的 unit 文件

對應到 centos6 其實就是 /etc/init.d/ 下面的服務腳本,只是到了 systemd 裏面叫做 service unit 文件,命名必須以 .service 結尾。

  • service unit(服務腳本) 文件在系統中的存放路徑

    /usr/lib/systemd/system
  • 先來看一個簡單的示例
[Unit]
Description=nginx
After=network.target

[Service]
Type=forking
PIDFile=/alidata/server/nginx/logs/nginx.pid
ExecStart=/alidata/server/nginx/sbin/nginx -c /alidata/server/nginx/conf/nginx.conf
ExecReload=/alidata/server/nginx/sbin/nginx -s reload
ExecStop=/alidata/server/nginx/sbin/nginx -s stop
PrivateTmp=true

[Install]
WantedBy=multi-user.target

整個文件分爲三個部分,[Unit]、[Service]、[Install]

[Unit] :記錄文件的通用信息,如描述,開機啓動順序

[Service] :記錄 unit 文件類型,服務控制的相關指令

[Install] :安裝信息

Unit 常用配置指令

  • Description:對本service的描述。

  • Before, After:定義啓動順序,Before=xxx.service,代表本服務在xxx.service啓動之前啓動。After=xxx.service,代表本服務在xxx之後啓動。

  • Requires: 這個單元啓動了,那麼它“需要”的單元也會被啓動; 它“需要”的單元被停止了,它自己也活不了。但是請注意,這個設定並不能控制某單元與它“需要”的單元的啓動順序(啓動順序是另外控制的),即 Systemd 不是先啓動 Requires 再啓動本單元,而是在本單元被激活時,並行啓動兩者。於是會產生爭分奪秒的問題,如果 Requires 先啓動成功,那麼皆大歡喜; 如果 Requires 啓動得慢,那本單元就會失敗(Systemd 沒有自動重試)。所以爲了系統的健壯性,不建議使用這個標記,而建議使用 Wants 標記。可以使用多個 Requires。

  • Wants:推薦使用。本單元啓動了,它“想要”的單元也會被啓動。但是啓動不成功,對本單元沒有影響。

  • Conflicts:一個單元的啓動會停止與它“衝突”的單元,反之亦然。

Service 常用配置指令

  • Type:service的種類,包含下列幾種類型:

    • simple 默認,這是最簡單的服務類型。意思就是說啓動的程序就是主體程序,這個程序要是退出那麼一切都退出。
    • forking 表示 ExecStart= 進程將會在啓動過程中使用 fork() 系統調用,啓動成功以後,父進程將會退出,而子進程將作爲該服務的主進程繼續運行。 在啓動守護進程時使用這個類型。
    • oneshot 用於一次性執行服務,比如清理臨時文件,該類型可以設置多個 ExecStart= 指令,它們按照出現的順序依次執行。一旦遇到錯誤,就會立即停止,不再繼續執行,同時服務也將進入 failed 狀態。 在指定的 ExecStart= 指令執行結束前,該服務一直處於 “啓動中”(activating) 狀態,而一旦執行結束,該服務又立即變爲停止(inactive)狀態。也就是說該服務不存在 活動(active) 狀態。 這意味着如果再一次啓動服務,將會再一次執行服務定義的動作,先後順序會晚於該服務單元,將會一直等到該服務變成"停止"(inactive)狀態後,纔開始啓動。 該類型常和 RemainAfterExit 指令同時使用。
  • RemainAfterExit :當該服務的所有進程全部退出之後, 是否依然將此服務視爲活動(active)狀態。 默認值爲 no

  • PIDFile :指定守護進程的 PID 文件,強烈建議在 Type=forking 的情況下明確設置此選項。因爲有些服務啓動之後會派生多個進程,systemd 將會不知道哪一個纔是該服務的主進程,指定 PID 文件有助於 Systemd 準確的找到服務的主進程。 systemd 不會寫入此文件, 但會在此服務停止後刪除它。 該指令要放在 ExecStart 上面。

  • ExecStart :啓動服務時需要執行的命令,可以用 $變量名 引用 Environment 設置的進程環境變量 和 操作系統的環境變量 如 $USER

  • ExecStop :可選指令,用於設置服務被要求停止前所執行的指令,此處指令執行完後,該服務剩餘的進程都會被 kill 掉。 如果該指令沒填寫,在停止服務時會直接 kill 該服務下的所有進程。

  • ExecStartPre :設置在 ExecStart 指令執行之前執行的命令

  • ExecStartPost :設置在 ExecStart 指令執行成功之後執行的命令

  • ExecStopPost :設置該服務停止之後執行的命令

  • Environment :設置進程的環境變量, 值是一個空格分隔的 VAR=VALUE 列表。 可以多次使用此選項以增加新的變量或者修改已有的變量 (同一個變量以最後一次的設置爲準)。 若設爲空, 則表示清空先前所有已設置的變量。 注意: (1)不會在字符串內部進行變量展開(也就是"$"沒有特殊含義); (2)如果值中包含空格或者等號, 那麼必須在字符串兩邊使用雙引號(")界定。 例如: Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6" 設置了 "VAR1", "VAR2", "VAR3" 三個變量,其值分別爲 "word1 word2", "word3", "$word 5 6"

  • EnvironmentFile :與 Environment= 類似, 不同之處在於此選項是從文本文件中讀取環境變量的設置。 文件中的空行以及以分號(;)或井號(#)開頭的行會被忽略, 其他行的格式必須符合 VAR=VALUE 的shell變量賦值語法。 行尾的反斜槓()將被視爲續行符, 這與shell語法類似。 若想在變量值中包含空格, 則必須在值的兩端加上雙引號(")界定。文件必須用絕對路徑表示(可以包含通配符)。 但可在路徑前加上"-"前綴表示忽略不存在的文件。 可以多次使用此選項, 以從多個不同的文件中讀取設置。 若設爲空, 則表示清空所有先前已經從文件中讀取的環境變量。

Install 常用配置指令

  • Alias : 啓用時使用的別名,可以設爲一個空格分隔的別名列表。 每個別名的後綴(也就是單元類型)都必須與該單元自身的後綴相同。systemctl enable 命令將會爲每個別名創建一個指向該單元文件的軟連接。 注意,因爲 mount, slice, swap, automount 單元不支持別名,所以不要在這些類型的單元中使用此選項。

  • WantedBy,RequiredBy : 接受一個空格分隔的單元列表, 表示在使用 systemctl enable 啓用此單元時, 將會在每個列表單元的 .wants/ 或 .requires/ 目錄中創建一個指向該單元文件的軟連接。 這相當於爲每個列表中的單元文件添加了 Wants=此單元 或 Requires=此單元 選項。 這樣當列表中的任意一個單元啓動時,該單元都會被啓動。

應用示例

1、創建一個簡單服務

[Unit]
Description=簡單的Foo服務

[Service]
ExecStart=/usr/sbin/foo-daemon

[Install]
WantedBy=multi-user.target

該單元文件創建了一個運行 /usr/sbin/foo-daemon 守護進程的服務,未設置 Type= 等價於 Type=simple 默認設置。 注意,本例中的 /usr/sbin/foo-daemon 必須在啓動後持續運行到服務被停止。 如果該進程只是爲了派生守護進程,那麼應該使用 Type=forking

2、一次性服務

[Unit]
Description=清理老舊的 Foo 數據

[Service]
Type=oneshot
ExecStart=/usr/sbin/foo-cleanup

[Install]
WantedBy=multi-user.target

Type=oneshot 用於只需要執行一次,不需要持久運行的單元,例如文件系統檢查或者清理磁盤文件。此單元會啓動後一直等待指定的動作完成, 然後再回到停止狀態。 在 /usr/sbin/foo-cleanup 執行結束前, 該服務一直處於"啓動中"(activating)狀態,而一旦執行結束,該服務又立即變爲"停止"(inactive)狀態。 也就是說,對於 Type=oneshot 類型的服務,不存在"活動"(active)狀態。 這意味着,如果再一次啓動該服務,將會再一次執行該服務定義的動作。 注意,在先後順序上晚於該服務的單元, 將會一直等到該服務變成"停止"(inactive)狀態後, 纔會開始啓動。

Type=oneshot 是唯一可以設置多個 ExecStart= 指令的服務類型。 多個 ExecStart= 指令將按照它們出現的順序依次執行, 一旦遇到錯誤,就會立即停止,不再繼續執行, 同時該服務也將進入"失敗"(failed)狀態。

3、可停止的一次性服務

[Unit]
Description=簡單的靜態防火牆

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/simple-firewall-start
ExecStop=/usr/local/sbin/simple-firewall-stop

[Install]
WantedBy=multi-user.target

有時候,單元需要執行一個程序以完成某個設置(啓動), 然後又需要再執行另一個程序以撤消先前的執行(停止)。 而第一次執行之後,該單元應該處於 活動(active) 狀態,但實際上並無任何進程在運行。網絡配置服務就是一個典型的例子,只需要執行一次的服務。

可以通過設置 RemainAfterExit=yes 來滿足這種需求。 在這種情況下,systemd 將會在啓動成功後將該單元視爲處於"活動"(active)狀態(而不是"停止"(inactive)狀態)。 RemainAfterExit=yes 雖然可以用於所有 Type= 類型, 但是在實踐中主要用於 Type=oneshot 和 Type=simple 類型。 對於 Type=oneshot 類型, systemd 一直等到服務啓動成功之後,纔會將該服務置於"活動"(active)狀態。 所以,依賴於該服務的其他單元必須等待該服務啓動成功之後,才能啓動。 但是對於 Type=simple 類型, 依賴於該服務的其他單元無需等待,將會和該服務同時並行啓動。

4、編寫守護進程單元 nginx.service

[Unit]
Description=nginx
After=network.target

[Service]
Type=forking
PIDFile=/alidata/server/nginx/logs/nginx.pid
ExecStart=/alidata/server/nginx/sbin/nginx -c /alidata/server/nginx/conf/nginx.conf
ExecReload=/alidata/server/nginx/sbin/nginx -s reload
ExecStop=/alidata/server/nginx/sbin/nginx -s stop
PrivateTmp=true

[Install]
WantedBy=multi-user.target

PrivateTmp 設爲 yes 表示在進程的文件系統名字空間中掛載私有的 /tmp 與 /var/tmp 目錄, 也就是不與名字空間外的其他進程共享臨時目錄。 這樣做會增加進程的臨時文件安全性,但同時也讓進程之間無法通過 /tmp 或 /var/tmp 目錄進行通信。 同時,當服務停止之後,所有先前在臨時目錄中創建的文件都將被刪除。

5、編寫守護進程單元 php7-fpm.service

[Unit]
Description=PHP7.0.8 start file

[Service]
Type=forking
PIDFile=/alidata/server/php7.0.8/var/run/php-fpm.pid
ExecStart=/alidata/server/php7.0.8/sbin/php-fpm --daemonize --fpm-config /alidata/server/php7.0.8/etc/php-fpm.conf --pid /alidata/server/php7.0.8/var/run/php-fpm.pid
ExecStartPost=/usr/bin/chown www.www /alidata/server/php7.0.8/var/run/php7-fpm.sock
StandardOutput=syslog
StandardError=inherit

[Install]
WantedBy=multi-user.target
  • StandardOutput

設置進程的標準輸出(STDOUT)。 可設爲 inherit, null, tty, journal, syslog, kmsg, journal+console, syslog+console, kmsg+console, socket, fd 之一。 syslog 表示 syslog 日誌服務。 注意,此時所有日誌都會隱含的複製一份到 journal 中。

  • StandardError

設置進程的標準錯誤(STDERR)。 取值範圍及含義與 StandardOutput= 相同。 但有如下例外:(1) inherit 表示使用 StandardOutput= 的值。 (2) fd 的文件描述符名稱的默認值爲 "stderr" 此值默認爲 inherit

6、編寫守護進程單元 filebeat.service

[Unit]
Description=Filebeat sends log files to Logstash or directly to Elasticsearch.
Documentation=https://www.elastic.co/products/beats/filebeat
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/usr/share/filebeat/bin/filebeat -c /etc/filebeat/filebeat.yml -path.home /usr/share/filebeat -path.config /etc/filebeat -path.data /var/lib/filebeat -path.logs /var/log/filebeat -E serverip=$serverip
Restart=always

[Install]
WantedBy=multi-user.target

注意,在 Centos7 使用 Systemd 啓動 filebeat 時是不會傳遞系統環境變量的。 filebeat 如果在配置文件中引用了系統的環境變量,那麼會導致服務啓動失敗。 報錯大概是找不到那個引用了系統變量自定義的那個字段。 有兩個解決方法:

1、在 unit 文件中使用 Environment 相關參數,定義要使用的環境變量,在 ExecStart 啓動命令時引用。 -E serverip=$serverip

2、在 /etc/profile.d/ 目錄下創建定義系統環境變量的文件,裏面的內容類似
export serverip=ifconfig eth0 | grep 'inet' | awk '{print $2}' | cut -d':' -f2 ,然後在啓動命令時直接用 $變量名 引用系統環境變量。

systemctl 服務管理命令

'''查看系統所有已安裝的服務項'''
systemctl list-unit-files --type=service

'''查看系統所有運行的服務項'''
systemctl list-units --type=service

'''查看系統所有開機自啓動的服務項'''
systemctl list-unit-files --type=service |  grep enabled

'''查看出錯的服務'''
systemctl list-units --type=service --state=failed

'''啓動服務'''
systemctl start nginx.service

'''停止服務'''
systemctl stop nginx.service

'''重載服務的配置文件'''
systemctl reload nginx.service

'''查看服務的狀態'''
systemctl status nginx.service

'''設置服務開機啓動'''
systemctl enable nginx.service

'''禁用服務的開機啓動'''
systemctl disable nginx.service

'''禁止服務的啓動,用於防止服務被其他服務間接啓動,也無法通過 start 或 restart 命令來啓動'''
systemctl mask nginx.service

'''取消對服務的啓動禁止'''
systemctl unmask nginx.service

'''重新讀取所有服務項,修改或添加服務單元文件之後需要執行'''
systemctl daemon-reload

'''查看系統啓動耗時'''
systemd-analyze

'''查看各項服務啓動耗時'''
systemd-analyze blame | grep .service

參考文檔:http://www.jinbuguo.com/systemd/systemd.index.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章