리눅스 서버를 운영하는 관리자나 개발자라면 누구나 한 번쯤 'Disk Full' 메시지와 함께 서버가 먹통이 되는 아찔한 경험을 하게 됩니다. 로그 파일이 감당할 수 없을 정도로 커졌거나, 나도 모르는 사이에 임시 파일이 쌓여 소중한 디스크 자원을 잠식하기 때문입니다.

오늘은 서버 용량 부족 문제를 해결하기 위해, 어떤 디렉토리가 용량을 많이 차지하고 있는지 범인을 찾아내는 핵심 명령어와 효율적인 관리 팁을 정리해 보겠습니다.


1. 전체 디스크 사용량 확인: df 명령어

가장 먼저 해야 할 일은 서버 전체의 디스크 상태를 파악하는 것입니다. 이때 사용하는 명령어가 df (disk free)입니다.

1.1 df -h 활용하기

단순히 df만 입력하면 바이트 단위로 출력되어 읽기 어렵습니다. -h (human-readable) 옵션을 추가하여 GB, MB 단위로 확인하세요.

Bash
 
df -h
  • Filesystem: 파티션 이름
  • Size: 전체 용량
  • Used: 사용 중인 용량
  • Avail: 남은 용량
  • Use%: 사용률 (이 수치가 90%를 넘었다면 즉시 조치가 필요합니다.)

2. 특정 디렉토리 용량 확인: du 명령어

어떤 파티션이 가득 찼는지 확인했다면, 이제 구체적으로 어떤 폴더가 범인인지 찾아야 합니다. 이때 du (disk usage) 명령어를 사용합니다.

2.1 하위 디렉토리별 용량 요약 (du -sh)

현재 디렉토리의 전체 합계 용량만 보고 싶을 때 사용합니다.

Bash
 
du -sh .

2.2 깊이 조절하여 확인하기 (--max-depth)

루트(/) 디렉토리부터 하위 1단계 폴더들의 용량을 한눈에 보고 싶을 때 유용합니다.

Bash
 
sudo du -h --max-depth=1 / | sort -hr
  • sort -hr: 용량이 큰 순서대로 정렬하여 출력합니다. 이 명령어를 통해 /var/log나 /tmp 중 어디가 문제인지 바로 파악할 수 있습니다.

3. 리눅스 서버의 용량 도둑, 주요 타겟 3곳

디스크 용량을 많이 잡아먹는 대표적인 디렉토리는 보통 정해져 있습니다.

3.1 /var/log (시스템 및 서비스 로그)

애플리케이션의 디버그 로그가 무한정 쌓이거나, syslog가 거대해지는 경우가 많습니다.

  • 해결책: logrotate 설정을 통해 오래된 로그는 압축하거나 삭제하도록 설정하세요.

3.2 /tmp (임시 파일)

시스템이나 프로그램이 실행 중에 생성한 임시 파일들이 삭제되지 않고 남는 경우입니다.

  • 해결책: 재부팅 시 자동으로 삭제되거나, 주기적으로 오래된 파일을 정리하는 스크립트를 실행하세요.

3.3 Docker 관련 데이터 (/var/lib/docker)

도커를 사용 중이라면 사용하지 않는 이미지, 컨테이너, 볼륨이 상당한 용량을 차지합니다.

  • 해결책: 아래 명령어로 쓰지 않는 자원을 일괄 정리하세요.
Bash
 
docker system prune -a --volumes

4. 실무 꿀팁: 큰 파일 한 번에 찾기 (find)

디렉토리 단위가 아니라, 특정 크기(예: 500MB) 이상의 '대용량 파일'만 골라내고 싶을 때 find 명령어가 제격입니다.

Bash
 
sudo find / -type f -size +500M -exec ls -lh {} \;

이 명령어는 전체 파일 시스템에서 500MB가 넘는 파일들의 목록과 상세 정보를 출력해 줍니다.


