콘텐츠로 이동

Linux Basics

분류: Layer 4 - CS 기초: 운영체제 & Linux

Linux는 대부분의 서버에서 사용되는 운영체제이며, 서버 관리의 기본 언어가 되는 CLI(명령줄 인터페이스) 환경이다.

AWS든 GCP든, 서버에 들어가면 Linux다. 로그를 보고, 프로세스를 확인하고, 파일을 찾고, 네트워크를 진단하는 모든 작업이 Linux 명령어로 이뤄진다. Docker 컨테이너 안도 Linux다. CLI를 못 다루면 서버 문제가 생겼을 때 할 수 있는 게 거의 없다.

파일 시스템과 inode — 파일 이름 뒤에 숨어있는 구조

비유: 도서관을 생각해보자. 책(파일 데이터)은 선반 어딘가에 꽂혀 있고, 카탈로그 카드(inode)에는 그 책의 위치와 저자·발행일·장르 등 메타데이터가 적혀 있다. 사람들은 카탈로그(디렉토리)에서 책 이름으로 카드를 찾고, 카드에서 책 위치를 확인한다. 책의 실제 내용과 카탈로그 카드는 분리되어 있다.

원리: Linux에서 모든 파일과 디렉토리는 inode(index node) 라는 자료구조로 관리된다. inode에는 파일 이름 빼고 모든 메타데이터가 저장된다.

inode에 저장되는 것저장되지 않는 것
파일 크기, 권한(rwx), 소유자파일 이름
생성/수정/접근 시간파일 내용(별도 블록)
데이터 블록 포인터
링크 카운트(하드 링크 수)

왜 파일 이름이 inode에 없는가? 파일 이름은 디렉토리에 저장된다. 디렉토리는 (파일이름 → inode번호) 매핑 테이블이다. 이 분리 덕분에 같은 inode를 여러 이름(하드 링크)으로 가리킬 수 있고, 파일을 이동할 때 inode를 건드리지 않고 디렉토리 항목만 바꾸면 된다.

Terminal window
# inode 번호 확인
ls -li /var/log/nginx/access.log
# 예상 출력:
# 1234567 -rw-r--r-- 1 root root 102400 Apr 9 10:00 /var/log/nginx/access.log
# └──────┘ ← inode 번호
# inode 상세 정보 확인
stat /var/log/nginx/access.log
# 예상 출력:
# File: /var/log/nginx/access.log
# Size: 102400
# Inode: 1234567 Links: 1
# Access: (0644/-rw-r--r--) Uid: (0/root) Gid: (0/root)
# Access: 2024-04-09 09:00:00
# Modify: 2024-04-09 10:00:00
# Change: 2024-04-09 10:00:00
# 파일시스템의 inode 사용량 확인 (inode 고갈 진단)
df -i
# 예상 출력:
# Filesystem Inodes IUsed IFree IUse% Mounted on
# /dev/xvda1 1310720 102400 1208320 8% /

📖 더 보기: Inodes and the Linux filesystem — Red Hat Blog — inode 구조, 하드 링크 vs 심볼릭 링크, inode 고갈 문제 실무 가이드 (입문)

inode 원리의 크로스 도메인 매핑 — “번호로 실체를 특정한다”는 구조는 DB와 컨테이너에서도 동일하게 나타난다:

개념Linux inodeDB 인덱스 (B-Tree)컨테이너 레이어 해시
식별자inode 번호 (정수)Primary Key / Row ID레이어 SHA256 해시
실체 위치데이터 블록 포인터Heap Page 오프셋overlay2 lowerdir 경로
이름·경로디렉토리(별도 저장)인덱스 엔트리(별도 구조)이미지 태그(별도 메타데이터)
고갈 패턴inode 소진 → 파일 생성 불가인덱스 bloat → 쿼리 저하레이어 누적 → 이미지 용량 폭증
재사용 원리하드 링크(같은 inode, 여러 이름)FK 참조(같은 Row ID, 여러 테이블)공유 레이어(같은 해시, 여러 이미지)

