limits.conf에서 nofile을 올렸는데도 Too many open files가 납니다
Ubuntu 22.04에 systemd로 띄운 자바 백엔드(스프링부트, OpenJDK 17)를 운영 중입니다. 지난번 Communications link failure 질문 때 쓰던 그 서버인데, 트래픽이 조금 늘고 나서 애플리케이션 로그에 소켓 생성 실패가 뜨기 시작했습니다.
java.net.SocketException: Too many open files
at java.base/sun.nio.ch.Net.accept(Native Method)
...
Caused by: java.io.IOException: Too many open files파일 디스크립터 문제로 보고 limits.conf를 올렸습니다.
# /etc/security/limits.conf
* soft nofile 65536
* hard nofile 65536
appuser soft nofile 65536
appuser hard nofile 65536SSH로 appuser로 직접 로그인해서 확인하면 값은 제대로 올라가 있습니다.
$ ulimit -n
65536그런데 서비스를 재시작하고 실제 프로세스 한도를 보면 여전히 1024입니다.
$ systemctl restart myapp
$ cat /proc/$(pgrep -f myapp.jar)/limits | grep "open files"
Max open files 1024 1024 files/etc/pam.d 쪽에 pam_limits.so도 들어있는 걸 확인했고 서버 재부팅까지 해봤는데 systemd로 뜬 프로세스만 1024로 남습니다. 쉘에서 본 값과 서비스 프로세스 값이 왜 이렇게 다른지, 서비스에 65536을 제대로 먹이려면 어디를 손대야 하는지 궁금합니다.
증상은 limits.conf 설정 자체가 틀린 게 아니라, systemd로 뜬 서비스에는 그 파일이 애초에 적용되지 않기 때문입니다. /etc/security/limits.conf는 PAM 모듈(pam_limits.so)이 읽어서 거는 한도인데, pam_limits는 로그인 세션에서만 동작합니다. SSH 로그인, su, login 같은 건 PAM 세션을 거치니까 appuser로 접속해서 ulimit -n 하면 65536이 제대로 보이는 겁니다. 반면 systemd가 부팅 시점에 서비스로 띄우는 프로세스는 로그인 세션이 아니라서 PAM을 타지 않고, 따라서 limits.conf를 전혀 읽지 않습니다. 그래서 서비스 프로세스만 기본값 1024로 남아 있는 거고요. 재부팅을 해도 systemd 경로는 그대로라 안 바뀝니다.
systemd 공식 매뉴얼을 보면 nofile 한도는 서비스 유닛의 LimitNOFILE= 지시어로 거는 게 정석이고, 이 값이 ulimit -n에 대응한다고 되어 있습니다. 그러니 limits.conf가 아니라 유닛 파일(또는 드롭인)을 손대야 합니다.
드롭인으로 넣는 걸 권장합니다. 원본 유닛을 안 건드리고 override만 추가합니다.
sudo systemctl edit myapp.service열리면 아래를 넣습니다.
[Service]
LimitNOFILE=65536그다음 반영하고 재시작합니다. 중요한 점은 rlimit은 프로세스 생성 시점에 정해지기 때문에 reload로는 안 바뀌고 반드시 restart를 해야 합니다.
sudo systemctl daemon-reload
sudo systemctl restart myapp.service확인은 질문자분이 쓰신 방법 그대로 하시면 됩니다.
cat /proc/$(pgrep -f myapp.jar)/limits | grep "open files"참고로 LimitNOFILE=65536 처럼 값 하나만 주면 soft와 hard가 둘 다 그 값이 됩니다. soft와 hard를 따로 주고 싶으면 LimitNOFILE=65536:524288 형식으로 soft:hard로 적으면 됩니다. 최신 systemd에서 hard 기본값이 524288로 꽤 높게 잡혀 있으니 soft만 올려도 보통 충분합니다.
서비스가 여러 개라 매번 넣기 번거로우면 /etc/systemd/system.conf의 DefaultLimitNOFILE=로 시스템 전체 서비스 기본값을 올릴 수도 있는데, 이건 전역에 영향을 주니 가능하면 문제 서비스 유닛에 LimitNOFILE= 명시하는 쪽을 권합니다.
마지막으로 한도만 올리고 끝내지 마시고, 트래픽 늘면서 fd가 실제로 새는 건 아닌지도 같이 보시는 게 좋습니다. 운영 중에 아래로 현재 열린 fd 수를 세어서 평소 대비 계속 증가하는지 확인해보세요. 소켓이나 파일을 안 닫는 코드가 있으면 65536도 언젠가 또 터집니다.
ls /proc/$(pgrep -f myapp.jar)/fd | wc -l설정 근거는 systemd.exec, systemd-system.conf 매뉴얼에서 확인했습니다. systemd.exec(LimitNOFILE), systemd-system.conf(DefaultLimitNOFILE)
🔗 https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html
🔗 https://www.freedesktop.org/software/systemd/man/latest/systemd-system.conf.html
로그인 후 답글을 남길 수 있습니다.