Tomcat이 갑자기 멈췄습니다. 로그는 /opt/tomcat/logs/catalina.out에 있다는데 권한 오류로 파일을 읽을 수가 없습니다. systemctl status는 뭔가 출력하는데 해석이 안 됩니다. 로그를 보려면 권한을 알아야 하고, 권한을 이해하려면 파일시스템 구조를 알아야 합니다.
매일 쓰는 명령어들이지만 제대로 이해하고 쓰는 것과 그냥 외워서 쓰는 것은 장애 대응 속도에서 바로 차이납니다.
- 1/opt, /var/log, /etc, /tmp, /home, /usr/local 각 디렉터리의 용도를 설명할 수 있다
- 2chmod 숫자 모드로 파일/디렉터리 권한을 설정할 수 있다
- 3chown으로 파일 소유자와 그룹을 변경할 수 있다
- 4visudo로 sudo 권한을 안전하게 설정할 수 있다
- 5systemctl로 서비스를 시작/중지/재시작하고 자동 시작을 설정할 수 있다
du -sh /opt /var/log /etc /tmp 2>/dev/nullid && groupssudo -l 2>/dev/null | head -10systemctl list-units --type=service --state=running | wc -lLinux 디렉터리 구조
인프라 엔지니어가 꼭 알아야 할 디렉터리
파일을 어디에 두느냐는 단순한 정리 습관이 아니라 운영 문제입니다. 로그가 /var/log가 아닌 곳에 쌓이면 모니터링 스크립트가 그 파일을 찾지 못하고, /tmp에 설정 파일을 올리면 재부팅 하나로 모든 것이 사라집니다. 각 디렉터리의 목적을 알아야 파일을 올바른 위치에 두고, 디스크 풀 같은 장애 상황에서도 빠르게 원인을 좁힐 수 있습니다.
확대
디스크가 꽉 찼다는 알림이 왔는데 어디서 공간을 잡아먹고 있는지 찾을 수 없었습니다. /var/log 아래 로그 파일이 수십 GB로 불어있었지만 그 경로가 어디인지 몰랐던 것입니다. Tomcat 로그가 어디에 쌓이는지, 설정 파일이 /etc 에 있어야 하는데 /home 에 올려뒀다면 — 백업 스크립트도, 모니터링도 그 파일을 찾지 못합니다. 각 디렉터리의 목적을 알아야 파일을 올바른 위치에 두고, 장애 시에도 빠르게 찾을 수 있습니다.
Linux FHS(Filesystem Hierarchy Standard)에서 각 디렉터리는 목적이 명확합니다. 잘못된 위치에 파일을 두면 나중에 찾기도 어렵고, 백업이나 모니터링 스크립트도 제대로 작동하지 않습니다.
| 디렉터리 | 용도 | 실무 사용 예 |
|---|---|---|
/opt | 서드파티 소프트웨어 설치 | Tomcat, JBoss, 사내 미들웨어 |
/etc | 시스템 및 서비스 설정 파일 | /etc/nginx/, /etc/ssh/sshd_config |
/var/log | 서비스 로그 파일 | /var/log/nginx/, /var/log/messages |
/var/run | 실행 중인 프로세스 PID 파일 | nginx.pid, httpd.pid |
/tmp | 임시 파일 (재부팅 시 삭제) | 배포 파일 임시 보관, 빌드 임시 파일 |
/home | 일반 사용자 홈 디렉터리 | /home/deploy, /home/ops-admin |
/usr/local | 로컬 설치 소프트웨어 | 직접 컴파일 설치한 바이너리 |
/proc | 실행 중인 프로세스 정보 (가상 FS) | /proc/meminfo, /proc/cpuinfo |
# 각 디렉터리 실제 크기 확인
du -sh /opt /var/log /etc
# /var/log에서 가장 큰 로그 파일 찾기 (디스크 꽉 찰 때 자주 씀)
sudo find /var/log -type f -name "*.log" -exec du -sh {} + | sort -rh | head -10
# /tmp에 오래된 파일 있는지 확인
find /tmp -type f -mtime +7 | wc -l
주의: /tmp는 재부팅 시 자동으로 정리되거나(tmpfs), logrotate/tmpwatch로 주기적으로 정리됩니다. 영구 보관이 필요한 파일은 절대 /tmp에 두지 마세요.
확대
1. 서비스 관련 주요 경로 탐색
ls /opt/ 2>/dev/null; ls /etc/ | grep -E 'nginx|tomcat|java|ssh' 2>/dev/null; echo '---'; ls /var/log/ | head -102. systemd 서비스 파일 위치 확인
ls /etc/systemd/system/*.service 2>/dev/null | head -5; ls /lib/systemd/system/ | grep -E 'nginx|ssh|cron' 2>/dev/null3. 로그 디렉터리 구조 확인
ls -la /var/log/ | head -15- ls /opt/ → ls /var/log/ → ls /etc/systemd/system/*.service 순서로 확인 — /opt의 미들웨어 존재, /var/log의 서비스 로그 디렉터리, systemd 등록 여부를 이 순서로 체크해야 설치 현황 파악 완료
- /var/log/ 기준: 디렉터리 크기가 1GB 이상이면 logrotate 미설정 또는 오래된 로그 누적 — df -h /var/log 로 파티션 사용률 함께 확인
- /opt/에 미들웨어가 있는데 /etc/systemd/system/에 .service 파일이 없으면 → 수동 기동 환경으로 재부팅 시 서비스가 자동 시작 안 됨 — systemctl enable로 등록 필요
파일 권한 — chmod / chown
chmod 숫자 모드 완전 이해
배포 후 서비스가 설정 파일을 읽지 못해 기동에 실패했는데, 파일 권한이 600으로 되어 있어 서비스 계정이 접근하지 못했습니다.
확대
반대로 DB 비밀번호가 담긴 설정 파일에 644를 걸어둔 채 운영했다가 보안 감사에서 적발된 경우도 있습니다. 숫자 하나 차이가 서비스 장애가 되기도 하고 보안 사고가 되기도 합니다. 인프라 엔지니어가 가장 자주 쓰는 권한 값은 5가지입니다. 이것만 외워두면 됩니다.
권한 계산: r=4, w=2, x=1
7 = 4+2+1 = rwx (읽기+쓰기+실행)
6 = 4+2+0 = rw- (읽기+쓰기)
5 = 4+0+1 = r-x (읽기+실행)
4 = 4+0+0 = r-- (읽기 전용)
0 = 0+0+0 = --- (접근 불가)
자주 쓰는 권한 패턴:
# 755: 실행 파일, 디렉터리 표준 (owner: rwx, group/others: r-x)
chmod 755 /opt/tomcat/bin/startup.sh
chmod 755 /opt/app # 디렉터리
# 644: 일반 파일 표준 (owner: rw-, group/others: r--)
chmod 644 /etc/nginx/nginx.conf
# 640: 설정 파일 (비밀번호 포함) — others 읽기 차단
chmod 640 /opt/app/config/database.yml
# 600: 개인 키, 비밀 파일 — owner만 읽기/쓰기
chmod 600 ~/.ssh/id_rsa
chmod 600 /etc/ssl/private/server.key
# 700: 관리자 전용 디렉터리
chmod 700 /opt/app/scripts
# 재귀적으로 적용 (-R 옵션)
chmod -R 755 /opt/tomcat/bin
chmod -R 640 /opt/app/config
# 현재 권한 확인
ls -la /opt/tomcat/bin/startup.sh
# -rwxr-xr-x 1 tomcat tomcat 2573 Jan 15 10:00 /opt/tomcat/bin/startup.sh
# stat으로 상세 확인
stat /opt/tomcat/bin/startup.sh
# Access: (0755/-rwxr-xr-x) Uid: ( 999/ tomcat) Gid: ( 999/ tomcat)
chown으로 소유자/그룹 변경
배포 스크립트가 root 권한으로 WAR 파일을 복사했는데 서비스 계정인 tomcat이 그 파일을 읽지 못해 기동 실패가 났습니다. 파일 소유자는 root인데 실행 주체는 tomcat이었기 때문입니다. 배포할 때마다 이 실수가 반복되는 팀들이 많습니다. chown을 배포 스크립트에 포함시키지 않으면 권한 오류는 반복됩니다.
파일의 소유자(owner)와 그룹(group)을 변경합니다. 배포 후 파일 소유자가 잘못 설정되어 서비스가 파일을 읽지 못하는 경우가 자주 발생합니다.
# 소유자만 변경
chown tomcat /opt/tomcat/webapps/app.war
# 소유자와 그룹 동시 변경
chown tomcat:tomcat /opt/tomcat/webapps/app.war
# 그룹만 변경
chgrp tomcat /opt/tomcat/logs
# 재귀적으로 하위 전체 변경
chown -R tomcat:tomcat /opt/tomcat/webapps
chown -R nginx:nginx /var/log/nginx
# 심볼릭 링크 포함 변경
chown -Rh tomcat:tomcat /opt/tomcat
배포 후 권한 설정 패턴 (실무에서 반복해서 씀):
# WAR 파일 배포 후 소유권 정리
sudo cp /tmp/app-1.2.3.war /opt/tomcat/webapps/app.war
sudo chown tomcat:tomcat /opt/tomcat/webapps/app.war
sudo chmod 644 /opt/tomcat/webapps/app.war
# 설정 파일 배포 후 권한 정리
sudo cp /tmp/application.yml /opt/app/config/
sudo chown appuser:appuser /opt/app/config/application.yml
sudo chmod 640 /opt/app/config/application.yml
1. 실습 디렉터리 생성
mkdir -p /tmp/perm-lab && cd /tmp/perm-lab && touch config.yml secret.key deploy.sh && echo '실습 파일 생성 완료'2. 파일 권한 설정
chmod 640 /tmp/perm-lab/config.yml && chmod 600 /tmp/perm-lab/secret.key && chmod 750 /tmp/perm-lab/deploy.sh && ls -la /tmp/perm-lab/3. 권한 의미 확인
stat -c '%n: mode=%a (%A)' /tmp/perm-lab/*- ls -la 출력에서 먼저 맨 앞 권한 문자열(drwxr-x---)을 보고, 그 다음 소유자:그룹 필드 확인 — 권한 숫자와 소유자가 함께 맞아야 서비스가 파일을 읽을 수 있음
- 권한 기준: 600=소유자만 읽기/쓰기(SSH 키, DB 패스워드), 640=그룹 읽기 추가(설정 파일), 755=모든 사용자 읽기/실행(스크립트), 777이면 즉시 수정 — others 쓰기 권한은 보안 사고 원인
- 권한이 640인데 서비스 계정이 파일을 못 읽으면 → 소유자 그룹과 서비스 계정 그룹이 일치하지 않는 것 — id <서비스계정> 으로 그룹 확인 후 chown 또는 usermod -aG로 그룹 추가
sudo 설정
visudo로 sudo 권한 안전하게 설정
배포 계정에 NOPASSWD: ALL을 걸어줬다가 보안 감사에 걸렸습니다. 모든 명령어를 root 권한으로 실행할 수 있다는 것은 사실상 root 계정을 하나 더 만든 셈이기 때문입니다. 반대로 권한이 너무 좁게 설정되면 배포 스크립트가 실행 중간에 멈춥니다. 어떤 명령어에만 sudo를 허용할지 정확히 지정해야 보안과 운영 편의성을 동시에 잡을 수 있습니다.
/etc/sudoers 파일을 직접 편집하면 문법 오류 시 sudo 전체가 망가집니다. 반드시 visudo를 사용하세요.
# sudoers 파일 편집 (문법 검증 포함)
sudo visudo
# 또는 /etc/sudoers.d/ 에 별도 파일로 관리 (권장)
sudo visudo -f /etc/sudoers.d/deploy
실무에서 자주 쓰는 설정 패턴:
# /etc/sudoers.d/deploy 파일 내용
# deploy 사용자가 비밀번호 없이 서비스 재시작 가능
deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart tomcat
deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx
# ops-admin 그룹은 모든 sudo 가능 (패스워드 필요)
%ops-admin ALL=(ALL) ALL
# 인자를 제한하지 않은 cp/mv/chown을 NOPASSWD로 허용하지 않습니다.
# 필요한 작업은 root 소유의 인자 검증 래퍼(허용 경로·파일명만 허용)를 만들고
# 그 래퍼 하나만 sudoers에 등록한 뒤, visudo -c로 문법을 검증합니다.
# 현재 sudo 권한 확인
sudo -l
# sudo로 다른 사용자로 명령 실행
sudo -u tomcat /opt/tomcat/bin/startup.sh
# sudo 이력 확인 (audit 목적)
sudo grep sudo /var/log/auth.log | tail -20 # Ubuntu
sudo grep sudo /var/log/secure | tail -20 # RHEL
주의: NOPASSWD: ALL은 매우 위험합니다. 특정 명령어만 NOPASSWD로 설정하는 것이 원칙입니다.
systemctl 서비스 관리
systemctl 핵심 명령어
서버를 재부팅했더니 Tomcat이 자동으로 뜨지 않았습니다. systemctl enable을 빠뜨렸기 때문입니다. start는 지금 당장 서비스를 켜는 것이고, enable은 부팅 때 자동으로 켜지도록 등록하는 것입니다. 이 차이를 모르면 서버 재시작 후 "서비스가 죽었다"는 알람을 받게 됩니다.
systemd 기반의 현대 Linux에서 서비스 관리는 systemctl로 합니다. service 명령어는 내부적으로 systemctl을 호출하는 wrapper로 더 이상 직접 사용하지 않습니다.
자주 쓰는 systemctl 명령어:
# 서비스 상태 확인
systemctl status nginx
systemctl status tomcat
# 서비스 시작/중지/재시작
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
# 설정 파일 재로드 (재시작 없이 — Nginx에서 자주 씀)
sudo systemctl reload nginx
# 부팅 시 자동 시작 등록 (start와 enable은 독립적)
sudo systemctl enable nginx
sudo systemctl enable --now nginx # enable + start 동시
# 부팅 자동 시작 해제
sudo systemctl disable nginx
# 실행 중인 서비스 목록
systemctl list-units --type=service --state=running
# 실패한 서비스 목록 (장애 시 가장 먼저 확인)
systemctl list-units --type=service --state=failed
# 서비스 로그 확인 (journald)
journalctl -u nginx --since "1 hour ago"
journalctl -u tomcat -n 100 --no-pager # 마지막 100줄
journalctl -u nginx -f # 실시간 팔로우
서비스 상태 해석:
systemctl status nginx
# ● nginx.service - A high performance web server
# Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
# Active: active (running) since Thu 2025-01-15 14:30:00 KST; 2h 30min ago
# Process: 1234 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
# Main PID: 1235 (nginx)
# CGroup: /system.slice/nginx.service
# ├─1235 nginx: master process /usr/sbin/nginx
# └─1236 nginx: worker process
# Loaded 줄: enabled → 부팅 시 자동 시작, disabled → 자동 시작 안 됨
# Active 줄: active(running) → 정상 실행 중
# failed → 시작 실패
# inactive(dead) → 중지 상태
custom systemd service 파일 작성 (자체 앱 등록):
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application Server
After=network.target
[Service]
Type=forking
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/start.sh
ExecStop=/opt/myapp/bin/stop.sh
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
# 새 unit 파일 등록
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
실습용 서비스 디렉터리 생성
sudo mkdir -p /opt/myapp/{bin,config,logs,webapps} && sudo useradd -r -s /sbin/nologin -d /opt/myapp -M appuser 2>/dev/null; echo done디렉터리 소유권 설정
sudo chown -R appuser:appuser /opt/myapp && ls -la /opt/myapp각 용도에 맞는 권한 설정
sudo chmod 755 /opt/myapp/bin && sudo chmod 750 /opt/myapp/config && sudo chmod 755 /opt/myapp/logs && ls -la /opt/로그 디렉터리에서 최근 변경 파일 찾기
find /var/log -type f -mmin -30 -name '*.log' 2>/dev/null | head -10systemctl 상태 점검 루틴
systemctl list-units --type=service --state=failed 2>/dev/null && echo '---failed services above---' && systemctl list-units --type=service --state=running | wc -l- ls -la /opt/myapp/ 에서 먼저 맨 왼쪽 권한 문자열을 보고, 그 다음 소유자:그룹 필드 확인 — bin(755), config(750), logs(755) 순서로 각 디렉터리 용도에 맞는 권한인지 체크
- 권한 기준: config가 750 미만(예: 700)이면 그룹 계정이 설정을 못 읽음, 755 이상이면 외부 계정에 설정 파일 노출 위험 — 설정 디렉터리는 반드시 750이 표준
- bin이 755인데 config도 755이면 → 설정 파일이 others에게 읽히는 구조 — DB 패스워드 등 민감 정보가 있는 config 디렉터리는 반드시 chmod 750 /opt/myapp/config 실행
심화 — 권한은 '바꾸기' 전에 '만들어질 때' 정해진다
심화: umask와 기본 권한 — chmod가 안 먹는 것처럼 보일 때
chmod로 권한을 잡는 법은 배웠지만, 실무에서 더 자주 발목을 잡는 건 "내가 chmod 하기도 전에, 또는 chmod 한 뒤에도 파일이 엉뚱한 권한으로 생기는" 상황입니다. 그 뒤에는 umask가 있습니다.
- 생성 시점의 권한: 프로세스가 파일을 새로 만들 때 커널은 요청 모드(파일 기본
666, 디렉터리777)에서 그 프로세스의 umask 비트를 빼서 최종 권한을 정합니다. 로그인 셸의 흔한 umask022면 새 파일은644, 새 디렉터리는755가 됩니다. 즉chmod는 '이미 있는' 파일을 고치는 것이고, umask는 '앞으로 생길' 파일의 기본값을 정합니다. - 왜 chmod가 안 먹는 것처럼 보이나: 로그 파일·세션 파일처럼 서비스가 스스로 매번 새로 만드는 파일은, 내가
chmod 640을 걸어도 서비스가 재기동하며 자기 umask로 다시 만들면 권한이 원위치됩니다. 사후 chmod는 재생성 한 번에 덮입니다. - 생성 권한을 통제하는 법: 프로세스 단위로는
systemd유닛의UMask=0027지시어로 그 서비스가 만드는 파일의 기본 권한을 좁힐 수 있습니다. 앱 자체 설정(로그 파일 permission 옵션)이 있으면 그걸 쓰고, 그마저 없으면 상위 디렉터리 권한(750)으로 others 접근을 원천 차단하는 것이 가장 확실합니다 — 파일 권한이 느슨해도 디렉터리에서 막히면 못 들어갑니다.
정리하면 권한 사고는 '누가 이 파일을 만드는가'를 먼저 물어야 합니다. 내가 만들면 umask·chmod로, 서비스가 만들면 그 서비스의 생성 권한(UMask·앱 설정)과 디렉터리 권한으로 통제합니다.
상황: DB 접속정보가 섞여 나오는 애플리케이션 로그 파일에 chmod 640을 걸어 두는데, 며칠 뒤 감사에서 또 644(others 읽기 가능)로 지적됩니다. 확인해 보면 서비스 재기동·로그 롤링 직후에 권한이 풀려 있습니다.
원인: 그 로그 파일은 내가 만든 게 아니라 서비스가 실행 중 스스로 새로 생성합니다. 서비스 프로세스의 umask가 022라, 파일이 만들어지는 순간 644로 태어납니다. 배포 때 손으로 건 chmod 640은 다음 재생성 때 그대로 덮여 무력화됩니다.
진단: 파일을 누가 만드는지부터 확인합니다 — stat과 ls -l로 소유자·타임스탬프를 보고, 서비스 재기동 직후 권한이 바뀌는지 관찰합니다. 서비스 프로세스의 umask는 cat /proc/<PID>/status의 Umask 항목으로 확인합니다(대개 0022).
해결: 사후 chmod를 반복하지 말고 생성 시점을 잡습니다. systemd 서비스라면 유닛 [Service]에 UMask=0027을 넣어 그 서비스가 만드는 파일이 처음부터 640으로 태어나게 합니다. 앱에 로그 permission 설정이 있으면 그것을 쓰고, 둘 다 여의치 않으면 로그 디렉터리 자체를 chmod 750으로 막아 others 접근을 원천 차단합니다. 재발 방지의 핵심은 'chmod를 또 걸기'가 아니라 '느슨하게 만들어지지 않게 하기'입니다.
실제 업무에서 이 지식이 쓰이는 상황:
파일 권한과 systemctl은 매일 쓰입니다. 특히 배포 직후에는 반드시 확인해야 합니다.
배포 체크리스트 예시:
- WAR/JAR 파일이 올바른 위치에 있는가? (
ls -la /opt/app/webapps/) - 파일 소유자가 서비스 계정인가? (
ls -la) - 설정 파일 권한이 640인가? (비밀번호 노출 방지)
- 서비스가 정상 시작됐는가? (
systemctl status) - 로그에 에러가 없는가? (
journalctl -u servicename -n 50)
"배포했는데 안 됩니다"라는 제보의 절반은 이 체크리스트 중 하나가 빠진 경우입니다.
명령어·단축키 빠른 참조
파일시스템 구조·권한·systemctl 서비스 관리에서 다룬 명령을 모았습니다.
| 명령어/단축키 | 용도 | 자주 쓰는 예 |
|---|---|---|
du -sh | 디렉터리 용량 확인(디스크 풀 원인) | du -sh /var/log/* | sort -rh | head |
find | 조건별 파일 탐색(로그·임시파일) | find /var/log -mmin -30 -name '*.log', find /tmp -mtime +7 |
ls -la / stat | 권한·소유자 확인 | stat -c '%n: %a (%A)' file, ls -la /opt/ |
chmod | 권한 설정(숫자 모드) | chmod 640 config.yml, chmod -R 755 /opt/tomcat/bin |
chown / chgrp | 소유자·그룹 변경(배포 후 필수) | chown -R tomcat:tomcat /opt/tomcat/webapps |
visudo | sudoers 안전 편집(문법 검증 포함) | visudo -f /etc/sudoers.d/deploy |
sudo -l / sudo -u | sudo 권한 점검·다른 계정 실행 | sudo -l, sudo -u tomcat /opt/tomcat/bin/startup.sh |
usermod -aG | 그룹 추가(sudo/wheel) | usermod -aG wheel username → 재로그인 또는 newgrp |
systemctl status | 서비스 상태·enabled 여부 | systemctl status nginx(Loaded·Active 줄 확인) |
systemctl start/stop/restart/reload | 서비스 제어 | systemctl reload nginx(무중단 재적용) |
systemctl enable --now | 부팅 자동시작+즉시 시작 | systemctl enable --now nginx |
systemctl list-units --state=failed | 실패 서비스 확인(장애 1순위) | systemctl list-units --type=service --state=failed |
journalctl -u | 서비스 로그 조회 | journalctl -u tomcat -n 100 --no-pager, -f(실시간) |
systemctl daemon-reload | unit 파일 변경 반영 | daemon-reload && systemctl enable --now myapp |
관련 모듈로 더 깊이:
- Linux 사용자/그룹/권한 체계와 서비스 계정 관리 — chmod/chown을 사용자·그룹·서비스 계정 관점에서 더 깊이
- 프로세스(ps), 포트(netstat), 리소스(top) 모니터링 실무 — 파일시스템 다음 단계인 프로세스/포트/리소스 점검 실무
- SSH 접속 보안 강화와 배스천 호스트 기반 접근 제어 — 서버에 안전하게 접속하기 위한 SSH 키 인증과 접근 제어
다음 모듈에서는 실행 중인 서비스의 상태를 프로세스/포트/리소스 관점에서 점검하는 방법을 다룹니다.