근거: Docker overlay2는 Linux overlayfs(커널 3.18, 2014) 위에서 동작하며, 컨테이너 수정 시 copy-on-write로 upper 레이어에 새 inode를 생성한다. 레이어를 삭제하지 않으면 inode·디스크 모두 고갈될 수 있어 docker system prune 정기 실행이 필요하다. (Linux File System Architecture — DEV Community)

실무 연결 — inode 고갈 장애: 디스크 용량(블록)이 남아있어도 inode가 모두 소진되면 새 파일을 만들 수 없다. 이 경우 df -h는 여유 공간이 있어 보이지만 df -i를 보면 IUse%가 100%다. 소용량 파일이 수백만 개 생성되는 환경(로그 파일, 임시 파일, PHP 세션 파일)에서 발생한다. Docker 컨테이너가 /tmp에 임시 파일을 무한 생성하는 패턴도 원인이 된다.

모든 것이 파일이다. 디렉토리 구조: / (루트), /home (사용자), /var/log (로그), /etc (설정), /tmp (임시).

Linux 파일 시스템은 계층 구조(트리 형태)이다. /(루트)가 최상위이고, 모든 파일과 디렉토리는 여기서 시작한다. 디바이스, 프로세스 정보(/proc), 시스템 설정도 모두 파일로 표현된다. 예를 들어 cat /proc/1/status를 실행하면 PID 1 프로세스의 상태를 파일처럼 읽을 수 있다.

꼭 알아야 할 명령어

명령어용도예시
ls파일 목록ls -la /var/log
cd디렉토리 이동cd /home/deploy
cat파일 내용 출력cat config.yml
tail파일 끝부분 보기tail -f /var/log/app.log (실시간)
grep문자열 검색grep "ERROR" app.log
find파일 찾기find / -name "*.log"
ps프로세스 목록ps aux
top/htop리소스 사용량htop
df디스크 사용량df -h
curlHTTP 요청curl -v http://localhost:3000
chmod파일 권한chmod 755 script.sh
ssh원격 접속ssh user@server-ip

프로세스가 내부적으로 어떻게 동작하는가

비유: 프로세스는 “실행 중인 프로그램의 인스턴스”이다. 같은 프로그램을 두 번 실행하면 두 개의 프로세스가 생긴다.

각 프로세스는 Linux 커널로부터 고유한 PID(Process ID)를 부여받고, 독립된 메모리 공간을 가진다. ps 명령어는 /proc 가상 파일시스템에서 정보를 읽어온다.

Terminal window
# 모든 프로세스 확인
ps aux
# 예상 출력:
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# root 1 0.0 0.1 168936 13040 ? Ss 09:00 0:05 /sbin/init
# deploy 1234 0.3 2.1 812340 87432 ? Sl 09:01 1:23 node dist/main.js
# root 5678 0.0 0.0 14428 1012 pts/0 R+ 10:30 0:00 ps aux

📖 더 보기: Linux Process Management - DigitalOcean — 프로세스 생성부터 종료까지 생명주기와 관리 명령어 설명 (입문)

파이프(|)와 리다이렉션(>)

명령어의 출력을 다른 명령어의 입력으로 연결. 강력한 조합 도구.

Terminal window
# 에러 로그 줄 수 세기
cat app.log | grep "ERROR" | wc -l
# 예상 출력: 42
# 파일에 쓰기
echo "hello" > file.txt
# 실시간 로그에서 에러만 필터링
tail -f /var/log/app.log | grep "ERROR"
# 예상 출력 (실시간 스트리밍):
# 2024-01-15T10:30:01 ERROR [AppController] Database connection failed
# 2024-01-15T10:30:05 ERROR [AuthService] Token expired for user 123

로그 분석 실전 조합 (grep + awk + tail)

Terminal window
# 특정 시간대의 에러만 추출
grep "ERROR" app.log | grep "2024-01-15 10:"
# IP별 요청 횟수 집계 (awk 활용)
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
# 예상 출력:
# 1523 203.0.113.42
# 892 198.51.100.7
# 234 192.168.1.100
# NestJS 앱 로그에서 에러만 뽑아 최신 50줄 확인
tail -n 1000 app.log | grep "ERROR" | tail -50

