systemd服务需通过ExecStartPre调用健康检查脚本(如wait-for-db.sh)主动探测数据库端口连通性、认证可用性和SQL执行能力,而非仅依赖After/Wants声明启动顺序;脚本应使用专用低权限账号并支持超时重试。
要让服务在数据库(如 mysql 或 postgresql)完全就绪后再启动,不能只靠“先启数据库、再启应用”这种简单顺序——因为 systemd 启动数据库服务(mysql.service)成功,只表示进程已拉起,**不代表监听端口已打开、tcp 可连、用户认证已就绪、甚至表结构已初始化完成**。真正的“就绪”需要主动探测。
关键思路:用 systemd 的 `ExecStartPre` + 健康检查脚本
这不是靠 `After=` 和 `Wants=` 就能解决的,而是要在你的服务单元中插入一个前置检查步骤,直到数据库真正可连接才继续启动主程序。
- `After=mysql.service` 确保数据库服务已开始启动(但不保证就绪)
- `Wants=mysql.service` 确保数据库被拉起(避免因依赖缺失跳过)
- `ExecStartPre=/usr/local/bin/wait-for-db.sh` 执行自定义等待脚本,超时失败则中止本服务启动
写一个可靠的等待脚本(以 MySQL 为例)
保存为 /usr/local/bin/wait-for-db.sh,并赋予执行权限:
#!/bin/bash
MAX_ATTEMPTS=60
DELAY=2
HOST="127.0.0.1"
PORT="3306"
USER="healthcheck"
PASS="check123"
for i in $(seq 1 $MAX_ATTEMPTS); do
if mysql -h"$HOST" -P"$PORT" -u"$USER" -p"$PASS" -e "SELECT 1;" >/dev/null 2>&1; then
echo "✅ MySQL is ready at $HOST:$PORT"
exit 0
fi
echo "⏳ Waiting for MySQL ($i/$MAX_ATTEMPTS)..."
sleep $DELAY
done
echo "❌ Failed to connect to MySQL after $MAX_ATTEMPTS attempts"
exit 1
注意:该脚本需使用一个专用只读账号(推荐创建 healthcheck 用户并限制权限),避免暴露生产账号密码。
在你的服务 unit 文件中引用它
例如你的应用叫 myapp.service,编辑 /etc/systemd/system/myapp.service:
[Unit] Description=My Application Service After=mysql.service Wants=mysql.service [Service] Type=simple User=myapp WorkingDirectory=/opt/myapp ExecStartPre=/usr/local/bin/wait-for-db.sh ExecStart=/opt/myapp/bin/server Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target
启用后运行:sudo systemctl daemon-reload && sudo systemctl enable --now myapp.service
进阶建议:避免硬编码密码
更安全的做法是把数据库凭据放在 /etc/mysql/debian.cnf(Debian/Ubuntu)或 /root/.my.cnf(CentOS/RHEL)中,并让脚本读取配置文件而非命令行参数。也可改用 socket 连接(-S /var/run/mysqld/mysqld.sock)减少网络依赖。


















