리눅스 데몬 만들기(python) #1

Kim Seung Su
7 min readApr 28, 2018

--

최근에 데몬을 하나 만들일이 생겨서 기억을 더듬어 가면서 코딩을 했다. 너무 오래전에 해본거라 기억이 가물가물해서 시간을 조금 잡아 먹었다. 잊어먹지 않도록 내용을 정리해봤다.

샘플 코드는 전부 파이썬으로 되어 있는지만 다른 언어로 구현하는것도 어렵지 않을 꺼다. 샘플 코드는 파이썬 2, 3에서 모두 동작한다.

전체 소스코드는 https://github.com/HatsuneMiku3939/python3-daemon 여기서 볼 수 있다.

먼저 데몬이 아닌 일반 프로세스로 동작하는 프로그램을 만드는걸로 시작하자. 1초에 한번씩 화면에 로그를 찍어주는 매우 훌륭한 프로그램이다.

이걸 조금씩 수정해 가면서 최종적으로 복수의 워커 프로세스로 동작하는 데몬을 만들어보자. 하는김에 로그도 수집하고 최근 트랜드에 걸맞게 컨테이너라이즈도 해보자.

위 코드는 종로하려면 Ctrl-C 를 눌려서 강제 종료하는것 말고는 방법이 없다. 그리고 지저분하게 종료된다.

INFO:SingingMiku:Miku Miku Ni Shite Ageru
INFO:SingingMiku:World is Mine
INFO:SingingMiku:39
^CTraceback (most recent call last):
File "main.py", line 19, in <module>
singing_miku.main()
File "main.py", line 16, in main
time.sleep(1)
KeyboardInterrupt

첫번째 수정으로 시그널 핸들러를 등록해서 깔금하게 종료될 수 있게 하자. 그리고 로그를 파일로 출력하는 옵션도 추가한다.

로그에 pid도 출력하도록 했다. 로그 레벨이 전부 INFO이고 그냥 메시지만 출력하고 있는데 나중에 구조화된 형태로 로그 포멧을 바꾸고 별도의 시스템을 이용해서 로그를 수집할 꺼니까 이걸로 충분하다. 이제 Ctrl-C 를 누르 깔끔하게 종료된다.

Start Singing, PID 2973
Miku Miku Ni Shite Ageru
World is Mine
39
^CReceive Signal 2
Stop Singing

해당 pid로 시그널을 날려서 종료할 수도 있다.

$ kill 4046 Start Singing, PID 4046
Miku Miku Ni Shite Ageru
World is Mine
39
Miku Miku Ni Shite Ageru
World is Mine
Receive Signal 15
Stop Singing

옵션을 지정하면 로그파일을 지정할 수 있다.

$ python3 main.py --log /tmp/miku.log

이제 이걸 데몬으로 만들어 본다. 이 단게의 소스코드는 깃헙 레포지토리의 3-basic-daemon 디렉토리에 있다.

코드도 길어지고 SingingMiku 클래스는 바뀐게 없어서 별도의 파일로 분리했다. double fork를 하는 이유는 포크된 데몬 프로세스를 부모 프로세스로부터 완전히 분리하고 세션에도 떨어지도록 하기 위해서다. 자세한 이유는 Advanced Programming in the UNIX® Environment 를 읽어보자.

옵션으로 반드시 pid 파일의 위치를 지정하도록 했다. 이제 실행을 시켜보면 화면에 아무것도 안나온다. 표준 입출력을 전부 /dev/null 로 돌려놨기 때문이다.

$ python main.py --pid /tmp/miku.pid --log /tmp/miku.log

하지만 ps 커맨드로 프로세스가 실행 중인걸 확인 할 수 있다.

$ ps axuf | grep python
..... \_ python main.py --pid /tmp/miku.pid --log /tmp/miku.log

시그널을 날려서 종료하고 로그도 확인해보자.

$ kill `cat /tmp/miku.pid`; cat /tmp/miku.log
Start Singing, PID 8766
Miku Miku Ni Shite Ageru
World is Mine
39
Receive Signal 15
Stop Singing

엄청 잘되는것 같다. 이제 시스템에 설치하고 서비스로 돌려보자! miku.service 파일은 github의 레포지토리의 service 디렉토리에 올려저 있다.

$ sudo mkdir -p /opt/miku /var/log/miku
$ sudo cp main.py /opt/miku/
$ sudo cp miku.py /opt/miku/
$ sudo cp miku.service /etc/systemd/system/
$ sudo systemctl daemon-reload
$ sudo systemctl enable miku.service
Created symlink /etc/systemd/system/multi-user.target.wants/miku.service → /etc/systemd/system/miku.service.

/opt/miku 로 복사할 py 파일들은 3-basic-daemon 디렉토리의 파일을 사용하자. 이제 실행을 시키고 스테이터스도 확인해 보자.

$ sudo systemctl start miku.service
$ sudo systemctl status miku.service
● miku.service - Singing Miku service
Loaded: loaded (/etc/systemd/system/miku.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2018-04-28 15:34:47 JST; 1s ago
Process: 14753 ExecStart=/usr/bin/python /opt/miku/main.py --pid /var/run/miku.pid --log /var/log/miku/miku.log (code=exited, status=0/SUCCESS)
Main PID: 14766 (python)
Tasks: 1 (limit: 4915)
Memory: 4.5M
CPU: 48ms
CGroup: /system.slice/miku.service
└─14766 /usr/bin/python /opt/miku/main.py --pid /var/run/miku.pid --log /var/log/miku/miku.log

매우 잘된다! 종료하고 로그도 확인해보자.

$ sudo systemctl stop miku.service
$ cat /var/log/miku/miku.log
Start Singing, PID 14766
Miku Miku Ni Shite Ageru
World is Mine
39
Miku Miku Ni Shite Ageru
World is Mine
39
Miku Miku Ni Shite Ageru
World is Mine
Receive Signal 15
Stop Singing

일단 다시 켠다음 logrorate를 등록하고 시험삼아서 한번 로테이션 시켜보자. logrorate 설정파일도 github의 레포지토리의 service 디렉토리에서 확인할 수 있다.

$ sudo systemctl start miku.service
$ sudo cp miku /etc/logrotate.d/miku
$ sudo logrorate -f /etc/logrotate.d/miku
$ ls /var/log/miku/
miku.log miku.log.1

로테이션도 잘 된다. 훌륭하다! 다음 명령어들을 입력해서 설치된 미쿠 데몬을 깔끔하게 삭제하자.

$ sudo rm /etc/logrotate.d/miku
$ sudo systemctl stop miku.service
$ sudo systemctl disable miku.service
Removed /etc/systemd/system/multi-user.target.wants/miku.service.
$ sudo rm /etc/systemd/system/miku.service
$ sudo rm -rf /opt/miku /var/log/miku

다음편에서 여러 워커를 기동해서 성능을 높여보고 로그 수집도 해보고 컨테이너라이즈도 해보자.

리눅스 데몬 만들기(python) #2

--

--