Systemd 서비스 파일 마스터하기: 종합 가이드
Systemd는 대부분의 최신 Linux 배포판에서 서비스 및 시스템 프로세스를 관리하기 위한 사실상의 표준이 되었습니다. Systemd 서비스 단위 파일을 생성하고 관리하는 방법을 이해하는 것은 애플리케이션을 안정적으로 배포하고 유지 관리하려는 모든 시스템 관리자 또는 개발자에게 중요합니다. 이 가이드는 기본적인 구문부터 고급 구성까지 Systemd 서비스 파일의 필수 사항을 안내하여 Linux 서비스를 효과적으로 관리할 수 있도록 해줍니다.
이 글은 Systemd 서비스 단위 파일을 처음부터 생성하고 구성하는 데 중점을 둡니다. 기본적인 구문을 다루고, 일반적이고 필수적인 지시문을 탐색하며, 강력한 서비스 관리를 위한 모범 사례에 대해 논의할 것입니다. 이 가이드가 끝날 때쯤에는 자신만의 Systemd 서비스 파일을 작성하고 애플리케이션이 원활하고 안정적으로 실행되도록 할 수 있게 될 것입니다.
Systemd 단위 파일 이해
Systemd는 서비스, 소켓, 장치, 마운트 지점 등 다양한 시스템 리소스를 설명하기 위해 단위 파일(unit files)을 사용합니다. 일반적으로 .service 확장자로 끝나는 서비스 단위 파일은 Systemd가 특정 데몬 또는 애플리케이션을 관리해야 하는 방식을 정의합니다.
이러한 파일은 섹션으로 구성되며, 각 섹션은 구성 지시문을 나타내는 키-값 쌍을 포함합니다. 우리가 중점적으로 다룰 주요 섹션은 [Unit], [Service], [Install]입니다.
Systemd 서비스 파일의 구조
일반적인 Systemd 서비스 파일은 다음과 같은 구조를 가집니다:
[Unit]
Description=A brief description of the service.
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my_application --config /etc/my_app.conf
Restart=on-failure
User=myuser
Group=mygroup
[Install]
WantedBy=multi-user.target
각 섹션과 일반적인 지시문을 분석해 보겠습니다:
[Unit] 섹션
이 섹션은 단위에 대한 메타데이터를 제공하고 다른 단위와의 관계를 정의합니다. 의존성 및 순서 지정을 위해 사용됩니다.
Description=: 서비스의 사람이 읽을 수 있는 이름입니다. 이는systemctl status출력에서 보게 될 내용입니다.Documentation=: 서비스 설명서의 URL 또는 경로입니다.Requires=: 강력한 의존성을 정의합니다. 여기에 나열된 단위 중 하나라도 시작에 실패하면, 이 단위도 시작에 실패합니다.Wants=: 약한 의존성을 정의합니다. 여기에 나열된 단위 중 하나라도 시작에 실패하더라도, 이 단위는 여전히 시작을 시도합니다.Before=: 이 단위가 나열된 단위 이전에 시작되도록 보장합니다.After=: 이 단위가 나열된 단위 이후에 시작되도록 보장합니다. 이는 매우 일반적이며, 예를 들어After=network.target은 서비스가 시작되기 전에 네트워크가 활성화되도록 보장합니다.Conflicts=: 여기에 나열된 단위가 시작되면 이 단위는 중지되며, 그 반대도 마찬가지입니다.
[Service] 섹션
이 섹션은 서비스 자체의 동작을 구성합니다. 프로세스를 시작, 중지 및 관리하는 방법을 정의하는 곳입니다.
-
Type=: 프로세스 시작 유형을 지정합니다. 일반적인 값은 다음과 같습니다:simple(기본값): 주 프로세스는ExecStart=에 지정된 프로세스입니다. Systemd는ExecStart=프로세스가 포크된 직후 서비스가 시작되었다고 가정합니다.forking:ExecStart=프로세스가 자식을 포크하고 부모는 종료됩니다. Systemd는 부모가 종료될 때 서비스가 시작되었다고 간주합니다. 이 유형에서는 종종PIDFile=을 지정해야 합니다.oneshot:simple과 유사하지만, 프로세스는 작업이 완료된 후 종료될 것으로 예상됩니다. 설정 스크립트에 유용합니다.notify: 데몬이 성공적으로 시작되었을 때 Systemd에 알림 메시지를 보냅니다. 이는 이를 지원하는 최신 데몬에 선호되는 유형입니다.dbus: 서비스가 D-Bus 이름을 획득합니다.
-
ExecStart=: 서비스를 시작하기 위해 실행할 명령입니다. 이것이 가장 중요한 지시문입니다. 여러 개의ExecStart=라인을 가질 수 있으며, 이들은 순차적으로 실행됩니다. ExecStop=: 서비스를 중지하기 위해 실행할 명령입니다.ExecReload=: 다시 시작하지 않고 서비스 구성을 다시 로드하기 위해 실행할 명령입니다.-
Restart=: 서비스가 자동으로 다시 시작되어야 하는 시점을 정의합니다. 일반적인 값:no(기본값): 절대 다시 시작하지 않습니다.on-success: 서비스가 정상적으로 종료되는 경우에만 다시 시작합니다 (종료 코드 0).on-failure: 서비스가 0이 아닌 종료 코드로 종료되거나, 시그널로 종료되거나, 타임아웃되는 경우에 다시 시작합니다.on-abnormal: 시그널로 종료되거나 타임아웃되는 경우에 다시 시작합니다.on-abort: 시그널로 인해 비정상적으로 종료되는 경우에만 다시 시작합니다.always: 종료 상태와 관계없이 항상 다시 시작합니다.
-
RestartSec=: 서비스를 다시 시작하기 전에 대기할 시간입니다 (기본값은 100ms). User=: 서비스를 실행할 사용자입니다.Group=: 서비스를 실행할 그룹입니다.WorkingDirectory=: 명령을 실행하기 전에 변경할 디렉터리입니다.Environment=: 서비스에 대한 환경 변수를 설정합니다.EnvironmentFile=: 파일에서 환경 변수를 읽어옵니다.PIDFile=: PID 파일 경로입니다 (Type=forking에서 자주 사용됨).StandardOutput=/StandardError=: stdout/stderr가 어디로 가야 하는지를 제어합니다 (예:journal,syslog,null,inherit).journal은 기본값이며 로깅에 강력하게 권장됩니다.
[Install] 섹션
이 섹션은 단위가 활성화되거나 비활성화되는 방법을 정의하며, 일반적으로 심볼릭 링크를 생성하여 이루어집니다.
WantedBy=: 이 서비스가 활성화될 때 이 서비스를 '원해야' 하는 대상을 지정합니다. 일반적인 값:multi-user.target: 시스템이 다중 사용자 명령줄 상태에 도달했을 때 시작해야 하는 서비스용입니다.graphical.target: 시스템이 그래픽 로그인 상태에 도달했을 때 시작해야 하는 서비스용입니다.
첫 Systemd 서비스 파일 생성하기
/opt/my_app/my_app.py에 있는 가상의 Python 스크립트 my_app.py를 위한 간단한 서비스 파일을 만들어 보겠습니다.
1. 서비스 파일 생성:
사용자 정의 애플리케이션을 위한 서비스 파일은 일반적으로 /etc/systemd/system/에 배치됩니다. 파일 이름을 my_app.service로 지정하겠습니다.
# 디렉터리가 없으면 생성
sudo mkdir -p /etc/systemd/system/
# 텍스트 편집기를 사용하여 서비스 파일 생성
sudo nano /etc/systemd/system/my_app.service
2. my_app.service에 다음 내용 추가:
[Unit]
Description=My Custom Python Application
After=network.target
[Service]
Type=simple
User=appuser
Group=appgroup
WorkingDirectory=/opt/my_app/
ExecStart=/usr/bin/python3 /opt/my_app/my_app.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
예시 설명:
Description: 애플리케이션을 명확하게 식별합니다.After=network.target: 시작하기 전에 네트워크가 사용 가능한지 확인합니다.Type=simple:my_app.py가 주 프로세스이며 포크되지 않는다고 가정합니다.User=appuser,Group=appgroup: 애플리케이션이 실행되어야 하는 사용자 및 그룹을 지정합니다. 이러한 사용자 및 그룹이 시스템에 존재하며 적절한 권한을 가지고 있는지 확인하십시오. 생성해야 할 수도 있습니다:
bash sudo groupadd appgroup sudo useradd -r -g appgroup appuser sudo chown -R appuser:appgroup /opt/my_app/WorkingDirectory: 스크립트의 컨텍스트를 설정합니다.ExecStart: Python 스크립트를 실행하는 명령입니다./usr/bin/python3가 Python 인터프리터에 대한 올바른 경로이며 스크립트가 실행 가능한지 확인하십시오.Restart=on-failure: 스크립트가 충돌하면 Systemd가 다시 시작을 시도합니다.WantedBy=multi-user.target: 시스템이 다중 사용자 환경으로 부팅될 때 이 서비스가 자동으로 시작됩니다.
3. Systemd 관리자 구성 다시 로드:
서비스 파일을 생성하거나 수정한 후에는 Systemd에 구성을 다시 로드하도록 알려야 합니다.
sudo systemctl daemon-reload
4. 서비스 활성화 및 시작:
- 활성화(Enable): 부팅 시 서비스가 자동으로 시작되도록 합니다.
bash sudo systemctl enable my_app.service - 시작(Start): 서비스를 즉시 시작합니다.
bash sudo systemctl start my_app.service
5. 서비스 상태 확인:
서비스가 실행 중인지 확인하고 잠재적인 오류를 확인하려면:
sudo systemctl status my_app.service
문제가 있는 경우, status 명령은 종종 journald의 오류 메시지나 로그를 표시합니다.
6. 로그 보기:
Systemd는 로깅을 위해 journald와 통합됩니다. 다음을 사용하여 서비스의 로그를 볼 수 있습니다:
sudo journalctl -u my_app.service
또한 실시간으로 로그를 추적할 수도 있습니다:
sudo journalctl -f -u my_app.service
기타 유용한 명령:
- 서비스 중지:
sudo systemctl stop my_app.service - 서비스 다시 시작:
sudo systemctl restart my_app.service - 구성 다시 로드 (앱이 지원하는 경우):
sudo systemctl reload my_app.service - 부팅 시 자동 시작 비활성화:
sudo systemctl disable my_app.service
고급 구성 및 모범 사례
보안 고려 사항:
- 비(非) 루트 사용자로 서비스 실행: 절대적으로 필요한 경우가 아니면 항상
User=와Group=을 지정하십시오. 이는 최소 권한의 원칙을 따릅니다. - 서비스 격리: 향상된 보안을 위해
PrivateTmp=true,ProtectSystem=true,NoNewPrivileges=true와 같은 샌드박싱 기능 사용을 고려하십시오.PrivateTmp=true: 서비스에 고유한 개인/tmp및/var/tmp디렉터리를 제공합니다.ProtectSystem=true:/usr,/boot,/etc를 읽기 전용으로 만듭니다.NoNewPrivileges=true: 서비스가 새로운 권한을 획득하는 것을 방지합니다.
복잡한 시작 처리:
PIDFile=와 함께Type=forking사용: 포크하는(fork) 레거시 애플리케이션의 경우,PIDFile=이 올바른 파일을 가리키는지 확인하십시오.Type=notify: 애플리케이션이 이를 지원하는 경우, Systemd가 서비스가 실제로 준비되었음을 알 수 있는 가장 강력한 방법입니다.ExecStartPre=및ExecStartPost=:ExecStart=전후에 실행할 명령입니다. 설정 또는 정리 작업에 유용합니다.
리소스 제어:
Systemd는 리소스 사용량을 제한할 수 있도록 허용합니다:
CPUShares=: 상대적인 CPU 시간 할당입니다.MemoryLimit=: 서비스가 사용할 수 있는 최대 메모리입니다.IOWeight=: 상대적인 I/O 대역폭입니다.
예시:
[Service]
# ... other directives ...
MemoryLimit=512M
CPUShares=512 # 기본값 1024 대비 약 50%의 CPU 시간
타이머 vs. Cron
Systemd 타이머는 기존의 cron 작업에 대한 현대적인 대안을 제공합니다. 이는 Systemd의 로깅 및 의존성 관리와 더 유연하고 더 잘 통합됩니다.
- Cron:
crontab파일에 정의된 예약 작업. - Systemd 타이머 (
.timer단위): 이 단위는.service단위를 예약합니다. 해당.service파일이 실행되어야 하는 시점을 지정하는.timer파일을 정의합니다.
예시:
매일 오전 3시에 스크립트를 실행하려면:
-
my_script.service: 실행할 서비스입니다.
```ini
[Unit]
Description=My daily script[Service]
Type=oneshot
ExecStart=/opt/my_scripts/run_daily.sh
User=scriptuser
``` -
my_script.timer: 서비스를 예약하는 타이머입니다.
```ini
[Unit]
Description=Run my daily script once a day[Timer]
매일 03:00에 실행
OnCalendar=--* 03:00:00
Persistent=true # 가동 중지 시간으로 인해 놓친 경우 즉시 실행[Install]
WantedBy=timers.target
```
사용 방법:
- 두 파일을 모두
/etc/systemd/system/에 배치합니다. sudo systemctl daemon-reload를 실행합니다.- 타이머를 활성화하고 시작합니다:
sudo systemctl enable my_script.timer및sudo systemctl start my_script.timer.
타이머는 Persistent=true (부팅 시 놓친 작업 실행), 캘린더 이벤트 (예: hourly, daily, weekly), 그리고 journalctl과의 더 나은 통합과 같은 이점을 제공합니다.
일반적인 문제 해결
- 서비스가 시작되지 않음:
systemctl status <service_name>및journalctl -u <service_name>을 확인하십시오. 오타, 잘못된 경로, 누락된 의존성 또는 권한 오류를 찾으십시오. - 잘못된
Type=: 서비스가 즉시 실패하거나 멈추는 경우,Type=이 잘못되었을 수 있습니다.simple또는forking을 시도하고,forking을 사용하는 경우PIDFile이 올바른지 확인하십시오. - 권한 거부: 지정된
User=및Group=이 필요한 파일 및 디렉터리에 대한 읽기/쓰기 액세스 권한이 있는지 확인하십시오. - 환경 변수: 애플리케이션이 특정 환경 변수에 의존하는 경우,
Environment=또는EnvironmentFile=을 사용하여 올바르게 설정되었는지 확인하십시오. - 의존성: 서비스가 시작되기 전에 전제 조건이 충족되도록
After=및Requires=지시문이 올바르게 설정되었는지 확인하십시오.
결론
Systemd 서비스 파일은 Linux에서 애플리케이션을 관리하는 강력한 도구입니다. 단위 파일의 구조, 주요 지시문의 목적, 구성 모범 사례를 이해함으로써 서비스의 안정성, 보안 및 관리 용이성을 크게 향상시킬 수 있습니다. 간단한 스크립트를 배포하든 복잡한 애플리케이션을 배포하든, Systemd 서비스 파일을 마스터하는 것은 최신 Linux 시스템 관리에서 필수적인 기술입니다.
서비스 파일을 항상 철저히 테스트하고, 디버깅을 위해 systemctl status와 journalctl을 사용하며, Systemd가 제공하는 보안 기능을 활용하는 것을 잊지 마십시오.