怎样实现服务启停脚本

wen 实用脚本 23

本文目录导读:

怎样实现服务启停脚本

  1. Systemd Service (推荐 - Linux系统)
  2. Shell脚本版 (通用)
  3. Java应用服务脚本
  4. Docker版服务管理
  5. Python装饰器版服务框架
  6. 最佳实践建议

我来详细介绍如何实现服务启停脚本,包含不同场景的实现方式。

Systemd Service (推荐 - Linux系统)

创建服务文件

sudo vi /etc/systemd/system/myapp.service
[Unit]
Description=My Application Service
After=network.target
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/python3 /opt/myapp/app.py
ExecStop=/bin/kill -SIGTERM $MAINPID
ExecReload=/bin/kill -SIGHUP $MAINPID
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target

命令使用

# 启动服务
sudo systemctl start myapp
# 停止服务
sudo systemctl stop myapp
# 重启服务
sudo systemctl restart myapp
# 查看状态
sudo systemctl status myapp
# 开机自启
sudo systemctl enable myapp

Shell脚本版 (通用)

基本服务启停脚本

#!/bin/bash
# 配置项
APP_NAME="myapp"
APP_PATH="/opt/myapp"
APP_CMD="python3 app.py"
PID_FILE="/var/run/${APP_NAME}.pid"
LOG_FILE="/var/log/${APP_NAME}.log"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 获取进程PID
get_pid() {
    if [ -f "${PID_FILE}" ]; then
        cat "${PID_FILE}"
    else
        echo ""
    fi
}
# 检查服务状态
status() {
    local pid=$(get_pid)
    if [ -n "${pid}" ] && kill -0 "${pid}" 2>/dev/null; then
        echo -e "${GREEN}${APP_NAME} 正在运行 (PID: ${pid})${NC}"
        return 0
    else
        echo -e "${RED}${APP_NAME} 未运行${NC}"
        return 1
    fi
}
# 启动服务
start() {
    echo -e "${YELLOW}正在启动 ${APP_NAME}...${NC}"
    # 检查是否已运行
    if status > /dev/null 2>&1; then
        echo -e "${YELLOW}${APP_NAME} 已经在运行中${NC}"
        return 1
    fi
    # 进入工作目录
    cd "${APP_PATH}" || {
        echo -e "${RED}无法进入工作目录: ${APP_PATH}${NC}"
        return 1
    }
    # 启动服务
    nohup ${APP_CMD} >> "${LOG_FILE}" 2>&1 &
    local pid=$!
    echo ${pid} > "${PID_FILE}"
    # 等待启动完成
    sleep 2
    if kill -0 "${pid}" 2>/dev/null; then
        echo -e "${GREEN}${APP_NAME} 启动成功 (PID: ${pid})${NC}"
        return 0
    else
        echo -e "${RED}${APP_NAME} 启动失败,请检查日志${NC}"
        return 1
    fi
}
# 停止服务
stop() {
    echo -e "${YELLOW}正在停止 ${APP_NAME}...${NC}"
    local pid=$(get_pid)
    if [ -z "${pid}" ]; then
        echo -e "${YELLOW}${APP_NAME} 未在运行${NC}"
        return 0
    fi
    # 先尝试优雅停止
    kill "${pid}" 2>/dev/null
    # 等待进程结束
    local timeout=30
    while [ ${timeout} -gt 0 ]; do
        if ! kill -0 "${pid}" 2>/dev/null; then
            break
        fi
        sleep 1
        timeout=$((timeout - 1))
    done
    # 如果无法优雅停止,强制终止
    if kill -0 "${pid}" 2>/dev/null; then
        echo -e "${YELLOW}强制停止进程...${NC}"
        kill -9 "${pid}" 2>/dev/null
    fi
    # 清理PID文件
    rm -f "${PID_FILE}"
    echo -e "${GREEN}${APP_NAME} 已停止${NC}"
}
# 重启服务
restart() {
    stop
    sleep 2
    start
}
# 查看日志
logs() {
    if [ -f "${LOG_FILE}" ]; then
        tail -f "${LOG_FILE}"
    else
        echo -e "${RED}日志文件不存在: ${LOG_FILE}${NC}"
    fi
}
# 主函数
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    status)
        status
        ;;
    logs)
        logs
        ;;
    *)
        echo "用法: $0 {start|stop|restart|status|logs}"
        exit 1
        ;;
