systemd 서비스 파일 마스터하기: 종합 가이드
올바른 유닛 섹션, 재시작 동작, 로그, 보안 및 타이머를 사용하여 신뢰할 수 있는 systemd 서비스 파일을 생성합니다.
Systemd 서비스 파일 마스터하기: 종합 가이드
Systemd 서비스 파일은 Linux가 애플리케이션을 시작, 중지, 재시작 및 감독하는 방법을 지시합니다. 서비스가 수동으로는 시작되지만 부팅 시 실패하거나, 너무 공격적으로 재시작되거나, 로그를 잘못된 위치에 기록하는 경우 일반적으로 유닛 파일을 확인해야 합니다.
이 가이드는 처음부터 systemd 서비스 유닛 파일을 생성하고 구성하는 데 중점을 둡니다. 핵심 섹션, 작동하는 Python 서비스 예제, 일반적인 문제 해결 명령어, 그리고 프로덕션에서 사용할 가치가 있는 몇 가지 보안 및 리소스 제어 기능을 살펴볼 것입니다.
Systemd 유닛 파일 이해하기
Systemd는 유닛 파일을 사용하여 서비스, 소켓, 장치, 마운트 지점 등과 같은 다양한 시스템 리소스를 설명합니다. 일반적으로 .service 확장자로 끝나는 서비스 유닛 파일은 systemd가 특정 데몬 또는 애플리케이션을 관리하는 방법을 정의합니다.
이러한 파일은 섹션으로 구성되며, 각 섹션에는 구성 지시문을 나타내는 키-값 쌍이 포함됩니다. 우리가 중점적으로 살펴볼 주요 섹션은 [Unit], [Service] 및 [Install]입니다.
Systemd 서비스 파일의 구조
일반적인 systemd 서비스 파일은 다음과 같은 구조를 가집니다:
[Unit]
Description=서비스에 대한 간단한 설명.
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은 기본 네트워크 대상 이후에 이 유닛을 시작하지만 외부 연결을 보장하지는 않습니다. 네트워크에 종속된 서비스는After=network-online.target과 배포판의 wait-online 서비스가 필요할 수 있습니다.Conflicts=: 여기에 나열된 유닛이 시작되면 이 유닛이 중지되고 그 반대의 경우도 마찬가지입니다.
[Service] 섹션
이 섹션은 서비스 자체의 동작을 구성합니다. 프로세스를 시작, 중지 및 관리하는 방법을 정의하는 곳입니다.
Type=: 프로세스 시작 유형을 지정합니다. 일반적인 값은 다음과 같습니다:simple(기본값): 메인 프로세스는ExecStart=에 지정된 프로세스입니다. Systemd는ExecStart=프로세스가 포크된 직후 서비스가 시작된 것으로 간주합니다.forking:ExecStart=프로세스가 자식을 포크하고 부모는 종료됩니다. Systemd는 부모가 종료될 때 서비스가 시작된 것으로 간주합니다. 이 유형에서는 종종PIDFile=을 지정해야 합니다.oneshot:simple과 유사하지만 프로세스가 작업을 완료한 후 종료될 것으로 예상됩니다. 설정 스크립트에 유용합니다.notify: 데몬이 성공적으로 시작되었을 때 systemd에 알림 메시지를 보냅니다. 이를 지원하는 최신 데몬에게 선호되는 유형입니다.dbus: 서비스가 D-Bus 이름을 획득합니다.
ExecStart=: 서비스를 시작하기 위해 실행할 명령어입니다. 대부분의 서비스 유형에 대해 하나의ExecStart=명령어를 사용합니다.Type=oneshot의 경우 여러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,null또는inherit). 일반적인 systemd 기반 배포판에서는 관리자 기본값이 변경되지 않는 한 서비스 출력은 일반적으로 저널에 기록됩니다.
[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=내 사용자 정의 Python 애플리케이션
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: 애플리케이션이 실행될 사용자와 그룹을 지정합니다. 이러한 사용자와 그룹이 시스템에 존재하고 적절한 권한이 있는지 확인하십시오. 필요한 경우 생성해야 할 수 있습니다: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. 서비스 활성화 및 시작:
- 활성화: 부팅 시 서비스가 자동으로 시작되도록 합니다.
sudo systemctl enable my_app.service - 시작: 서비스를 즉시 시작합니다.
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=strict,ProtectHome=true및NoNewPrivileges=true와 같은 샌드박싱 기능을 고려하십시오. 합법적인 파일 쓰기를 차단할 수 있으므로 애플리케이션으로 테스트하십시오.PrivateTmp=true: 서비스에 자체 개인/tmp및/var/tmp디렉토리를 제공합니다.ProtectSystem=strict: 서비스에 대해 대부분의 파일 시스템을 읽기 전용으로 만듭니다. 서비스가 써야 하는 디렉토리에는ReadWritePaths=를 사용하십시오.NoNewPrivileges=true: 서비스가 새로운 권한을 획득하는 것을 방지합니다.
복잡한 시작 처리
Type=forking및PIDFile=: 포크하는 이전 애플리케이션의 경우PIDFile=이 올바른 파일을 가리키는지 확인하십시오.Type=notify: 애플리케이션이 지원하는 경우 systemd가 실제로 준비되었는지 확인하는 가장 강력한 방법입니다.ExecStartPre=및ExecStartPost=:ExecStart=전후에 실행할 명령어입니다. 설정 또는 정리 작업에 유용합니다.
리소스 제어
Systemd를 사용하면 리소스 사용을 제한할 수 있습니다:
CPUWeight=: 서비스의 상대적 CPU 가중치입니다.MemoryMax=: 서비스가 사용할 수 있는 최대 메모리입니다.IOWeight=: 커널 및 cgroup 설정에서 지원하는 경우 상대적 I/O 가중치입니다.
예시:
[Service]
# ... 기타 지시문 ...
MemoryMax=512M
CPUWeight=50
타이머 vs. Cron
Systemd 타이머는 기존 cron 작업에 대한 현대적인 대안을 제공합니다. 더 유연하고 systemd의 로깅 및 종속성 관리와 더 잘 통합됩니다.
- Cron:
crontab파일에 정의된 예약된 작업입니다. - Systemd 타이머(
.timer유닛): 이러한 유닛은.service유닛을 예약합니다. 해당.service파일이 실행되어야 하는 시기를 지정하는.timer파일을 정의합니다.
예시:
매일 오전 3시에 스크립트를 실행하려면:
my_script.service: 실행할 서비스입니다.[Unit] Description=내 일일 스크립트 [Service] Type=oneshot ExecStart=/opt/my_scripts/run_daily.sh User=scriptusermy_script.timer: 서비스를 예약하는 타이머입니다.[Unit] Description=하루에 한 번 내 일일 스크립트 실행 [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 <서비스_이름>및journalctl -u <서비스_이름>을 확인하십시오. 오타, 잘못된 경로, 누락된 종속성 또는 권한 오류를 찾으십시오. - 잘못된
Type=: 서비스가 즉시 실패하거나 중단되면Type=이 잘못되었을 수 있습니다.simple또는forking을 시도하고forking을 사용하는 경우PIDFile이 올바른지 확인하십시오. - 권한 거부됨: 지정된
User=및Group=이 필요한 파일 및 디렉토리에 대한 읽기/쓰기 권한이 있는지 확인하십시오. - 환경 변수: 애플리케이션이 특정 환경 변수에 의존하는 경우
Environment=또는EnvironmentFile=을 사용하여 올바르게 설정되었는지 확인하십시오. - 종속성:
After=,Wants=및Requires=가 의도한 것과 일치하는지 확인하십시오.After=는 시작 순서를 지정합니다. 자체적으로 다른 유닛을 가져오지 않습니다.
프로덕션 호스트에서 새 유닛을 활성화하기 전에 다음을 실행하십시오:
sudo systemd-analyze verify /etc/systemd/system/my_app.service
이렇게 하면 부팅 시 서비스에 의존하기 전에 많은 구문 및 지시문 오류를 잡을 수 있습니다.
핵심 요점
앱을 정확하게 설명하는 가장 작은 서비스 파일을 작성한 다음 의도적으로 재시작 정책, 로깅, 보안 제한 및 리소스 제한을 추가하십시오. 모든 변경 후에는 systemctl daemon-reload를 실행하고, 유닛을 확인한 다음, 프로덕션에서 신뢰하기 전에 systemctl status 및 journalctl -u를 확인하십시오.