systemd 는 리눅스 운영체제의 시스템 및 서비스 관리자입니다. 시스템 부팅시에 첫번째 프로세스 (PID 1) 로 실행하며, 고전적으로 사용하는 init 를 대체합니다. RHEL, CentOS, Ubuntu 등 다양한 Linux 배포판에서 채용하고 있습니다.
SSH 서버, 웹 서버 등과 같이 클라이언트와 직접 통신하기 위해 소켓을 사용하는 데몬을 작성하거나 백그라운 작업을 실행해야 하는 경우가 종종 발생합니다. 두번의 fork를 사용하여 데몬을 직접 만들수도 있지만 systemd 를 사용하면 매우 쉽게 서비스를 만들 수 있습니다.
Python 으로 간단한 소켓 프로그램을 만들어 보겠습니다. echo-server.py 는 TCP 10000 번 포트로 메시지를 수신하고, 수신한 메시지를 그대로 반환합니다.
#!/usr/bin/env python3 import socket HOST = "0.0.0.0" PORT = 10000 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((HOST, PORT)) s.listen() while True: conn, addr = s.accept() with conn: print("connected: %s:%s" % (HOST, PORT)) while True: data = conn.recv(1024) if not data: break print("received: %s" % data) conn.sendall(data)
스크립트를 바로 실행할 수 있도록 mode 를 변경하고, 서버를 시작하겠습니다.
$ chmod +x echo-server.py
$ ./echo-server.py
그리고 다른 터미널에서 telnet 을 사용하여 서버에 접속하여 잘 동작하는지 테스트 해 보겠습니다.
$ telnet localhost 10000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. HELLO WORLD HELLO WORLD ^] telnet> quit Connection closed.
이제 이 서버가 항상 실행되기를 바랍니다. 오류가 발생(예치기 않은 종료)해도 다시 실행하고, 서버가 재시작하여도 실행하기를 바랍니다. 이것이 systemd 가 작동하는 방식입니다.
systemd 는 작업의 단위는 Unit 으로 관리합니다. 다음과 같이 서비스 Unit 파일을 만들어 보겠습니다. 실행 파일의 경로(/path/to)는 적절하게 변경합니다.
[Unit] Description=Echo Service After=network.target [Service] Type=simple ExecStart=/path/to/echo-server.py [Install] WantedBy=multi-user.target
Unit 파일은 ini 형식을 사용하며, Unit, Service, Install 의 세가지 섹션을 가집니다.
서비스의 관리는 systemd 의 관리 명령인 systemctl 을 사용합니다.
start 명령을 사용하여 서비스를 시작합니다.
$ sudo systemctl start echo-server
status 명령을 사용하여 서비스의 상태를 확인합니다. 서버의 상태, PID, 메모리 사용량, CPU 사용량, 로그 등을 확인할 수 있습니다.
$ sudo systemctl status echo-server ? echo-server.service - Echo Service Loaded: loaded (/etc/systemd/system/echo-server.service; disabled; vendor preset: enabled) Active: active (running) since Fri 2024-04-12 02:40:09 KST; 1min 24s ago Main PID: 3297307 (python3) Tasks: 1 (limit: 38017) Memory: 3.6M CPU: 17ms CGroup: /system.slice/echo-server.service ??3297307 python3 /path/to/echo-server/echo-server.py Apr 12 02:40:09 10b-server systemd[1]: Started Echo Service.
다시 서비스에 접속하여, 잘 동작하는지 테스트 하겠습니다.
$ telnet localhost 10000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Hello World!!! Hello World!!! ^] telnet> quit Connection closed.
stop 명령을 사용하여 서비스를 종료합니다. 그리고 status 명령으로 상태를 확인합니다.
$ sudo systemctl stop echo-server $ sudo systemctl status echo-server ? echo-server.service - Echo Service Loaded: loaded (/etc/systemd/system/echo-server.service; disabled; vendor preset: enabled) Active: inactive (dead) Apr 12 02:40:09 10b-server systemd[1]: Started Echo Service. Apr 12 02:49:43 10b-server systemd[1]: Stopping Echo Service... Apr 12 02:49:43 10b-server systemd[1]: echo-server.service: Deactivated successfully. Apr 12 02:49:43 10b-server systemd[1]: Stopped Echo Service.
이제 부팅시 자동으로 시작되도록 하겠습니다. enable 명령을 사용하며, 자동시작이 되지 않게 하려면 disable 명령을 사용합니다.
$ sudo systemctl enable echo-server
이제 서비스가 좀 더 세밀하게 동작하도록 몇 가지 구성 옵션에 대해 살펴보도록 하겠습니다.
사용자와 그룹을 지정하지 않는다면, root 권한으로 서비스를 시작합니다. User, Group 지시어를 사용하여 실행 권한을 조정할 수 있습니다.
User=apache Group=apache
만약 mysqld.service 를 사용하는 서비스가 있다면, 이후에 실행해야 정상적으로 동작할 수 있습니다. 이런 경우 After 지시어를 사용하여 실행하는 순서를 지정합니다.
After=mysqld.service
systemd 는 어떤 이유로든 프로그램이 종료되면, 서비스를 다시 시작하지 않습니다. Restart 지시어를 사용하면, 항상 시작하도록 할 수 있습니다.
Restart=always
systemd 는 기본적으로 100ms 이후 다시 시작을 시도합니다. RestartSec 지시어를 사용하여 대기 시간을 조정할 수 있습니다.
RestratSec=1
systemd 는 10초 내에 5회 이상 서비스 시작에 실패하면, 재시작을 포기합니다. StartLimitBurst, StartLimitIntervalSec 지시어를 변경하여 조정할 수 있습니다.
StartLimitBurst=5 StartLimitIntervalSec=10