5. 결론: 예방이 최선의책입니다

디스크 용량 문제는 서비스 중단으로 이어지는 치명적인 사고입니다. 사고가 터진 후 대응하는 것도 중요하지만, 모니터링 시스템(Prometheus, Grafana 등)을 구축하여 사용률이 80%를 넘을 때 알람을 받도록 설정하는 것이 가장 바람직합니다.

애플리케이션을 개발하다 보면 데이터베이스 접속 정보, 외부 API 키, 인증 토큰 등 외부로 노출되어서는 안 되는 민감한 정보를 다루게 됩니다. 도커(Docker) 컨테이너 환경에서는 이러한 설정을 어떻게 관리하느냐가 전체 시스템의 보안 수준을 결정합니다.

오늘은 입문자가 가장 많이 실수하는 부분부터, 현업에서 권장하는 .env 파일을 활용한 환경 변수 관리 및 보안 전략을 상세히 알아보겠습니다.


1. 환경 변수 관리가 중요한 이유

코드 내부에 비밀번호나 API 키를 직접 입력(Hard-coding)하는 것은 매우 위험합니다. 코드가 GitHub와 같은 원격 저장소에 올라가는 순간, 전 세계 누구나 내 서버의 권한을 탈취할 수 있기 때문입니다.

도커 환경에서 환경 변수를 분리하여 관리하면 다음과 같은 이점이 있습니다.

  • 보안성 향상: 민감한 정보를 코드와 분리하여 저장합니다.
  • 유연성: 소스 코드를 수정하지 않고도 개발(Dev), 테스트(Test), 운영(Prod) 환경에 맞는 설정을 즉시 적용할 수 있습니다.
  • 협업 효율: 팀원 간에 공통된 설정 형식을 공유하면서도 개인별 환경에 맞게 조정이 가능합니다.

2. .env 파일 작성 및 도커 연동

가장 대중적인 방법은 프로젝트 루트 디렉토리에 .env 파일을 만드는 것입니다.

2.1 .env 파일 예시

Plaintext
 
# 데이터베이스 설정
DB_HOST=db.example.com
DB_USER=admin
DB_PASSWORD=your_secret_password_1234

# API 키
STRIPE_API_KEY=sk_test_51Mz...

2.2 Docker Run 명령어로 전달하기

컨테이너를 실행할 때 --env-file 옵션을 사용하여 파일의 내용을 한 번에 주입할 수 있습니다.

Bash
 
docker run --env-file .env my-application-image

2.3 Docker Compose에서 활용하기

docker-compose.yml 파일을 사용하면 더 직관적인 관리가 가능합니다.

YAML
 
version: '3.8'
services:
  web:
    image: my-app:latest
    env_file:
      - .env
    ports:
      - "8080:8080"

3. 절대로 잊지 말아야 할 보안 수칙

환경 변수 파일을 사용한다고 해서 보안이 완성되는 것은 아닙니다. 아래 두 가지는 반드시 실천해야 합니다.

3.1 .gitignore에 추가하기

.env 파일이 Git 저장소에 포함되지 않도록 반드시 제외 처리해야 합니다.

Plaintext
 
# .gitignore 파일 내용
.env
.env.local
.env.*.local

3.2 샘플 파일 제공 (.env.example)

팀원들이 어떤 환경 변수가 필요한지 알 수 있도록, 실제 값은 비워두고 키 이름만 적어둔 예시 파일을 공유하는 것이 매너입니다.

Plaintext
 
# .env.example (이 파일은 Git에 올려도 안전합니다)
DB_HOST=
DB_USER=
DB_PASSWORD=

4. 고급 보안 기법: Docker Secrets

