
로그를 열었더니 같은 줄이 2,900번 있었습니다
댓글 서버를 NAS에 올리는 중이었습니다. Container Manager에서 프로젝트를 만들고 빌드가 끝났다는 표시를 봤습니다. 확인만 하면 되는 단계였습니다.
http://192.168.0.x:8080/ping
이 페이지가 작동하지 않습니다.
1. 안 뜬 건지 죽은 건지
이럴 때 제일 먼저 갈리는 게 두 가지입니다. 컨테이너가 아직 안 뜬 건지, 떴다가 죽은 건지.
Container Manager 목록에서 상태를 봤습니다. 실행 중이었습니다. 그런데 새로고침하니 CPU 사용률이 튀었다 떨어졌다 했습니다.
이게 재시작 루프입니다. 컨테이너가 뜨고, 죽고, Docker가 다시 띄우고, 또 죽습니다. 목록에서는 계속 “실행 중”으로 보입니다. 죽어 있는 순간이 워낙 짧아서요.
restart: always를 걸어놨으니 당연한 일이었습니다. 이 설정은 운영할 때는 좋은데, 처음 띄울 때는 문제를 감춥니다. 원인을 찾는 동안에는 잠깐 꺼두는 게 낫습니다.
restart: "no"
이러면 한 번 죽고 그대로 멈춥니다. 죽은 상태로 남아 있으면 로그가 깨끗해집니다.
2. 15MB짜리 로그
로그를 내려받았더니 15MB였습니다. 텍스트로만 15MB면 웬만한 소설 열 권 분량입니다.
이걸 처음부터 읽으면 안 됩니다. 로그가 이렇게 커졌다는 것 자체가 정보입니다. 정상적으로 도는 서버는 로그가 이렇게 안 쌓입니다. 뭔가 같은 걸 계속 반복하고 있다는 뜻입니다.
그래서 순서를 이렇게 잡았습니다.
- 맨 앞 50줄 — 처음 뜰 때 뭘 하다 멈췄나
- 맨 뒤 50줄 — 마지막에 뭘 하다 멈췄나
- 둘이 같으면 → 그 사이는 전부 같은 내용의 반복
- 반복이 시작되는 지점을 찾는다
앞뒤가 똑같았습니다. 그리고 중간을 아무 데나 잘라 봐도 똑같았습니다.

