기동 오류 추적
120분 안팎
학습 목표
제공된 잘못된 경로·권한·환경 설정 사례를 journalctl로 구분해 수정합니다.
개념
다시 시작하기 전에 실패한 단계를 찾습니다
기동 실패를 만났을 때 재시작을 반복하면 마지막 로그가 길어지고 최초 원인이 묻힙니다. 이번 목표는 경로 부재·읽기 권한·환경 값 오류를 구분해 최소 변경으로 되돌리는 것입니다. 같은 failed 상태도 프로그램 실행 전 실패와 Python 실행 후 예외가 다릅니다. 먼저 대상 유닛과 관측 시각을 고정하고 상태의 Result·ExecMainStatus와 해당 시각 저널 원문을 함께 읽습니다. 실패 횟수만으로 파일 권한 문제를 단정하지 않습니다.
journalctl은 저장된 저널을 조회합니다. stdout와 stderr를 journal로 연결한 이 유닛의 앱 메시지와 서비스 관리자의 기동 결과를 찾을 수 있습니다. 모든 앱이 모든 로그를 저널에 남기는 것은 아닙니다. 파일로만 기록하는 앱은 그 파일도 확인해야 합니다. 이번 service.py는 설정을 읽다가 예외가 나면 예외 클래스와 원인을 stderr 한 줄로 남기므로 어느 단계가 실패했는지 추적하기 쉽습니다.
필터를 넓게 시작하고 좁힙니다
첫 조회는 journalctl -u bootcamp-systems.service -b -n 80 --no-pager -o short-iso로 현재 부팅의 최근 기록을 봅니다. 날짜와 시간대가 함께 표시된 시각을 읽고 --since와 --until로 실패 구간을 좁힙니다. 부팅을 바꾸면 과거 기록이 조회 가능한지는 저장 정책에 따라 달라집니다. 이번 과제는 재부팅으로 실패를 재현하지 않습니다. 단순히 빈 조회가 나왔다고 정상 상태를 선언하지 않습니다.
-p err는 저널 우선순위 필터이며 MESSAGE에 ERROR 글자가 있는 줄을 찾는 문자열 검색과 다릅니다. stderr라는 이유만으로 원하는 우선순위가 자동 지정되었다고 가정하면 필요한 Python 예외를 놓칠 수 있습니다. 처음에는 우선순위 필터 없이 읽고 필드와 메시지를 확인합니다. 관리자에게 허용된 VM에서 sudo로 조회하되 서비스 계정에 전체 저널 접근 권한을 새로 주지는 않습니다.
실행 전 실패를 읽습니다
WorkingDirectory가 없거나 접근할 수 없으면 Python 실행 전 디렉터리 변경에서 실패할 수 있습니다. 200/CHDIR는 이 단계의 진단 단서입니다. ExecStart의 실제 실행 파일을 시작하지 못하면 203/EXEC가 단서이며 인터프리터 부재·실행 권한·실행 형식 등 여러 가능성이 있습니다. 숫자 하나로 세부 원인을 확정하지 않고 저널의 Failed at step 문장과 실제 경로를 대조합니다. Python traceback이 없는 상황도 이때는 설명 가능합니다.
이 앱은 /usr/bin/python3가 app.py를 읽는 방식입니다. 스크립트 읽기 불가가 항상 203/EXEC로 나온다고 암기하지 않습니다. 인터프리터는 실행됐으나 Python이 파일을 못 열면 앱 쪽 종료와 메시지로 나타날 수 있습니다. 경로는 인터프리터·작업 디렉터리·앱·데이터 각각 따로 확인합니다. missing이라는 단어만 검색하지 않고 누가 어떤 경로를 열려 했는지를 먼저 적습니다.
파일 부재와 권한 거부를 나눕니다
DATA_FILE이 없는 절대 경로를 가리키면 service.py의 사전 읽기에서 FileNotFoundError가 발생합니다. 이때 데이터 파일을 새로 만들어 성공시키는 것이 답은 아닙니다. 이전 데이터가 있는지 확인하고 환경 값을 그 경로로 돌립니다. JSON 파일이 존재하지만 JSON 구조가 깨졌다면 JSONDecodeError라는 다른 진단을 예상합니다. 파일 존재와 내용 해석을 같은 검사로 취급하면 데이터 손상을 경로 오류로 오해할 수 있습니다.
PermissionError는 해당 실행 주체의 경로 통과와 파일 읽기 권한을 확인하라는 단서입니다. 부모 디렉터리의 실행 권한과 파일의 읽기 권한을 나눠 봅니다. ls -ld로 경로 구성 요소를 확인하고 sudo -u bcweb test -r로 앱 관점의 접근을 확인합니다. 관리자 cat 성공은 bcweb 읽기 성공을 입증하지 못합니다. chmod 777이나 root 실행은 증상을 덮지만 서비스의 최소 권한을 설명하지 못합니다.
환경 값도 입력 데이터입니다
RUN_SECONDS=oops면 정수 변환의 ValueError가 발생하고 4나 31이면 허용 범위 검사에서 ValueError가 발생합니다. DATA_FILE 키 자체가 없으면 KeyError가 발생할 수 있습니다. 같은 클래스라도 상세 문장을 읽어 형식과 범위 오류를 구분합니다. 로그인 셸에서는 export가 있었는데 서비스 파일에는 키가 없다면 셸 실행 성공을 서비스 환경의 증거로 쓸 수 없습니다. EnvironmentFile의 실제 경로와 내용을 확인합니다.
이 앱은 바인딩 전에 데이터와 환경을 검증합니다. /health가 잠깐 성공한 뒤 첫 /items에서 실패하는 방식보다 잘못된 기동 설정을 빠르게 드러냅니다. 사전 읽기 성공 뒤 누군가 데이터를 변경하는 상황까지 없애 주는 것은 아닙니다. 변경 시각과 파일 권한을 통제해야 결과를 재현할 수 있습니다. 에러 메시지에는 환경 전체를 덤프하지 않으며 비밀값이 포함된 원문은 공개 증거에서 제외합니다.
실패 사례를 안전하게 주입합니다
journal 실습의 검증기는 환경 파일을 보관한 뒤 없는 DATA_FILE, 잘못된 RUN_SECONDS를 순서대로 적용합니다. 읽기 권한 사례는 원래 items.json의 권한을 바꾸지 않고 별도의 root 전용 m04-denied.json을 만들어 사용합니다. 마지막으로 존재하지 않는 WorkingDirectory를 주입해 Python 실행 전 실패를 비교합니다. 한 번에 한 조건만 바꾸며 원본 데이터와 이전 app.conf는 손대지 않습니다.
각 사례가 끝나면 서비스의 자연 종료를 기다리고 다음 조건으로 넘어갑니다. Restart=no여서 재시작 제한에 원인이 묻히지 않습니다. reset-failed는 실패 기록과 제한 카운터를 초기화하는 작업이며 파일이나 환경 값을 고치는 작업은 아닙니다. 원인을 되돌린 후에 새 기동을 검사합니다. 유닛 경로 변경은 daemon-reload가 필요하고 환경 파일 내용은 다음 기동 때 읽습니다.
관측·가설·수정을 한 줄로 연결합니다
evidence의 사례별 로그에는 원문 시각과 유닛을 유지합니다. service.md에는 관측한 예외·어떤 설정과 연결했는지·어떤 최소 변경을 했는지·복구 확인을 기록합니다. FileNotFoundError만 적으면 없는 경로가 앱인지 데이터인지 알 수 없습니다. DATA_FILE 값과 원래 데이터 경로를 연결해 설명합니다. 권한 사례는 서비스 계정 관점 검사와 수정 전후 차이를 제시해 관리자 읽기와 구분합니다.
복구는 상태의 failed가 사라진 것으로 끝내지 않습니다. 원래 환경 값과 유닛 해시를 확인하고 새로운 기동 중 active·bcweb PID·/items의 원래 데이터를 다시 검사합니다. 정상 기동 뒤 남은 예전 오류 로그는 새 실패가 아닐 수 있어 시각 범위를 적습니다. 원문을 삭제해 정상처럼 보이게 만드는 대신 재현과 복구 시점을 분리합니다. 실패가 난 지점으로부터 가장 가까운 증거를 먼저 선택합니다.
검사 통과의 한계를 적습니다
check.sh는 정해진 오류 문자열과 복원 후 응답을 확인하며 모든 Linux 배포판의 문장이나 모든 장애 원인을 인증하지 않습니다. OS 버전별 세부 문구가 다르면 원인을 대조하고 검사 조건의 적절성을 검토합니다. 사람은 service.md의 원인 연결과 원문 시각이 맞는지도 읽습니다. 아무 로그도 없으면 조회 권한·시간 범위·출력 대상부터 점검하며 기록이 없다는 것을 성공이라는 뜻으로 바꾸지 않습니다.
실습 외 앱이나 다른 서비스의 설정을 바꾸지 않습니다. 단순 진단을 위해 서버를 재부팅하거나 다른 프로세스를 종료할 필요도 없습니다. 유닛 실패 코드의 의미는 systemd.exec 공식 문서, 조회 필터는 journalctl 공식 문서와 대조합니다. 자세한 로그 운영 정책은 더 읽기로 보냅니다.
따라하기
예외 분류 연습
교육용 환경 값을 실제 Python으로 변환합니다. 실제 저널 출력으로 제시하지 않습니다.
for value in ['20','oops','31']:
try:
seconds=int(value)
if not 5 <= seconds <= 30:raise ValueError('RUN_SECONDS must be 5..30')
print('ok',seconds)
except ValueError as e:print(type(e).__name__,str(e))실행 결과
ok 20 ValueError invalid literal for int() with base 10: 'oops' ValueError RUN_SECONDS must be 5..30
대상과 시간으로 조회
VM에서 최근 기동의 원문과 서비스 종료 상태를 읽습니다. ExecMainStatus와 예외 문장을 연결해 기록하며 빈 결과면 권한·범위를 확인합니다.
systemctl show bootcamp-systems.service -p Result -p ExecMainStatus
sudo journalctl -u bootcamp-systems.service -b -n 80 --no-pager -o short-iso검증기가 단일 조건 주입
journal ZIP의 유닛과 환경 TODO를 고칩니다. 검사는 새 fixture를 써서 원본 데이터 권한을 유지합니다. 사례별 로그에서 FileNotFoundError·ValueError·PermissionError·CHDIR를 찾습니다.
bash check.sh
cat evidence/path.log
cat evidence/environment.log
cat evidence/permission.log
cat evidence/working-directory.log복구 근거 제출
service.md의 자동 설명을 실제 원문과 대조합니다. restored.log의 새 기동 시각과 natural exit를 읽고 복원 HTTP 검사가 통과했는지 확인합니다.
cat evidence/restored.log
cat evidence/service.md확인 문제
실습
유닛과 환경 TODO를 수정한 후 검증기가 제공하는 경로·환경·권한·작업 디렉터리 오류를 원문 로그에서 구분합니다. evidence/service.md에 최소 수정 이유와 복구 시각을 보완합니다. 실제 Linux VM external 검사입니다.
실행 명령
bash check.sh
기대 결과
PASS journal: unit, account, HTTP, journal, backup hashes, restore; natural exit
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- 파일 권한 때문에 서비스가 실패하는 상황을 설명합니다.