만약 단순한 .env 파일을 넘어 더 강력한 보안이 필요하다면 Docker Secrets 기능을 고려해 볼 수 있습니다. 이는 Docker Swarm 모드에서 지원하는 기능으로, 변수 값을 메모리에만 저장하고 디스크에는 암호화하여 보관합니다.

  1. 민감한 정보를 Docker Secret으로 등록합니다.
  2. 컨테이너 실행 시 필요한 서비스에만 해당 Secret을 마운트합니다.
  3. 컨테이너 내부의 /run/secrets/ 경로에서 파일을 읽어 사용합니다.

5. 결론: 안전한 배포를 위한 첫걸음

도커 컨테이너 환경에서 환경 변수를 올바르게 관리하는 것은 '선택'이 아닌 '필수'입니다.

  1. 변수는 파일로 분리하고,
  2. Git 업로드를 방지하며,
  3. **상황에 맞는 도구(Docker Compose, Secrets)**를 선택하는 습관을 들이세요.

웹 서비스를 운영하다 보면 성능 최적화와 보안이라는 두 마리 토끼를 잡아야 하는 순간이 옵니다. 특히 Node.js, Python(Django/Flask), Go 등으로 만든 애플리케이션 서버를 외부 네트워크에 직접 노출하는 것은 보안상 위험할 뿐만 아니라 성능 면에서도 효율적이지 않습니다.

이때 가장 대중적으로 사용되는 해결책이 바로 Nginx를 활용한 리버스 프록시(Reverse Proxy) 구성입니다. 오늘은 리버스 프록시의 개념부터 실제 설정 방법, 그리고 이를 통해 얻을 수 있는 이점까지 상세히 정리해 보겠습니다.


1. 리버스 프록시(Reverse Proxy)란 무엇인가?

일반적인 프록시(Forward Proxy)가 클라이언트의 요청을 대신 전달한다면, 리버스 프록시는 서버의 앞단에 위치하여 클라이언트의 요청을 받아 내부 서버로 전달하는 역할을 합니다.

사용자는 실제 애플리케이션 서버(예: 3000번 포트)에 직접 접속하는 것이 아니라, 80(HTTP) 또는 443(HTTPS) 포트를 통해 Nginx에 먼저 도달하게 됩니다. Nginx는 이 요청을 적절한 내부 서버로 토스(Toss)해 주는 '중개인' 역할을 수행합니다.


2. 리버스 프록시 도입의 3가지 핵심 이점

2.1 보안 강화 (Security)

실제 서비스가 돌아가는 서버의 IP와 포트를 외부로부터 숨길 수 있습니다. 공격자는 Nginx의 IP만 볼 수 있으며, 내부 인프라 구조를 파악하기 어렵게 됩니다. 또한, Nginx 단에서 WAF(웹 방화벽) 설정을 추가하여 악의적인 요청을 1차적으로 걸러낼 수 있습니다.

2.2 부하 분산 (Load Balancing)

하나의 Nginx 뒷단에 여러 대의 애플리케이션 서버를 두고, 요청을 골고루 나누어 줄 수 있습니다. 특정 서버에 장애가 발생하더라도 다른 서버로 요청을 유도하여 서비스 가용성을 높일 수 있습니다.

2.3 정적 파일 캐싱 및 압축 (Performance)

이미지, CSS, JS와 같은 정적 자원을 Nginx가 직접 캐싱하거나 gzip 압축을 통해 전송 속도를 높일 수 있습니다. 이는 애플리케이션 서버가 비즈니스 로직에만 집중할 수 있도록 부담을 덜어줍니다.


3. Nginx 리버스 프록시 실전 설정법

이제 Ubuntu 환경에서 Nginx를 설치하고 리버스 프록시를 설정하는 과정을 살펴보겠습니다. (내부 서버는 localhost:3000에서 동작 중이라고 가정합니다.)

3.1 Nginx 설치

먼저 패키지 목록을 업데이트하고 Nginx를 설치합니다.

Bash
 
sudo apt update
sudo apt install nginx -y

3.2 설정 파일 생성 및 수정

