리눅스 부팅 자동 명령 실행: cron과 systemd

데비안 리눅스 환경에서 cron.dsystemd를 이용해 부팅 시 특정 스크립트나 프로그램을 자동으로 실행하도록 설정하는 방법을 설명한다. 단순한 명령을 한 번 실행하려면 /etc/cron.d/@reboot를, 실행 순서와 의존 관계, 상태 감시 및 자동 재시작이 필요한 프로그램은 systemd를 사용하는 것이 좋다.

가장 빠른 장애 복구 방법: 재부팅

개인 PC든 서버든, 문제가 발생했을 때 가장 빠르고 효과적인 해결 방법은 재부팅이다. 재부팅 자체는 어렵지 않지만 문제는 그다음이다. 부팅이 완료될 때마다 서비스를 다시 살리기 위해 수동으로 명령을 입력하고 스크립트를 실행해야 한다면, 복구 시간은 불필요하게 길어진다. 이는 곧 서버 정지 시간의 증가와 서비스 신뢰도 저하, 그리고 무엇보다 사용자의 귀찮음으로 이어진다.

사람이 개입하는 과정에는 언제나 변수가 따른다. 새벽에 몽롱한 상태에서 명령의 실행 순서를 헷갈리거나 일부 단계를 빠뜨리는 일은 꽤나 자주 발생한다. 때문에 시스템이 부팅될 때 필요한 명령과 서비스를 자동으로 실행하는 구조는 선택이 아니라 필수다. 서버를 껐다 켜는 것만으로 서비스가 정상 상태로 복구된다면, 운영자는 한밤중에 서버가 죽었다는 전화를 받고도 다음과 같이 훨씬 단순하고 안정적으로 대응할 수 있다.

일단 껐다가 켜고, 안 켜지면 전기선 뽑았다가 다시 꼽아~!

데비안 리눅스에서는 부팅 시 명령이나 프로그램을 자동으로 실행하는 방법으로 전통적인 cron@reboot 기능과, 서비스 관리자인 systemd를 사용할 수 있다. 이 글에서는 실제 운영 환경을 기준으로 두 방식의 차이와 선택 기준을 살펴보고, systemd를 이용해 보다 안정적인 자동 실행 구조를 구성하는 방법을 정리해 본다.

cron을 이용한 자동 실행 등록

cron은 리눅스에서 오래전부터 사용되어 온 전통적인 자동 실행 방식이다. 보통 반복 스케줄 작업만 관리하는 것으로 오해하기 쉽지만, @reboot 옵션을 사용하면 시스템이 부팅된 후 지정된 명령이나 스크립트를 실행시킬 수 있다.

/etc/cron.d/를 이용한 자동 실행 등록 방법

cron을 이용한 작업은 사용자별 crontab을 사용하거나 시스템 전역 설정에 등록할 수 있다. crontab은 개인 사용자의 작업을 관리할 때는 적합하지만 본 글에서는 시스템 전체의 부팅 작업을 관리하는 것이 목적이므로, /etc/cron.d/ 디렉토리에 설정 파일을 직접 생성하는 방법을 설명한다.

다음과 같이 /etc/cron.d/ 디렉토리에 자동 실행 작업을 저장할 파일을 생성한다. 파일 이름에는 확장자를 붙이지 않고 영문자, 숫자, 밑줄 및 하이픈만 사용하는 것이 안전하다.

sudo nano /etc/cron.d/boot-script

파일을 열었으면 다음과 같이 @reboot, 명령을 실행할 사용자, 실행할 스크립트의 절대 경로를 차례로 입력한다.

# 시스템이 부팅되어 cron 데몬이 시작될 때 eqmaker 권한으로 실행하는 스크립트의 예
@reboot eqmaker /path/to/script.sh

/etc/cron.d/의 설정은 사용자 crontab과 달리 명령을 실행할 사용자 이름을 반드시 지정해야 한다. 위의 예에서는 eqmaker 사용자의 권한으로 /path/to/script.sh를 실행한다. 루트 권한이 필요한 작업이라면 사용자 이름을 root로 지정하면 된다.