esac
exit $?

Java应用服务脚本

#!/bin/bash
# Java应用配置
APP_NAME="spring-boot-app"
JAR_NAME="app.jar"
JAVA_HOME="/usr/local/java/jdk11"
APP_DIR="/opt/${APP_NAME}"
JAVA_OPTS="-Xms512m -Xmx1024m -Dspring.profiles.active=prod"
PID_FILE="/var/run/${APP_NAME}.pid"
LOG_FILE="/var/log/${APP_NAME}/app.log"
# 加载环境变量
if [ -f "${APP_DIR}/env.sh" ]; then
    source "${APP_DIR}/env.sh"
fi
# 启动Java应用
start() {
    echo "Starting ${APP_NAME}..."
    cd "${APP_DIR}"
    # 使用exec启动Java进程
    exec ${JAVA_HOME}/bin/java ${JAVA_OPTS} \
        -jar ${APP_DIR}/${JAR_NAME} \
        2>&1 &
    echo $! > ${PID_FILE}
    echo "${APP_NAME} started with PID $(cat ${PID_FILE})"
}
# 优雅停止Java应用
stop() {
    echo "Stopping ${APP_NAME}..."
    if [ ! -f ${PID_FILE} ]; then
        echo "${APP_NAME} is not running"
        return 0
    fi
    local pid=$(cat ${PID_FILE})
    # 发送SIGTERM信号
    kill -15 ${pid}
    # 等待进程结束
    for i in $(seq 1 30); do
        if ! kill -0 ${pid} 2>/dev/null; then
            echo "${APP_NAME} stopped successfully"
            rm -f ${PID_FILE}
            return 0
        fi
        sleep 1
    done
    # 超时后强制停止
    echo "Force stopping ${APP_NAME}..."
    kill -9 ${pid} 2>/dev/null
    rm -f ${PID_FILE}
}

Docker版服务管理

#!/bin/bash
# Docker容器管理
CONTAINER_NAME="myapp"
IMAGE_NAME="myapp:latest"
NETWORK="myapp-network"
VOLUMES="-v /data/myapp:/app/data"
start() {
    echo "Starting container ${CONTAINER_NAME}..."
    # 检查容器是否存在
    if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
        # 如果存在则启动
        docker start ${CONTAINER_NAME}
    else
        # 不存在则创建并启动
        docker run -d \
            --name ${CONTAINER_NAME} \
            --restart unless-stopped \
            --network ${NETWORK} \
            ${VOLUMES} \
            -p 8080:8080 \
            ${IMAGE_NAME}
    fi
}
stop() {
    echo "Stopping container ${CONTAINER_NAME}..."
    docker stop -t 30 ${CONTAINER_NAME}
}
restart() {
    stop
    sleep 5
    start
}
status() {
    docker ps --filter "name=${CONTAINER_NAME}" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
}
logs() {
    docker logs -f --tail 100 ${CONTAINER_NAME}
}

Python装饰器版服务框架

