← 글 목록

라즈베리파이 클러스터 트러블슈팅 회고와 nginx 배포 설정 점검 (연재 3/3)

/ 6분 분량

과거 PR 기록을 되짚어 라즈베리파이 클러스터에서 겪은 사건들을 정리하다가, 실제 운영 중인 nginx 설정과 배포 스크립트에서 위험 요소를 발견해 함께 고친 하루를 정리했습니다.

학습 주제

  • 라즈베리파이 클러스터 인프라 트러블슈팅 사례 정리 (반복 재부팅, 호스트네임 초기화)
  • nginx 리버스 프록시 설정과 배포 스크립트 점검
  • 날짜: 2026-08-04

탐구 과정

과거에 겪었던 사건들이 기억 속에서 흐릿해지기 전에 문서로 남기고 싶었다. 특히 세 가지가 유독 기억에 남았다. nginx의 포워드(프록시) 정책을 설정했던 일, 라즈베리파이의 펌웨어 폴더까지 들어가서 호스트네임을 고쳤던 일, 그리고 원인 모르게 자꾸 전원이 꺼지던 문제. 이 사건들을 그냥 흘려보내지 않고 정리해두면 나중에 같은 실수를 반복하지 않을 것 같아서, git 로그와 PR 히스토리, 그리고 이미 블로그에 발행된 인프라 사고 글들을 다시 훑어봤다.

정리하다 보니 자연스럽게 지금 운영 중인 nginx 설정 파일(deployment/nginx-blog.conf)도 다시 들여다보게 됐는데, 여기서 실제 운영 환경과 어긋나는 부분을 발견했다. 회고 목적으로 시작한 작업이 뜻밖에 실제 버그 발견으로 이어진 셈이다.

핵심 학습 내용

1. 전원이 자꾸 꺼졌던 문제 (2026-07-14~18)

원인이 하나가 아니라 세 개가 겹쳐 있었다.

  • EEPROM의 USB_MSD_STARTUP_DELAY 설정 문제
  • crontab + Ansible로 걸려 있던 자동 재부팅 스케줄
  • systemd에 좀비처럼 남아 무한 재시작을 반복하던 서비스

증상은 하나(전원 꺼짐)인데 원인이 여러 개 겹쳐 있을 수 있다는 걸 몸으로 배운 사건이었다. "왜 자꾸 꺼지지?"라는 질문에 답을 하나로 특정하려다 오래 헤맸다.

2. 펌웨어 폴더까지 가서 호스트네임을 고친 사건 (2026-02-25~03-03)

클러스터의 호스트네임이 자꾸 원래대로 돌아가는 문제였다. /etc/hostname을 고쳐도 재부팅하면 초기화되어 있었는데, 범인은 /boot/firmware/user-data(cloud-init 설정 파일)였다. 부팅할 때마다 이 파일이 호스트네임을 덮어쓰고 있었던 것이다. 여기에 워커 노드의 와이파이 단절까지 겹치면서 실제 서비스 장애로 번졌다.

# 부팅 시마다 hostname을 초기화하던 원인
/boot/firmware/user-data  # cloud-init 설정 파일

호스트네임처럼 당연히 /etc/ 밑에서 관리될 거라 생각했던 설정이, 사실은 부팅 단계의 cloud-init이 매번 덮어쓰고 있었다는 걸 알게 된 게 핵심이었다.

3. nginx 리버스 프록시 정책과 배포 스크립트

오늘 가장 신경 써서 손댄 부분이다. deployment/nginx-blog.conf가 실제 운영 방식과 어긋나 있었다.

  • 기존 설정은 root + try_files로 정적 파일을 직접 서빙하는 걸 가정하고 있었는데, 실제로는 proxy_pass http://localhost:4321로 Node 서버에 요청을 넘기는 구조였다. 설정 파일과 실제 운영 아키텍처가 어긋나 있었던 것.
  • limit_req_zone은 http 블록에서만 선언 가능한데, 기존엔 server 블록 안(nginx-blog.conf)에 넣으려고 해서 별도 파일(nginx-ratelimit.conf)로 분리해야 했다.
  • 가장 아찔했던 발견은 deployment/redeploy.sh였다. 이 스크립트가 pm2 start ecosystem.config.cjs를 --env production 옵션 없이 실행하고 있었는데, 이 스크립트는 자동배포(GitHub Actions)에서 쓰이는 스크립트였다. 즉 다음 자동배포가 돌 때 NODE_ENV=production 설정이 조용히 development로 되돌아갈 수 있는 상황이었다.
# 위험했던 부분
pm2 start ecosystem.config.cjs

# 수정
pm2 start ecosystem.config.cjs --env production

이해한 내용

  • 장애 원인은 종종 하나가 아니라 여러 개가 동시에 작동한다. "증상 = 원인"으로 단순하게 매칭하려 하면 놓치는 게 생긴다.
  • 설정 파일은 여러 계층(예: /etc/hostname vs cloud-init의 /boot/firmware/user-data)에 걸쳐 있을 수 있고, 겉으로 당연해 보이는 위치가 실제 진실의 원천이 아닐 수 있다.
  • nginx 설정처럼 배포 파일은 실제 운영 환경과 주기적으로 대조하지 않으면 조용히 낡아버린다. 문서를 정리하다가 실제 버그를 발견한 것도 이 때문이었다.
  • 배포 스크립트의 플래그 하나(--env production)가 빠진 것만으로 보안 설정 전체가 되돌아갈 수 있다는 걸 확인했다. 자동화된 배포 파이프라인일수록 이런 회귀를 사람이 놓치기 쉽다.

실전 적용

  • 오늘 정리한 사건들은 시간순(최신 위)으로 기록해뒀다. 나중에 비슷한 증상을 만나면 여기부터 찾아볼 생각이다.
  • nginx 설정과 배포 스크립트는 코드만큼이나 실제 운영 상태와의 동기화가 중요하다는 걸 느꼈고, 앞으로 배포 관련 파일을 고칠 때는 실제 서버에 반영된 설정과 비교해보고 시작하는 습관을 들이려 한다.
  • deployment/ 경로 변경은 배포 워크플로우를 트리거하기 때문에, production 환경 승인 절차도 함께 챙겨야 한다는 걸 다시 확인했다.

추가 학습 계획

  • cloud-init의 동작 원리를 더 제대로 이해하고 싶다. 어떤 파일이 어느 시점에 어떤 우선순위로 적용되는지 정리해보고 싶다.
  • systemd 서비스가 좀비처럼 무한 재시작되는 걸 방지하는 설정(RestartSec, StartLimitBurst 등)을 따로 공부해볼 계획이다.
  • nginx의 limit_req_zone, limit_req 옵션을 좀 더 깊이 파서 트래픽 제한 정책을 세밀하게 튜닝해보고 싶다.
  • PM2 ecosystem 설정에서 환경별(--env) 분기가 어떻게 동작하는지, 다른 실수할 여지는 없는지 점검해볼 예정이다.