强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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

⚠️ 注意事项

  1. 实例名长度:建议控制在 64 字符以内,避免路径溢出
  2. 特殊字符:实例名中的特殊字符会被转义,注意配置文件路径匹配
  3. 实例名验证:启动前验证实例名合法性,避免注入攻击
  4. 资源隔离:每个实例应有独立的数据目录和日志文件

💡 提示

  • 使用 systemctl list-units 'prefix@*' 查看模板的所有实例
  • %i%f 的区别在于路径分隔符 / 的处理
  • 模板文件可以与 drop-in 配合,实现全局默认和实例特定配置
  • 使用 systemctl edit [email protected] 为特定实例创建 drop-in

扩展阅读