Linux 완벽 가이드: 파일 시스템부터 프로세스 관리, 서버 운영까지

Linux 완벽 가이드: 파일 시스템부터 프로세스 관리, 서버 운영까지

“리눅스 서버에서 배포해본 경험이 있으신가요?” — 백엔드 면접에서 빠지지 않는 질문이다. Docker를 쓰더라도 컨테이너 내부는 결국 Linux이고, 장애 상황에서 로그를 추적하고 프로세스를 관리하는 건 리눅스 명령어 없이는 불가능하다. 이 글은 파일 시스템 구조부터 프로세스·네트워크·서버 운영까지 — 백엔드 개발자가 반드시 알아야 할 Linux 지식을 다룬다.


1. Linux 파일 시스템 구조

1.1 디렉토리 계층 구조 (FHS)

Linux는 Filesystem Hierarchy Standard(FHS) 에 따라 디렉토리를 구성한다. 모든 것이 / (루트)에서 시작하는 트리 구조다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/
├── bin/       → 기본 실행 파일 (ls, cp, mv, cat, grep 등)
├── sbin/      → 시스템 관리 명령어 (iptables, fdisk, reboot)
├── etc/       → 시스템 설정 파일 (nginx.conf, crontab, passwd)
├── home/      → 일반 사용자 홈 디렉토리 (/home/deploy)
├── root/      → root 사용자 홈 디렉토리
├── var/       → 가변 데이터 (로그, 캐시, 메일)
│   ├── log/   → 시스템·애플리케이션 로그
│   ├── run/   → 실행 중인 프로세스 PID 파일
│   └── tmp/   → 임시 파일 (재부팅 시 삭제)
├── tmp/       → 임시 파일 (모든 사용자 접근 가능)
├── usr/       → 사용자 프로그램·라이브러리
│   ├── bin/   → 일반 사용자 명령어
│   ├── lib/   → 라이브러리
│   └── local/ → 수동 설치 프로그램 (/usr/local/bin)
├── opt/       → 서드파티 소프트웨어 (/opt/java, /opt/tomcat)
├── dev/       → 디바이스 파일 (/dev/sda, /dev/null)
├── proc/      → 가상 파일 시스템 (프로세스·커널 정보)
├── sys/       → 가상 파일 시스템 (하드웨어·커널 모듈 정보)
└── mnt/       → 임시 마운트 포인트

1.2 주요 디렉토리 실무 활용

디렉토리 실무에서 자주 보는 파일 용도
/etc/nginx/ nginx.conf, sites-available/ Nginx 설정
/etc/systemd/system/ myapp.service 서비스 등록
/var/log/ syslog, nginx/access.log 로그 확인
/opt/ /opt/jdk-17/, /opt/tomcat/ JDK, Tomcat 설치 경로
/proc/ /proc/cpuinfo, /proc/meminfo CPU, 메모리 정보 확인
/home/deploy/ 애플리케이션 배포 경로 배포 디렉토리

1.3 “Everything is a File” 철학

Linux에서는 모든 것이 파일이다. 일반 파일, 디렉토리, 디바이스, 소켓, 파이프까지 모두 파일로 추상화된다.

1
2
3
4
5
6
7
8
9
10
11
12
# 디바이스도 파일
ls -l /dev/sda          # 블록 디바이스 (디스크)
ls -l /dev/null         # 특수 파일 (블랙홀 - 입력을 버림)
echo "test" > /dev/null # 출력 버리기

# 프로세스 정보도 파일
cat /proc/1/status      # PID 1 (init/systemd) 프로세스 정보
cat /proc/cpuinfo       # CPU 정보
cat /proc/meminfo       # 메모리 정보

# 네트워크 소켓도 파일
ls -l /proc/$(pgrep java)/fd  # Java 프로세스의 파일 디스크립터 목록

2. 파일 권한과 소유권

2.1 권한 구조

Linux 파일 권한은 소유자(Owner) - 그룹(Group) - 기타(Others) 3단계로 구분하며, 각각 읽기(r) - 쓰기(w) - 실행(x) 3가지 권한을 갖는다.

1
2
3
4
5
6
7
8
9
10
11
ls -l myapp.jar

-rwxr-xr-- 1 deploy deploy 52428800 Mar 19 10:00 myapp.jar
│└┬┘└┬┘└┬┘   └──┬─┘ └──┬─┘
│ │   │   │      │      │
│ │   │   │      │      └── 그룹 (deploy)
│ │   │   │      └── 소유자 (deploy)
│ │   │   └── Others: r-- (읽기만)
│ │   └── Group:  r-x (읽기+실행)
│ └── Owner: rwx (읽기+쓰기+실행)
└── 파일 타입 (- 일반파일, d 디렉토리, l 심볼릭링크)

2.2 권한 변경 (chmod)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 숫자 모드 (가장 많이 사용)
# r=4, w=2, x=1 → 조합으로 표현
chmod 755 deploy.sh   # rwxr-xr-x (소유자 모든 권한, 나머지 읽기+실행)
chmod 644 config.yml  # rw-r--r-- (소유자 읽기+쓰기, 나머지 읽기만)
chmod 600 id_rsa      # rw------- (소유자만 읽기+쓰기, SSH 키에 필수)
chmod 700 .ssh/       # rwx------ (소유자만 접근, SSH 디렉토리에 필수)

# 기호 모드
chmod +x deploy.sh    # 모든 사용자에게 실행 권한 추가
chmod u+w config.yml  # 소유자에게 쓰기 권한 추가
chmod go-w config.yml # 그룹·기타에서 쓰기 권한 제거

# 재귀적 변경 (디렉토리 전체)
chmod -R 755 /opt/myapp/

2.3 소유권 변경 (chown)

1
2
3
4
5
6
7
8
# 소유자 변경
chown deploy myapp.jar

# 소유자 + 그룹 변경
chown deploy:deploy myapp.jar

# 재귀적 변경 (디렉토리 전체)
chown -R deploy:deploy /opt/myapp/

2.4 특수 권한

1
2
3
4
5
6
7
8
9
10
11
12
# SetUID (4): 실행 시 파일 소유자 권한으로 실행
chmod 4755 /usr/bin/passwd   # 일반 사용자도 비밀번호 변경 가능
ls -l /usr/bin/passwd
# -rwsr-xr-x  → s가 SetUID 표시

