TT Lab
시작하기
배우기 러닝패스 코스

RHEL 계열 관리

systemd 유닛 파일 작성

TT Lab 에서 이어서 보기

목표

systemd 유닛 파일을 처음부터 작성하고, 타이머 쌍을 만들고, 유닛 검증 스크립트를 직접 만듭니다.

왜 중요한가

유닛 파일에서 가장 중요한 한 줄은 Type= 입니다. Type=simple 인데 프로세스가 데몬화하면 systemd 는 부모가 죽는 것을 서비스 종료로 보고 실패 처리합니다. 반대로 Type=forking 인데 포그라운드로 돌면 systemd 가 영원히 기다리다 타임아웃으로 죽입니다 — "시작이 30초 멈추다 실패한다" 의 전형적인 원인입니다.

그리고 [Install] 섹션이 없으면 systemctl enable 이 아무것도 하지 않아 부팅 시 자동 시작이 안 됩니다. 이건 재부팅 전까지 아무도 모릅니다.

이 실습은 AlmaLinux 9 가상머신에서 돕니다. systemd 가 PID 1 로 실제로 돌기 때문에, 유닛 파일을 쓴 다음 systemctl enable --now 로 곧바로 띄워 볼 수 있습니다. 파일 한 줄이 어떤 동작으로 이어지는지를 눈으로 확인하면서 배웁니다.\n\n예전에는 파드에서 돌아 systemctl 자체가 없었고, 작성과 검증까지가 전부였습니다. 처음 뜨는 데 30초쯤 걸립니다.

단계

  1. /etc/systemd/system/labhub-api.service 를 만들고 [Unit], [Service], [Install] 세 섹션을 넣으세요. /root/unit 작업 디렉터리도 만드세요.
  2. [Unit] 에 Description, Documentation, After=network-online.target, Wants=network-online.target 네 줄을 넣으세요.
  3. [Service] 에 Type=notify, ExecStart=/usr/local/bin/labhub-api --config /etc/labhub/api.yaml, User=labhub, Group=labhub, WorkingDirectory=/opt/labhub 를 넣으세요. ExecStart 는 절대 경로여야 합니다.\n\n 실습 프로그램이 셸 스크립트이므로 NotifyAccess=all 도 함께 넣습니다. 기본값 main 은 MainPID 가 보낸 알림만 받는데, systemd-notify 는 스크립트의 자식이라 PID 가 다릅니다. 그러면 systemd 가 준비 신호를 못 받고 기다리다 Job for labhub-api.service failed because a timeout was exceeded 로 끝납니다 — 유닛 파일에는 아무 문제가 없어 보입니다.
  4. 재시작 정책을 추가하세요. [Service] 에 Restart=on-failure 와 RestartSec=5 를, [Unit] 에 StartLimitIntervalSec=300 과 StartLimitBurst=5 를 넣습니다. 이 둘은 systemd 230 에서 [Service] 에서 [Unit] 로 옮겨 갔습니다 — [Service] 에 적으면 Unknown key name 만 남기고 조용히 무시되어 속도 제한이 걸리지 않습니다.
  5. 보안 지시자를 추가하세요. NoNewPrivileges=true, ProtectSystem=strict, ProtectHome=true, PrivateTmp=true, ReadWritePaths=/var/lib/labhub /var/log/labhub
  6. [Install] 에 WantedBy=multi-user.target 을 넣고, systemctl enable 이 만들 심링크를 직접 만드세요. 경로는 /etc/systemd/system/multi-user.target.wants/labhub-api.service 이고 원본 유닛을 가리켜야 합니다.
  7. 타이머 쌍을 만드세요. /etc/systemd/system/labhub-backup.service(Type=oneshot, ExecStart 절대 경로)와 /etc/systemd/system/labhub-backup.timer([Timer] 에 OnCalendar=*-*-* 02:30:00, Persistent=true, Unit=labhub-backup.service, [Install] 에 WantedBy=timers.target).
  8. /root/unit/lint.sh 를 작성하세요. 첫 번째 인자로 유닛 파일 경로를 받아 다음 세 규칙을 검사하고, 위반이 없으면 종료 코드 0, 있으면 0 이 아닌 값으로 끝나야 합니다.
    • 규칙 1: [Unit], [Service], [Install] 세 섹션이 모두 있어야 한다
    • 규칙 2: ExecStart 값이 절대 경로(/ 로 시작)여야 한다
    • 규칙 3: Type=forking 이면 PIDFile= 이 있어야 한다 채점기가 여러분의 labhub-api.service(통과해야 함)와 /opt/fixtures/unit/bad-sample.service(실패해야 함) 양쪽으로 실행합니다.

