본문 바로가기
서버연구 블로그 서버연구 블로그

Linux 파일 디스크립터 한계는 왜 발생할까? ulimit과 FD 고갈 분석

읽는 시간 약 13분

서버에 CPU와 메모리가 충분하고 디스크에도 특별한 문제가 없는데 갑자기 새로운 연결을 받지 못하는 경우가 있습니다.

웹 서버 로그에는 Too many open files가 기록되고, 애플리케이션은 새로운 파일을 열지 못하거나 외부 서버와의 연결에 실패합니다. 심한 경우 SSH 접속이나 모니터링 에이전트까지 영향을 받을 수 있습니다.

이때 확인해야 할 자원 중 하나가 **파일 디스크립터(File Descriptor, FD)**입니다.

Linux에서는 일반 파일뿐 아니라 네트워크 소켓, 파이프, 로그 파일 등 다양한 자원을 파일 디스크립터를 통해 관리합니다. 따라서 FD 고갈은 단순히 “파일을 너무 많이 열었다”는 의미가 아닙니다.

특히 동시 접속이 많은 웹 서버나 프록시 서버에서는 파일 디스크립터가 사실상 동시 연결을 처리하기 위한 핵심 자원이 될 수 있습니다.

파일 디스크립터 고갈은 왜 발생할까?

Linux 프로세스가 파일이나 소켓을 열면 커널은 해당 자원을 식별하기 위한 정수 값을 프로세스에 제공합니다.

이 값이 파일 디스크립터입니다.

예를 들어 프로세스가 파일을 열었다면 다음과 같은 FD가 할당될 수 있습니다.

0 : standard input
1 : standard output
2 : standard error
3 : log file
4 : TCP socket
5 : TCP socket
6 : database connection
...

여기서 중요한 것은 네트워크 연결 역시 FD를 소비한다는 점입니다.

Nginx 서버가 클라이언트 10,000명과 TCP 연결을 유지하고 있다면 상당수의 연결에 각각 소켓 FD가 필요합니다.

Reverse Proxy 구조라면 상황은 더 복잡해집니다.

Client
   ↓
Nginx
   ↓
Application Server
   ↓
Database

Nginx는 클라이언트와의 연결뿐 아니라 백엔드 서버와의 연결에도 소켓을 사용합니다.

따라서 클라이언트 연결 1개가 반드시 FD 1개만 사용한다고 생각하면 안 됩니다.

파일 디스크립터 고갈의 대표적인 원인은 다음과 같습니다.

  • 동시 TCP 연결 증가
  • Keep-Alive 연결 증가
  • 애플리케이션의 파일 close 누락
  • 소켓 close 누락
  • 로그 파일 및 임시 파일 증가
  • DB·Redis 등 외부 서비스 연결 증가
  • 프로세스의 FD 제한값이 지나치게 낮은 경우

결국 FD 고갈은 사용량 증가와 제한값이 충돌하면서 발생하는 자원 부족 현상이라고 볼 수 있습니다.

Linux는 파일 디스크립터를 어떻게 제한할까?

FD 문제를 이해하려면 Linux의 제한이 하나만 존재하는 것이 아니라는 점을 알아야 합니다.

대표적으로 프로세스 단위 제한과 시스템 전체 제한이 존재합니다.

현재 셸의 제한값은 다음 명령으로 확인할 수 있습니다.

ulimit -n

예를 들어 결과가 다음과 같다고 가정해보겠습니다.

1024

이는 현재 셸 환경에서 프로세스가 열 수 있는 파일 디스크립터 제한과 관련된 값입니다.

좀 더 자세히 확인하려면 다음 명령을 사용할 수 있습니다.

ulimit -Sn
ulimit -Hn

-S는 Soft Limit, -H는 Hard Limit을 의미합니다.

예를 들어 다음과 같을 수 있습니다.

Soft Limit : 1024
Hard Limit : 65535

Soft Limit은 현재 적용되는 제한이고, Hard Limit은 일반적으로 Soft Limit을 올릴 수 있는 상한과 관련됩니다.

하지만 여기서 운영자가 자주 실수합니다.

터미널에서 실행한 ulimit -n 결과만 보고 실제 서비스 프로세스에도 동일한 값이 적용되고 있다고 판단하는 것입니다.

systemd를 통해 실행되는 Nginx, Java 애플리케이션 등의 프로세스는 로그인 셸과 다른 제한값을 가지고 있을 수 있습니다.