설정 파일을 저장한 다음 설정 파일의 소유자와 권한을 확인한다. 보통 root가 소유하고 다른 사용자가 수정할 수 없도록 설정한다.

sudo chown root:root /etc/cron.d/boot-script sudo chmod 644 /etc/cron.d/boot-script

실행할 스크립트에도 실행 계정이 실행 권한을 가지고 있어야 한다.

sudo chmod +x /path/to/script.sh

이제 시스템이 부팅되어 cron 데몬이 시작되면 지정된 사용자의 권한으로 /path/to/script.sh가 실행된다.

cron의 한계

/etc/cron.d/의 설정 자체는 매우 간단하다. 문제는 이 단순함이 운영 환경에서는 치명적인 약점이 될 수 있다는 것이다. 특히 @reboot를 이용한 부팅 자동 실행에는 다음과 같은 한계가 있다.

  • 다른 서비스와의 실행 순서 및 의존 관계를 직접 제어하기 어렵다.
  • 실행한 프로세스가 종료되더라도 자동으로 다시 시작하지 않는다.
  • 프로세스의 실행 상태를 지속적으로 추적하지 않는다.

@reboot 작업은 시스템의 모든 부팅 과정이 완료된 시점이 아니라 cron 데몬이 시작될 때 실행된다. 따라서 네트워크나 다른 서비스가 아직 올라오지 않은 상태에서 명령이 실행될 수 있다.

예를 들어 라우터 역할을 하는 시스템이라면 최소한 네트워크 설정이 끝난 다음에 라우터 프로그램을 기동해야 한다. 하지만 cron은 부팅 도중 실행 명령을 만나면 냅다 라우터 프로그램을 실행해 버린다. 이런 상황이 발생하면 라우터 프로그램이 정상적으로 작동할 리가 없다.

또 다른 예로, 와우자 스트리밍 엔진에서 라이브 스트림을 생성하도록 요청하는 스크립트가 있다고 가정해 보자. 와우자가 완전히 초기화되기 전에 스크립트가 먼저 실행되면 요청이 정상적으로 처리되지 않게 된다.

결국 /etc/cron.d/ 방식은 부팅할 때 단순한 명령을 한 번 실행하는 용도에는 적합하지만, 실행 순서와 의존 관계, 상태 감시가 필요한 서비스를 운용하기에는 부족함이 있다. 이러한 작업에는 이어서 소개할 systemd가 더 적합하다.

systemd

systemd는 데비안을 비롯한 여러 현대 리눅스 배포판에서 널리 사용하는 시스템 및 서비스 관리자다. 시스템이 부팅될 때 필요한 구성 요소를 시작하고, 실행 중인 서비스의 상태와 의존 관계를 관리한다.

단순히 명령만 던지는 cron.d와 달리, systemd는 실행하는 대상을 서비스 유닛으로 등록해 관리하기 때문에 다음과 같은 이점을 제공한다.

  • 부팅 순서 및 서비스 간 의존 관계 설정
  • 서비스 프로세스의 실행 상태 추적 및 확인
  • 비정상 종료 시 자동 재시작 정책 설정
  • systemd-journald를 통한 실행 기록과 오류 로그 확인
  • 서비스의 시작·중지·재시작 및 활성화 상태 관리

즉, 언제 실행할 것인가, 무엇이 먼저 준비되어야 하는가, 프로세스가 종료되면 어떻게 대응할 것인가를 서비스 단위로 정의할 수 있다. 따라서 실행 순서와 상태 감시 및 장애 복구가 필요한 프로그램이라면 cron을 대신해 systemd를 사용하는 게 더 낫다.

systemd 서비스 유닛

systemd에서 프로그램을 서비스로 관리하려면 실행 대상과 관리 방법을 서비스 유닛(Service Unit)으로 정의해야 한다. 실행할 명령 한 줄을 등록하는 cron과 달리, 서비스 유닛에는 프로그램의 실행 방법과 의존 관계 및 장애 대응 방법을 설정할 수 있다.

