Run a Script on Startup in Linux
The Kubernetes ecosystem is huge and quite complex, so it’s easy to forget about costs when trying out all of the exciting tools.
To avoid overspending on your Kubernetes cluster, definitely have a look at the free K8s cost monitoring tool from the automation platform CAST AI. You can view your costs in real time, allocate them, calculate burn rates for projects, spot anomalies or spikes, and get insightful reports you can share with your team.
Connect your cluster and start monitoring your K8s costs right away:
1. Overview
In this quick tutorial, we’ll explore different options for executing a script on startup in Linux. This comes in handy in plenty of situations, such as if we want to start a server application automatically.
2. Solutions
Without further ado, let’s create a simple script to execute:
#!/bin/sh echo "Last reboot time: $(date)" > /etc/motd
This piece of code sets the message of the day to be the last reboot time, so that each user can see it after their first login.
Then we’ll save our file and make it executable:
Now that our script is ready, let’s see how to schedule its execution.
2.1. Using cron
Let’s begin with the easiest solution, which involves using cron. In order to do this, we need to edit our crontab file:
Here we’ll add a line using the @reboot expression, which will execute our code once at startup:
@reboot sh /home/ec2-user/reboot_message.sh
This solution is quick and clean, since we don’t have to deal with additional configuration, but not every version of cron supports @reboot.
2.2. Using rc.local
Now let’s consider another solution that takes advantage of the /etc/rc.d/rc.local file. Since this file already runs at startup, we can append a line that invokes our script:
sh /home/ec2-user/reboot_message.sh
In order for this to work, we need to ensure that the rc.local file itself is executable:
2.3. Using init.d
Similar to the previous solution, the /etc/init.d folder contains lifecycle executables of the services managed by the system. We can also add our own by creating an LSB-compliant wrapper that starts our service:
#! /bin/sh # chkconfig: 345 99 10 case "$1" in start) # Executes our script sudo sh /home/ec2-user/reboot_message.sh ;; *) ;; esac exit 0
This wrapper will launch our code when it’s invoked with the start argument. However, we must include a line with the chkconfig configuration, which contains the service runlevel and the start/stop priority.
After placing the wrapper in the init.d folder, we need to register our service for startup execution:
$ chkconfig --add service_wrapper.sh
Since the chkconfig command isn’t available on Debian systems, update-rc.d can be used as an alternative there:
$ update-rc.d service_wrapper.sh defaults
2.4. Using systemd
Finally, let’s see how to run a script with systemd. Similar to init.d, we need to create a service descriptor, called a unit file, under /etc/systemd/system:
[Unit] Description=Reboot message systemd service. [Service] Type=simple ExecStart=/bin/bash /home/ec2-user/reboot_message.sh [Install] WantedBy=multi-user.target
The file is organized into different sections:
- Unit – contains general metadata, like a human-readable description
- Service – describes the process and daemonizing behavior, along with the command to start the service
- Install – enables the service to run at startup using the folder specified in WantedBy to handle dependencies
Next, we’ll need to set the file permissions to 644, and enable our service by using systemctl:
$ chmod 644 /etc/systemd/system/reboot_message.service $ systemctl enable reboot_message.service
One thing to keep in mind is that, although many major distributions support systemd, it’s not always available.
3. Conclusion
In this article, we took a look at different ways of executing a script at startup in Linux. Each one of them has its pros and cons, but generally speaking, systemd and cron should be preferred when available. Consequently, rc.local and init.d should be used as fallbacks.
Пишем Init скрипт
Инициализация — важнейшая процедура, лежащая в основе любой операционной системы на основе Unix/Linux для управления работой каждого скрипта и службы.
Я описывал в своих статьях процесс создания скриптов для systemD/Upstart и сейчас я хотел бы написать о написании init скриптов. Тема не новая и используется давно, но я большую часть своих тем беру именно из черновиков, которые накопились за года 3-4. У меня не хватает времени на все публикации + наполнять контент максимально (по крайней мере, сразу) и по этому, имеет что имеем. Но не смотря на это, у меня уже довольно много читателей — и это хорошо, это радует!
По сути, инициализация следует за этим процессом:
- Загрузка сервера.
- Запуск init процесса (обычно как PID 1).
- Запуск предопределенного набора задач для запуска активируется последовательно.
Инициализация отвечает за то, чтобы сервер мог загрузиться и отключиться. В некоторых Unix/Linux дистрибутивах используется стандартный процесс инициализации init/systemD/Upstart.
Пишем Upstart скрипт
И так, хотелось бы рассказать как можно запускать томкат. Но для начала, нужно узнать какой механизм инициализации используется:
Т.к речь идет о sysv init/Upstart, то и продолжим тему.
PS: Вот еще полезное чтиво:
INIT скрипт может иметь следующие состояния:
- start — Служит командой для запуска службы.
- stop — Выполняет остановку службы.
- restart — Перезапуст службы, а по факту — остановка и затем запуск сервиса.
- reload — Перезагрузка службы, т.е команда для перечитывания конфигурации без перезапуска или остановки службы.
- force-reload — Перезагрузка конфигурации (перечитать конфиг), если служба поддерживает это. В противном случае — выполнит перезапуск службы.
- status — Покажет состояние службы.
Вот скилет скрипта для использования:
#!/bin/bash -x # # Source function library. . /etc/rc.d/init.d/functions case "$1" in start) echo -n "Starting services: " touch /var/lock/subsys/ ;; stop) echo -n "Shutting down services: " rm -f /var/lock/subsys/ ;; status) ;; restart) ;; reload) ;; probe) ;; *) echo "Usage:Данный каркас можно юзать для любых целей.
Примеры Init скрипта
Приведу наглядный пример запуска томката.
Запуск TOMCAT с init
Создадим init скрипт для запуска:
#!/bin/bash -x # # Provides: tomcat9 # Required-Start: $local_fs $remote_fs $network # Required-Stop: $local_fs $remote_fs $network # Should-Start: $named # Should-Stop: $named # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start Tomcat. # Description: Start the Tomcat servlet engine. ### END INIT INFO export CATALINA_HOME=/usr/local/tomcat9 export JAVA_HOME=/usr/local/jdk1.8.0_131 export PATH=$JAVA_HOME/bin:$PATH service_name="tomcat9" start() < echo "Starting Tomcat 9. " /bin/su -s /bin/bash tomcat -c $CATALINA_HOME/bin/startup.sh >stop() < echo "Stopping Tomcat 9. " /bin/su -s /bin/bash tomcat -c $CATALINA_HOME/bin/shutdown.sh >status() < if (( $(ps -ef | grep -v grep | grep $service_name | wc -l) >0 )); then echo "$service_name is running. " else echo "$service_name is down. " fi > case $1 in start|stop|status) $1;; restart) stop; start;; *) echo "Usage : $0"; exit 1;; esac exit 0 Даем права на запуск (на исполнение):
# chmod 755 /etc/init.d/tomcat9Открываем браузер и смотрим что вышло!
#!/bin/bash -x DAEMON_PATH="/home/wes/Development/projects/myapp" DAEMON=myapp DAEMONOPTS="-my opts" NAME=myapp DESC="My daemon description" PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME case "$1" in start) printf "%-50s" "Starting $NAME. " cd $DAEMON_PATH PID=`$DAEMON $DAEMONOPTS > /dev/null 2>&1 & echo $!` #echo "Saving PID" $PID " to " $PIDFILE if [ -z $PID ]; then printf "%s\n" "Fail" else echo $PID > $PIDFILE printf "%s\n" "Ok" fi ;; status) printf "%-50s" "Checking $NAME. " if [ -f $PIDFILE ]; then PID=`cat $PIDFILE` if [ -z "`ps axf | grep $ | grep -v grep`" ]; then printf "%s\n" "Process dead but pidfile exists" else echo "Running" fi else printf "%s\n" "Service not running" fi ;; stop) printf "%-50s" "Stopping $NAME" PID=`cat $PIDFILE` cd $DAEMON_PATH if [ -f $PIDFILE ]; then kill -HUP $PID printf "%s\n" "Ok" rm -f $PIDFILE else printf "%s\n" "pidfile not found" fi ;; restart) $0 stop $0 start ;; *) echo "Usage: $0" exit 1 esac Подойдет к большинству скриптов, а для остальных — можно отредактировать немного.
И, вот еще полезный довольно вариант:
#!/bin/sh ### BEGIN INIT INFO # Provides: # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start daemon at boot time # Description: Enable service provided by daemon. ### END INIT INFO dir="" cmd="" user="" name=`basename $0` pid_file="/var/run/$name.pid" stdout_log="/var/log/$name.log" stderr_log="/var/log/$name.err" get_pid() < cat "$pid_file" >is_running() < [ -f "$pid_file" ] && ps -p `get_pid` >/dev/null 2>&1 > case "$1" in start) if is_running; then echo "Already started" else echo "Starting $name" cd "$dir" if [ -z "$user" ]; then sudo $cmd >> "$stdout_log" 2>> "$stderr_log" & else sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" & fi echo $! > "$pid_file" if ! is_running; then echo "Unable to start, see $stdout_log and $stderr_log" exit 1 fi fi ;; stop) if is_running; then echo -n "Stopping $name.." kill `get_pid` for i in 1 2 3 4 5 6 7 8 9 10 # for i in `seq 10` do if ! is_running; then break fi echo -n "." sleep 1 done echo if is_running; then echo "Not stopped; may still be shutting down or shutdown may have failed" exit 1 else echo "Stopped" if [ -f "$pid_file" ]; then rm "$pid_file" fi fi else echo "Not running" fi ;; restart) $0 stop if is_running; then echo "Unable to stop, will not attempt to start" exit 1 fi $0 start ;; status) if is_running; then echo "Running" else echo "Stopped" exit 1 fi ;; *) echo "Usage: $0" exit 1 ;; esac exit 0 Вот и все, статья «Пишем Init скрипт» завершена.