📖 더 보기: Log Like a Pro: Mastering grep, awk, and sed — 실제 서버 로그 분석에 사용하는 grep/awk/sed 조합 예시 (중급)

프로세스와 포트

Terminal window
# node 프로세스 찾기
ps aux | grep node
# 예상 출력:
# deploy 1234 0.3 2.1 812340 87432 ? Sl 09:01 1:23 node dist/main.js
# 3000번 포트를 점유 중인 프로세스 확인 (포트 충돌 디버깅)
lsof -i :3000
# 예상 출력:
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# node 1234 deploy 23u IPv4 12345 0t0 TCP *:3000 (LISTEN)
# ss (Socket Statistics) — 현대 Linux의 표준 포트/소켓 확인 도구 (netstat 대체)
ss -tlnp
# 예상 출력:
# State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
# LISTEN 0 511 0.0.0.0:3000 0.0.0.0:* users:(("node",pid=1234,fd=23))
# LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=678,fd=3))
# 옵션: -t(TCP), -l(LISTEN 중), -n(숫자 주소), -p(프로세스 이름)
# 모든 TCP 연결 상태 확인 (ESTABLISHED, TIME_WAIT 등)
ss -tn | grep ESTABLISHED | wc -l
# 예상 출력: 42 ← 현재 활성 연결 42개
# 프로세스 강제 종료
kill -9 1234

SIGTERM vs SIGKILL — 언제 어떤 시그널을 쓰는가

kill -9(SIGKILL)는 커널이 프로세스를 즉시 제거하며 프로세스가 핸들러를 등록할 수 없다. 반면 kill(SIGTERM, 기본값)은 프로세스에게 “정리하고 종료하라”고 요청하는 신호다.

상황권장 시그널이유
서비스 재시작 / 정상 배포SIGTERM진행 중 요청 완료, DB 커넥션 반납, 파일 플러시 후 종료
graceful shutdown 대기 중 (10~30초 타임아웃)SIGTERM → 대기 → SIGKILL업계 표준 에스컬레이션 패턴
프로세스가 응답 없음 (deadlock, 무한루프)SIGKILLSIGTERM 무시되는 경우에만 최후 수단
포트 충돌로 새 서버 기동 불가SIGKILL빠른 해제 필요. 단, 데이터 손상 위험 인지 후 사용
Terminal window
# 권장 절차: SIGTERM 먼저, 안 죽으면 SIGKILL
kill 1234 # SIGTERM (기본값, 핸들러 실행 기회 부여)
sleep 15 # graceful shutdown 대기
kill -0 1234 2>/dev/null && kill -9 1234 # 살아있으면 강제 종료
# 프로세스가 시그널을 무시하는지 진단
strace -e trace=signal -p 1234 # 수신 시그널 추적
cat /proc/1234/status | grep State # D(Uninterruptible Sleep) 이면 SIGKILL도 효과 없음

SIGKILL 오남용 시 진행 중인 파일 쓰기 손상, 고아 자식 프로세스 발생, DB 트랜잭션 롤백 미완료 등의 부작용이 생긴다. systemd는 TimeoutStopSec(기본 90초) 초과 후 자동으로 SIGKILL을 보내는 에스컬레이션을 내장하고 있다. (SIGKILL vs SIGTERM — SUSE Communities)

환경변수

서버/컨테이너에서 설정값(DB 주소, API Key 등)은 환경변수로 주입된다. 코드에 하드코딩하지 않고 환경변수로 분리하는 것이 기본 원칙이다.

Terminal window
# 모든 환경변수 출력
printenv
# 특정 변수 확인 (NestJS의 DATABASE_URL 확인)
echo $DATABASE_URL
# 예상 출력: postgresql://user:pass@mydb.xxxx.ap-northeast-2.rds.amazonaws.com:5432/mydb
# 현재 셸에서 변수 설정
export PORT=3000

권한 (rwx)

-rwxr-xr-- = 소유자(rwx) / 그룹(r-x) / 기타(r—). 숫자로는 754.