서비스 유닛의 정의를 기록한 파일을 서비스 유닛 파일(Service Unit File)이라고 하며, 서비스 파일이나 유닛 파일이라고도 부른다. 이 파일들은 /etc/systemd/system/ 디렉토리에 위치하며, 안을 열어 보면 아래와 같이 세 섹션으로 이루어져 있다.

[Unit] 섹션
이 서비스가 언제, 어떤 조건에서 시작되어야 하는지를 정의한다.
[Service] 섹션
실제로 무엇을 어떻게 실행하고, 실행 중 상태를 어떻게 관리할지를 정의한다.
[Install] 섹션
systemctl enable을 실행할 때 이 서비스를 어떤 대상 유닛에 연결할지 정의한다.

systemd 서비스 파일의 작성과 적용 예시

아래의 예제는 본 필자가 진행한 프로젝트의 일부로, VPN 연결을 위해 OpenVPN 클라이언트를 기동하는 서비스다.

먼저 /etc/systemd/system/ 디렉토리에 vpn213.service라는 서비스 유닛 파일을 생성한다.

sudo nano /etc/systemd/system/vpn213.service

서비스 파일에는 다음과 같이 입력한다.

[Unit]
Description=OpenVPN client for VPN213
Wants=network-online.target
After=network-online.target

[Service]
Type=exec
ExecStart=/usr/sbin/openvpn --config /etc/openvpn/client/vpn213.ovpn
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

이 예제는 OpenVPN을 별도의 백그라운드 프로세스로 전환하지 않고 포그라운드 상태로 실행하여 systemd가 프로세스를 직접 추적하도록 구성한 것이다. 따라서 vpn213.ovpn 설정 파일에 daemon이나 writepid 지시어가 있다면 제거해야 한다.

Description
이 서비스 파일을 구분하기 위한 메모이다. 실제 서비스에 영향을 주지는 않는다.
Wants
network-online.target도 함께 시작하도록 약한 의존 관계를 설정한다.
After
이 프로세스가 실행되는 순서를 지정한다. network-online.target은 네트워크가 준비된 상태를 의미한다. 즉, 네트워크 준비가 끝난 다음에 실행하게 한다.
Type
이 프로세스가 동작하는 모드를 지정한다. exec는 프로그램이 메모리에 적재되어 정상적으로 실행되는 것까지 확인한 다음 성공으로 판단한다.
ExecStart
실행할 명령어 또는 절대 경로를 포함한 스크립트
ExecReload
서비스를 reload해야 할 때 동작. 메인 프로세스에 SIGHUP 신호를 보내 설정을 다시 읽는다.
Restart
서비스 프로세스가 종료되었을 때, systemd가 이를 다시 실행할지 여부와 조건. on-failure는 서비스가 실패한 경우이다.
RestartSec
서비스가 실패한 후 다시 시작하기까지 기다릴 시간을 지정한다. 이 예제에서는 5초 후 재시작한다.
WantedBy=multi-user.target
systemctl enable을 실행할 때 서비스를 multi-user.target에 연결한다. 이를 통해 부팅 과정에서 이 서비스가 함께 시작되도록 설정한다.

결과적으로 이 서비스는 다음과 같이 동작한다.

  1. 서비스를 활성화하면 부팅 과정에서 multi-user.target에 연결되어 시작 대상에 포함된다.
  2. network-online.target의 시작 작업 이후에 서비스 시작을 시도한다.
  3. /usr/sbin/openvpn --config /etc/openvpn/client/vpn213.ovpn을 실행하고 해당 프로세스를 메인 프로세스로 추적한다.
  4. systemctl reload vpn213.service를 실행하면 메인 프로세스에 SIGHUP 신호를 보내 설정을 다시 읽도록 요청한다.
  5. 프로세스가 비정상적으로 종료되면 5초 후 자동으로 다시 시작한다.

단순히 cron.d를 사용할 때보다도 더 서비스 환경에 적합한 구성인 것을 알 수 있다.

