- 출처 / 참고: 리눅스 파일시스템 마운트 관리 및 스토리지 복구 가이드
명령어:
ulimit -n,sysctl -p,systemctl daemon-reload,lsof -p <PID>
키워드: File Descriptor, ulimit, nofile, systemd LimitNOFILE, fs.file-max, Socket Exhaustion
사용처: 웹 서버(Nginx, Apache), 데이터베이스(MySQL, Redis), Node.js/Java 백엔드 등에서 동시 접속 급증 시 소켓 및 파일 핸들 고갈 장애 해결
실행예제
# 1. 애플리케이션 로그에서 오류 확인 (Nginx/DB/App 공통)
# [alert] 12345#12345: *67890 socket() failed (24: Too many open files) while connecting to upstream
# java.io.IOException: Too many open files
# 2. 현재 시스템 전체 파일 디스크립터(FD) 사용량 및 최대 한도 점검
$ cat /proc/sys/fs/file-nr
18240 0 65536
# 출력 설명: [할당된 FD 수] [미사용 할당 FD 수] [시스템 전역 최대 한도] (현재 한도에 근접한 상태)
# 3. 현재 셸 및 사용자 세션의 FD 제한 확인 (Soft / Hard Limit)
$ ulimit -Sn
1024
$ ulimit -Hn
4096
# 4. 대상 프로세스(예: PID 12345)의 실제 런타임 적용 한도 및 현재 열린 FD 개수 확인
$ cat /proc/12345/limits | grep "Max open files"
Max open files 1024 4096 files
$ ls /proc/12345/fd | wc -l
1024
스크립트
#!/usr/bin/env bash
#
# 고트래픽 환경 대응: 커널 파라미터, PAM limits, systemd 파일 디스크립터(FD) 일괄 상향 스크립트
#
set -euo pipefail
TARGET_NOFILE=1048576 # 1M (고트래픽 서버 권장 표준 한도)
echo "=== [1] 시스템 전체 커널 파라미터 (/etc/sysctl.d/99-file-max.conf) 설정 ==="
cat <<EOF > /etc/sysctl.d/99-file-max.conf
fs.file-max = 2097152
fs.nr_open = ${TARGET_NOFILE}
EOF
sysctl -p /etc/sysctl.d/99-file-max.conf
echo "=== [2] 사용자 및 세션 레벨 제한 설정 (/etc/security/limits.d/99-nofile.conf) ==="
cat <<EOF > /etc/security/limits.d/99-nofile.conf
* soft nofile ${TARGET_NOFILE}
* hard nofile ${TARGET_NOFILE}
root soft nofile ${TARGET_NOFILE}
root hard nofile ${TARGET_NOFILE}
EOF
echo "=== [3] systemd 글로벌 서비스 기본값 설정 (/etc/systemd/system.conf & user.conf) ==="
# systemd 데몬 관리 서비스 전체에 대한 디폴트 LimitNOFILE 반영
mkdir -p /etc/systemd/system.conf.d
cat <<EOF > /etc/systemd/system.conf.d/30-nofile.conf
[Manager]
DefaultLimitNOFILE=${TARGET_NOFILE}:${TARGET_NOFILE}
EOF
systemctl daemon-reexec
echo "=== [4] 특정 서비스(예: nginx) systemd 오버라이드 단독 적용 ==="
if systemctl is-active --quiet nginx 2>/dev/null; then
mkdir -p /etc/systemd/system/nginx.service.d
cat <<EOF > /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=${TARGET_NOFILE}
EOF
systemctl daemon-reload
systemctl restart nginx
echo "Nginx 서비스의 LimitNOFILE이 성공적으로 갱신되었습니다."
fi
echo "=== [완료] 파일 디스크립터 한도 확장 설정이 완료되었습니다. ==="
해설
- 에러 원인 분석:
- 리눅스는 네트워크 소켓, 파일, 파이프, 디렉터리 등을 모두 파일 디스크립터(FD, File Descriptor)로 취급합니다.
- 트래픽이 폭증할 때 프로세스가 동시에 열어야 하는 TCP 연결 소켓 수가 배정된 FD 제한(
Soft Limit)을 초과하면 커널은EMFILE (24: Too many open files)에러를 반환하며 새로운 연결 수립을 거부합니다.
- 계층별 적용 체계 및 스크립트 핵심 로직:
- 커널 전역 레벨 (
fs.file-max,fs.nr_open):fs.file-max: 시스템 전체에서 모든 프로세스가 합산하여 열 수 있는 절대 최대 FD 수입니다.fs.nr_open: 개별 프로세스가 요청할 수 있는 단일 프로세스 최대 상한선입니다.limits.conf의 값보다 이 값이 항상 크거나 같아야 적용 오류가 발생하지 않습니다.
- 사용자 세션 레벨 (
/etc/security/limits.d/):- PAM(
pam_limits.so)을 통과하는 로그인 셸, SSH 세션, cron 등의 상한선을 제어합니다. soft: 프로세스가 기본으로 사용하는 현재 한도값입니다.hard: root 권한 없이 사용자가ulimit -n으로 올릴 수 있는 최대 상한선입니다.
- PAM(
- 데몬/서비스 레벨 (
systemd):- 최신 배포판(RHEL 7+, Ubuntu 16.04+)의 백그라운드 서비스(Nginx, MySQL, Redis 등)는 PAM을 거치지 않고
systemd에 의해 직접 기동됩니다. - 따라서
limits.conf를 수정해도 systemd 유닛에는 적용되지 않으므로, 유닛 파일 내LimitNOFILE=또는DefaultLimitNOFILE=을 반드시 설정해야 합니다.
- 최신 배포판(RHEL 7+, Ubuntu 16.04+)의 백그라운드 서비스(Nginx, MySQL, Redis 등)는 PAM을 거치지 않고
- 커널 전역 레벨 (
주의사항
- systemd 기반 서비스의
limits.conf무시 현상:systemctl로 기동하는 서비스는/etc/security/limits.conf설정을 완전히 무시합니다. 반드시systemctl edit <서비스명>을 통해[Service]섹션 아래에LimitNOFILE=...을 선언해야 합니다.
fs.nr_open을 초과하는 ulimit 설정 시 로그인 불가 장애:limits.conf의nofile값을 커널의fs.nr_open(기본값 1,048,576)보다 크게 설정하면, SSH 로그인 시 PAM 모듈 오류로 인해 사용자가 시스템에 접속하지 못하는 장애가 발생할 수 있습니다. 상향 시 항상fs.nr_open을 먼저 검토해야 합니다.
- 애플리케이션 자체 설정과의 동기화:
- Nginx의 경우 OS 한도 외에도
nginx.conf상단의worker_rlimit_nofile지시어가 별도로 존재합니다. OS 레벨만 늘리고 웹 서버 설정값을 누락하면 동일한 에러가 계속 발생하므로 양쪽 설정을 맞춰주어야 합니다.
- Nginx의 경우 OS 한도 외에도
- 리소스 누수(FD Leak) 감별:
- 정상적인 트래픽 증가가 아닌, 애플리케이션 버그로 소켓(
CLOSE_WAIT)이나 파일 핸들을 닫지 않아 FD가 계속 누적되는 상황일 수도 있습니다.lsof -p <PID>를 주기적으로 모니터링하여 특정 유형의 파일/소켓이 비정상적으로 누적되는지 사전 확인해야 합니다.
- 정상적인 트래픽 증가가 아닌, 애플리케이션 버그로 소켓(