# SetGID (2): 실행 시 파일 그룹 권한으로 실행 / 디렉토리에 설정하면 하위 파일 그룹 자동 상속
chmod 2755 /shared/project/

# Sticky Bit (1): 디렉토리 내 파일을 소유자만 삭제 가능
chmod 1777 /tmp/  # 누구나 파일 생성 가능, 자기 파일만 삭제 가능
ls -ld /tmp/
# drwxrwxrwt  → t가 Sticky Bit 표시

2.5 실무 권한 설정 예시

1
2
3
4
5
6
7
# Spring Boot 배포 서버 일반적인 권한 설정
chown -R deploy:deploy /opt/myapp/
chmod 755 /opt/myapp/
chmod 644 /opt/myapp/application.yml     # 설정 파일: 읽기 전용
chmod 755 /opt/myapp/deploy.sh           # 배포 스크립트: 실행 가능
chmod 600 /opt/myapp/application-prod.yml # 운영 설정(DB 비밀번호 포함): 소유자만
chmod 600 ~/.ssh/id_rsa                  # SSH 키: 소유자만 (필수!)

3. 필수 명령어

3.1 파일·디렉토리 조작

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 탐색
ls -la            # 숨김 파일 포함 상세 목록
ls -lh            # 사람이 읽기 쉬운 파일 크기 (KB, MB)
ls -lt            # 수정 시간 순 정렬
tree -L 2         # 디렉토리 트리 (2단계까지)

# 이동·복사·삭제
cp -r src/ backup/ # 디렉토리 재귀 복사
mv old.txt new.txt # 이름 변경 / 이동
rm -rf build/      # 디렉토리 강제 삭제 (주의!)
mkdir -p a/b/c     # 중간 디렉토리 포함 생성

# 파일 찾기
find / -name "*.log" -mtime +7        # 7일 이상 된 로그 파일 찾기
find /opt -name "*.jar" -size +100M   # 100MB 넘는 JAR 파일 찾기
find . -type f -name "*.tmp" -delete  # tmp 파일 찾아서 삭제
which java                            # 명령어 실행 경로 확인
whereis nginx                         # 바이너리·소스·매뉴얼 위치

3.2 파일 내용 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 파일 보기
cat application.yml            # 전체 내용 출력
head -n 20 access.log          # 처음 20줄
tail -n 50 error.log           # 마지막 50줄
tail -f /var/log/myapp.log     # 실시간 로그 추적 (★ 매우 중요)
tail -f myapp.log | grep ERROR # 실시간으로 ERROR만 추적

# less: 대용량 파일 탐색 (vi 키바인딩)
less /var/log/syslog
# /keyword → 검색, n → 다음, N → 이전, q → 종료

# 파일 비교
diff file1.txt file2.txt       # 두 파일 차이 비교
diff -u old.yml new.yml        # unified 형식 (Git diff와 유사)

3.3 텍스트 처리 3대장: grep, awk, sed

grep — 패턴 검색

1
2
3
4
5
6
7
8
9
10
11
# 기본 검색
grep "ERROR" /var/log/myapp.log           # ERROR 포함 줄
grep -i "exception" myapp.log             # 대소문자 무시
grep -r "TODO" /opt/myapp/src/            # 디렉토리 재귀 검색
grep -n "NullPointer" myapp.log           # 줄 번호 표시
grep -c "ERROR" myapp.log                 # ERROR 발생 횟수
grep -v "DEBUG" myapp.log                 # DEBUG 제외 (반전)
grep -A 5 "Exception" myapp.log           # 매칭 줄 + 뒤 5줄 (After)
grep -B 3 "Exception" myapp.log           # 매칭 줄 + 앞 3줄 (Before)
grep -E "ERROR|WARN" myapp.log            # 정규식 (ERROR 또는 WARN)
grep -P "\d{4}-\d{2}-\d{2}" myapp.log    # Perl 정규식 (날짜 패턴)

awk — 컬럼 기반 텍스트 처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 기본 사용: 공백 기준으로 컬럼 분리
awk '{print $1}' access.log               # 첫 번째 컬럼 (IP 주소)
awk '{print $1, $7, $9}' access.log       # IP, URL, 상태코드

# 구분자 지정
awk -F: '{print $1, $3}' /etc/passwd      # : 구분자로 사용자명, UID 출력

# 조건 필터
awk '$9 == 500' access.log                # HTTP 500 에러만
awk '$9 >= 400' access.log                # 4xx, 5xx 에러 모두
awk '$10 > 1000000' access.log            # 응답 크기 1MB 초과

# 집계
awk '{sum += $10} END {print sum}' access.log          # 총 전송량
awk '{count[$1]++} END {for(ip in count) print ip, count[ip]}' access.log  # IP별 요청 수

sed — 텍스트 치환·편집

1
2
3
4
5
6
7
8
9
10
# 문자열 치환
sed 's/8080/9090/g' application.yml          # 8080 → 9090 전체 치환 (출력만)
sed -i 's/8080/9090/g' application.yml       # 파일 직접 수정 (-i: in-place)
sed -i 's/DEBUG/INFO/g' logback.xml          # 로그 레벨 변경

# 특정 줄 처리
sed -n '10,20p' myapp.log                    # 10~20줄만 출력
sed '5d' config.txt                          # 5번째 줄 삭제
sed '/^#/d' config.yml                       # 주석(#으로 시작) 줄 삭제
sed '/^$/d' config.yml                       # 빈 줄 삭제

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

파이프와 리다이렉션은 Linux 명령어의 핵심이다. 단순한 명령어를 조합해서 강력한 처리를 만든다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌────────────────────────────────────────────────────────────────────┐
│                  파이프와 리다이렉션 개념도                          │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│  ┌──────┐    stdout    ┌──────┐    stdout    ┌──────┐             │
│  │ cmd1 │ ──── | ────→ │ cmd2 │ ──── | ────→ │ cmd3 │             │
│  └──────┘   (파이프)   └──────┘   (파이프)   └──────┘             │
│                                                    │               │
│                                          > file (덮어쓰기)        │
│                                          >> file (추가)           │
│                                          2> error.log (에러만)    │
│                                          &> all.log (전체)        │
│                                                                    │
│  stdin 리다이렉션: cmd < file                                      │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 파이프 활용 예시
cat access.log | grep "POST /api" | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
# → POST /api 요청한 IP를 빈도순으로 상위 10개 출력