Terminal window
# 파일 권한 확인
ls -la script.sh
# 예상 출력:
# -rw-r--r-- 1 deploy deploy 1234 Jan 15 10:00 script.sh
# (소유자: 읽기/쓰기, 그룹: 읽기, 기타: 읽기)
# 실행 권한 부여
chmod +x script.sh
ls -la script.sh
# 예상 출력:
# -rwxr-xr-x 1 deploy deploy 1234 Jan 15 10:00 script.sh

chmod 777 오남용 리스크 — 최소 권한 원칙 적용 기준

chmod 777은 “모든 사용자에게 읽기·쓰기·실행 권한 부여”로, 빠른 해결처럼 보이지만 보안 사고의 주요 원인이다.

시나리오위험올바른 대안
웹서버 업로드 디렉토리를 777로 설정공격자가 PHP 웹셸 업로드 후 서버 탈취chmod 755 + 소유자만 쓰기 허용
설정 파일(.env, credentials)을 777로 설정DB 비밀번호·API Key 노출chmod 600 (소유자 읽기·쓰기만)
배포 스크립트를 777로 설정임의 사용자가 스크립트 수정·실행 가능chmod 750 (그룹 읽기·실행만)
실제 사고 (2020, 금융기관)777 디렉토리로 cryptojacking 스크립트 주입, 시스템 자원 탈취권한 감사 자동화 필요
Terminal window
# 현재 777 파일 탐색 (보안 감사)
find /var/www -perm 777 -type f
# 안전한 권한 설정 기준
chmod 644 config.yml # 파일: 소유자 읽기/쓰기, 나머지 읽기만
chmod 600 .env # 민감 파일: 소유자만 읽기/쓰기
chmod 755 deploy.sh # 실행 스크립트: 소유자 전체, 나머지 읽기/실행
chmod 750 /app/scripts # 디렉토리: 그룹 접근, 외부 차단

최소 권한 원칙(Principle of Least Privilege): 필요한 최소한의 권한만 부여한다. chmod 777은 문제의 근본 원인(잘못된 소유자, 그룹 설정)을 감추고 Linux 권한 체계 전체를 무력화한다. (Chmod 777 Is Not a Fix — xygeni.io)

systemd와 journalctl — 서비스 관리의 표준

비유: systemd는 “서버의 관리자”이다. 시스템이 부팅될 때 어떤 서비스를 어떤 순서로 시작할지를 결정하고, 서비스가 죽으면 자동으로 재시작한다.

AWS EC2에 직접 NestJS를 배포하거나 시스템 데몬(nginx, 크론 등)을 다룰 때 systemd 명령어가 필요하다. ECS/Docker 환경에서는 systemd 대신 컨테이너 수명 관리가 이를 대체하지만, EC2에 직접 배포된 서비스나 서버 수준 디버깅 시 반드시 마주친다.

Terminal window
# 서비스 상태 확인
systemctl status nginx
# 예상 출력:
# ● nginx.service - A high performance web server
# Loaded: loaded (/lib/systemd/system/nginx.service; enabled)
# Active: active (running) since Mon 2024-01-15 09:00:00 UTC; 2h 30min ago
# Main PID: 1234 (nginx)
# 서비스 시작/중지/재시작
systemctl start nginx
systemctl stop nginx
systemctl restart nginx
# 부팅 시 자동 시작 등록
systemctl enable my-nestapp
# 최근 로그 50줄 보기
journalctl -u nginx -n 50
# 예상 출력:
# Jan 15 09:00:01 server nginx[1234]: nginx: the configuration file /etc/nginx/nginx.conf test is successful
# 실시간 로그 스트리밍 (tail -f 와 동일)
journalctl -u my-nestapp -f
# 특정 시간 이후 에러만 필터링
journalctl -u my-nestapp --since "2024-01-15 10:00" -p err
# 예상 출력:
# Jan 15 10:30:01 server node[1234]: Error: connect ECONNREFUSED 127.0.0.1:5432

📖 더 보기: How To Use journalctl - DigitalOcean — journalctl 필터링, 시간 범위 지정, 우선순위별 로그 조회 완전 가이드 (입문)