파일을 저장한 뒤 다음 명령으로 서비스 유닛의 문법과 설정을 확인한다.

sudo systemd-analyze verify /etc/systemd/system/vpn213.service

오류가 없다면 systemd 관리자가 새로 작성한 유닛 파일을 다시 읽도록 한다.

sudo systemctl daemon-reload

마지막으로 서비스를 부팅 시 자동으로 시작하도록 활성화한다.

sudo systemctl enable --now vpn213.service

서비스의 실행 상태와 로그는 다음 명령으로 확인할 수 있다.

systemctl status vpn213.service journalctl -u vpn213.service

systemd의 단점

여러 장점만 있어 보이는 systemd도 몇 가지 단점이 존재하는데, 한마디로 사용하기가 좀 까다롭다.

  • 엄격한 문법과 경로 규칙 : cron보다 훨씬 까다롭다. 설정 파일의 오타 하나에도 서비스 로드 자체가 거부되며, 실행 파일과 설정 파일의 경로는 셸의 작업 환경에 의존하지 않도록 절대 경로로 명확하게 지정하는 것이 안전하다.
  • 무한 루프(Crash Loop) : 만약 소스에 치명적인 오류가 있는데 Restart=always를 걸어두면, 혼자 무한 루프를 돌며 재시작을 시도한다. 일정 횟수 이상 빠르게 실패하면 systemd의 시작 속도 제한에 걸려 start-limit-hit 상태로 중단될 수도 있다.
  • 환경 변수 선언 : 필요한 환경 변수는 서비스 파일의 Environment=에 지정하거나 EnvironmentFile=을 이용해 별도 파일에서 불러와야 한다.
  • 디버깅 : 에러 메시지를 확인하기 위해서는 터미널에서 바로 보는 게 아니라 journalctl 같은 별도의 명령어를 통해 로그를 확인해야 한다.

설정 항목이 많은 만큼 처음에는 복잡하게 느껴질 수 있지만, 서비스의 실행 상태와 실패 원인을 한곳에서 확인하고 재시작 정책까지 관리할 수 있다는 점은 운영 환경에서 오히려 장점이 된다.

결론과 마무리

시스템이 부팅될 때 특정 프로그램이나 스크립트를 자동으로 실행해야 하는 일은 자주 발생한다. 단순히 스크립트 하나를 실행하고 끝나는 작업이라면 cron 작업을 등록하는 방식이 더 간결한 선택이 될 수 있다.

하지만 서비스를 운영하는 환경이라면 반드시 확인해야 할 것이 있다.

제대로 실행되었는가?

네트워크가 준비된 다음 실행되어야 하는지, 먼저 시작되어야 할 다른 서비스가 있는지, 실행한 프로세스가 종료되면 다시 시작해야 하는지까지 고려하면 cron 방식은 구조적인 한계를 드러낸다. 이럴 때 systemd를 사용하면 서비스의 실행 순서와 의존 관계 및 재시작 정책을 명확하게 설정할 수 있다. 문제가 발생했을 때는 서비스 상태와 로그를 통해 실패 원인을 추적할 수도 있다.

어쨌든 중요한 것은 어느 한 가지 방법만 고집하는 것이 아니라, cronsystemd의 차이를 이해하고 적절한 방법을 선택하는 것이다. 부팅할 때 명령을 한 번 실행하는 것으로 충분하다면 cron을 사용하고, 실행 순서와 상태 감시 및 장애 복구가 필요한 프로그램이라면 systemd 서비스로 구성하면 된다.

불필요한 수동 작업과 반복적인 장애 대응을 효과적으로 줄일 수 있다면 그 자체로 운영의 부담은 크게 줄어들 것이다.

FAQ