# 리다이렉션
echo "Hello" > output.txt        # 덮어쓰기
echo "World" >> output.txt       # 추가(append)
java -jar myapp.jar > app.log 2>&1   # stdout + stderr를 같은 파일로
java -jar myapp.jar > /dev/null 2>&1 # 모든 출력 버리기
java -jar myapp.jar 2> error.log     # 에러만 별도 파일로

# nohup + 리다이렉션 (백그라운드 실행)
nohup java -jar myapp.jar > app.log 2>&1 &
# nohup: 터미널 종료해도 계속 실행
# &: 백그라운드 실행

3.5 실무 조합 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 오늘 날짜의 ERROR 로그만 추출
grep "$(date +%Y-%m-%d)" myapp.log | grep "ERROR" > today_errors.log

# 2. API 응답 시간이 3초 넘는 요청 찾기 (로그 형식에 따라 조정)
grep "took" myapp.log | awk '$NF > 3000 {print $0}'

# 3. Nginx access.log에서 HTTP 500 에러 발생 IP Top 10
awk '$9 == 500 {print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10

# 4. 특정 시간대 로그 추출
sed -n '/2026-03-19 14:00/,/2026-03-19 15:00/p' myapp.log

# 5. 디스크 용량을 많이 차지하는 로그 파일 찾기
find /var/log -name "*.log" -size +100M -exec ls -lh {} \;

# 6. 여러 서버 로그에서 특정 트랜잭션 ID 추적
grep -r "txId=abc123" /var/log/myapp/

4. 프로세스 관리

4.1 프로세스 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ps: 프로세스 스냅샷
ps aux                          # 전체 프로세스 목록
ps aux | grep java              # Java 프로세스 찾기
ps -ef | grep nginx             # Nginx 프로세스 찾기
ps -p 1234 -o pid,ppid,%cpu,%mem,cmd  # 특정 PID 상세 정보

# top: 실시간 시스템 모니터링
top
# P → CPU 사용률 정렬
# M → 메모리 사용률 정렬
# k → PID 입력하여 프로세스 종료
# q → 종료

# htop: top의 개선판 (컬러, 스크롤, 트리뷰)
htop
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌────────────────────────────────────────────────────────────────────┐
│  top - 14:30:00 up 45 days, load average: 0.85, 0.92, 0.88       │
│  Tasks: 234 total, 1 running, 233 sleeping                       │
│  %Cpu(s): 12.3 us, 3.2 sy, 0.0 ni, 83.5 id, 0.8 wa             │
│  MiB Mem:  7854.3 total,  1234.5 free,  4567.8 used,  2052.0 cache│
│  MiB Swap:  2048.0 total,  2048.0 free,     0.0 used              │
├────────────────────────────────────────────────────────────────────┤
│    PID USER     PR  NI   VIRT    RES    SHR S %CPU  %MEM  COMMAND │
│   1234 deploy   20   0  4.2g   1.8g  23456 S 15.3  23.4  java    │
│   5678 www-data 20   0  125m    45m   8901 S  3.2   0.6  nginx   │
│   9012 mysql    20   0  1.2g   890m  12345 S  2.1  11.3  mysqld  │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│  load average: 1분 / 5분 / 15분 평균 부하                          │
│    → CPU 코어 수 이하면 정상, 초과하면 과부하                       │
│                                                                    │
│  us: 사용자 영역 CPU    sy: 커널 영역 CPU                          │
│  wa: I/O 대기          id: 유휴(idle)                              │
│    → wa가 높으면 디스크 병목, us가 높으면 애플리케이션 부하          │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

4.2 프로세스 종료 (kill)

1
2
3
4
5
6
7
8
9
10
11
# 시그널 종류
kill -l                      # 전체 시그널 목록

# 주요 시그널
kill PID                     # SIGTERM (15) - 정상 종료 요청 (기본값)
kill -9 PID                  # SIGKILL (9) - 강제 종료 (최후의 수단)
kill -HUP PID                # SIGHUP (1) - 설정 리로드 (Nginx 등)

# 프로세스 이름으로 종료
pkill -f "myapp.jar"         # 이름으로 종료
killall nginx                # 같은 이름 프로세스 전체 종료
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌────────────────────────────────────────────────────────────────────┐
│                     프로세스 종료 시그널                            │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│  SIGTERM (15) ─── "정상적으로 종료해주세요"                        │
│  │                 프로세스가 cleanup 작업 수행 가능                │
│  │                 Spring: shutdown hook 실행                      │
│  │                 → 먼저 시도해야 하는 시그널                      │
│  │                                                                 │
│  │  (프로세스가 응답하지 않으면)                                    │
│  ▼                                                                 │
│  SIGKILL (9) ──── "즉시 종료 (강제)"                               │
│                    cleanup 없이 즉시 종료                          │
│                    커널이 직접 프로세스 제거                        │
│                    → 최후의 수단으로만 사용                         │
│                                                                    │
│  SIGHUP (1) ───── "설정 다시 읽어주세요"                           │
│                    프로세스 재시작 없이 설정 리로드                 │
│                    Nginx: kill -HUP $(cat /run/nginx.pid)          │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

4.3 백그라운드 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 방법 1: nohup (가장 전통적)
nohup java -jar myapp.jar > /var/log/myapp/app.log 2>&1 &
echo $!  # 방금 실행한 백그라운드 프로세스 PID 확인

# 방법 2: systemd (권장 - 자동 재시작, 로그 관리)
# → 5장에서 상세 설명

# 방법 3: screen / tmux (터미널 세션 유지)
tmux new -s myapp        # 새 세션 생성
java -jar myapp.jar      # 앱 실행
# Ctrl+B, D              # 세션 분리 (detach)
tmux attach -t myapp     # 세션 다시 접속

# 포그라운드 ↔ 백그라운드 전환
./long_task.sh            # 실행 중에
# Ctrl+Z                 # 일시 중지 (Stopped)
bg                        # 백그라운드로 보내기
fg                        # 포그라운드로 가져오기
jobs                      # 현재 셸의 작업 목록

