systemd 教程 / 多实例服务模板
多实例服务模板
systemd 模板(Template)允许使用一个 Unit 文件创建多个服务实例,这是管理同类服务(如多站点 Web 服务器、多个数据库实例)的最佳实践。
1. 模板 Unit 文件(@.service)
1.1 模板文件命名
模板文件必须包含 @ 符号:
/etc/systemd/system/[email protected] # 正确
/etc/systemd/system/myservice.service # 普通服务
1.2 模板文件示例
# /etc/systemd/system/[email protected]
[Unit]
Description=Web Application Instance %i
After=network.target
[Service]
Type=forking
PIDFile=/run/nginx-%i.pid
ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/%i.conf
ExecStart=/usr/sbin/nginx -c /etc/nginx/%i.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
1.3 实例名与模板变量
| 变量 | 含义 | 示例(实例名 www.example.com) |
|---|---|---|
%i | 实例名(转义后) | www.example.com |
%I | 实例名(未转义) | www.example.com |
%f | 实例名(文件系统转义) | www.example.com |
%p | 单元名前缀 | webapp |
%h | 用户主目录 | /home/www |
%u | 用户名 | www |
%H | 主机名 | webserver01 |
%t | 运行时目录 | /run |
实例名转义规则:
| 字符 | %i 转义为 | %f 转义为 |
|---|---|---|
/ | - | / |
- | \x2d | - |
. | . | . |
2. 模板服务的管理
2.1 启动实例
# 启动单个实例
sudo systemctl start [email protected]
sudo systemctl start [email protected]
# 启用实例(开机自启)
sudo systemctl enable [email protected]
2.2 查看实例状态
# 查看单个实例
sudo systemctl status [email protected]
# 查看所有实例
sudo systemctl list-units 'webapp@*'
# 查看已启用的实例
sudo systemctl list-unit-files 'webapp@*'
2.3 管理所有实例
# 停止所有实例
sudo systemctl stop 'webapp@*'
# 重启所有实例
sudo systemctl restart 'webapp@*'
3. 实际案例
3.1 多站点 Nginx
模板文件:
# /etc/systemd/system/[email protected]
[Unit]
Description=Nginx Site %i
After=network.target
[Service]
Type=forking
PIDFile=/run/nginx-%i.pid
ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/sites/%i.conf
ExecStart=/usr/sbin/nginx -c /etc/nginx/sites/%i.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Nginx 配置文件:
# /etc/nginx/sites/www.example.com.conf
worker_processes auto;
pid /run/nginx-www.example.com.pid;
events { worker_connections 1024; }
http {
server {
listen 80;
server_name www.example.com;
root /var/www/www.example.com;
access_log /var/log/nginx/www.example.com.access.log;
}
}
启动实例:
sudo systemctl start [email protected]
sudo systemctl enable [email protected]
3.2 多数据库实例
模板文件:
# /etc/systemd/system/[email protected]
[Unit]
Description=PostgreSQL %i
After=network.target
[Service]
Type=forking
User=postgres
Group=postgres
ExecStart=/usr/pgsql-15/bin/pg_ctl -D /var/lib/pgsql/%i -l /var/log/pgsql/%i.log start
ExecStop=/usr/pgsql-15/bin/pg_ctl -D /var/lib/pgsql/%i -m fast stop
ExecReload=/usr/pgsql-15/bin/pg_ctl -D /var/lib/pgsql/%i reload
TimeoutSec=120
Restart=on-failure
[Install]
WantedBy=multi-user.target
3.3 Worker 进程池
模板文件:
# /etc/systemd/system/[email protected]
[Unit]
Description=Worker Process %i
After=network.target redis.service
Requires=redis.service
[Service]
Type=simple
User=worker
Group=worker
WorkingDirectory=/opt/worker
ExecStart=/opt/worker/bin/worker --id=%i --queue=default --concurrency=4
Restart=always
RestartSec=5
StartLimitBurst=10
StartLimitIntervalSec=60
CPUQuota=50%
MemoryMax=512M
[Install]
WantedBy=multi-user.target
批量启动脚本:
#!/bin/bash
# manage-workers.sh
ACTION=$1
COUNT=${2:-4}
case ${ACTION} in
start)
for i in $(seq 1 ${COUNT}); do
systemctl start worker@${i}.service
systemctl enable worker@${i}.service
done ;;
stop)
systemctl stop 'worker@*' ;;
restart)
systemctl restart 'worker@*' ;;
scale)
CURRENT=$(systemctl list-units 'worker@*' --no-legend | wc -l)
if [ ${COUNT} -gt ${CURRENT} ]; then
for i in $(seq $((CURRENT+1)) ${COUNT}); do
systemctl start worker@${i}.service
systemctl enable worker@${i}.service
done
elif [ ${COUNT} -lt ${CURRENT} ]; then
for i in $(seq $((COUNT+1)) ${CURRENT}); do
systemctl disable worker@${i}.service
systemctl stop worker@${i}.service
done
fi ;;
esac
4. 模板 Timer
4.1 模板 Timer 文件
# /etc/systemd/system/[email protected]
[Unit]
Description=Backup Timer for %i
[Timer]
OnCalendar=%i
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
# /etc/systemd/system/[email protected]
[Unit]
Description=Backup Service for %i
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh %i
Nice=19
IOSchedulingClass=idle
⚠️ 注意:Timer 的实例名就是时间表达式,需要正确转义。
5. 模板 Socket
5.1 Socket 模板文件
# /etc/systemd/system/[email protected]
[Unit]
Description=MyApp Socket %i
[Socket]
ListenStream=/run/myapp/%i.sock
SocketUser=www-data
SocketMode=0660
[Install]
WantedBy=sockets.target
# /etc/systemd/system/[email protected]
[Unit]
Description=MyApp Instance %i
Requires=myapp@%i.socket
[Service]
Type=simple
ExecStart=/opt/myapp/bin/myapp --socket=/run/myapp/%i.sock
User=www-data
IdleTimeout=60
6. 模板最佳实践
6.1 设计原则
| 原则 | 说明 | 示例 |
|---|---|---|
| 单一职责 | 每个模板只管理一类服务 | [email protected] |
| 参数化配置 | 使用 %i 替代硬编码路径 | /etc/nginx/%i.conf |
| 独立数据 | 每个实例有独立的数据目录 | /var/lib/myapp/%i/ |
| 统一管理 | 支持批量操作 | systemctl stop 'webapp@*' |
6.2 配置文件组织
/etc/systemd/system/
├── [email protected] # 模板文件
└── [email protected]/ # 全局 drop-in
└── defaults.conf
/etc/nginx/sites/
├── www.example.com.conf # 实例配置
└── api.example.com.conf
/var/www/
├── www.example.com/ # 实例数据
└── api.example.com/
7. 模板与 drop-in 结合
7.1 全局 drop-in
为所有实例添加默认配置:
# /etc/systemd/system/[email protected]/defaults.conf
[Service]
CPUQuota=80%
MemoryMax=1G
PrivateTmp=true
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/var/log/nginx /var/cache/nginx
7.2 实例特定 drop-in
为特定实例添加额外配置:
# /etc/systemd/system/[email protected]/extra.conf
[Service]
MemoryMax=2G
Environment=NGINX_WORKER_CONNECTIONS=4096
7.3 优先级顺序
1. 实例特定 drop-in(最高优先级)
2. 模板全局 drop-in
3. 模板文件
4. 系统默认配置(最低优先级)
8. 实例管理脚本
#!/bin/bash
# manage-sites.sh
ACTION=$1
SITE=$2
case ${ACTION} in
add)
cat > /etc/nginx/sites/${SITE}.conf <<EOF
server {
listen 80;
server_name ${SITE};
root /var/www/${SITE};
}
EOF
mkdir -p /var/www/${SITE}
systemctl start nginx-site@${SITE}.service
systemctl enable nginx-site@${SITE}.service
echo "Site ${SITE} added and started."
;;
remove)
systemctl disable nginx-site@${SITE}.service
systemctl stop nginx-site@${SITE}.service
rm -f /etc/nginx/sites/${SITE}.conf
echo "Site ${SITE} removed."
;;
list)
systemctl list-units 'nginx-site@*' --no-legend
;;
*)
echo "Usage: $0 {add|remove|list} [site]"
exit 1
;;
esac
⚠️ 注意事项
- 实例名长度:建议控制在 64 字符以内,避免路径溢出
- 特殊字符:实例名中的特殊字符会被转义,注意配置文件路径匹配
- 实例名验证:启动前验证实例名合法性,避免注入攻击
- 资源隔离:每个实例应有独立的数据目录和日志文件
💡 提示
- 使用
systemctl list-units 'prefix@*'查看模板的所有实例 %i和%f的区别在于路径分隔符/的处理- 模板文件可以与 drop-in 配合,实现全局默认和实例特定配置
- 使用
systemctl edit [email protected]为特定实例创建 drop-in