Spring boot 에 systemd 유닛 만들기
요즘 프로젝트를 하고 있는데, 역시나 자바 시스템이 있다. Spring Boot3 을 사용하고 있고 자바 17을 쓰는등 나름대로 괜찮은 환경에서 개발이 이루어지고 있다. 그런데, 이것을 서버에서 배포를 하고 Spring Boot 를 실행해야 하는데, 어떻게 하나 봤더니 초보자 수준도 못 벗어나는 설정을 하고 있으니… 안타까운 마음에 어떻게 하는 것이 좋은 것인지 한번 적어봤다.
Spring boot, jar 실행 파일
Spring Boot3 를 컴파일 하면 jar 파일 나온다. 그리고 별다른 서버 없이도 바로 실행하고 접속이 가능해 진다. 한가지 재미있는 사실은 많은 사람들이 Spring boot3 에 실행 파일 jar 이 내장된 WAS 서버가 구동되면서 실행된다는 걸 모른다는 거다. 심지여 그것이 Tomcat 이라는 것도. 물론 어떤 프로그래밍 모델인지에 따라서 Tomcat 이 되기도 하고 Netty 되기도 하지만, 과연 Reactive Programming Model 로 짜는 사람이 몇이나 있나 싶다. 대부분 Spring MVC 이고 Servlet 이면 기본적으로 Tomcat 이 구동된다. 내장형 Tomcat
프로젝트에서 쓰는 방법
현재 프로젝트에서는 jar 실행을 위해서 스크립트 파일을 만들었다. 대략 다음과 같다.
1 2 3 |
#!/bin/bash java -jar -Dspring.profiles.active=prd -Dspring.jpa.hibernate.ddl-auto=none /app/helloworld.jar |
자바 옵션인 -D 옵션과 jar 실행 파일을 인자로 주고 있다. 그런데, 특이하게도 데몬화를 하지 않았고 많은 초보자식 설정인 nohup 도 없다. 이렇게되면 프로그램이 포그라운드(Foreground) 실행이되어서 쉘이 묶이게 된다.
어떻게 했지? 봤더니 systemd 에 이 실행파일을 넣고 systemd 를 통해서 시작/중지를 하고 있었다. 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[Unit] Description=Spring Boot hello world After=network-online.target Wants=network-online.target [Service] Type=simple User=lgbcadm Environment=RUNTIME_BASE_DIR=/app ExecStart=/app/helloworld_run.sh ExecStop=/bin/kill -TERM ${MAINPID} ExecReload=/bin/kill -HUP ${MAINPID} Restart=on-failure [Install] WantedBy=multi-user.target |
‘ExecStart=’ 에 쉘 스크립트 실행 파일을 지정해 주고 있다.
왜 이렇게 해야 하나? 왜? 이해 할 수 없는 구조다.
Spring 공식 문서
Systemd 유닛에 등록하는 방법은 Spring 공식문서에도 나와 있다.
57.1.2 Installation as a systemd service
이 문서에 나온 systemd 유닛 파일은 프로젝트 유닛 파일과 다르다. 공식 문서에 내용은 그냥 아주 기초적인 내용만 적혀 있다. 실제 프로젝트에 적용하기에는 무리가 있다.
설정파일 작성
Spring boot 를 위한 설정을 systemd 유닛에서 사용할 수 있는 별도의 파일로 작성한다. 이 설정은 JAVA_OPTS 의 내용을 담으면 된다.
1 |
JAVA_OPTS='-Dspring.profiles.active=prd -Dspring.jpa.hibernate.ddl-auto=none' |
이것을 이제 systemd 에서 사용하면 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 |
[Unit] Description=myapp After=syslog.target [Service] EnvironmentFile=/etc/sysconfig/helloworld.conf ExecStart=/usr/bin/java $JAVA_OPTS -jar /app/helloworld.jar SuccessExitStatus=143 [Install] WantedBy=multi-user.target |
공식문서에 나와 있는 내용과 EnvironmentFile 속성으로 외부에서 JAVA_OPTS 를 지정한 내용을 읽어오도록 하면 충분히 사용가능해 진다.
프로젝트에서 처럼 스크립트로 한번 감싸고 이것을 다시 systemd 유닛에서 실행되도록 할 필요가 없다는 것이다.
몇가지 개선 설정
공식문서는 간단한 예이다. 실행이 가능한 정도의 유닛 파일일 뿐이다. systemd 를 이용해 자바 애플리케이션을 실행할 때에는 여러가지를 고려해야 한다.
일단 자바 애플리케이션은 네트워크를 기반으로 한다. 따라서 네트워크 온라인 상태여야 한다. 또, StdOut, StdErr 를 Journal 로 내보내도록 해야 한다.