3. 로그에 없는 줄이 답이었습니다
정상적으로 뜬 서버 로그에는 마지막에 이런 줄이 나옵니다.
[INFO] activate http rest server on :8080
“서버 열었다”는 뜻입니다. 이 줄이 나와야 /ping이 응답합니다.
제 로그에는 이 줄이 한 번도 없었습니다. 서버를 여는 데까지 못 갔다는 겁니다. 그 앞 단계 어딘가에서 죽고 있었습니다.
로그를 볼 때 에러 메시지를 찾게 되는데, 있어야 할 줄이 없는 것도 그만큼 중요한 단서입니다. 저는 이걸 다른 사람 로그와 비교해보고 나서야 알았습니다.
4. 제일 흔한 범인은 권한입니다
컨테이너가 뜨자마자 죽는 이유 1위는 권한입니다.
컨테이너 안의 프로그램은 보통 root가 아닌 계정으로 돕니다. Remark42는 uid 1001로 돕니다. 그런데 NAS에서 만든 폴더는 제 계정 소유입니다. 그대로 연결하면 프로그램이 자기 데이터 폴더에 쓰지를 못합니다.
이건 이미 처리해둔 상태였습니다. 폴더 소유자를 바꾸는 명령을 미리 돌려놨거든요.
chown -R 1001:1001 /volume1/docker/remark42/var
SSH가 막혀 있었는데, 시놀로지는 제어판 → 작업 스케줄러에서 사용자를 root로 놓고 스크립트를 돌릴 수 있습니다. SSH를 열지 않고도 root 명령을 한 번 실행할 수 있는 통로입니다.
나중에 이미지 소스를 열어보니, 컨테이너 시작 스크립트가 이 폴더를 직접 확인하고 있었습니다.
if [ -d "/srv/var" ]; then
chown -R app:app /srv/var || echo "WARNING: ..."
else
echo "ERROR: /srv/var doesn't exist..."
exit 199
fi
폴더가 없으면 exit 199로 그냥 끝냅니다. 볼륨 연결을 빠뜨리면 컨테이너는 이 지점에서 죽습니다. 흔한 실수라서 만든 쪽에서 아예 확인을 넣어둔 겁니다.
5. latest를 쓴 게 문제를 키웠습니다
제 compose에는 이렇게 적혀 있었습니다.
image: umputun/remark42:latest
편해 보여서 그렇게 썼습니다. 이게 나빴습니다.
latest는 버전이 아니라 “제일 최근에 올라온 것”이라는 표시입니다. 오늘 받은 것과 내일 받은 것이 다를 수 있고, 지금 내 NAS에서 돌고 있는 게 정확히 어떤 빌드인지 알 방법이 없습니다.
그래서 이런 상황이 됩니다.
- 검색해서 나온 남의 해결 방법이 내 버전에 해당하는지 알 수 없음
- 어제는 되던 게 오늘 안 되면 내가 뭘 바꿔서인지 이미지가 바뀌어서인지 구분 불가
- 고쳐도 그게 왜 고쳐졌는지 남에게 설명할 수 없음
버전을 박았습니다.
image: umputun/remark42:v1.16.4
개인 프로젝트라 대충 해도 된다고 생각했는데, 대충 해서 손해 본 게 정확히 이 부분이었습니다. 문제가 생겼을 때 재현이 안 됩니다.
6. 못 고치면 우회합니다
이 이미지는 서버를 켜기 전에 준비 스크립트를 하나 돌립니다. 웹 파일들을 훑어서 주소를 바꿔치기하는 작업입니다.
find . -regex '.*\.\(html\|js\|mjs\)$' -print -exec sed -i "s|{% REMARK_URL %}|${REMARK_URL}|g" {} \;
제 로그를 채우고 있던 게 이 -print의 출력이었습니다. 파일 65개 목록이 338번 찍혀 있었습니다.
서버 프로그램 자체는 이 스크립트 없이도 돕니다. 준비 작업이 필요한 건 웹 화면 쪽인데, 그건 설정으로도 넘길 수 있습니다. 그래서 시작 지점을 바꿔서 스크립트를 건너뛰게 했습니다.
entrypoint: ["/srv/remark42", "server"]
다만 이 우회에는 대가가 있습니다. 시작 지점을 바꾸면 Remark42의 준비 스크립트만이 아니라, 그 앞에서 도는 베이스 이미지의 시작 스크립트(/init.sh)까지 건너뜁니다. 그 스크립트가 마지막에 프로그램을 uid 1001 계정으로 낮춰서 실행해주는데, 이게 빠지면 서버가 root 권한으로 돕니다. compose에 user: "1001:1001"을 같이 적어두면 막을 수 있습니다. 이건 나중에 소스를 다시 읽다가 알았습니다.
다시 배포했더니 이번엔 이 줄이 나왔습니다.
[INFO] activate http rest server on :8080
bolt store ... ./var/ryumaspace.db SiteID:ryumaspace
[INFO] anonymous access enabled
/ping을 눌렀더니 pong이 나왔습니다.
7. 못 밝힌 것
솔직히 적어두겠습니다. 저 준비 스크립트가 왜 끝나지 않았는지는 끝까지 못 밝혔습니다.
나중에 소스를 받아서 확인해봤는데 스크립트 자체에는 이상한 데가 없었습니다. 최신 버전과 비교해도 이 파일은 한 글자도 다르지 않았습니다. 시놀로지 파일시스템과의 궁합인지, 볼륨 연결 방식인지, 로그를 내보내는 과정에서 겹쳐 보인 건지 확실하지 않습니다.
지금 서버는 잘 돌고 있고, 우회한 부분이 뭘 하던 건지도 알고 있어서 일단 여기서 멈췄습니다. 언젠가 다시 붙어볼 생각입니다.
원인을 못 밝히고 우회하는 게 찜찜한데, 실제로는 이렇게 끝나는 일이 꽤 많습니다. 중요한 건 뭘 우회했는지 알고 우회하는 것입니다. “이유는 모르겠지만 이거 지우니까 되네”로 끝내면 다음에 똑같이 막힙니다.
정리
컨테이너가 안 뜰 때 볼 순서입니다.
restart를 잠깐 꺼서 죽은 자리에 멈추게 한다- 로그의 앞뒤를 먼저 본다. 같으면 그 사이는 반복이다
- “서버 열었다”에 해당하는 줄이 나왔는지 확인한다. 없으면 그 앞에서 죽은 것이다
- 볼륨 폴더 소유자를 확인한다 (uid 1001 같은 것)
- 이미지 태그를 버전으로 박는다. 이건 문제가 생기기 전에 해두는 것이다
4번과 5번은 사실 처음부터 해뒀으면 겪지 않았을 일입니다. 그걸 알면서도 “일단 되게 해놓고 나중에” 하다가 이렇게 됩니다.
고친 곳
2026년 9월 29일, 글에 붙일 참고 문서를 하나씩 다시 열어보다가 본문과 어긋나는 곳을 찾아 고쳤습니다.
- 6절의 우회 방법에 부작용을 덧붙였습니다. 시작 지점을 바꾸면 베이스 이미지가 권한을 낮춰주는 단계도 같이 빠져서, 서버가 root로 돌게 됩니다.
참고한 문서
- Docker CLI 문서 — 재시작 정책 — 1절. restart always와 재시작 간격이 두 배씩 늘어나는 규칙
- Docker 문서 — Compose services (restart, entrypoint) — 6절. entrypoint가 이미지의 시작 명령을 덮어씀
- Remark42 소스 (v1.16.4) — docker-init.sh — 4·6절. -print가 붙은 find, exit 199
- baseimage 소스 (v1.17.0) — init.sh — 4절 uid 1001, 6절 우회의 부작용
- Docker 문서 — 이미지 빌드 모범 사례 (태그는 바뀔 수 있음) — 5절. latest 대신 버전 태그
댓글
로그인도 이메일도 필요 없습니다. 이름만 적고 편하게 남겨주세요.
지금은 댓글을 불러올 수 없습니다. 잠시 뒤에 다시 시도해 주세요. 급한 내용이면 ryuma@kakao.com로 보내주셔도 됩니다.