실제 실행 중인 프로세스의 제한을 확인하는 것이 중요합니다.

먼저 PID를 찾습니다.

pidof nginx

그리고 해당 프로세스의 limits를 확인합니다.

cat /proc/<PID>/limits

여기서 다음 항목을 확인합니다.

Max open files

운영 환경에서는 ulimit -n보다 실제 서비스 PID의 /proc/<PID>/limits를 확인하는 것이 더 중요합니다.

FD가 부족하면 서버에서는 어떤 증상이 나타날까?

파일 디스크립터가 한계에 도달하면 프로세스는 새로운 파일이나 소켓을 열 수 없습니다.

대표적인 오류가 다음과 같습니다.

Too many open files

웹 서버에서는 새로운 클라이언트 연결을 정상적으로 처리하지 못할 수 있고 애플리케이션에서는 로그 파일, 설정 파일, DB 연결 또는 외부 API 연결 생성이 실패할 수 있습니다.

문제는 FD 고갈이 CPU 100%처럼 직관적으로 보이지 않을 수 있다는 것입니다.

예를 들어 서버 상태가 다음과 같을 수 있습니다.

CPU Usage       35%
Memory Usage    58%
Disk Usage      47%
Load Average    정상

표면적으로는 여유가 있습니다.

그런데 요청 실패율은 계속 증가합니다.

이런 상황에서 CPU나 메모리만 확인하면 원인을 찾지 못할 수 있습니다.

또 하나 중요한 것은 FD Leak입니다.

정상적인 서버라면 트래픽이 감소했을 때 사용 중인 FD도 어느 정도 감소하는 것이 자연스럽습니다.

하지만 애플리케이션이 파일이나 소켓을 제대로 닫지 않는다면 요청이 끝난 뒤에도 FD가 남을 수 있습니다.

시간       트래픽       Open FD

10:00      1,000       2,100
11:00      2,000       4,200
12:00      1,000       4,500
13:00        500       4,800
14:00        300       5,100

트래픽은 감소했는데 FD가 계속 증가한다면 단순한 트래픽 증가보다 FD Leak 가능성을 함께 의심해야 합니다.

실제 서버에서는 어떻게 분석해야 할까?

가장 먼저 확인할 것은 문제가 발생한 프로세스가 현재 몇 개의 FD를 사용하고 있는지입니다.

ls /proc/<PID>/fd | wc -l

예를 들어 결과가 다음과 같다고 가정하겠습니다.

8124

이 프로세스가 사용할 수 있는 최대 FD가 8192라면 이미 한계에 매우 가까운 상태입니다.

다음으로 제한값을 확인합니다.

cat /proc/<PID>/limits | grep "open files"

예를 들어 다음과 같다면,

Max open files    8192    8192

현재 8,124개를 사용하면서 최대값이 8,192개이므로 새로운 연결이 조금만 증가해도 문제가 발생할 가능성이 높습니다.

하지만 여기서 분석을 끝내면 안 됩니다.

무엇이 FD를 사용하고 있는지 확인해야 합니다.

대표적으로 lsof를 사용할 수 있습니다.

lsof -p <PID>

개수만 보고 싶다면 다음과 같이 확인할 수도 있습니다.

lsof -p <PID> | wc -l

네트워크 연결 상태도 함께 확인합니다.

ss -s

또는 특정 프로세스의 소켓 사용 상태를 확인하면서 TCP 연결 증가와 FD 증가가 같이 움직이는지 비교합니다.

운영자가 봐야 할 핵심은 단순히 **”FD가 많다”**가 아닙니다.

FD 증가
   ↓
트래픽 증가와 비례하는가?
   ↓
트래픽 감소 후 FD도 감소하는가?
   ↓
특정 파일 또는 소켓이 비정상적으로 누적되는가?
   ↓
프로세스의 FD 제한에 얼마나 근접했는가?

이 흐름으로 봐야 합니다.

시스템 전체의 파일 핸들 상태도 확인할 수 있습니다.

cat /proc/sys/fs/file-nr

또한 시스템 전체에서 허용하는 최대 파일 핸들 값은 다음으로 확인합니다.

cat /proc/sys/fs/file-max

여기서 중요한 점은 프로세스 한계와 시스템 전체 한계를 구분하는 것입니다.

특정 Nginx 프로세스만 FD 한계에 도달한 것과 서버 전체 파일 핸들 자원이 부족한 것은 원인이 다르기 때문입니다.