4.4 프로세스 상태

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌────────────────────────────────────────────────────────────────────┐
│                    Linux 프로세스 상태                              │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│    Created ──→ Ready ──→ Running ──→ Terminated                   │
│                  ▲          │                                      │
│                  │          ▼                                      │
│                  ├──── Sleeping (S)  I/O 대기, sleep() 등          │
│                  │                                                  │
│                  ├──── Stopped (T)   Ctrl+Z, 디버거 중단           │
│                  │                                                  │
│                  └──── Zombie (Z)    종료했지만 부모가 수거 안 함   │
│                                                                    │
│  ps aux의 STAT 컬럼:                                              │
│    R: Running    S: Sleeping    T: Stopped                        │
│    Z: Zombie     D: Disk sleep (uninterruptible, I/O 대기)        │
│                                                                    │
│  Zombie 프로세스가 쌓이면?                                         │
│    → PID 고갈, 부모 프로세스 문제 의심                             │
│    → kill로 Zombie 직접 종료 불가, 부모 프로세스를 종료해야 함    │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

5. systemd와 서비스 관리

5.1 systemd 개요

systemd는 Linux의 init 시스템이자 서비스 매니저다. PID 1로 실행되며 시스템 부팅, 서비스 관리, 로그 수집을 담당한다.

5.2 서비스 관리 명령어 (systemctl)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 서비스 시작·중지·재시작
systemctl start nginx          # 시작
systemctl stop nginx           # 중지
systemctl restart nginx        # 재시작
systemctl reload nginx         # 설정만 리로드 (무중단)
systemctl status nginx         # 상태 확인 (★ 가장 많이 사용)

# 부팅 시 자동 시작
systemctl enable nginx         # 부팅 시 자동 시작 등록
systemctl disable nginx        # 자동 시작 해제
systemctl is-enabled nginx     # 자동 시작 여부 확인

# 전체 서비스 목록
systemctl list-units --type=service                # 실행 중인 서비스
systemctl list-units --type=service --state=failed # 실패한 서비스

5.3 커스텀 서비스 등록 (Spring Boot 예시)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# /etc/systemd/system/myapp.service
[Unit]
Description=My Spring Boot Application
After=network.target mariadb.service
Wants=mariadb.service

[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/java -jar -Dspring.profiles.active=prod /opt/myapp/myapp.jar
ExecStop=/bin/kill -TERM $MAINPID
Restart=on-failure
RestartSec=10
SuccessExitStatus=143

# 환경 변수 설정
Environment=JAVA_HOME=/opt/jdk-17
Environment=SERVER_PORT=8080
EnvironmentFile=/opt/myapp/.env

# 리소스 제한
LimitNOFILE=65536
LimitNPROC=4096

# 로그
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

[Install]
WantedBy=multi-user.target
1
2
3
4
5
# 서비스 등록·시작
systemctl daemon-reload          # 서비스 파일 변경 후 반드시 실행
systemctl enable myapp           # 부팅 시 자동 시작
systemctl start myapp            # 서비스 시작
systemctl status myapp           # 상태 확인

5.4 주요 설정 항목 설명

항목 설명
After 이 서비스보다 먼저 시작되어야 하는 유닛
Wants 약한 의존성 (실패해도 이 서비스는 시작됨)
Requires 강한 의존성 (실패하면 이 서비스도 시작 안 됨)
Restart=on-failure 비정상 종료 시 자동 재시작
RestartSec=10 재시작 전 대기 시간 (초)
SuccessExitStatus=143 SIGTERM(143)을 정상 종료로 처리
LimitNOFILE 열 수 있는 파일 디스크립터 수 (연결 많은 서버는 올려야 함)

6. 로그 관리

6.1 로그 파일 위치

1
2
3
4
5
6
7
8
9
10
11
# 시스템 로그
/var/log/syslog            # 시스템 전반 로그 (Ubuntu/Debian)
/var/log/messages          # 시스템 전반 로그 (CentOS/RHEL)
/var/log/auth.log          # 인증 관련 로그 (SSH 접속 시도 등)
/var/log/kern.log          # 커널 로그
/var/log/dmesg             # 부팅 시 하드웨어 로그

# 애플리케이션 로그
/var/log/nginx/access.log  # Nginx 접근 로그
/var/log/nginx/error.log   # Nginx 에러 로그
/var/log/mysql/error.log   # MySQL/MariaDB 에러 로그

6.2 journalctl (systemd 로그)

systemd로 관리하는 서비스 로그는 journalctl로 확인한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 전체 로그
journalctl                               # 전체 시스템 로그
journalctl -u myapp                      # 특정 서비스 로그
journalctl -u myapp -f                   # 실시간 추적 (tail -f와 동일)
journalctl -u myapp --since "1 hour ago" # 최근 1시간
journalctl -u myapp --since "2026-03-19 14:00" --until "2026-03-19 15:00"

# 부팅별 로그
journalctl -b                            # 현재 부팅 로그
journalctl -b -1                         # 이전 부팅 로그

# 출력 제어
journalctl -u myapp -n 100              # 최근 100줄
journalctl -u myapp --no-pager          # 페이저 없이 출력
journalctl -u myapp -p err              # 에러 수준 이상만
# 수준: emerg, alert, crit, err, warning, notice, info, debug

# 디스크 사용량 관리
journalctl --disk-usage                  # 로그 디스크 사용량
journalctl --vacuum-size=500M            # 500MB로 제한
journalctl --vacuum-time=7d              # 7일 넘은 로그 삭제

6.3 로그 로테이션 (logrotate)

1
2
3
4
5
6
7
8
9
10
11
12
# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
    daily              # 매일 로테이션
    rotate 30          # 30개 파일 유지 (30일치)
    compress           # gzip 압축
    delaycompress      # 이전 파일은 다음 로테이션 때 압축
    missingok          # 로그 파일 없어도 에러 안 냄
    notifempty         # 빈 파일은 로테이션 안 함
    copytruncate       # 파일을 복사 후 원본 비움 (서비스 재시작 불필요)
    dateext            # 날짜 확장자 사용 (myapp.log-20260319)
    maxsize 100M       # 100MB 넘으면 즉시 로테이션
}
1
2
3
# logrotate 수동 실행
logrotate -f /etc/logrotate.d/myapp     # 강제 실행
logrotate -d /etc/logrotate.d/myapp     # 테스트 (실제 실행 안 함)