리눅스가 부팅될 때 명령이나 스크립트를 자동으로 실행하는 방법은?
간단한 명령이나 스크립트를 부팅할 때 한 번 실행하려면 /etc/cron.d/@reboot 작업을 등록할 수 있다. 다른 서비스와의 실행 순서나 의존 관계가 중요하고 프로세스의 상태 감시와 자동 재시작이 필요하다면 systemd 서비스로 등록하는 것이 적합하다.
cron, cron.d, crontab의 차이는?
cron은 등록된 작업을 지정된 시간에 실행하는 데몬이다. crontabcron이 실행할 일정과 명령을 기록한 작업표이자 이를 편집하는 명령의 이름이다. /etc/cron.d/는 시스템 관리자가 시스템 전역의 cron 작업 파일을 저장하는 디렉토리이며, 각 작업에 명령을 실행할 사용자 이름을 지정해야 한다.
사용자가 로그인하지 않아도 사용자 crontab의 @reboot 작업이 실행되는가?
사용자가 로그인하지 않아도 실행된다. 사용자 crontab의 작업은 로그인 세션이 아니라 시스템에서 실행 중인 cron 데몬이 해당 사용자의 권한으로 실행한다. 다만 로그인 셸의 환경 변수나 그래픽 세션은 자동으로 제공되지 않는다.
@reboot 작업은 부팅이 완전히 끝난 다음 실행되는가?
아니다. @reboot 작업은 시스템의 모든 부팅 과정이 완료된 시점이 아니라 cron 데몬이 시작될 때 실행된다. 따라서 네트워크나 다른 서비스가 아직 준비되지 않은 상태에서 명령이 실행될 수 있다.
systemd란 무엇인가?
systemd는 데비안을 비롯한 여러 리눅스 배포판에서 사용하는 시스템 및 서비스 관리자다. 시스템 부팅 과정과 서비스의 실행 순서, 의존 관계, 프로세스 상태, 로그 및 재시작 정책 등을 관리한다.
systemd 서비스를 생성할 때 무엇을 정의해야 하는가?
서비스 유닛 파일에는 일반적으로 [Unit], [Service], [Install] 섹션을 작성한다. [Unit]에는 설명과 의존 관계 및 실행 순서를, [Service]에는 실행 명령과 서비스 유형 및 재시작 정책을, [Install]에는 systemctl enable을 실행할 때 연결할 대상 유닛을 정의한다.
network-online.target 이후에 실행하면 인터넷 연결이 보장되는가?
반드시 보장되는 것은 아니다. network-online.target은 사용하는 네트워크 관리자가 네트워크 구성이 준비되었다고 판단한 상태를 나타내지만, 인터넷이나 원격 서버와 실제로 통신할 수 있다는 사실까지 확인하지는 않는다. 해당 네트워크 관리자의 wait-online 서비스도 정상적으로 구성되어 있어야 한다.
서비스 파일을 작성한 뒤 daemon-reloadenable을 모두 실행해야 하는가?
새 서비스 파일을 작성하거나 기존 파일을 수정했다면 sudo systemctl daemon-reloadsystemd가 유닛 파일을 다시 읽게 해야 한다. 이어서 sudo systemctl enable --now vpn213.service를 실행하면 부팅 시 자동 시작하도록 활성화하는 동시에 현재 시스템에서도 즉시 서비스를 시작할 수 있다.
systemd 서비스의 실행 상태와 오류를 확인하는 방법은?
systemctl status vpn213.service로 현재 실행 상태와 최근 오류를 확인할 수 있다. 더 자세한 실행 기록은 journalctl -u vpn213.service로 확인한다. 서비스 파일을 적용하기 전에는 systemd-analyze verify /etc/systemd/system/vpn213.service로 문법과 설정 오류를 검사할 수 있다.
Restart=on-failureRestart=always의 차이는?
Restart=on-failure는 프로세스가 오류 코드, 신호 또는 시간 초과 등으로 비정상 종료되었을 때 서비스를 다시 시작한다. Restart=always는 정상 종료를 포함해 프로세스가 종료될 때마다 다시 시작한다. 일반적인 서버 프로그램의 장애 복구가 목적이라면 정상 종료까지 재시작하지 않는 on-failure가 더 적합할 수 있다.

갱신 내역

  • 최초 게시
  • 수정 및 주소 이전 (https://www.EQMaker.kr/)