背景 最近工作频繁和 Linux 打交道,发现一个挺有用的配置:systemd,多次使用到 systemd 的场景:
jenkins 启动节点的服务,避免手动执行 nohup、手动重连
gitlab-runner 的服务,gitlab-runner install 时会创建一个 systemd 服务
配置 python http 服务,启动一个服务,让其他人可以访问某个目录下的 html 静态文件
systemd 功能 systemd (System Daemon)是 Linux 的系统和服务管理器,用于控制系统启动和后台服务。
统一管理(systemctl start|stop|enable|status):所有服务用同样的命令
开机自启(WantedBy=multi-user.target)
自动重启崩溃的服务(Restart=always)
日志集成:通过 journalctl 统一查看日志
依赖控制(After=、Requires=):可以定义服务启动顺序
资源限制: 可以限制 CPU、内存等资源
实践场景 Jenkins Agent 服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [Unit] Description =Jenkins Agent ServiceAfter =network.target[Service] Type =simpleUser =magnoliaWorkingDirectory =/home/magnolia/jenkins_homeExecStart =/usr/bin/java -jar /home/magnolia/jenkins_home/agent.jar \ -url http://127.0.0.1:8081/ \ -secret @/home/magnolia/jenkins_home/secret-file \ -name linux \ -webSocket \ -workDir /home/magnolia/jenkins_home/ Restart =on -failureRestartSec =10 StandardOutput =journalStandardError =journal[Install] WantedBy =multi-user.target
在用户目录,创建一个 service 文件,软连接到 /etc/systemd/system/ (分离操作系统文件和本地配置 )
1 sudo ln -s /home/magnolia/jenkins_home/jenkins-agent.service /etc/systemd/system/jenkins-agent.service
1 2 3 4 5 sudo systemctl stop jenkins-agentsudo systemctl daemon-reloadsudo systemctl start jenkins-agentsudo systemctl status jenkins-agentsudo journalctl -u jenkins-agent -f
GitLab Runner 服务 1 2 3 4 5 6 7 8 9 10 11 12 13 [Unit] Description =GitLab RunnerConditionFileIsExecutable =/usr/local/bin/gitlab-runnerAfter =network.target[Service] StartLimitInterval =5 StartLimitBurst =10 ExecStart =/usr/local/bin/gitlab-runner "run" "--config" "/etc/gitlab-runner/config.toml" "--working-directory" "/home/tools/gitlab-runner" "--service" "gitlab-runner" "--user" "gitlab-runner" Restart =alwaysRestartSec =120 EnvironmentFile =-/etc/sysconfig/gitlab-runner[Install] WantedBy =multi-user.target
Python HTTP 静态文件服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [Unit] Description =Python HTTP Static File ServerAfter =network.target[Service] Type =simpleUser =autotestWorkingDirectory =/home/autotest/work/auto-doc/doc/build/ExecStart =/usr/bin/python3.6 -m http.server 8000 --bind 0.0 .0.0 Restart =on -failureRestartSec =5 [Install] WantedBy =multi-user.target
Flask 服务 一个用于启动 flask 服务的 sh 脚本方式,start.sh:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #!/bin/bash source ~/tools/miniforge3/etc/profile.d/conda.shconda activate vltenvLinux which pythonpython --version flask --version gunicorn --version export $(grep -v '^#' .env | xargs)TIMESTAMP=$(date '+%Y_%m_%d_%H_%M_%S' ) ENABLE_SCHEDULER=true gunicorn -w 4 -b 0.0.0.0:5001 app:app \ --access-logfile "logs/gunicorn_access_${TIMESTAMP} .log" \ --error-logfile "logs/gunicorn_error_${TIMESTAMP} .log" \ --log-level info \ --timeout 120 \ --keep-alive 5 \ --max-requests 1000 \ --max-requests-jitter 100 \ --preload
转为 systemd 管理
step1,创建 systemd 服务文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 (vltenvLinux) [autotest@dgvxl2905 vlt_track]$ cat vlt_track_app.service [Unit] Description=VLT TRACK Flask Application with Gunicorn After=network.target [Service] Type=notify User=autotest WorkingDirectory=/home/autotest/vlt/vlt_track Environment="PATH=/home/autotest/tools/miniforge3/envs/vltenvLinux/bin:/usr/local/bin:/usr/bin:/bin" EnvironmentFile=/home/autotest/vlt/vlt_track/.env Environment="ENABLE_SCHEDULER=true" ExecStart=/home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn \ -w 4 \ -b 0.0.0.0:5000 \ app:app \ --access-logfile /home/autotest/vlt/vlt_track/logs/gunicorn_access.log \ --error-logfile /home/autotest/vlt/vlt_track/logs/gunicorn_error.log \ --log-level info \ --timeout 120 \ --keep-alive 5 \ --max-requests 1000 \ --max-requests-jitter 100 \ --preload ExecReload=/bin/kill -USR1 $MAINPID Restart=always RestartSec=10 [Install] WantedBy=multi-user.target
1 2 mkdir -p /home/autotest/vlt/vlt_track/logssudo ln -s /home/autotest/vlt/vlt_track/vlt_track_app.service /etc/systemd/system/vlt_track_app.service
step2,创建 logrotate 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 (base) [root@dgvxl2905 vlt_track]# cd /etc/logrotate.d/ (base) [root@dgvxl2905 logrotate.d]# cat vlt_track /home/autotest/vlt/vlt_track/logs/gunicorn_*.log { su autotest autotest daily rotate 30 compress delaycompress notifempty missingok dateext dateformat _%Y%m%d create 0644 autotest autotest sharedscripts postrotate /bin/systemctl reload vlt_track_app.service > /dev/null 2>&1 || true endscript }
测试 logrotate 配置
1 2 3 4 5 6 7 8 9 10 11 (base) [root@dgvxl2905 logrotate.d]# logrotate -d /etc/logrotate.d/vlt_track reading config file /etc/logrotate.d/vlt_track Allocating hash table for state file, size 15360 B Handling 1 logs rotating pattern: /home/autotest/vlt/vlt_track/logs/gunicorn_*.log after 1 days (30 rotations) empty log files are not rotated, old logs are removed switching euid to 5991 and egid to 5991 considering log /home/autotest/vlt/vlt_track/logs/gunicorn_access_2025_08_06_11_21_11.log log does not need rotating (log has been already rotated)considering log /home/autotest/vlt/vlt_track/logs/gunicorn_access_2025_08_06_11_48_12.log
step3,启动服务
1 2 3 systemctl daemon-reload systemctl start vlt_track_app systemctl status vlt_track_app
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 (base) [root@dgvxl2905 vlt_track]# systemctl status vlt_track_app ● vlt_track_app.service - VLT TRACK Flask Application with Gunicorn Loaded: loaded (/home/autotest/vlt/vlt_track/vlt_track_app.service; linked; vendor preset: disabled) Active: active (running) since Fri 2025-10-17 10:17:41 CST; 2s ago Main PID: 6208 (gunicorn) Status: "Gunicorn arbiter booted" Tasks: 8 Memory: 54.3M CGroup: /system.slice/vlt_track_app.service ├─6208 /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/python /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn -w 4 -b 0.0.0.0:... ├─6213 /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/python /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn -w 4 -b 0.0.0.0:... ├─6228 /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/python /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn -w 4 -b 0.0.0.0:... ├─6229 /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/python /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn -w 4 -b 0.0.0.0:... └─6230 /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/python /home/autotest/tools/miniforge3/envs/vltenvLinux/bin/gunicorn -w 4 -b 0.0.0.0:... Oct 17 10:17:40 dgvxl2905 systemd[1]: Starting VLT TRACK Flask Application with Gunicorn... Oct 17 10:17:41 dgvxl2905 gunicorn[6208]: 2025-10-17 10:17:41.336 | INFO | utils.api_scanner:start_scheduler:297 - API扫描-定时任务已启动 Oct 17 10:17:41 dgvxl2905 gunicorn[6208]: 2025-10-17 10:17:41.336 | INFO | utils.api_scanner:start_scheduler:298 - API扫描-定时任务时间: 1点0分 Oct 17 10:17:41 dgvxl2905 gunicorn[6208]: 2025-10-17 10:17:41.336 | INFO | utils.api_scanner:init_api_scanner_scheduler:564 - API扫描-定时任务已…PID: 6208) Oct 17 10:17:41 dgvxl2905 gunicorn[6208]: 2025-10-17 10:17:41.338 | INFO | utils.statistics_collector:start_scheduler:37 - 统计数据-定时任务已启…PID: 6208) Oct 17 10:17:41 dgvxl2905 gunicorn[6208]: 2025-10-17 10:17:41.338 | INFO | utils.statistics_collector:init_statistics_collector_scheduler:70 - …(PID: 6208) Oct 17 10:17:41 dgvxl2905 systemd[1]: Started VLT TRACK Flask Application with Gunicorn. Hint: Some lines were ellipsized, use -l to show in full.
Timer 场景:每天凌晨2点自动清理 /tmp 目录中的旧文件。
step1:创建要执行的脚本
1 2 3 4 5 6 7 8 (base) magnolia@Magnolia:~/tools/systemd_tasks$ sudo vi cleanup-tmp.sh (base) magnolia@Magnolia:~/tools/systemd_tasks$ cat cleanup-tmp.sh echo "$(date) : 开始清理临时文件..." find /tmp -type f -mtime +7 -delete echo "$(date) : 清理完成!" (base) magnolia@Magnolia:~/tools/systemd_tasks$ sudo chmod +x cleanup-tmp.sh
step2:创建 Service 文件(定义要做什么)
1 2 3 4 5 6 7 8 (base) magnolia@Magnolia:~/tools/systemd_tasks$ sudo vi cleanup-tmp.service (base) magnolia@Magnolia:~/tools/systemd_tasks$ cat cleanup-tmp.service [Unit] Description=清理临时文件 [Service] Type=oneshot ExecStart=/home/magnolia/tools/systemd_tasks/cleanup-tmp.sh
step3:创建 Timer 文件(定义什么时候执行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 (base) magnolia@Magnolia:~/tools/systemd_tasks$ sudo vi cleanup-tmp.timer (base) magnolia@Magnolia:~/tools/systemd_tasks$ cat cleanup-tmp.timer [Unit] Description=每天凌晨2点清理临时文件 [Timer] OnCalendar=*-*-* 02:00:00 Persistent=true [Install] WantedBy=timers.target (base) magnolia@Magnolia:~/tools/systemd_tasks$ ls -l total 12 -rw-r--r-- 1 root root 103 Oct 8 10:37 cleanup-tmp.service -rwxr-xr-x 1 root root 166 Oct 8 10:31 cleanup-tmp.sh -rw-r--r-- 1 root root 139 Oct 8 10:40 cleanup-tmp.timer
step4:软连接到 /etc/systemd/system/
在用户目录集中管理自定义配置,便于维护,systemd 通过软连接获取
1 2 3 4 5 6 (base) magnolia@Magnolia:/etc/systemd/system$ sudo ln -s /home/magnolia/tools/systemd_tasks/cleanup-tmp.service /etc/systemd/system/cleanup-tmp.service (base) magnolia@Magnolia:/etc/systemd/system$ sudo ln -s /home/magnolia/tools/systemd_tasks/cleanup-tmp.timer /etc/systemd/system/cleanup-tmp.timer (base) magnolia@Magnolia:/etc/systemd/system$ ls -l total 48 lrwxrwxrwx 1 root root 54 Oct 8 10:54 cleanup-tmp.service -> /home/magnolia/tools/systemd_tasks/cleanup-tmp.service lrwxrwxrwx 1 root root 52 Oct 8 10:56 cleanup-tmp.timer -> /home/magnolia/tools/systemd_tasks/cleanup-tmp.timer
step5:启用和管理 Timer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 (base) magnolia@Magnolia:/etc/systemd/system$ sudo systemctl daemon-reload (base) magnolia@Magnolia:/etc/systemd/system$ sudo systemctl enable cleanup-tmp.timer Created symlink /etc/systemd/system/timers.target.wants/cleanup-tmp.timer → /home/magnolia/tools/systemd_tasks/cleanup-tmp.timer. (base) magnolia@Magnolia:/etc/systemd/system$ sudo systemctl start cleanup-tmp.timer (base) magnolia@Magnolia:/etc/systemd/system$ sudo systemctl status cleanup-tmp.timer ● cleanup-tmp.timer - 每天凌晨2点清理临时文件 Loaded: loaded (/etc/systemd/system/cleanup-tmp.timer; enabled; preset: enabled) Active: active (waiting) since Wed 2025-10-08 10:57:27 CST; 4s ago Trigger: Thu 2025-10-09 02:00:00 CST; 15h left Triggers: ● cleanup-tmp.service Oct 08 10:57:27 Magnolia systemd[1]: Started cleanup-tmp.timer - 每天凌晨2点清理临时文件. (base) magnolia@Magnolia:/etc/systemd/system$ systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES Wed 2025-10-08 15:26:32 CST 4h 28min Wed 2025-10-08 02:40:01 CST 1h 12min ago motd-news.timer motd-news.service Wed 2025-10-08 16:23:34 CST 5h 25min Fri 2025-10-03 10:54:44 CST 18h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service Thu 2025-10-09 00:00:00 CST 13h Wed 2025-10-08 00:00:07 CST 1h 19min ago dpkg-db-backup.timer dpkg-db-backup.service Thu 2025-10-09 00:00:00 CST 13h Wed 2025-10-08 00:00:07 CST 1h 19min ago logrotate.timer logrotate.service Thu 2025-10-09 01:59:50 CST 15h Wed 2025-10-08 08:38:33 CST 1h 11min ago apt-daily.timer apt-daily.service Thu 2025-10-09 02:00:00 CST 15h - - cleanup-tmp.timer cleanup-tmp.service Thu 2025-10-09 06:47:26 CST 19h Wed 2025-10-08 07:07:45 CST 1h 11min ago apt-daily-upgrade.timer apt-daily-upgrade.service Thu 2025-10-09 07:51:38 CST 20h Wed 2025-10-08 02:38:48 CST 1h 13min ago man-db.timer man-db.service Sun 2025-10-12 03:10:17 CST 3 days Sun 2025-10-05 03:25:10 CST 11h ago e2scrub_all.timer e2scrub_all.service 9 timers listed. Pass --all to see loaded but inactive timers, too. (base) magnolia@Magnolia:/etc/systemd/system$ sudo journalctl -u cleanup-tmp.timer -f Oct 08 10:57:27 Magnolia systemd[1]: Started cleanup-tmp.timer - 每天凌晨2点清理临时文件.