6.4 실시간 장애 대응 로그 분석

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 실시간 에러 모니터링
tail -f /var/log/myapp/app.log | grep --line-buffered "ERROR"

# 2. 특정 시간대 에러 빈도 분석
grep "2026-03-19 14:" myapp.log | grep -c "ERROR"

# 3. 에러 유형별 집계
grep "ERROR" myapp.log | awk -F'Exception' '{print $1}' | sort | uniq -c | sort -rn

# 4. OOM (Out of Memory) 발생 확인
dmesg | grep -i "out of memory"
journalctl -k | grep -i "oom"

# 5. 최근 에러와 전후 맥락 확인
grep -B 5 -A 10 "OutOfMemoryError" myapp.log

7. 네트워크 명령어

7.1 네트워크 상태 확인

1
2
3
4
5
6
7
8
9
10
11
# IP 주소 확인
ip addr show               # 네트워크 인터페이스·IP 확인 (ifconfig 대체)
hostname -I                 # IP 주소만 간단히 확인

# 포트 확인 (★ 매우 자주 사용)
ss -tlnp                    # TCP Listening 포트와 프로세스 확인
# -t: TCP  -l: Listening  -n: 숫자로 표시  -p: 프로세스 정보
ss -tlnp | grep 8080        # 8080 포트 사용하는 프로세스

netstat -tlnp               # ss와 동일한 기능 (구버전)
lsof -i :8080               # 8080 포트 사용하는 프로세스 (상세)
1
2
3
4
5
# 실무: 포트 충돌 해결
$ ss -tlnp | grep 8080
LISTEN  0  100  *:8080  *:*  users:(("java",pid=1234,fd=45))

$ kill 1234                  # 기존 프로세스 종료 후 재시작

7.2 네트워크 연결 테스트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 기본 연결 확인
ping google.com                  # ICMP 연결 확인
ping -c 4 10.0.0.1              # 4번만 핑

# DNS 확인
nslookup myapp.example.com      # DNS 조회
dig myapp.example.com            # 상세 DNS 조회

# 포트 연결 확인
telnet db-server 3306            # DB 서버 3306 포트 연결 테스트
nc -zv db-server 3306            # netcat으로 포트 확인 (-z: 스캔만, -v: 상세)

# HTTP 요청
curl -v http://localhost:8080/health    # 상세 요청 (헤더 포함)
curl -s http://localhost:8080/api/test  # 조용한 모드
curl -X POST -H "Content-Type: application/json" \
  -d '{"name":"test"}' http://localhost:8080/api/users

# 경로 추적
traceroute google.com            # 네트워크 경로 추적

7.3 방화벽 (firewalld / ufw)

1
2
3
4
5
6
7
8
9
10
11
12
13
# UFW (Ubuntu)
ufw status                       # 방화벽 상태 확인
ufw allow 80/tcp                 # HTTP 허용
ufw allow 443/tcp                # HTTPS 허용
ufw allow from 10.0.0.0/24 to any port 8080  # 내부 네트워크에서만 8080 허용
ufw deny 3306                    # MySQL 포트 외부 차단
ufw enable                       # 방화벽 활성화

# firewalld (CentOS/RHEL)
firewall-cmd --list-all                          # 현재 규칙 확인
firewall-cmd --permanent --add-port=8080/tcp     # 8080 포트 열기
firewall-cmd --permanent --add-service=http      # HTTP 서비스 허용
firewall-cmd --reload                            # 변경 적용

7.4 SSH 원격 접속

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# SSH 접속
ssh deploy@10.0.0.100                    # 기본 접속
ssh -i ~/.ssh/mykey.pem deploy@10.0.0.100 # 키 파일 지정
ssh -p 2222 deploy@10.0.0.100            # 포트 지정

# SSH 설정 (~/.ssh/config) — 매번 긴 명령어 안 쳐도 됨
# ~/.ssh/config
Host prod-server
    HostName 10.0.0.100
    User deploy
    Port 22
    IdentityFile ~/.ssh/prod_key.pem

Host staging
    HostName 10.0.0.200
    User deploy
    IdentityFile ~/.ssh/staging_key.pem

# 이후 간단하게 접속
ssh prod-server
ssh staging

# SCP (파일 전송)
scp myapp.jar deploy@prod-server:/opt/myapp/     # 파일 업로드
scp deploy@prod-server:/var/log/myapp.log ./     # 파일 다운로드
scp -r ./config/ deploy@prod-server:/opt/myapp/  # 디렉토리 전송

# SSH 터널링 (포트 포워딩)
ssh -L 3307:db-server:3306 deploy@bastion-server
# 로컬 3307 → bastion → db-server:3306 으로 터널링
# 이후 localhost:3307로 DB 접속 가능

8. 시스템 모니터링

8.1 디스크 관리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 디스크 사용량 확인
df -h                    # 파일 시스템별 사용량 (★ 자주 사용)
df -h /                  # 루트 파티션 사용량
df -ih                   # inode 사용량 (파일 개수 제한)

