Prometheus 설정 리로드 실패 — 단일 파일 bind mount와 inode

기초·개념3분조회 –

Prometheus 설정을 고쳐 올려도 반영이 안 됐습니다. /-/reload 를 치면 HTTP 200 이 오고 로그에도 “configuration reloaded successfully” 가 찍히는데, 정작 도는 건 옛날 설정입니다. docker compose restart 를 해도 똑같았습니다.

docker compose 로 띄우고 설정 파일은 호스트에 두고 컨테이너 안으로 마운트해서 씁니다. 타겟을 추가하거나 룰을 고칠 때는 호스트 파일을 갱신하고 /-/reload 만 부르면 되는 구조입니다.

이 방식을 쓰는 건 컨테이너를 다시 만들 필요가 없어서입니다. 타겟 하나 추가하자고 Prometheus 를 내렸다 올리면 그 사이 수집이 끊깁니다.

믿을 수 없는 reload 응답

/-/reload 의 200 응답은 “파일을 다시 읽었다” 지 “내가 방금 고친 내용이 반영됐다” 가 아닙니다. 컨테이너가 보고 있는 파일을 다시 읽었을 뿐이고 그 파일이 여전히 옛날 내용이면 옛날 걸로 잘 리로드됩니다.

반영됐는지는 응답 대신 /api/v1/targets 를 직접 쿼리해서 봐야 합니다. 타겟 목록이 그대로면 리로드는 됐지만 내용이 안 바뀐 겁니다.

원인 — 단일 파일 bind mount의 inode 고정

compose 에서 설정을 마운트한 자리는 이랬습니다.

volumes:
  - ./prometheus.prod.yml:/etc/prometheus/prometheus.yml

파일 하나를 콕 집어 마운트하면 도커는 그 파일의 inode 를 컨테이너 안에 묶으니, 경로가 아니라 inode 가 기준이 됩니다.

그런데 rsync 는 파일을 제자리에서 고치지 않고 임시 파일에 쓴 다음 원자적으로 rename 하고, mv 도 그렇고 임시 파일을 만들었다 교체하는 대부분의 에디터도 마찬가지입니다. rename 하면 그 자리에 새 inode 가 생깁니다. 호스트의 경로는 새 inode 를 가리키지만 컨테이너는 여전히 옛날 inode 를 붙잡고 있으니, 컨테이너 안에서 보면 파일 내용이 하나도 안 바뀐 겁니다.

restart 가 안 통한 이유도 여기 있어서, restart 는 프로세스만 다시 띄울 뿐 마운트(inode 바인딩)를 다시 걸지 않습니다.

해결 — 강제 재생성, 그리고 디렉토리 마운트

당장 급하면 컨테이너를 재생성해서 마운트를 새로 걸었습니다.

docker compose up -d --force-recreate

근본적으로는 파일이 아니라 디렉토리를 마운트하면 되고, 그러면 안에서 파일이 rename 돼도 컨테이너는 경로로 접근하니 새 파일을 봅니다.

volumes:
  - ./prometheus/:/etc/prometheus/

타겟이 자주 바뀌는 거라면 file_sd_configs 로 빼고 refresh_interval 을 주면 Prometheus 가 파일을 주기적으로 다시 읽어 reload 자체가 필요 없어집니다.

inode 고정과 파일 교체 — 설정은 디렉토리째 마운트

단일 파일 bind mount 는 inode 를 고정하고 rsync 나 mv, 에디터의 원자적 교체는 inode 를 바꾸기 때문에, 이 둘이 겹치면 호스트에서 아무리 고쳐도 컨테이너 안으로 안 들어갑니다. 여기에 reload 200 이 성공의 증거처럼 보이니 반영됐다고 믿게 됩니다.

설정을 고쳤는데도 안 먹으면 먼저 컨테이너 안에서 그 파일을 직접 열어 봅니다. 호스트와 내용이 다르면 마운트 문제고, 내용이 같은데도 동작이 그대로면 그때가 리로드 문제입니다. 설정 파일을 처음부터 디렉토리째 마운트해 두면 이 문제 자체를 안 만납니다.

  1. 불러오는 중