我們運行 linux 服務器的主要目的是通過運行程序提供服務,比如 mysql、web server等。因此管理 linux 服務器主要工作就是配置並管理上面運行的各種服務程序。在 linux 系統中服務程序的管理主要由 init 系統負責。如同筆者在《初識 systemd》一文中的介紹,linux 的 init 系統已經從最初的 sysvinit 進化到了如今的 systemd。本文主要介紹在 systemd 環境中如何編寫運行服務的配置文件。
unit(單元)的配置文件
Unit 是 systemd 進行任務管理的基本單位,我們在前文中已經介紹過,service 類型的 unit 代表一個后台服務進程。接下來我們就詳細的介紹如何配置 service 類型的 unit。下面我們先來看一個簡單的服務配置:
[Unit] Description=Prometheus Server Documentation=https://prometheus.io/docs/introduction/overview/ After=network.target [Service] User=prometheus Restart=on-failure WorkingDirectory=/usr/local/share/prometheus/ ExecStart=/usr/local/share/prometheus/prometheus \ -config.file=/usr/local/share/prometheus/prometheus.yml [Install] WantedBy=multi-user.target
這是筆者主機上 prometheus 服務的配置文件。把上面的內容保存到文件 /lib/systemd/system/prometheus.service 中,然后就可以使用 systemctl 命令管理 prometheus 服務了。注意,服務類型的配置文件名稱必須以 .service 結尾。
查看上面配置信息的詳細內容,我們會發現整個配置的內容分為三個部分:
[Unit] unit 本身的說明,以及與其它有依賴關系的服務的設置,包括在什么服務之后才啟動此 unit 之類的設置。
[Service] 不同的 unit 類型就得要使用相對應的設置項目,比如 timer 類型的 unit 應該是 [Timer],socket 類型的 unit 應該是 [Socket]。服務類型的 unit 就是 [Service],這個項目內主要在規范服務啟動的腳本、環境配置文件文件名、重新啟動的方式等等。
[Install] 這個部分主要設置把該 unit 安裝到哪個 target 。
服務類型 unit 的詳細配置
配置文件分為三個部分,每個部分中都可以提供詳細的配置信息。為了精確的控制服務的運行方式,我們需要了解這些詳細的配置選項,並最終讓服務以我們期望的方式運行。
[Unit] 部分
Description 關於該 unit 的簡易說明。
Documentation 文檔相關的內容,如 Documentation=https://prometheus.io/docs/introduction/overview/
Documentation=man:sshd(8)
Documentation=file:/etc/ssh/sshd_config
After 說明本 unit 是在哪個服務啟動之后才啟動的意思。僅是說明服務啟動的順序而已,並沒有強制要求 。
Before 與 After 的意義相反,在指定的服務啟動前最好啟動本個服務的意思。僅是說明服務啟動的順序而已,並沒有強制要求 。
Requires 本 unit 需要在哪個服務啟動后才能夠啟動!就是設置服務間的依賴性。如果在此項設置的前導服務沒有啟動成功,那么本 unit 就不會被啟動!
Wants 與 Requires 剛好相反,規范的是這個 unit 之后還要啟動什么服務,如果這 Wants 后面接的服務如果沒有啟動成功,其實不會影響到這個 unit 本身!
Conflicts 這個項目后面接的服務如果有啟動,那么本 unit 就不能啟動!如果本 unit 啟動了,則指定的服務就不能啟動。
[Service] 部分
Type
說明這個服務的啟動方式,會影響到 ExecStart,主要有下面幾種類型:
simple:默認值,這個服務主要由 ExecStart 設置的程序來啟動,啟動后常駐於內存中。
forking:由 ExecStart 指定的啟動的程序通過 spawns 產生子進程提供服務,然后父進程退出。
oneshot:與 simple 類似,不過這個程序在工作完畢后就結束了,不會常駐在內存中。
dbus:與 simple 類似,但這個服務必須要在取得一個 D-Bus 的名稱后,才會繼續運行!因此設置這個項目時,通常也要設置 BusName= 才行。
idle:與 simple 類似,意思是,要執行這個服務必須要所有的工作都順利執行完畢后才會執行。這類的服務通常是開機到最后才執行即可的服務。
notify:與 simple 類似,但這個服務必須要收到一個 sd_notify() 函數發送的消息后,才會繼續運行。
ExecStart
就是實際執行此服務的程序。接受 "命令 參數 參數..." 的格式,不能接受 <, >, >>, |, & 等特殊字符,很多的 bash 語法也不支持。所以,要使用這些特殊的字符時,最好直接寫入到腳本里面去!
ExecStartPre 和 ExecStartPost 分別在服務啟動前后,執行額外的命令。
ExecStop 用來實現 systemctl stop 命令,關閉服務。
ExecReload 用來實現 systemctl reload 命令,重新加載服務的配置信息。
Restart 當設置為 Restart=1 時,如果服務終止,就會自動重啟此服務。
RestartSec 與 Restart 配合使用,在服務終止多長時間之后才重新啟動它。默認是 100ms。
KillMode
可以是 process, control-group, none 中的一種,如果是 process 則服務終止時,只會終止主要的程序(ExecStart接的后面那串指令),如果是 control-group 時,則由此 daemon 所產生的其他 control-group 的程序,也都會被關閉。如果是 none 的話,則沒有程序會被關閉。
TimeoutSec
若這個服務在啟動或者是關閉時,因為某些緣故導致無法順利 "正常啟動或正常結束" 的情況下,則我們要等多久才進入 "強制結束" 的狀態!
RemainAfterExit
當設置為 RemainAfterExit=1 時,則當這個服務所屬的所有程序都終止之后,此服務會再嘗試啟動。這對於 Type=oneshot 的服務很有幫助!
環境變量的設置對很多程序來說都是十分重要的,下面的配置則可以以不同的方式為服務程序設置環境變量:
Environment 用來設置環境變量,可以使用多次:
[Service] # Client Env Vars Environment=ETCD_CA_FILE=/path/to/CA.pem Environment=ETCD_CERT_FILE=/path/to/server.crt
EnvironmentFile 通過文件的方式設置環境變量,可以把下面的內容保存到文件 testenv 中:
AAA_IPV4_ANCHOR_0=X.X.X.X BBB_IPV4_PRIVATE_0=X.X.X.X CCC_HOSTNAME=test.example.com
然后這樣設置:
[Service]
EnvironmentFile=/testenv
接下來就可以在 ExecStart 配置中使用在文件中設置的環境變量,如:
ExecStart=/xxx --abc=xx${AAA_IPV4_ANCHOR_0}yy
[Install] 部分
WantedBy 這個設置后面接的大部分是 *.target unit。意思是,這個 unit 本身是附掛在哪個 target unit 下面。
Also 當目前這個 unit 被 enable 時,Also 后面接的 unit 也要 enable 的意思。
Alias 當 systemctl enable 相關的服務時,則此服務會進行鏈接文件的創建!
Timer 類型 unit 的詳細配置
Timer 類型的 unit 主要用來執行定時任務,並有可能取代 cron 服務。由於 timer 類型的 unit 經常與服務類型的 unit 一起使用,所以本文也附帶介紹一下 timer unit 的配置。與服務類型的 unit 不同,timer unit 配置文件中的主要部分是 [Timer],下面是其主要的配置項:
OnActiveSec 當 timers.target 啟動后多久才執行這個 unit。
OnBootSec 當開機后多久才執行這個 unit。
OnStartupSec 當 systemd 第一次啟動后多久才執行這個 unit。
OnUnitActiveSec 這個 timer 配置文件所管理的那個 unit 服務在最后一次啟動后,隔多久后再執行一次。
OnUnitInactiveSec 這個 timer 配置文件所管理的那個 unit 服務在最后一次停止后,隔多久后再執行一次。
Unit 一般不需要設置,基本上我們設置都是 服務名稱.server + 服務名稱.timer。如果你的服務名稱和 timer 名稱不相同,就需要在 .timer 文件中通過 Unit 項指定服務的名稱。
OnCalendar 使用實際時間(非循環時間)的方式來啟動服務。
Persistent 當使用 OnCalendar 的設置時,指定該功能要不要持續執行。
通過上面的介紹,相信大家對 systemd 服務類型和 timer 類型的 unit 配置已經有了基本的理解,下面讓就讓我們配置兩個實際的例子。
配置 redis 服務
在 ubuntu 上我們一般會手動編譯並安裝 redis。在安裝完成后需要把 redis 配置為 systemd 管理的服務,下面介紹具體的配置過程。
添加 redis 配置文件
首先手動創建 /etc/redis 目錄並添加配置文件:
$ sudo mkdir /etc/redis
並把代碼目錄中的配置文件 redis.conf 拷貝到 /etc/redis 目錄中:
$ sudo cp /tmp/redis-4.0.0/redis.conf /etc/redis/
然后修改配置文件 /etc/redis/redis.conf 中的 supervised 為 systemd:
supervised systemd
接着繼續在配置文件 /etc/redis/redis.conf 中配置工作目錄,把 dir ./ 修改為:
dir /var/lib/redis
配置由 systemd 管理 redis 服務
創建 /etc/systemd/system/redis.service 文件
$ sudo vim /etc/systemd/system/redis.service
編輯其內容如下:
[Unit] Description=Redis In-Memory Data Store After=network.target [Service] User=redis Group=redis ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf ExecStop=/usr/local/bin/redis-cli shutdown Restart=always [Install] WantedBy=multi-user.target
啟動服務並配置為開機啟動:
$ sudo systemctl start redis $ sudo systemctl enable redis $ sudo systemctl status redis
通過腳本定時備份文件
備份文件的 bash 腳本:
#!/bin/bash mydate() { date "+%Y%m%d%H%M%S" } backupdate=$(mydate) tar -zcf /tmp/backup.${backupdate}.tar.gz /home/nick/learn
把上面的代碼保存到文件 /usr/local/bin/backupdir.sh,並添加可執行權限:
$ sudo chmod +x /usr/local/bin/backupdir.sh
然后創建 service unit 配置文件:
[Unit] Description=nick backup learn dir service [Service] User=nick Group=nick Type=simple ExecStart=/usr/local/bin/backupdir.sh [Install] WantedBy=multi-user.target
把上面的 unit 配置保存到文件 /etc/systemd/system/nickbak.service。
然后執行下面的命令測試服務的執行情況:
$ sudo systemctl daemon-reload $ sudo systemctl start nickbak.service
這樣的備份任務只會在執行 sudo systemctl start nickbak.service 時執行一次。下面我們通過 timer unit 把它配置為定時執行。
創建 timer unit 配置文件:
[Unit] Description=nick backup learn dir timer [Timer] OnCalendar=*:0/15 Persistent=true Unit=nickbak.service [Install] WantedBy=multi-user.target
把上面的 unit 配置保存到文件 /etc/systemd/system/nickbak.timer。配置中 OnCalendar=*:0/15 表示每 15 分鍾執行一次 nickbak.service 服務。
執行下面的命令把 nickbak.timer 設置為開機啟動,並啟動 nickbak.timer:
$ sudo systemctl daemon-reload $ sudo systemctl enable nickbak.timer $ sudo systemctl start nickbak.timer
現在來看看 nickbak.timer 的狀態:
$ sudo systemctl status nickbak.timer
從現在開始 nickbak.timer 會每隔 15 分鍾執行一次 nickbak.service 服務。
總結
systemd 提供了服務管理(其實是 unit 管理)的方方面面,我們需要做的就是寫好服務 unit 的配置文件,然后利用 systemd 來管理我們的服務。這是一個看似簡單實則繁瑣的任務(很多的配置項其實需要我們在實踐中不斷的調整並優化)。希望本文對大家來說是個簡單的入門。
參考:
鳥哥的私房菜