보안 하드닝 — 서버를 안전하게 운영하는 기본 원칙 (2025)

프로덕션 EC2 서버를 처음 만들었다면 반드시 아래 설정을 점검한다. 이 체크리스트는 CIS Benchmark 기반이다.

  1. SSH Root 로그인 비활성화

    Terminal window
    # /etc/ssh/sshd_​config 수정
    sudo sed -​i 's/PermitRootLogin​ yes/PermitRootLogin no/' /etc/ssh/sshd_​config
    sudo systemctl restart sshd
    # → 해커가 root 계정을 직접 노릴 수 없게 됨
  2. SSH 키 기반 인증만 허용 (비밀번호 로그인 차단)

    Terminal window
    # /etc/ssh/sshd_​config
    Password​Authentication no
    Pubkey​Authentication yes
  3. 불필요한 서비스 비활성화

    Terminal window
    # 실행 중인 서비스 목록 확인
    systemctl list-units --type=service --state=running
    # 필요 없는 서비스 비활성화 (예: telnet, ftp)
    sudo systemctl disable telnet
    sudo systemctl stop telnet
  4. Fail2Ban — 브루트포스 공격 차단

    Fail2Ban은 로그 파일을 모니터링하여, 일정 횟수 이상 로그인 실패 시 해당 IP를 자동으로 차단한다.

    Terminal window
    sudo apt install fail2ban
    sudo systemctl enable fail2ban
    # 기본 설정: 5회 실패 시 10분 차단
  5. 최소 권한 사용자로 서비스 실행

    NestJS를 root로 실행하지 않는다. 전용 deploy 유저를 만들고 해당 유저로 서비스를 실행한다. root로 실행된 서비스가 해킹당하면 서버 전체가 위험해진다.

    Terminal window
    # 서비스 전용 유저 생성 (로그인 쉘 없음, 홈 없음)
    sudo use​radd --no-create-home --shell /usr/sbin/nologin deploy
    # systemd Unit 파일에서 User=deploy 설정
  • 서버 로그 확인 (tail -f, grep)
  • 서버 상태 점검 (CPU, 메모리, 디스크)
  • 배포 스크립트 실행
  • SSH로 원격 서버 접속
  • Docker 컨테이너 안에서 디버깅
  • 서비스 장애 시 서버에 접속해서 로그 확인
  • 배포 관련 스크립트 읽기/수정
  • Docker 컨테이너 안에 들어가서 상태 확인
  • CI/CD 파이프라인의 셸 스크립트 이해
개념 A개념 B차이점
>>>>는 덮어쓰기, >>는 이어쓰기
killkill -9kill은 정상 종료 요청, kill -9는 강제 종료
절대경로상대경로/var/log는 절대, ./log는 현재 위치 기준 상대
sudosusudo는 한 명령만 관리자 권한 실행, su는 사용자 전환
tail -fjournalctl -ftail -f는 파일 직접 스트리밍, journalctl -f는 systemd 서비스 로그 스트리밍

🔧 “Permission denied” — 파일/스크립트 실행 불가

섹션 제목: “🔧 “Permission denied” — 파일/스크립트 실행 불가”

증상: bash: ./deploy.sh: Permission denied 또는 cat: /etc/nginx/nginx.conf: Permission denied

원인: 파일에 실행 권한(x)이 없거나, 현재 사용자가 해당 파일의 읽기/실행 권한이 없음

해결:

Terminal window
# 현재 파일 권한 확인
ls -la deploy.sh
# 출력: -rw-r--r-- 1 deploy deploy 512 Jan 15 10:00 deploy.sh (실행 권한 없음)
# 실행 권한 추가
chmod +x deploy.sh
# 소유자 문제일 경우
sudo chown deploy:deploy deploy.sh

🔧 “No space left on device” — 디스크 꽉 참

섹션 제목: “🔧 “No space left on device” — 디스크 꽉 참”

증상: 파일 쓰기/로그 기록 실패, No space left on device 에러. Docker 빌드/컨테이너 기동 실패

