LabHub
배우기 러닝패스 코스

FDE 캡스톤: 창고가 같은 주문을 세 번 받았다 · 고객 VM 에 서비스로 설치하기 · 실습

재부팅했더니 아무것도 뜨지 않았다

LabHub 에서 이어서 보기

이 실습은 진짜 VM 에서 돕니다

우분투 24.04 VM 한 대입니다. systemd 가 PID 1 이고 쿠버네티스는 없습니다. 준비 단계가 작은 주문 조회 앱(/opt/orders-svc/app.py)을 깔고, 이전 담당자가 셸에서 nohup 으로 띄워 둔 상태를 재현해 둡니다. 채점은 VM 안의 에이전트가 하므로 재부팅하지 마세요 — 채점이 끊깁니다. 부팅 때 뜨는지는 enable 상태와 target 의존으로 판정합니다.

목표

고객 VM 에 앱을 전용 사용자·환경 파일·재시작 정책·부팅 target 을 갖춘 systemd 서비스로 설치하고, enable 누락·kill -9·daemon-reload 누락·환경 파일 교체가 각각 무엇을 바꾸는지 직접 확인한 뒤, 인계용 점검 스크립트로 끝낸다.

왜 중요한가

"설치했고 잘 돈다" 는 지금 떠 있다는 뜻일 뿐이다. 셸에서 띄운 프로세스는 서비스 관리자가 모르는 프로세스라 죽어도 아무도 되살리지 않고, 재부팅하면 아무도 띄우지 않는다. systemctl start 만 하고 enable 을 빠뜨려도 똑같다. 부팅 때 기동은 [Install] 섹션이 enable 때 만드는 심링크 하나에 달려 있고, 유닛 파일을 고친 뒤 daemon-reload 를 잊으면 systemd 는 옛 설정으로 계속 돈다. 이 차이들을 한 번씩 손으로 만들어 봐야 고객 현장에서 "왜 아무것도 안 떠요" 에 5분 안에 답한다.

예상 60분이다. 준비에 몇 분이 걸린다. 세션이 끝나면 VM 이 사라지니 남기고 싶은 파일은 종료 전에 따로 보관한다.

단계

1. 8181 포트를 잡고 있는 이전 담당자의 프로세스를 찾아 /root/svc/legacy.txtpid=·user=·cgroup=(/proc/<pid>/cgroup 의 경로)·unit=(그 경로의 마지막 조각)·survives_reboot=(yes 또는 no) 다섯 줄로 적는다.
2. 로그인할 수 없는 시스템 계정 orders 를 만들고, /var/lib/orders(소유자 orders, 750)와 /etc/orders/orders.env(root:orders, 640)를 만든다. 환경 파일에는 ORDERS_PORT=8181, 16자 이상의 새 ORDERS_TOKEN, ORDERS_DATA_DIR=/var/lib/orders 를 넣는다.
3. /etc/systemd/system/orders.service 를 쓴다(User=orders, EnvironmentFile, ExecStart=/usr/bin/python3 /opt/orders-svc/app.py, Restart=on-failure, Wants·After=network-online.target, WantedBy=multi-user.target). daemon-reload 뒤 시작하면 실패한다. journal 에서 앱이 남긴 원인 줄(bind failed pid=…)을 찾아 /root/svc/why-failed.txt 에 그대로 옮긴다.
4. 이전 담당자의 프로세스를 내리고 orders.service 를 기동한다. curl http://127.0.0.1:8181/healthz 가 orders 사용자와 서비스의 주 프로세스 PID 로 응답해야 한다.
5. 서비스를 enable 하고 그때 나온 출력(만들어진 심링크 경로 포함)을 /root/svc/enable.txt 에 저장한다.
6. 주 프로세스를 kill -9 로 죽이고 systemd 가 되살리는지 본다. /root/svc/kill.txtold_pid=·new_pid= 를 적는다.
7. 유닛에 샌드박싱(NoNewPrivileges=yes, ProtectSystem=strict, ProtectHome=yes, PrivateTmp=yes, ReadWritePaths=/var/lib/orders)을 더한다. 바꾸기 전에 systemd-analyze security 점수를 재 두고, 파일을 고친 직후 systemctl 이 내는 daemon-reload 경고를 /root/svc/reload.txt 에 저장한 뒤, daemon-reload·재시작하고 다시 잰다. /root/svc/security.txtbefore=·after= 를 적는다.
8. 환경 파일의 ORDERS_TOKEN 을 새 값으로 바꾸고 서비스에 반영한다. /root/svc/rotate.txtold_token_sha=·new_token_sha=(토큰의 sha256 16진수)와 daemon_reload_needed=(yes 또는 no)를 적는다.
9. 인계용 점검 스크립트 /root/svc/verify.sh 를 쓴다. bash verify.sh <유닛> 이 그 유닛이 재부팅 뒤에도 뜰 조건(로드됨, enabled, WantedBy 에 multi-user.target, Restart 가 on-failure 또는 always, Wants 와 After 에 network-online.target, User 가 비어 있지 않고 root 가 아님, NeedDaemonReload=no)을 모두 갖추면 0, 하나라도 어기면 어긴 항목을 출력하고 0 이 아닌 값으로 끝난다. 채점기는 준비된 다른 유닛들(orders-audit·orders-report·orders-worker·orders-sync·orders-rootjob)에도 돌려 본다.

참고

단계 9개

  1. 8181 을 잡고 있는 떠돌이 프로세스
  2. 전용 계정과 환경 파일
  3. 첫 기동이 실패한 이유를 journal 에서
  4. 떠돌이를 내리고 서비스로 세우기
  5. enable 이 만드는 심링크 하나
  6. kill -9 해도 되살아나는가
  7. 샌드박싱과 daemon-reload 누락
  8. 토큰 교체는 무엇으로 반영되나
  9. 재부팅 없이 부팅을 증명하는 점검 스크립트