기본 설정 파일을 수정하기보다는, 새로운 설정 파일을 만들어 관리하는 것이 좋습니다.

Bash
 
sudo nano /etc/nginx/sites-available/my-app

파일 내부에 아래 내용을 입력합니다. (도메인이 있다면 server_name에 작성하세요.)

Nginx
 
server {
    listen 80;
    server_name your-domain.com; # 도메인이 없다면 서버 IP 입력

    location / {
        # 요청을 내부 서버(3000번 포트)로 전달
        proxy_pass http://localhost:3000;
        
        # 실제 클라이언트의 정보를 전달하기 위한 헤더 설정
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        
        # 클라이언트의 실제 IP 주소 전달
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

3.3 설정 활성화 및 테스트

작성한 설정을 활성화하고 구문에 에러가 없는지 확인합니다.

Bash
 
# 설정 파일 심볼릭 링크 생성
sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/

# Nginx 설정 테스트
sudo nginx -t

# Nginx 재시작
sudo systemctl restart nginx

4. 자주 발생하는 문제와 해결법 (Troubleshooting)

리버스 프록시 설정 후 흔히 겪는 에러 두 가지를 짚어보겠습니다.

  • 502 Bad Gateway: Nginx는 정상이나, proxy_pass로 연결된 내부 애플리케이션 서버가 죽어있을 때 발생합니다. pm2 status 등으로 서버 상태를 확인하세요.
  • 413 Request Entity Too Large: 클라이언트가 업로드하는 파일 용량이 Nginx 기본 제한을 초과할 때 발생합니다. http나 server 블록에 client_max_body_size 20M; 설정을 추가하여 해결할 수 있습니다.

5. 결론: 현대 웹 아키텍처의 필수 요소

리버스 프록시는 단순한 전달자를 넘어, 웹 서비스의 안정성과 확장성을 책임지는 핵심 인프라 기술입니다. 특히 최근처럼 마이크로서비스 아키텍처(MSA)가 각광받는 환경에서는 Nginx를 활용한 게이트웨이 구성이 더욱 중요해지고 있습니다.

오늘 안내해 드린 기본 설정을 바탕으로, 이후에는 HTTPS 보안 인증서(SSL) 적용과 로드 밸런싱 설정까지 확장해 보시길 권장합니다. 안정적인 서버 운영으로 사용자에게 더 나은 경험을 제공해 보세요!

 

리눅스 서버(Ubuntu)를 처음 생성하고 공인 IP를 할당받으면, 그 순간부터 전 세계의 수많은 봇(Bot)들이 여러분의 서버에 접속하기 위해 무차별 대입 공격(Brute-force)을 시도합니다. 특히 기본 설정인 22번 포트root 계정은 해커들의 주요 타겟이 됩니다.

오늘은 내 소중한 서버를 안전하게 보호하기 위한 가장 기초적이면서도 필수적인 보안 설정 두 가지, SSH 포트 변경Root 로그인 차단 방법을 상세히 알아보겠습니다.


1. 왜 기본 설정을 변경해야 하는가?

서버 운영에 있어 '보안의 기본'은 공격자가 예측하기 어렵게 만드는 것입니다.

  • SSH 기본 포트(22번): 누구나 알고 있는 포트입니다. 공격자들은 자동화된 스크립트로 22번 포트가 열려 있는지 확인하고 접속을 시도합니다. 포트 번호만 바꿔도 자동화 공격의 90% 이상을 차단할 수 있습니다.
  • Root 계정: 리눅스에서 모든 권한을 가진 절대적인 계정입니다. ID가 'root'라는 것을 이미 알고 있기 때문에, 비밀번호만 알아내면 서버의 모든 제어권을 뺏기게 됩니다.

따라서 별도의 관리자 계정을 생성하고, root 접속은 원천 차단하는 것이 서버 보안의 시작입니다.


2. 작업 전 주의사항 (필독)

설정을 변경하다가 실수하면 **본인조차 서버에 접속하지 못하는 상황(Lock-out)**이 발생할 수 있습니다. 작업을 시작하기 전에 아래 사항을 반드시 확인하세요.

  1. 관리자 권한을 가진 일반 사용자 계정이 미리 생성되어 있어야 합니다.
  2. 사용 중인 클라우드(AWS, GCP, Azure 등)의 방화벽(Security Group) 설정에서 변경할 포트 번호를 미리 허용(Inbound)해 두어야 합니다.

3. SSH 설정 파일 수정하기

모든 SSH 관련 설정은 /etc/ssh/sshd_config 파일에서 관리합니다. 안전을 위해 원본 파일을 백업하고 수정을 시작해 보겠습니다.

3.1 설정 파일 백업 및 열기

Bash
 
# 설정 파일 백업
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

# 설정 파일 편집 (nano 또는 vim 사용)
sudo nano /etc/ssh/sshd_config

3.2 SSH 포트 번호 변경

파일 상단 근처에 #Port 22라는 문구를 찾으세요. 주석(#)을 제거하고 원하는 포트 번호로 수정합니다.

  • 추천 범위: 1024 ~ 65535 사이의 사용하지 않는 번호 (예: 2026, 8822 등)
Plaintext
 
# 수정 전
#Port 22

# 수정 후
Port 2026

3.3 Root 로그인 차단 설정

파일을 아래로 내리다 보면 PermitRootLogin 항목이 보입니다. 이를 no로 변경하여 외부에서 root 계정으로 직접 접속하는 것을 막습니다.

Plaintext
 
# 수정 전
#PermitRootLogin prohibit-password (또는 yes)

# 수정 후
PermitRootLogin no

 


4. 변경 사항 적용 및 테스트

설정을 마쳤다면 파일을 저장하고(Nano 기준: Ctrl+O -> Enter -> Ctrl+X), SSH 서비스를 재시작해야 적용됩니다.

4.1 서비스 재시작

Bash
 
sudo systemctl restart ssh

4.2 방화벽 설정 (UFW 기준)

서버 자체 방화벽인 UFW를 사용 중이라면 새 포트를 허용해 주어야 합니다.

Bash
 
# 새 포트 허용 (예: 2026)
sudo ufw allow 2026/tcp

# 기존 22번 포트 차단 (선택 사항)
sudo ufw delete allow 22/tcp

# 방화벽 상태 확인
sudo ufw status

5. 접속 테스트 (매우 중요!)

현재 접속되어 있는 터미널 창을 절대 끄지 마세요. 설정이 잘못되었을 경우를 대비해 현재 세션을 유지한 상태에서, 새로운 터미널 창을 열어 접속을 시도합니다.

Bash
 
# 새로운 터미널에서 접속 시도
ssh -p [변경한포트번호] [사용자계정]@[서버IP주소]

# 예시
ssh -p 2026 yumina@1.23.456.78

정상적으로 접속이 된다면 모든 설정이 성공적으로 완료된 것입니다. 이제 root 계정으로의 직접 접속은 차단되었으며, 지정한 포트를 통해서만 서버에 접근할 수 있습니다.


6. 결론 및 요약

서버 보안은 거창한 솔루션을 도입하는 것보다, 이처럼 기본적인 설정을 꼼꼼히 챙기는 것에서 시작됩니다.

오늘 배운 내용 요약:

  1. SSH 포트 변경: 자동화된 봇 공격으로부터 서버를 은닉합니다.
  2. PermitRootLogin no: 서버의 심장인 root 계정을 외부 노출로부터 보호합니다.
  3. 방화벽 설정: 서버 내부와 클라우드 외부 방화벽 모두에서 새 포트를 열어주어야 합니다.

가장 단순하지만 가장 강력한 이 두 가지 설정을 통해 더욱 안전한 서버 운영 환경을 구축하시기 바랍니다.

+ Recent posts