#!/usr/bin/env python3
import os
import sys
import signal
import argparse
import logging
import time
from contextlib import contextmanager
class ServiceManager:
    """服务管理器装饰器"""
    def __init__(self, name, pid_file=None, log_file=None):
        self.name = name
        self.pid_file = pid_file or f"/var/run/{name}.pid"
        self.log_file = log_file or f"/var/log/{name}.log"
        self.logger = logging.getLogger(name)
        self._setup_logging()
    def _setup_logging(self):
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler(self.log_file),
                logging.StreamHandler(sys.stdout)
            ]
        )
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            parser = argparse.ArgumentParser(description=f'{self.name} Service Manager')
            parser.add_argument('action', choices=['start', 'stop', 'restart', 'status'],
                              help='Service action')
            args = parser.parse_args()
            if args.action == 'start':
                return self._start(func, *args, **kwargs)
            elif args.action == 'stop':
                return self._stop()
            elif args.action == 'restart':
                return self._stop() or self._start(func, *args, **kwargs)
            elif args.action == 'status':
                return self._status()
        return wrapper
    def _start(self, func, *args, **kwargs):
        if self._get_pid() and os.path.exists(f'/proc/{self._get_pid()}'):
            self.logger.warning(f"{self.name} is already running")
            return
        pid = os.fork()
        if pid > 0:
            # Parent process
            with open(self.pid_file, 'w') as f:
                f.write(str(pid))
            self.logger.info(f"{self.name} started (PID: {pid})")
            sys.exit(0)
        elif pid == 0:
            # Child process
            self._daemonize()
            func(*args, **kwargs)
    def _stop(self):
        pid = self._get_pid()
        if not pid:
            self.logger.info(f"{self.name} is not running")
            return
        try:
            os.kill(pid, signal.SIGTERM)
            # Wait for process to exit
            for _ in range(30):
                time.sleep(1)
                if not os.path.exists(f'/proc/{pid}'):
                    break
            else:
                os.kill(pid, signal.SIGKILL)
            os.remove(self.pid_file)
            self.logger.info(f"{self.name} stopped")
        except ProcessLookupError:
            os.remove(self.pid_file)
            self.logger.info(f"{self.name} was not running")
    def _status(self):
        pid = self._get_pid()
        if pid and os.path.exists(f'/proc/{pid}'):
            self.logger.info(f"{self.name} is running (PID: {pid})")
            return True
        else:
            self.logger.info(f"{self.name} is not running")
            return False
    def _get_pid(self):
        if os.path.exists(self.pid_file):
            with open(self.pid_file) as f:
                return int(f.read().strip())
        return None
    def _daemonize(self):
        os.setsid()
        os.umask(0)
        sys.stdin.close()
        sys.stdout.close()
        sys.stderr.close()
# 使用示例
@ServiceManager("myapp", pid_file="/tmp/myapp.pid", log_file="/tmp/myapp.log")
def main():
    """Your application main function"""
    import time
    while True:
        print("Service is running...")
        time.sleep(10)
if __name__ == "__main__":
    main()

最佳实践建议

安全性考虑

# 1. 权限控制
chmod 750 /etc/init.d/myservice
chown root:root /etc/init.d/myservice
# 2. 防止重复启动
if [ -f $PID_FILE ] && kill -0 $(cat $PID_FILE) 2>/dev/null; then
    echo "Service is already running"
    exit 1
fi
# 3. 资源限制
ulimit -n 65535
ulimit -u 10240

测试脚本

#!/bin/bash
# test_service.sh - 服务脚本测试
# 测试用例
test_service() {
    local script=$1
    echo "Testing $script..."
    # 测试启动
    $script start
    if [ $? -eq 0 ]; then
        echo "✓ Start successful"
    else
        echo "✗ Start failed"
        return 1
    fi
    # 测试状态
    $script status
    if [ $? -eq 0 ]; then
        echo "✓ Status check passed"
    fi
    # 测试重启
    $script restart
    if [ $? -eq 0 ]; then
        echo "✓ Restart successful"
    fi
    # 测试停止
    $script stop
    if [ $? -eq 0 ]; then
        echo "✓ Stop successful"
    fi
}
# 运行测试
test_service "./myservice.sh"

选择哪种方式取决于你的具体需求:

  • 现代Linux系统:优先使用systemd
  • 传统系统或简单服务:使用Shell脚本
  • Java应用:使用专门的Java脚本
  • 容器化部署:使用Docker管理
  • Python应用:使用装饰器框架

建议始终包含错误处理、日志记录和状态检查功能。

抱歉,评论功能暂时关闭!