참고

세 섹션 골격

/etc/systemd/system/labhub-api.service 를 만들고 [Unit], [Service], [Install] 세 섹션을 넣으세요. /root/unit 작업 디렉터리도 만드세요.

대괄호로 감싼 섹션 이름 세 개가 필요합니다. 순서도 관례가 있습니다.

[Unit] 섹션 채우기

[Unit] 에 Description, Documentation, After=network-online.target, Wants=network-online.target 네 줄을 넣으세요.

설명, 문서, 순서, 의존 네 가지를 씁니다. 순서와 의존은 다른 지시자입니다.

[Service] 실행 설정

[Service] 에 Type=notify, ExecStart=/usr/local/bin/labhub-api --config /etc/labhub/api.yaml, User=labhub, Group=labhub, WorkingDirectory=/opt/labhub 를 넣으세요. ExecStart 는 절대 경로여야 합니다.\n\n 실습 프로그램이 셸 스크립트이므로 NotifyAccess=all 도 함께 넣습니다. 기본값 main 은 MainPID 가 보낸 알림만 받는데, systemd-notify 는 스크립트의 자식이라 PID 가 다릅니다. 그러면 systemd 가 준비 신호를 못 받고 기다리다 Job for labhub-api.service failed because a timeout was exceeded 로 끝납니다 — 유닛 파일에는 아무 문제가 없어 보입니다.

타입과 실행 명령이 핵심입니다. 실행 경로는 반드시 절대 경로여야 합니다.

재시작 정책

재시작 정책을 추가하세요. [Service] 에 Restart=on-failure 와 RestartSec=5 를, [Unit] 에 StartLimitIntervalSec=300 과 StartLimitBurst=5 를 넣습니다. 이 둘은 systemd 230 에서 [Service] 에서 [Unit] 로 옮겨 갔습니다 — [Service] 에 적으면 Unknown key name 만 남기고 조용히 무시되어 속도 제한이 걸리지 않습니다.

무한 루프를 막는 상한 지시자가 두 개 있습니다. 간격도 0이 아니어야 합니다.

보안 강화

보안 지시자를 추가하세요. NoNewPrivileges=true, ProtectSystem=strict, ProtectHome=true, PrivateTmp=true, ReadWritePaths=/var/lib/labhub /var/log/labhub

읽기 전용으로 만드는 지시자와 예외 경로를 지정하는 지시자를 함께 씁니다.

[Install] 과 enable 결과

[Install] 에 WantedBy=multi-user.target 을 넣고, systemctl enable 이 만들 심링크를 직접 만드세요. 경로는 /etc/systemd/system/multi-user.target.wants/labhub-api.service 이고 원본 유닛을 가리켜야 합니다.

enable 이 만드는 심링크 경로를 직접 만들어 보면 구조가 이해됩니다.

타이머 쌍 작성

타이머 쌍을 만드세요. /etc/systemd/system/labhub-backup.service(Type=oneshot, ExecStart 절대 경로)와 /etc/systemd/system/labhub-backup.timer([Timer] 에 OnCalendar=*-*-* 02:30:00, Persistent=true, Unit=labhub-backup.service, [Install] 에 WantedBy=timers.target).

.timer 와 .service 가 짝입니다. 놓친 실행을 따라잡는 지시자도 넣으세요.

유닛 검증 스크립트

/root/unit/lint.sh 를 작성하세요. 첫 번째 인자로 유닛 파일 경로를 받아 다음 세 규칙을 검사하고, 위반이 없으면 종료 코드 0, 있으면 0 이 아닌 값으로 끝나야 합니다.

채점기가 정상 유닛과 픽스처의 불량 유닛 양쪽으로 실행합니다. 세 가지 규칙을 검사하세요.