원인: 디스크 용량 소진. 서버에서 오래된 로그가 쌓이거나, Docker 이미지/레이어가 누적되는 경우 자주 발생

해결:

Terminal window
# 디스크 사용량 확인
df -h
# 예상 출력:
# Filesystem Size Used Avail Use% Mounted on
# /dev/xvda1 20G 19G 100M 100% / ← 100%!
# 어느 디렉토리가 많이 차지하는지 확인 (상위 5개)
du -sh /* 2>/dev/null | sort -rh | head -5
# Docker 미사용 이미지/컨테이너 정리
docker system prune -f
# 오래된 로그 파일 삭제
find /var/log -name "*.log" -mtime +30 -delete

🔧 포트가 이미 사용 중 — 서버 기동 실패

섹션 제목: “🔧 포트가 이미 사용 중 — 서버 기동 실패”

증상: Error: listen EADDRINUSE: address already in use :::3000 — NestJS 또는 Node.js 서버 시작 시 발생

원인: 이전 프로세스가 종료되지 않고 해당 포트를 계속 점유하고 있음

해결:

Terminal window
# 3000번 포트 사용 프로세스 확인
lsof -i :3000
# 출력: node 1234 deploy 23u IPv4 12345 0t0 TCP *:3000 (LISTEN)
# 해당 PID 종료
kill -9 1234
# 또는 한 줄로
kill -9 $(lsof -ti :3000)

🔧 systemd 서비스가 시작되지 않는 경우

섹션 제목: “🔧 systemd 서비스가 시작되지 않는 경우”

증상: systemctl start my-nestapp 실행 후 Active: failed (Result: exit-code) 상태

원인: 환경변수 누락, 실행 파일 경로 오류, 포트 충돌, 코드 에러 등 다양한 이유로 프로세스가 시작 직후 종료됨

해결:

Terminal window
# 서비스 상태와 최근 에러 확인
systemctl status my-nestapp
# 출력에서 에러 메시지 확인:
# Jan 15 10:30:01 server node[5678]: Error: Cannot find module 'dist/main.js'
# 더 자세한 로그 확인
journalctl -u my-nestapp -n 100 --no-pager
# 서비스 유닛 파일 확인 (환경변수, 실행 경로 등)
cat /etc/systemd/system/my-nestapp.service
# [Service]
# ExecStart=/usr/bin/node /home/deploy/dist/main.js
# EnvironmentFile=/home/deploy/.env ← 이 파일이 실제로 존재하는지 확인
# 유닛 파일 수정 후 반드시 데몬 리로드
systemctl daemon-reload
systemctl restart my-nestapp

🔧 EC2 SSH 접속 불가 — “Connection timed out” 또는 “Permission denied (publickey)”

섹션 제목: “🔧 EC2 SSH 접속 불가 — “Connection timed out” 또는 “Permission denied (publickey)””

증상: ssh: connect to host 1.2.3.4 port 22: Connection timed out 또는 Permission denied (publickey,gssapi-keyex,gssapi-with-mic)

원인별 구분:

에러 메시지원인해결
Connection timed outSecurity Group에 22번 포트 허용 규칙 없음EC2 → Security Groups → 인바운드 규칙에 내 IP/22 추가
Connection timed out (SG 확인 후에도)EC2가 Private Subnet에 있어 직접 접근 불가Bastion Host 또는 AWS Session Manager(SSM) 사용
Permission denied (publickey)잘못된 키 파일 또는 키 파일 권한 문제chmod 400 my-key.pem 후 재시도
Host key verification failedknown_hosts에 이전 IP 정보가 남아 있음ssh-keygen -R <서버IP> 실행 후 재접속
Terminal window
# SSH 키 권한 수정 후 접속
chmod 400 my-key.pem
ssh -i my-key.pem ec2-user@1.2.3.4
# AWS Session Manager로 SSH 없이 접속 (보안 강화 권고 방식)
aws ssm start-session --target i-xxxxxxxxxxxxxxxxx
# → 22번 포트/Security Group 불필요, IAM 권한만으로 접속

AWS 보안 권고: 22번 포트 개방 대신 **Session Manager(SSM)**을 사용하면 Security Group에 22번 포트를 열 필요가 없어 공격 면을 줄일 수 있다.

  • SSH로 서버에 접속할 수 있다
  • tail -f로 실시간 로그를 볼 수 있다
  • grep으로 로그에서 특정 에러를 검색할 수 있다
  • ps, top으로 프로세스/리소스 상태를 확인할 수 있다
  • 파이프(|)를 써서 명령어를 조합할 수 있다
  • printenv로 환경변수를 확인하고, 서비스 설정값이 어떻게 주입되는지 설명할 수 있다
  • journalctl -u <서비스명> -f로 systemd 서비스 로그를 실시간으로 볼 수 있다

bash scripting, cron job, systemd, journalctl, scp/rsync, tmux/screen, awk/sed, 환경변수

  • 로컬 터미널에서 tail -f + grep 조합으로 로그 필터링 연습

    Terminal window
    # Docker 컨테이너 로그 실시간 필터링
    docker logs -f <컨테이너명> | grep "ERROR"
  • ps aux | grep 으로 실행 중인 프로세스 찾아보기

    Terminal window
    ps aux | grep node
    # 예상 출력: deploy 1234 0.3 2.1 812340 87432 ? Sl 09:01 1:23 node dist/main.js
  • Docker 컨테이너 안에 docker exec -it <id> sh로 들어가서 명령어 연습

  • df -h, free -m으로 서버 리소스 확인해보기

    Terminal window
    df -h
    # 예상 출력:
    # Filesystem Size Used Avail Use% Mounted on
    # /dev/xvda1 20G 8.5G 11G 44% /
    free -m
    # 예상 출력:
    # total used free shared buff/cache available
    # Mem: 3938 2341 234 123 1362 1234
  • EC2에 배포된 서비스가 있다면 journalctl -u <서비스명> -n 50으로 최근 로그 확인

  • ls -listat으로 파일의 inode 번호와 메타데이터 확인

    Terminal window
    ls -li /etc/hostname
    # 예상 출력:
    # 131074 -rw-r--r-- 1 root root 12 Jan 15 09:00 /etc/hostname
    # └────┘ ← inode 번호
    stat /etc/hostname
    # 예상 출력:
    # File: /etc/hostname
    # Size: 12
    # Inode: 131074 Links: 1
    # Access: (0644/-rw-r--r--) Uid: (0/root)
    # inode 사용량 확인 (고갈 여부 진단)
    df -i
    # 예상 출력:
    # Filesystem Inodes IUsed IFree IUse% Mounted on
    # /dev/xvda1 1310720 45231 1265489 4% /
  1. 서버는 Linux이고, CLI가 그 언어이다 — tail, grep, ps, curl 4가지만 알아도 장애 대응의 80%가 해결된다
  2. 파이프(|)로 명령어를 연결하면 로그 필터링·집계·분석을 단 한 줄로 처리할 수 있다
  3. 파일 권한(rwx)과 프로세스 관리는 서버 운영의 기본이며, 대부분의 “왜 안 되지?” 문제는 여기서 시작된다
  4. systemd + journalctl이 프로덕션 서비스 관리의 표준이며, Docker/ECS 환경에서는 컨테이너 런타임이 이를 대체한다
  5. 컨테이너 안도 Linux다 — Linux를 알면 Docker·ECS Exec·K8s exec 모든 환경에서 디버깅이 가능하다

실무 아키텍처 원칙 (2025): EC2에 직접 SSH 접속하는 방식보다 **AWS Systems Manager Session Manager(SSM)**을 사용하는 것이 현재 AWS 권고 표준이다. 22번 포트를 열 필요 없이 IAM 권한만으로 브라우저/CLI에서 서버에 접속할 수 있어 공격 표면을 줄일 수 있다. 불변 인프라(Immutable Infrastructure) 패턴이 확산되면서 “서버에 SSH로 들어가서 수동 수정”하는 방식은 점점 지양되고 있으며, 설정 변경은 코드로 관리(IaC)하고 서버는 교체하는 방식이 표준이 되고 있다.