처음엔 그냥 명령어 몇 줄 치면 될 줄 알았다. apt install
치면 끝나는 줄 알았고, 서버 한 대쯤은 내가 다룰 수 있을 거라 생각했다. 그런데 어느 날, 진짜 서버를 맡고 나서야 알았다. 리눅스를 배우는 순간은 성공할 때가 아니라, 망했을 때라는 걸. 이 글은 내가 서버를 날려먹고, 로그에 매달리고, 복구하면서 다시 일어선 기록이다.
처음 맡은 서버: “그냥 블로그용 웹서버예요”
처음 운영한 서버는 사소했다. 회사에서 블로그용으로 쓰는 웹서버였다. 트래픽도 많지 않았고, 위험한 서비스도 없었다. 그저 아파치와 PHP, 마리아DB가 돌아가는 단순한 구조. 그런데 나는 방심했다. 업데이트 명령을 치기 전에 백업을 하지 않았다. 그리고, 운명처럼 그날 서비스가 죽었다.
“업데이트만 했는데 왜 접속이 안 되지?”
로그를 열어보니 PHP 모듈 버전이 바뀌면서 아파치가 제대로 기동되지 않았다. 에러 메시지는 낯설고 복잡했다. 결국 나는 새벽까지 문서를 뒤지며 해결책을 찾았다. 그때 처음 느꼈다. 리눅스는 단순히 ‘명령어를 아는 것’이 아니라, ‘문제를 읽는 법’을 배우는 과정이라는 걸.
로그는 거짓말을 하지 않는다
그때부터 나는 로그를 사랑하게 되었다. journalctl
, /var/log/syslog
, /var/log/nginx/error.log
… 로그는 언제나 진실을 말한다. 단지 우리가 못 알아들을 뿐이다.
# 최근 1시간 nginx 로그 확인
journalctl -u nginx --since "1 hour ago"
# 전체 시스템 메시지
sudo tail -f /var/log/syslog
처음엔 무슨 말인지 몰랐다. 하지만 반복해서 읽다 보면 패턴이 보인다. “Failed”, “Permission denied”, “Timeout”. 단어 하나하나가 단서다. 그리고 그 단서가 실마리가 된다.
보안 사고, 내 손으로 만든 재앙
두 번째 사건은 치명적이었다. 보안 설정을 허술하게 해둔 SSH 서버가 크롤러 공격에 뚫렸다. 비밀번호 로그인도 열려 있었고, 루트 접속도 가능했다. 그날 서버는 채굴기로 변해 있었다. CPU는 100%를 치솟고, 메모리는 증발하듯 사라졌다.
이 사건으로 나는 보안의 중요성을 뼈저리게 배웠다. 그리고 다시는 비밀번호 로그인을 열지 않았다.
# SSH 보안 설정 (/etc/ssh/sshd_config)
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
# 서비스 재시작
sudo systemctl restart sshd
지금은 기본 중의 기본이지만, 그때는 몰랐다. 무지의 대가는 혹독했다. 하지만 그 대가 덕분에 지금은 보안을 최우선으로 생각한다. 방화벽을 먼저 확인하고, 포트를 닫고, 로그인을 키 기반으로만 제한한다.
디스크 풀: “공간이 꽉 찼다고요?”
세 번째 위기는 새벽이었다. 서비스가 멈췄고, 에러 메시지는 이랬다. “No space left on device.” 나는 멍해졌다. 200GB 디스크가 다 찰 리 없다고 생각했기 때문이다. 하지만 로그 파일이 매일 누적되어 있었다. 로테이션 설정을 하지 않았던 것이다.
# 가장 용량을 많이 차지하는 디렉터리 찾기
du -sh * | sort -hr | head
# logrotate 설정 파일 예시 (/etc/logrotate.d/nginx)
/var/log/nginx/*.log {
daily
rotate 14
compress
missingok
notifempty
}
그날 이후 나는 모든 서버에 용량 모니터링 스크립트를 걸었다. 그리고 알림이 오도록 했다. 서버를 지키는 건 대단한 해킹 방어가 아니라, 사소한 습관의 집합이었다.
서비스는 언제나 갑자기 죽는다
운영하면서 가장 자주 겪는 일은 서비스 다운이다. 이유는 다양하다. 메모리 누수, 권한 문제, 설정 오류, 외부 API 장애. 하지만 공통점이 있다. 항상 갑자기 온다는 것. 그래서 대비가 필요하다.
# 서비스 상태 확인
systemctl status myapp
# 자동 재시작 설정
sudo systemctl edit myapp.service
[Service]
Restart=always
RestartSec=5
재시작 정책 하나만 추가해도 서비스 복구 확률이 달라진다. 사소한 설정이지만, 위기 때 빛난다.
백업, 잊는 순간 끝난다
가장 큰 실수는 백업을 하지 않은 것이다. 초기에 나는 그렇게 생각했다. “중요한 데이터가 아니니까 괜찮겠지.” 하지만 어느 날 DB가 깨졌다. 로그도, 덤프도, 스냅샷도 없었다. 그 순간 깨달았다. 백업은 선택이 아니라 ‘의무’라는 것을.
# MySQL 백업
mysqldump -u root -p database > backup-$(date +%F).sql
# 디렉터리 압축 백업
tar -czf /backup/etc-$(date +%F).tar.gz /etc
지금 나는 백업을 세 단계로 한다. 자동화 스크립트, 클라우드 업로드, 복구 테스트. 백업은 만드는 것보다 ‘복구 가능한지’가 더 중요하다.
자동화는 귀찮음을 이기는 기술
서버를 돌보다 보면 반복되는 작업이 많다. 로그 정리, 패키지 업데이트, 백업. 귀찮다고 미루면 언젠가 사고가 터진다. 그래서 자동화를 배웠다.
# 크론 예시 (매일 새벽 3시 백업)
0 3 * * * /usr/local/bin/backup.sh > /var/log/backup.log 2>&1
자동화는 게으름이 아니라 ‘지속성’이다. 그리고 지속성은 운영자의 가장 큰 무기다.
컨테이너 시대, 리눅스는 다시 태어났다
최근에는 도커와 쿠버네티스를 통해 서버 관리가 완전히 달라졌다. 환경 격리, 배포 자동화, 스케일링까지 한층 간단해졌다. 그러나 리눅스 기초가 없으면 이 기술들은 이해하기 어렵다.
# 간단한 Dockerfile 예시
FROM ubuntu:22.04
RUN apt update && apt install -y nginx
CMD ["nginx", "-g", "daemon off;"]
기초는 결국 어디서나 통한다. 쉘, 권한, 파일 시스템, 로그. 그것들을 알면 어떤 도구도 두렵지 않다.
문제를 대하는 태도, 그것이 실력이다
리눅스를 다루다 보면 ‘모르는 문제’를 만나는 건 일상이다. 그때 중요한 건 당황하지 않는 것이다. 로그를 읽고, 구글을 검색하고, 커뮤니티에 질문을 남기고, 차근차근 재현하는 것. 결국 대부분의 문제는 그렇게 풀린다.
그리고 실패는 무조건 나쁘지 않다. 실패는 지식을 ‘기억’이 아니라 ‘경험’으로 바꾸는 과정이다.
마지막 조언: 겁내지 말고, 부숴라
나는 초기에 서버를 여러 번 부쉈다. rm 명령어로 디렉터리를 날렸고, 방화벽 규칙을 잘못 넣어 서버에 접속도 못했다. 그런데 이상하게도, 그때마다 나는 한 단계씩 성장했다. 리눅스는 부서져야 배운다. 망쳐야 기억된다. 그리고 다시 세우면서 진짜 실력이 된다.
오늘도 터미널 앞에서 손이 떨린다. 하지만 이제는 안다. 떨리는 손으로 엔터를 누르는 순간, 나는 또 한 단계 올라간다는 것을.