Skip to main content

Linux 系统设置开机自动运行脚本

11 min read

在工作中,我们经常有个需求,那就是在系统启动之后,自动启动某个脚本或服务。在 Windows 下,我们有很多方法可以设置开机启动,但在 Linux 系统下我们需要如何操作呢?

Linux 下同样可以设置开机启动,但可能需要我们敲一些命令(可能也有 UI 界面的设置方法,但我不熟,我更多是玩命令)。下面我们就介绍三种简单但可行的开机启动设置方法。

版权声明

warning

本教程参考了One作者的教程,并结合自身实际部署过程中积累的经验编写而成。原始教程链接为Linux 系统设置开机自动运行脚本的方法),感谢原作者提供的宝贵参考资料。

本教程仅供学习和交流使用,任何人不得将本教程的内容用于商业用途。如需引用或转载,请务必注明原作者及本文出处。如侵权之处,请联系博主进行删除,谢谢~

部分内容引用One大佬的教程,感谢大佬的贡献。

方法一:修改 /etc/rc.d/rc.local 文件

/etc/rc.d/rc.local 文件会在 Linux 系统各项服务都启动完毕之后再被运行。所以你想要自己的脚本在开机后被运行的话,可以将自己脚本路径加到该文件里。

tip

这种方法,在任何 Linux 系统上都可以使用。

  • 环境:

CentOS Linux 7.9 (2009) (Core)

  • 我们先来查看下这个文件的内容是什么
cat  /etc/rc.d/rc.local

输出:

#!/bin/bash
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
#
# It is highly advisable to create own systemd services or udev rules
# to run scripts during boot instead of using this file.
#
# In contrast to previous versions due to parallel execution during boot
# this script will NOT be run after all other services.
#
# Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure
# that this script will be executed during boot.

#创建一个空文件 /var/lock/subsys/local,该文件的存在通常用来指示某个服务或任务已经成功运行
#这是旧版 Linux 系统中的一种惯例,但在现代系统中,它的作用已经非常有限。
touch /var/lock/subsys/local

这个脚本是位于 /etc/rc.d/rc.local 的示例文件,主要用于兼容性目的,特别是在 CentOS 7 中。/etc/rc.d/rc.local 是传统的启动脚本文件,允许用户在系统启动时执行自定义的脚本或命令,因此可以往里写开机要执行的命令或脚本。

warning

/etc/rc.local/etc/rc.d/rc.local的软链

  • 可执行权限: 要确保这个脚本在系统启动时被执行,需要给脚本添加可执行权限:
chmod +x /etc/rc.d/rc.local

演示

  • 首先,在根目录目录或指定的位置创建一个脚本文件,例如 /blog/start_all_containers.sh
vim /blog/start_all_containers.sh
  • 添加脚本内容
#!/bin/bash

# 定义一个函数来启动所有未运行的Docker容器
start_stopped_containers() {
# 查找所有未运行的容器
stopped_containers=$(docker ps -a -f "status=exited" -q)

# 如果有未运行的容器,启动它们
if [ ! -z "$stopped_containers" ]; then
echo "启动所有未运行的Docker容器..."
docker start $stopped_containers
else
echo "没有未运行的Docker容器需要启动。"
fi
}

# 调用函数
start_stopped_containers
  • 保存退出后,确保文件有执行权限
chmod +x /blog/start_all_containers.sh
  • 然后,我们再将脚本添加到 /etc/rc.d/rc.local 文件最后一行:
vim /etc/rc.d/rc.local
/blog/start_all_containers.sh

添加后的内容:

#!/bin/bash
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
#
# It is highly advisable to create own systemd services or udev rules
# to run scripts during boot instead of using this file.
#
# In contrast to previous versions due to parallel execution during boot
# this script will NOT be run after all other services.
#
# Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure
# that this script will be executed during boot.

touch /var/lock/subsys/local

#这里添加刚刚在/blog文件夹下创建的"start_all_containers.sh"脚本
/blog/start_all_containers.sh
  • 直接重启系统验证测试:
reboot

可以看到开机自启动所有未运行的Docker容器,输出如下:

[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ba8627412252 ruyu-blog-hd "java -jar /app/app.…" 37 hours ago Up About a minute 0.0.0.0:8088->8088/tcp, :::8088->8088/tcp ruyu-blog-hd
115838d43850 blog-ht "/docker-entrypoint.…" 2 days ago Up About a minute 80/tcp, 0.0.0.0:81->81/tcp, :::81->81/tcp blog-ht
3334358ca32b blog-qt "/docker-entrypoint.…" 2 days ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp blog-qt
d14e0fc37e68 filesite/machete "/var/www/machete/do…" 2 days ago Up About a minute 0.0.0.0:445->445/tcp, :::445->445/tcp, 9000/tcp, 0.0.0.0:1081->80/tcp, :::1081->80/tcp machete
19abde9750b1 registry.cn-shenzhen.aliyuncs.com/mogublog_business/frpc "/bin/sh -c '/usr/bi…" 2 days ago Up About a minute frpc
e9f405df3cfe binaryify/netease_cloud_music_api "docker-entrypoint.s…" 4 days ago Up About a minute 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp netease_cloud_music_api
de384f1f541f hitokoto/api "docker-entrypoint.s…" 4 days ago Up About a minute 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp hitokoto_hitokoto_api_1
86e5cf8d6306 redis:6.0.8 "docker-entrypoint.s…" 4 days ago Up About a minute 6379/tcp hitokoto_hitokoto_db_1
6726bc097856 minio/minio "/usr/bin/docker-ent…" 4 days ago Up About a minute 0.0.0.0:9000-9001->9000-9001/tcp, :::9000-9001->9000-9001/tcp minio
d2c98432baa2 rabbitmq "docker-entrypoint.s…" 4 days ago Up About a minute 4369/tcp, 0.0.0.0:5672->5672/tcp, :::5672->5672/tcp, 5671/tcp, 15691-15692/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp, :::15672->15672/tcp rabbit
61da49911e67 redis:7.2.3 "docker-entrypoint.s…" 4 days ago Up About a minute 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp redis
ec44863c383e mysql:8.0 "docker-entrypoint.s…" 5 days ago Up About a minute 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql

完成这些步骤后,脚本将在系统启动时自动运行,并尝试启动所有未运行的 Docker 容器。

  • 有关 /etc/rc.d/rc.local 执行时机如下:

image-20220625120119559

方法二:使用 crontab

tip

这种方法,在任何 Linux 系统上都可以使用。

  • 环境:

CentOS Linux 7.9 (2009) (Core)

crontab 是 Linux 下的计划任务,当时间达到我们设定的时间时,可以自动触发某些脚本的运行。

我们可以自己设置计划任务时间,然后编写对应的脚本。但是,有个特殊的任务,叫作 @reboot ,我们其实也可以直接从它的字面意义看出来,这个任务就是在系统重启之后自动运行某个脚本。

演示

下面是如何使用 crontab 来创建一个任务,以便定期启动所有未运行的 Docker 容器。

  1. 编写脚本

创建一个脚本文件,例如 /blog/start_stopped_docker_containers.sh

vim /blog/start_stopped_docker_containers.sh

并添加启动未运行的 Docker 容器的内容:

#!/bin/bash

# 定义一个函数来启动所有未运行的Docker容器
start_stopped_containers() {
# 查找所有未运行的容器
stopped_containers=$(docker ps -a -f "status=exited" -q)

# 如果有未运行的容器,启动它们
if [ ! -z "$stopped_containers" ]; then
echo "启动所有未运行的Docker容器..."
docker start $stopped_containers
else
echo "没有未运行的Docker容器需要启动。"
fi
}

# 调用函数
start_stopped_containers
  • 保存退出后,确保文件有执行权限
chmod +x /blog/start_stopped_docker_containers.sh
  1. 编辑 crontab

使用 crontab -e 打开 crontab 编辑器,然后添加以下行:

crontab -e

在打开的编辑器中,添加以下行在每次系统启动时,执行 /blog/start_stopped_docker_containers.sh 脚本

@reboot /blog/start_stopped_docker_containers.sh
  • 然后,直接重启即可。运行的效果跟上面类似。
reboot

方法三:使用-systemd-服务(推荐)

  • 环境:

CentOS Linux 7.9 (2009) (Core)

warning
  • 次方法仅适用于 systemd 系统。如何区分是不是 systemd 系统?很简单,只需运行 ps aux|more 命令,查看 pid1 的进程是不是 systemd

ps aux|more 命令输出:

[root@localhost ~]# ps aux|more
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.3 0.1 194092 7156 ? Ss 21:54 0:04 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
root 2 0.0 0.0 0 0 ? S 21:54 0:00 [kthreadd]
root 4 0.0 0.0 0 0 ? S< 21:54 0:00 [kworker/0:0H]
root 6 0.0 0.0 0 0 ? S 21:54 0:00 [ksoftirqd/0]
root 7 0.0 0.0 0 0 ? S 21:54 0:00 [migration/0]
root 8 0.0 0.0 0 0 ? S 21:54 0:00 [rcu_bh]
root 9 0.7 0.0 0 0 ? S 21:54 0:12 [rcu_sched]

演示

使用 systemd 创建一个服务来在系统启动时运行脚本是一种更现代和灵活的方法。以下是如何创建一个 systemd 服务来启动未运行的 Docker 容器的步骤。

  1. 创建脚本

创建一个 systemd 启动服务单元文件

vim /blog/start_stopped_docker_containers.sh

例如,你的脚本路径为 /blog/start_stopped_docker_containers.sh 添加以下内容:

#!/bin/bash

# 定义一个函数来启动所有未运行的Docker容器
start_stopped_containers() {
# 查找所有未运行的容器
stopped_containers=$(docker ps -a -f "status=exited" -q)

# 如果有未运行的容器,启动它们
if [ ! -z "$stopped_containers" ]; then
echo "启动所有未运行的Docker容器..."
docker start $stopped_containers
else
echo "没有未运行的Docker容器需要启动。"
fi
}

# 调用函数
start_stopped_containers

保存退出后,确保脚本具有执行权限:

sudo chmod +x /blog/start_stopped_docker_containers.sh
  1. 创建 systemd 服务文件
warning

请注意,这时后缀是 .service ,而不是 .sh

vim /etc/systemd/system/start-docker-containers.service

创建一个 systemd 服务单元文件,例如 /etc/systemd/system/start-docker-containers.service,并添加以下内容:

[Unit]
Description=Start stopped Docker containers
After=network.target docker.service

[Service]
ExecStart=/blog/start_stopped_docker_containers.sh
Type=oneshot
RemainAfterExit=true

[Install]
WantedBy=multi-user.target

解释:

  • [Unit] 部分定义了服务的描述和依赖。After=network.target docker.service 表示该服务在网络和 Docker 服务启动后运行。
  • [Service] 部分定义了服务的执行方式。ExecStart 指定了要运行的脚本。Type=oneshot 表示脚本运行一次即完成。RemainAfterExit=true 确保服务在脚本执行后仍然处于活动状态。
  • [Install] 部分定义了服务的安装目标,WantedBy=multi-user.target 表示服务在多用户运行级别启动时启动。
  1. 重新加载 systemd 配置

在创建或修改服务文件后,重新加载 systemd 配置以使更改生效:

sudo systemctl daemon-reload
  1. 启用并启动服务

启用服务,使其在每次系统启动时自动运行:

sudo systemctl enable start-docker-containers.service

立即启动服务以测试它是否正常工作:

sudo systemctl start start-docker-containers.service
  1. 检查服务状态

检查服务状态以确保它正在运行:

sudo systemctl status start-docker-containers.service

可以看到服务的当前状态以及最近的日志输出:

[root@localhost ~]# sudo systemctl status start-docker-containers.service
● start-docker-containers.service - Start stopped Docker containers
Loaded: loaded (/etc/systemd/system/start-docker-containers.service; enabled; vendor preset: disabled)
Active: active (exited) since 五 2024-09-06 23:35:42 PDT; 7s ago
Process: 130731 ExecStart=/bin/bash /blog/start_stopped_docker_containers.sh (code=exited, status=0/SUCCESS)
Main PID: 130731 (code=exited, status=0/SUCCESS)

906 23:35:42 localhost.localdomain systemd[1]: Starting Start stopped Docker containers...
906 23:35:42 localhost.localdomain bash[130731]: 没有未运行的Docker容器需要启动。
906 23:35:42 localhost.localdomain systemd[1]: Started Start stopped Docker containers.

通过这些步骤,你将成功创建一个 systemd 服务,该服务在每次系统启动时运行脚本,以启动所有未运行的 Docker 容器。

方法四:/etc/rc.d/init.d目录

演示

  1. 创建启动脚本

    /etc/rc.d/init.d 目录下创建一个新的启动脚本,例如 start-docker-containers

    vim /etc/rc.d/init.d/start-docker-containers.sh
  2. 编写脚本内容

    在脚本中添加以下内容:

    #!/bin/bash
    # chkconfig: 2345 99 01
    # description: Start stopped Docker containers

    ### BEGIN INIT INFO
    # Provides: start-docker-containers
    # Required-Start: $network $local_fs $remote_fs $docker
    # Required-Stop: $network $local_fs $remote_fs
    # Default-Start: 3 4 5
    # Default-Stop: 0 1 2 6
    # Short-Description: Start stopped Docker containers at boot time
    # Description: Starts all stopped Docker containers.
    ### END INIT INFO

    case "$1" in
    start)
    echo "Starting stopped Docker containers..."
    #确保路径有这个文件
    /usr/local/bin/start_stopped_docker_containers.sh
    ;;
    stop)
    echo "Stopping Docker containers (not implemented)..."
    ;;
    restart)
    echo "Restarting Docker containers (not implemented)..."
    ;;
    status)
    echo "Status of Docker containers (not implemented)..."
    ;;
    *)
    echo "Usage: $0 {start|stop|restart|status}"
    exit 1
    ;;
    esac

    exit 0

    解释:

    • chkconfig 行用于设置运行级别。
    • ### BEGIN INIT INFO### END INIT INFO 块提供了有关脚本的信息。
    • case "$1" 部分处理 start, stop, restart, 和 status 参数。
  3. 赋予脚本可执行权限

    使脚本可执行:

    sudo chmod +x /etc/rc.d/init.d/start-docker-containers.sh
  4. 设置脚本开机自启

    使用 chkconfig 工具来确保脚本在系统启动时运行(适用于基于 SysVinit 的系统):

    sudo chkconfig --add start-docker-containers.sh

    然后,确保脚本在所需的运行级别中启动(通常是 3, 4, 5):

    sudo chkconfig start-docker-containers.sh on
  5. /usr/local/bin目录下创建start_stopped_docker_containers.sh脚本

    vim /usr/local/bin/start_stopped_docker_containers.sh
  6. 编写脚本内容

    在脚本中添加以下内容:

    #!/bin/bash

    # 定义日志文件
    LOGFILE="/var/log/start_docker_containers.log"

    # 写入开始时间
    echo "Starting stopped Docker containers at $(date)" >> $LOGFILE

    # 获取所有停止的 Docker 容器
    stopped_containers=$(docker ps -a -q -f "status=exited")

    # 检查是否有停止的容器
    if [ -z "$stopped_containers" ]; then
    echo "No stopped Docker containers to start." >> $LOGFILE
    else
    # 启动每一个停止的容器
    for container in $stopped_containers; do
    echo "Starting container $container" >> $LOGFILE
    docker start $container >> $LOGFILE 2>&1
    done
    fi

    # 写入结束时间
    echo "Finished starting Docker containers at $(date)" >> $LOGFILE
  7. 赋予脚本可执行权限

    使脚本可执行:

    sudo chmod +x /usr/local/bin/start_stopped_docker_containers.sh
  8. 手动测试脚本

    你可以手动启动脚本来测试其是否工作正常:

    sudo /etc/rc.d/init.d/start-docker-containers.sh start

    输出:
    [root@localhost ~]# sudo /etc/rc.d/init.d/start-docker-containers.sh start
    Starting stopped Docker containers...

解释

  • 脚本内容:脚本用于在启动时执行 Docker 容器启动操作。
  • chkconfig 工具:用于管理系统服务的开机启动。
  • SysVinit:传统的初始化系统,通常在老旧系统中使用。

如果你的系统使用 systemd,建议优先使用 systemd 服务单元文件,因为 systemd 提供了更强大的服务管理功能。如果你的系统确实使用 SysVinit,这些步骤应该可以帮助你设置服务。

Loading Comments...