ulimit 값만 크게 올리면 해결될까?

FD 부족 현상이 발생하면 가장 먼저 생각하는 해결책은 제한값을 높이는 것입니다.

예를 들어 다음처럼 설정할 수 있습니다.

ulimit -n 65535

하지만 이것만으로 문제를 해결했다고 판단하면 위험합니다.

FD 제한이 너무 낮아서 정상적인 트래픽을 감당하지 못한 상황이라면 제한값 증가는 적절한 해결책이 될 수 있습니다.

반대로 애플리케이션에 FD Leak이 있는 상황이라면 제한값을 높이는 것은 장애 발생 시간을 늦출 뿐 근본적인 문제를 해결하지 못합니다.

예를 들어 매시간 1,000개의 FD가 비정상적으로 누적된다고 가정해보겠습니다.

기존 limit     10,000
변경 limit     100,000

제한값을 10배 늘리면 장애가 사라진 것처럼 보일 수 있습니다.

하지만 FD가 계속 증가한다면 결국 100,000개에도 도달합니다.

따라서 먼저 다음을 구분해야 합니다.

정상적인 동시접속 증가
        ↓
FD limit 부족
        ↓
limit 조정 검토

또는

트래픽과 무관한 FD 지속 증가
        ↓
FD Leak 의심
        ↓
애플리케이션 원인 분석

systemd 기반 서비스라면 서비스 단위 설정도 확인해야 합니다.

예를 들어 다음과 같은 설정이 사용될 수 있습니다.

[Service]
LimitNOFILE=65535

설정을 변경했다면 실제 프로세스에 적용되었는지 반드시 다시 확인해야 합니다.

cat /proc/<PID>/limits

설정 파일을 수정했다는 사실보다 현재 실행 중인 프로세스에 어떤 제한값이 실제 적용되어 있는지가 더 중요합니다.

또한 Nginx 같은 서버는 자체 설정과 worker 수, worker별 connection 한계 등도 함께 고려해야 합니다.

즉 FD 값 하나만 크게 만든다고 서버의 최대 동시접속 수가 자동으로 그만큼 증가하는 것은 아닙니다.

정리

Linux에서 파일 디스크립터는 단순한 파일 번호가 아닙니다.

일반 파일뿐 아니라 네트워크 소켓, 파이프 등 다양한 커널 자원을 프로세스가 다루기 위한 핵심 인터페이스입니다.

따라서 동시 접속이 많은 서버에서 FD는 CPU와 메모리 못지않게 중요한 자원이 될 수 있습니다.

FD 관련 장애를 분석할 때는 다음 순서로 접근하는 것이 좋습니다.

실제 프로세스의 FD 사용량 확인
        ↓
프로세스의 Soft/Hard Limit 확인
        ↓
어떤 파일·소켓이 FD를 소비하는지 확인
        ↓
트래픽과 FD 증가의 상관관계 분석
        ↓
FD Leak 여부 확인
        ↓
프로세스 제한과 시스템 전체 제한 구분
        ↓
필요한 경우 제한값 및 서비스 설정 조정

특히 Too many open files가 발생했다고 해서 바로 ulimit 값부터 크게 올리는 것은 좋은 접근이 아닙니다.

운영 환경에서 더 중요한 질문은 **”제한값이 얼마인가?”가 아니라 “왜 이 프로세스가 이만큼의 FD를 사용하고 있는가?”**입니다.

CPU와 메모리가 충분한데도 새로운 연결이 실패한다면 FD 사용량과 /proc/<PID>/limits를 함께 확인해야 합니다.

파일 디스크립터는 평소에는 잘 드러나지 않지만 한계에 도달하는 순간 서버의 연결 처리 능력을 직접 제한할 수 있는 자원입니다. 대규모 트래픽을 운영한다면 FD 사용량을 장애가 발생한 뒤 확인하는 지표가 아니라 평소부터 모니터링해야 하는 운영 지표로 관리하는 것이 중요합니다.

instone
함께 보면 좋은 글

댓글 0

첫 댓글을 남겨보세요.

error: Content is protected !!

광고 차단 알림

광고 클릭 제한을 초과하여 광고가 차단되었습니다.

단시간에 반복적인 광고 클릭은 시스템에 의해 감지되며, IP가 수집되어 사이트 관리자가 확인 가능합니다.