DevOps
Linux를 이해하는 가장 실용적인 방법: 구조부터 명령어, 트러블슈팅까지
Linux의 Kernel, Process, File System, Permission 구조를 살펴보고, 자주 사용하는 명령어와 실제 서버 트러블슈팅 방법까지 하나의 흐름으로 정리합니다.
Linux를 이해하는 가장 실용적인 방법: 구조부터 명령어, 트러블슈팅까지
개발을 하다 보면 Linux를 피하기는 꽤 어렵습니다.
로컬에서는 macOS나 Windows를 사용하더라도 서비스를 배포하는 순간 Linux를 만나게 되는 경우가 많습니다. Docker container 안으로 들어가도 Linux가 있고, AWS EC2 같은 서버를 생성해도 Linux를 사용하게 됩니다.
처음 Linux를 접했을 때는 ls, cd, grep, ps 같은 명령어부터 외우기 쉽습니다.
물론 명령어를 아는 것도 중요합니다. 하지만 실제 서버에서 장애를 만났을 때는 명령어 자체보다 Linux가 process, file, memory, permission을 어떻게 관리하는지 이해하고 있는 것이 훨씬 도움이 됩니다.
이 글에서는 Linux를 명령어 모음이 아니라 하나의 운영체제 구조로 살펴보겠습니다.
1. Linux는 정확히 무엇일까?
엄밀하게 이야기하면 Linux는 운영체제 전체가 아니라 Kernel을 의미합니다.
Kernel은 application과 hardware 사이에서 CPU, memory, storage, network 같은 system resource를 관리합니다.
개념적으로 단순화하면 다음과 같습니다.
Application
↓
System Call
↓
Linux Kernel
↓
CPU / Memory / Disk / Network
우리가 terminal에서 실행하는 ls, cat, grep 같은 프로그램도 결국 Kernel이 제공하는 기능을 이용합니다.
예를 들어 파일을 읽는 프로그램은 직접 disk hardware를 제어하지 않습니다. Kernel에 파일을 열고 읽어 달라고 요청합니다.
이 경계에서 사용되는 것이 system call입니다.
대표적인 system call에는 다음과 같은 것들이 있습니다.
open()
read()
write()
fork()
exec()
socket()
이 구조를 알고 있으면 Linux에서 발생하는 여러 문제를 조금 다른 시각으로 볼 수 있습니다.
Application이 느리다고 해서 항상 application code가 원인은 아닙니다. CPU가 부족할 수도 있고, disk I/O가 병목일 수도 있으며, network connection이 문제일 수도 있습니다.
Linux troubleshooting은 결국 이런 resource들을 하나씩 확인하는 과정에 가깝습니다.
2. "Everything is a file"
Linux를 공부하면서 자주 듣게 되는 표현이 있습니다.
Everything is a file.
조금 과장된 표현이지만 Linux의 설계를 이해하는 데 상당히 유용합니다.
일반적인 파일뿐 아니라 device나 process와 관련된 많은 정보도 file system을 통해 접근할 수 있습니다.
예를 들어 다음 경로를 살펴볼 수 있습니다.
/dev
/proc
/sys
/dev에는 device를 나타내는 파일이 있습니다.
ls /dev
/proc에는 실행 중인 process와 Kernel 상태에 대한 정보가 노출됩니다.
현재 system의 memory 정보를 보고 싶다면 다음 파일을 확인할 수 있습니다.
cat /proc/meminfo
CPU 정보도 마찬가지입니다.
cat /proc/cpuinfo
특정 process의 PID가 1234라면 해당 process의 정보도 확인할 수 있습니다.
ls /proc/1234
Linux에서는 이렇게 Kernel 내부의 여러 정보를 file interface를 통해 관찰할 수 있습니다.
3. Linux File System 구조
Linux server에 처음 접속하면 가장 먼저 익숙해져야 하는 것 중 하나가 directory 구조입니다.
최상위에는 /가 있습니다.
/
├── bin
├── dev
├── etc
├── home
├── proc
├── tmp
├── usr
└── var
각 directory에는 대략적인 역할이 있습니다.
/etc
System과 application의 configuration이 주로 위치합니다.
ls /etc
예를 들어 SSH 설정은 일반적으로 다음 경로에서 찾을 수 있습니다.
/etc/ssh/
/var
실행 중인 system에서 지속적으로 변경되는 데이터가 위치합니다.
대표적으로 log가 있습니다.
/var/log
Server에서 문제가 발생했을 때 자주 확인하게 되는 directory입니다.
/home
일반 user의 home directory입니다.
/home/username
현재 사용자의 home directory는 ~로 표현할 수도 있습니다.
cd ~
/tmp
Temporary file을 저장하는 공간입니다.
Application이 임시 데이터를 저장할 때 자주 사용합니다.
/proc
실제 disk에 저장된 일반적인 directory와는 조금 다릅니다.
Kernel이 process와 system 정보를 제공하는 virtual filesystem입니다.
이 구조를 모두 외울 필요는 없습니다. 중요한 것은 어떤 종류의 정보를 어디에서 찾아야 하는지 감을 잡는 것입니다.
4. Process를 이해하면 Linux가 조금 쉬워진다
Linux에서 실행 중인 program은 process가 됩니다.
현재 process를 확인하는 가장 기본적인 방법 중 하나는 ps입니다.
ps aux
특정 application을 찾고 싶다면 grep과 함께 사용할 수도 있습니다.
ps aux | grep nginx
여기서 등장하는 |는 pipe입니다.
앞 명령어의 stdout을 다음 명령어의 stdin으로 전달합니다.
ps aux
│
│ stdout
↓
grep nginx
Linux shell의 강력함은 작은 program들을 이렇게 조합할 수 있다는 점에서 나옵니다.
예를 들어 log에서 error만 찾고 싶다면 다음처럼 사용할 수 있습니다.
cat application.log | grep ERROR
다만 grep이 직접 파일을 읽을 수 있으므로 실제로는 다음과 같이 쓰는 편이 더 간단합니다.
grep ERROR application.log
실시간으로 process를 관찰하고 싶다면 다음 명령도 유용합니다.
top
환경에 설치되어 있다면 htop을 사용할 수도 있습니다.
5. Process가 종료되지 않는다면?
Server를 운영하다 보면 특정 process를 종료해야 할 때가 있습니다.
먼저 PID를 확인합니다.
ps aux | grep my-application
PID가 1234라고 가정해 보겠습니다.
kill 1234
많은 경우 여기서 바로 kill -9를 사용하는 모습을 볼 수 있습니다.
kill -9 1234
하지만 둘은 의미가 다릅니다.
기본 kill은 일반적으로 SIGTERM을 전달합니다.
SIGTERM
"종료할 시간을 줄 테니 정상적으로 종료해."
반면 kill -9는 SIGKILL입니다.
SIGKILL
"즉시 종료."
SIGKILL은 process가 cleanup 작업을 수행할 기회를 주지 않습니다.
따라서 일반적으로는 SIGTERM으로 정상 종료를 시도하고, process가 응답하지 않을 때 SIGKILL을 고려하는 편이 안전합니다.
kill -TERM 1234
# 종료되지 않는 경우
kill -KILL 1234
작은 차이지만 production server를 다룰 때는 꽤 중요합니다.
6. Permission: "왜 Permission denied가 나오지?"
Linux를 사용하다 보면 높은 확률로 다음 메시지를 만나게 됩니다.
Permission denied
File permission을 확인해 보겠습니다.
ls -l
결과는 다음과 비슷합니다.
-rwxr-xr-- 1 ubuntu developers 1024 Sep 4 09:30 deploy.sh
앞부분의 permission을 나누어 보면 다음과 같습니다.
rwx | r-x | r--
↓ ↓ ↓
user group others
각 문자는 다음 의미를 가집니다.
r = read
w = write
x = execute
숫자로 표현할 수도 있습니다.
r = 4
w = 2
x = 1
그래서 다음 명령은 owner에게 read/write/execute 권한을 주고, group과 others에는 read/execute 권한을 부여합니다.
chmod 755 deploy.sh
계산하면 다음과 같습니다.
7 = 4 + 2 + 1 = rwx
5 = 4 + 0 + 1 = r-x
5 = 4 + 0 + 1 = r-x
여기서 중요한 습관이 하나 있습니다.
Permission 문제가 발생했다고 무조건 다음과 같이 해결하지 않는 것입니다.
chmod 777 some-file
당장은 문제가 사라질 수 있지만 필요 이상의 권한을 열어 두게 됩니다.
먼저 owner와 group을 확인하고 어떤 process가 해당 파일에 접근해야 하는지 파악하는 편이 좋습니다.
ls -l some-file
id
7. Log를 읽는 기본 도구
Production server에서 문제가 발생하면 가장 먼저 보는 것 중 하나가 log입니다.
파일의 마지막 부분을 확인하려면 tail을 사용할 수 있습니다.
tail application.log
마지막 100줄을 보고 싶다면 다음과 같이 실행합니다.
tail -n 100 application.log
새로운 log를 실시간으로 확인하려면 -f 옵션을 사용합니다.
tail -f application.log
특정 error를 검색하려면 grep을 사용할 수 있습니다.
grep "ERROR" application.log
앞뒤 문맥까지 보고 싶다면 다음과 같이 실행할 수 있습니다.
grep -C 3 "ERROR" application.log
systemd를 사용하는 환경에서는 journalctl도 자주 사용합니다.
journalctl
특정 service의 log를 확인할 수도 있습니다.
journalctl -u nginx
최근 log를 실시간으로 보고 싶다면 다음과 같이 실행합니다.
journalctl -u nginx -f
장애 상황에서는 단순히 error 한 줄만 보는 것보다 그 직전에 어떤 일이 있었는지 함께 확인하는 것이 중요합니다.
8. Disk 문제 확인하기
Application에서 갑자기 파일을 저장하지 못하거나 database가 비정상적으로 동작한다면 disk 상태도 확인해 볼 필요가 있습니다.
가장 먼저 사용할 수 있는 명령은 다음과 같습니다.
df -h
Filesystem별 사용량을 사람이 읽기 쉬운 단위로 보여줍니다.
특정 directory가 얼마나 많은 공간을 사용하는지도 확인할 수 있습니다.
du -sh /var/log
하위 directory별로 확인하려면 다음과 같이 실행할 수 있습니다.
du -h --max-depth=1 /var
여기서 중요한 점은 df와 du가 서로 다른 질문에 답한다는 것입니다.
df → filesystem에 공간이 얼마나 남았는가?
du → 특정 directory/file이 공간을 얼마나 사용하고 있는가?
Disk가 가득 찼다면 큰 파일을 무작정 삭제하기 전에 어떤 application이 생성한 데이터인지 확인해야 합니다.
Log라면 rotation 설정이 잘못되었을 수도 있고, application이 예상보다 많은 temporary file을 생성하고 있을 수도 있습니다.
9. Memory 문제 확인하기
Memory 상태를 빠르게 확인할 때는 다음 명령을 사용할 수 있습니다.
free -h
처음 Linux memory를 확인하면 used 값이 생각보다 커 보여 당황할 수 있습니다.
Linux는 사용하지 않는 memory를 cache 등에 적극적으로 활용합니다.
따라서 단순히 used 숫자 하나만 보고 "memory가 부족하다"고 판단하기보다는 available, swap 사용량, process별 memory 사용량 등을 함께 확인해야 합니다.
Process별 상태는 다음처럼 확인할 수 있습니다.
top
Memory 사용량을 기준으로 process를 정렬할 수도 있습니다.
ps aux --sort=-%mem | head
Memory 부족이 의심된다면 Kernel log에서 OOM(Out Of Memory) 관련 기록도 확인할 수 있습니다.
journalctl -k | grep -i oom
환경에 따라 다음과 같이 확인하는 것도 가능합니다.
dmesg | grep -i oom
10. "서버가 느려요"라는 문제를 만났을 때
이제 실제 상황을 하나 생각해 보겠습니다.
사용자가 다음과 같이 이야기합니다.
서버가 갑자기 느려졌어요.
이 말만으로 원인을 알기는 어렵습니다.
그래서 범위를 하나씩 좁혀 나가야 합니다.
먼저 system load와 process 상태를 확인합니다.
uptime
top
CPU를 많이 사용하는 process가 있는지 확인합니다.
ps aux --sort=-%cpu | head
Memory도 확인합니다.
free -h
Disk 공간도 봅니다.
df -h
그리고 application과 system log를 확인합니다.
journalctl -p err
Network service라면 listening port도 확인해 볼 수 있습니다.
ss -lntp
전체적인 troubleshooting 흐름을 단순화하면 다음과 같습니다.
증상 확인
↓
Process / CPU
↓
Memory
↓
Disk
↓
Network
↓
Application / System Log
↓
원인 범위 좁히기
여기서 핵심은 처음부터 원인을 단정하지 않는 것입니다.
"느리니까 CPU 문제겠지"라고 생각하면 CPU만 보게 됩니다.
반대로 Linux가 관리하는 주요 resource를 순서대로 확인하면 훨씬 체계적으로 문제를 좁혀갈 수 있습니다.
11. 자주 사용하는 Linux 명령어
마지막으로 실제 작업에서 자주 사용하는 명령어를 목적별로 정리해 보겠습니다.
파일 탐색
pwd
ls -al
cd /path
find /var/log -name "*.log"
파일 내용 확인
cat file.txt
less file.txt
head file.txt
tail file.txt
tail -f application.log
문자열 검색
grep "ERROR" application.log
grep -R "DATABASE_URL" .
Process
ps aux
top
kill PID
System resource
free -h
df -h
du -sh /path
uptime
Network
ip addr
ss -lntp
curl https://example.com
Permission
ls -l
chmod 755 file
chown user:group file
이 명령어들을 전부 외우는 것보다 어떤 문제에서 어떤 정보를 확인해야 하는지 연결해서 기억하는 편이 좋습니다.
마무리
Linux를 잘 사용한다는 것은 많은 명령어를 외우는 것과 조금 다릅니다.
Linux가 application을 process로 실행하고, Kernel이 CPU와 memory 같은 resource를 관리하며, file system과 permission을 통해 resource에 접근하도록 만든다는 큰 구조를 이해하는 것이 먼저입니다.
그 구조가 보이기 시작하면 명령어도 자연스럽게 연결됩니다.
Process가 궁금하다 → ps, top
Memory가 궁금하다 → free
Disk가 궁금하다 → df, du
Network가 궁금하다 → ss, ip
Log가 궁금하다 → tail, grep, journalctl
결국 Linux에서 troubleshooting을 한다는 것은 "무슨 명령어를 입력해야 하지?"를 맞히는 일이 아닙니다.
현재 어떤 resource에서 문제가 발생하고 있는지 관찰하고, 증거를 모아 원인의 범위를 좁혀 가는 과정에 가깝습니다.
Linux를 처음 공부한다면 명령어를 하나 더 외우는 것보다, 오늘 실행한 명령어가 Kernel과 system의 어떤 정보를 보여 주고 있는지 한 번 더 생각해 보는 것을 추천합니다.
그 순간부터 Linux가 단순한 검은 terminal 화면이 아니라, 관찰할 수 있는 하나의 system으로 보이기 시작합니다.
Tags