# 디렉토리별 사용량
du -sh /var/log/         # /var/log 전체 크기
du -sh /var/log/*        # 하위 디렉토리별 크기
du -sh /* 2>/dev/null | sort -rh | head -10  # 가장 큰 디렉토리 10개

# 실무: 디스크 부족 시 대응
# 1. 큰 파일 찾기
find / -type f -size +500M -exec ls -lh {} \; 2>/dev/null
# 2. 오래된 로그 삭제
find /var/log -name "*.log.gz" -mtime +30 -delete
# 3. 패키지 캐시 정리
apt clean                # Ubuntu/Debian
yum clean all            # CentOS/RHEL
# 4. Docker 정리 (Docker 사용 시)
docker system prune -a

8.2 메모리 관리

1
2
# 메모리 확인
free -h                  # 메모리 사용량 (★ 자주 사용)
1
2
3
4
5
6
7
8
              total    used    free    shared  buff/cache  available
Mem:          7.7Gi   4.2Gi   512Mi    256Mi     3.0Gi      3.0Gi
Swap:         2.0Gi      0B   2.0Gi

# 핵심: available을 봐야 한다 (free가 아님!)
# buff/cache: OS가 파일 캐시로 사용 중, 필요하면 해제됨
# available = free + 해제 가능한 buff/cache
# → available이 적으면 진짜 메모리 부족
1
2
3
4
# Java 프로세스 메모리 확인
jps -v                           # JVM 옵션 확인
jmap -heap PID                   # 힙 메모리 상태
jstat -gc PID 1000               # GC 통계 (1초 간격)

8.3 CPU 관리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# CPU 정보 확인
nproc                    # CPU 코어 수
lscpu                    # CPU 상세 정보
uptime                   # load average 확인

# load average 해석
# load average: 0.85, 0.92, 0.88 (1분, 5분, 15분)
# → CPU 코어 수(예: 4)로 나눠서 해석
# → 0.85/4 = 0.21 → 여유 있음
# → 4.0/4 = 1.0 → 100% 사용 중
# → 8.0/4 = 2.0 → 과부하 (대기 큐 발생)

# CPU를 많이 쓰는 프로세스 확인
top -bn1 | head -20      # 비대화형 모드로 상위 프로세스
ps aux --sort=-%cpu | head -10  # CPU 사용률 내림차순

8.4 종합 모니터링 스크립트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/bin/bash
# server-health.sh - 서버 상태 한눈에 확인

echo "===== 서버 상태 리포트 ====="
echo "날짜: $(date)"
echo ""

echo "--- CPU ---"
uptime
echo ""

echo "--- 메모리 ---"
free -h | head -2
echo ""

echo "--- 디스크 ---"
df -h / | tail -1
echo ""

echo "--- Java 프로세스 ---"
ps aux | grep java | grep -v grep
echo ""

echo "--- 8080 포트 ---"
ss -tlnp | grep 8080
echo ""

echo "--- 최근 에러 (10분) ---"
journalctl -u myapp --since "10 minutes ago" -p err --no-pager
echo ""

echo "============================="

9. 패키지 관리

9.1 APT (Ubuntu/Debian)

1
2
3
4
5
6
7
8
apt update                       # 패키지 목록 업데이트
apt upgrade                      # 설치된 패키지 업그레이드
apt install nginx                # 패키지 설치
apt remove nginx                 # 패키지 삭제
apt purge nginx                  # 패키지 + 설정 파일 삭제
apt search openjdk               # 패키지 검색
apt list --installed             # 설치된 패키지 목록
apt show nginx                   # 패키지 정보 확인

9.2 YUM / DNF (CentOS/RHEL)

1
2
3
4
5
6
7
8
yum update                       # 패키지 업데이트
yum install nginx                # 패키지 설치
yum remove nginx                 # 패키지 삭제
yum search openjdk               # 패키지 검색
yum list installed               # 설치된 패키지 목록

# DNF (yum의 차세대 버전, CentOS 8+/RHEL 8+)
dnf install nginx                # 사용법은 yum과 동일

9.3 실무에서 자주 하는 설치

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Java (Ubuntu)
apt install openjdk-17-jdk
java -version
echo $JAVA_HOME

# Nginx
apt install nginx
systemctl enable nginx
systemctl start nginx

# Docker
curl -fsSL https://get.docker.com | sh
systemctl enable docker
usermod -aG docker deploy    # deploy 사용자에게 Docker 권한

10. 스케줄링 (cron)

10.1 cron 표현식

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌────────────────────────────────────────────────────────────────────┐
│                       cron 표현식 형식                              │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│   ┌───────── 분 (0-59)                                            │
│   │ ┌─────── 시 (0-23)                                            │
│   │ │ ┌───── 일 (1-31)                                            │
│   │ │ │ ┌─── 월 (1-12)                                            │
│   │ │ │ │ ┌─ 요일 (0-7, 0과 7은 일요일)                           │
│   │ │ │ │ │                                                        │
│   * * * * * command                                                │
│                                                                    │
│   예시:                                                            │
│   0 2 * * *     → 매일 새벽 2시                                   │
│   */5 * * * *   → 5분마다                                         │
│   0 0 * * 0     → 매주 일요일 자정                                │
│   0 3 1 * *     → 매월 1일 새벽 3시                               │
│   30 4 * * 1-5  → 평일 오전 4시 30분                              │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

10.2 crontab 관리

1
2
3
4
5
6
crontab -e          # 현재 사용자 cron 편집
crontab -l          # 현재 cron 목록 확인
crontab -r          # 전체 cron 삭제 (주의!)

# 다른 사용자의 cron 확인 (root 권한)
crontab -u deploy -l

10.3 실무 cron 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 매일 새벽 2시 로그 백업
0 2 * * * tar czf /backup/logs-$(date +\%Y\%m\%d).tar.gz /var/log/myapp/ 2>/dev/null

# 5분마다 헬스체크
*/5 * * * * curl -sf http://localhost:8080/health || systemctl restart myapp

# 매일 새벽 3시 DB 백업
0 3 * * * mysqldump -u backup -p'password' mydb | gzip > /backup/db-$(date +\%Y\%m\%d).sql.gz

# 매주 일요일 새벽 4시 7일 지난 백업 삭제
0 4 * * 0 find /backup -name "*.gz" -mtime +7 -delete

# 매시간 디스크 사용률 체크, 90% 넘으면 알림
0 * * * * [ $(df / --output=pcent | tail -1 | tr -d ' %') -gt 90 ] && echo "Disk alert" | mail -s "Disk Warning" admin@company.com

10.4 cron 디버깅

1
2
3
4
5
6
7
8
9
10
11
# cron 로그 확인
grep CRON /var/log/syslog         # Ubuntu
journalctl -u cron                # systemd 기반

# cron 실행 안 될 때 체크리스트:
# 1. 환경 변수 확인 (cron은 최소 환경으로 실행)
#    → 스크립트 상단에 PATH=/usr/local/bin:/usr/bin:/bin 추가
# 2. 절대 경로 사용 (which java → /usr/bin/java)
# 3. 권한 확인 (실행 권한 있는지)
# 4. 출력 리다이렉션 (에러 확인용)
#    */5 * * * * /opt/script.sh >> /var/log/cron-script.log 2>&1

11. 환경 변수와 셸 설정

11.1 환경 변수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 환경 변수 확인
echo $PATH               # 실행 파일 검색 경로
echo $HOME               # 홈 디렉토리
echo $USER               # 현재 사용자
echo $JAVA_HOME          # Java 설치 경로
env                      # 전체 환경 변수
printenv                 # 전체 환경 변수 (env와 유사)

# 환경 변수 설정
export JAVA_HOME=/opt/jdk-17            # 현재 셸에만 적용
export PATH=$PATH:$JAVA_HOME/bin        # PATH에 추가

# 영구 설정 (셸 재시작 후에도 유지)
# ~/.bashrc 또는 ~/.bash_profile에 추가
echo 'export JAVA_HOME=/opt/jdk-17' >> ~/.bashrc
echo 'export PATH=$PATH:$JAVA_HOME/bin' >> ~/.bashrc
source ~/.bashrc          # 즉시 적용

11.2 셸 설정 파일 로드 순서

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌────────────────────────────────────────────────────────────────────┐
│                  Bash 설정 파일 로드 순서                           │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│  로그인 셸 (SSH 접속, su -):                                       │
│    /etc/profile → ~/.bash_profile → ~/.bashrc                     │
│                                                                    │
│  비로그인 셸 (터미널 열기, bash 실행):                              │
│    ~/.bashrc만 실행                                                │
│                                                                    │
│  실무 팁:                                                          │
│    → 환경 변수는 ~/.bashrc에 넣는 게 가장 안전                     │
│    → ~/.bash_profile에서 ~/.bashrc를 source하는 패턴 권장          │
│                                                                    │
│  # ~/.bash_profile 내용                                            │
│  if [ -f ~/.bashrc ]; then                                        │
│      source ~/.bashrc                                              │
│  fi                                                                │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

11.3 유용한 alias 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
# ~/.bashrc에 추가
alias ll='ls -alh'
alias la='ls -A'
alias ..='cd ..'
alias ...='cd ../..'

# 실무용 alias
alias dps='docker ps --format "table \t\t"'
alias dlog='docker logs -f --tail 100'
alias mylog='tail -f /var/log/myapp/app.log'
alias ports='ss -tlnp'
alias mem='free -h'
alias disk='df -h'

12. 실무 자주 쓰는 시나리오

12.1 배포 스크립트 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/bin/bash
# deploy.sh - Spring Boot 무중단 배포 스크립트

APP_NAME="myapp"
APP_HOME="/opt/myapp"
JAR_FILE="$APP_HOME/$APP_NAME.jar"
LOG_FILE="/var/log/myapp/deploy.log"
HEALTH_URL="http://localhost:8080/actuator/health"

echo "$(date) - 배포 시작" >> "$LOG_FILE"

# 1. 새 JAR 파일 확인
NEW_JAR="$APP_HOME/$APP_NAME-new.jar"
if [ ! -f "$NEW_JAR" ]; then
    echo "새 JAR 파일이 없습니다: $NEW_JAR" >> "$LOG_FILE"
    exit 1
fi

# 2. 기존 JAR 백업
if [ -f "$JAR_FILE" ]; then
    cp "$JAR_FILE" "$APP_HOME/$APP_NAME-backup.jar"
fi

# 3. 새 JAR로 교체
mv "$NEW_JAR" "$JAR_FILE"

# 4. 서비스 재시작
systemctl restart myapp

# 5. 헬스체크 (최대 30초 대기)
for i in $(seq 1 30); do
    sleep 1
    STATUS=$(curl -sf "$HEALTH_URL" | grep -o '"status":"UP"')
    if [ "$STATUS" = '"status":"UP"' ]; then
        echo "$(date) - 배포 성공 (${i}초 소요)" >> "$LOG_FILE"
        exit 0
    fi
done

# 6. 실패 시 롤백
echo "$(date) - 헬스체크 실패, 롤백 시작" >> "$LOG_FILE"
cp "$APP_HOME/$APP_NAME-backup.jar" "$JAR_FILE"
systemctl restart myapp
echo "$(date) - 롤백 완료" >> "$LOG_FILE"
exit 1

12.2 장애 대응 체크리스트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 1단계: 증상 파악
systemctl status myapp           # 서비스 상태
curl -v http://localhost:8080/health  # 헬스체크
tail -n 100 /var/log/myapp/app.log   # 최근 로그

# 2단계: 리소스 확인
top -bn1 | head -5              # CPU/메모리 전체
free -h                          # 메모리 상세
df -h                            # 디스크
ss -tlnp                         # 포트 상태

# 3단계: 에러 분석
journalctl -u myapp --since "30 minutes ago" -p err
grep -c "ERROR" /var/log/myapp/app.log   # 에러 횟수
grep "OutOfMemory\|Connection refused\|Too many open files" /var/log/myapp/app.log

# 4단계: 네트워크 확인
ss -s                            # 소켓 상태 요약
ss -tn state established | wc -l # 활성 연결 수
nc -zv db-server 3306            # DB 연결 테스트

# 5단계: 조치
systemctl restart myapp          # 서비스 재시작
kill -3 PID                      # Thread dump (Java)
jmap -dump:format=b,file=heap.hprof PID  # Heap dump

12.3 서버 초기 세팅 체크리스트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 1. 시스템 업데이트
apt update && apt upgrade -y

# 2. 사용자 생성
useradd -m -s /bin/bash deploy
passwd deploy
usermod -aG sudo deploy          # sudo 권한 부여

# 3. SSH 설정
mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
# authorized_keys에 공개키 추가
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh

# 4. SSH 보안 강화 (/etc/ssh/sshd_config)
# PermitRootLogin no             # root 직접 로그인 차단
# PasswordAuthentication no      # 비밀번호 인증 차단 (키만 허용)
# Port 2222                      # 기본 포트 변경 (선택)
systemctl restart sshd

# 5. 방화벽 설정
ufw default deny incoming
ufw allow 22/tcp                 # SSH (또는 변경한 포트)
ufw allow 80/tcp                 # HTTP
ufw allow 443/tcp                # HTTPS
ufw enable

# 6. Java 설치
apt install openjdk-17-jdk -y
echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' >> /home/deploy/.bashrc

# 7. 타임존 설정
timedatectl set-timezone Asia/Seoul

# 8. swap 설정 (메모리 부족 대비)
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab

면접 예상 질문 & 답변

Q1. Linux에서 파일 권한 755와 644의 차이를 설명해주세요.

755rwxr-xr-x로, 소유자는 읽기·쓰기·실행이 모두 가능하고 그룹과 기타 사용자는 읽기·실행만 가능합니다. 주로 디렉토리나 실행 스크립트에 사용합니다.

644rw-r--r--로, 소유자는 읽기·쓰기가 가능하고 나머지는 읽기만 가능합니다. 일반 파일(설정 파일, 소스 코드 등) 에 사용합니다.

숫자의 의미는 r=4, w=2, x=1을 합산한 것입니다. 보안 관점에서 중요한 파일(SSH 키 등)은 600으로 설정하여 소유자만 접근할 수 있게 합니다.

Q2. 프로세스를 종료할 때 kill과 kill -9의 차이는?

kill PIDSIGTERM(15) 시그널을 보내는 것으로, 프로세스에게 “정상적으로 종료해달라”는 요청입니다. 프로세스는 이 시그널을 받으면 cleanup 작업(파일 닫기, 리소스 해제, Spring의 shutdown hook 등)을 수행한 뒤 종료합니다.

kill -9 PIDSIGKILL(9) 시그널로, 커널이 프로세스를 즉시 강제 종료합니다. cleanup이 실행되지 않기 때문에 데이터 손실이 발생할 수 있습니다.

실무에서는 항상 SIGTERM을 먼저 시도하고, 응답이 없을 때만 SIGKILL을 사용해야 합니다.

Q3. 서버에서 특정 포트를 사용하는 프로세스를 어떻게 확인하나요?

가장 많이 쓰는 방법은 ss -tlnp | grep 포트번호입니다. -t는 TCP, -l은 Listening 상태, -n은 숫자 표시, -p는 프로세스 정보를 보여줍니다. lsof -i :포트번호로도 확인할 수 있습니다.

예를 들어 8080 포트가 이미 사용 중이라 애플리케이션이 시작되지 않을 때, ss -tlnp | grep 8080으로 해당 프로세스의 PID를 확인한 뒤 종료하거나 포트를 변경합니다.

Q4. tail -f와 journalctl -f의 차이는?

tail -f 파일경로특정 로그 파일을 실시간으로 추적합니다. 파일이 로테이션되면 추적이 끊길 수 있습니다(tail -F를 쓰면 파일명이 바뀌어도 추적합니다).

journalctl -u 서비스 -fsystemd journal에서 해당 서비스의 로그를 실시간 추적합니다. 로그 로테이션과 무관하게 동작하고, -p err로 에러 수준 이상만 필터링하거나 --since 옵션으로 시간 범위를 지정할 수 있어 더 유연합니다.

실무에서는 systemd로 관리하는 서비스는 journalctl, 직접 파일로 떨어지는 로그(Nginx access.log 등)는 tail -f를 사용합니다.

Q5. 서버 디스크가 100%일 때 어떻게 대응하나요?

  1. df -h로 어떤 파티션이 가득 찼는지 확인합니다.
  2. du -sh /* 2>/dev/null | sort -rh | head -10으로 큰 디렉토리를 찾습니다.
  3. 일반적으로 /var/log에 로그가 쌓인 경우가 많습니다. find /var/log -name "*.log.gz" -mtime +30 -delete로 오래된 압축 로그를 삭제합니다.
  4. Docker를 쓰고 있다면 docker system prune으로 미사용 이미지·컨테이너를 정리합니다.
  5. 근본적으로는 logrotate 설정을 확인하여 로그가 자동으로 관리되도록 해야 합니다.

긴급 상황에서는 > /var/log/큰파일.log로 파일 내용만 비울 수도 있지만, 서비스에 영향이 없는지 확인해야 합니다.

Q6. nohup과 systemd의 차이는?

nohup은 터미널 종료 후에도 프로세스가 계속 실행되게 하는 간단한 명령어입니다. nohup java -jar app.jar &처럼 사용합니다. 하지만 프로세스가 죽으면 자동 재시작이 안 되고, 로그 관리도 수동으로 해야 합니다.

systemd는 리눅스의 서비스 매니저로, .service 파일을 작성하면 자동 재시작(Restart=on-failure), 부팅 시 자동 시작(enable), 로그 관리(journalctl), 리소스 제한(LimitNOFILE) 등을 제공합니다.

운영 환경에서는 반드시 systemd를 사용해야 합니다. nohup은 임시 테스트 용도로만 사용합니다.

Q7. Linux에서 좀비 프로세스란 무엇이고, 어떻게 처리하나요?

좀비 프로세스는 실행이 종료되었지만 부모 프로세스가 wait() 시스템 콜로 종료 상태를 수거하지 않아 프로세스 테이블에 남아있는 프로세스입니다. ps aux에서 상태가 Z로 표시됩니다.

좀비 프로세스는 이미 종료된 상태이므로 kill로 직접 제거할 수 없습니다. 부모 프로세스wait()를 호출하도록 해야 하며, 부모에게 SIGCHLD 시그널을 보내거나, 부모 프로세스를 종료하면 좀비가 init(PID 1)에 입양되어 자동으로 정리됩니다.

좀비가 대량으로 쌓이면 PID 고갈이 발생할 수 있으므로, 부모 프로세스의 코드에서 자식 프로세스 종료를 제대로 처리하는지 점검해야 합니다.


마무리

Linux는 백엔드 개발자의 필수 도구다. GUI 없이 명령어만으로 서버를 관리할 수 있어야 하고, 장애 상황에서 로그를 빠르게 추적하고 원인을 분석할 수 있어야 한다. 이 글에서 다룬 내용을 정리하면:

  • 파일 시스템: FHS 디렉토리 구조, Everything is a File 철학
  • 권한 관리: chmod/chown, 특수 권한 (SetUID, Sticky Bit)
  • 필수 명령어: grep/awk/sed 3대장, 파이프·리다이렉션 조합
  • 프로세스: ps/top/kill, 시그널(SIGTERM vs SIGKILL), 백그라운드 실행
  • systemd: 서비스 관리, 커스텀 서비스 등록, 자동 재시작
  • 로그: journalctl, logrotate, 실시간 장애 대응
  • 네트워크: ss/curl/ssh, 방화벽, SSH 터널링
  • 모니터링: df/free/top, 디스크·메모리·CPU 관리
  • 스케줄링: cron 표현식, 자동화 스크립트

이 명령어들을 직접 사용해보고, 특히 배포와 장애 대응 시나리오를 연습해두면 면접에서 자신 있게 답할 수 있다.