10년 차 자체 호스팅 Discourse 관리자 질문: 재빌드 시 런너 클리닝을 포함하지 않는 이유는?

여러분 안녕하세요. 저는 Lee이고, 2013년부터 간간이 Discourse를 셀프호스팅해 왔습니다. 시작하기만 해도 rbenv를 만져야 했던 기억이 납니다. Phusion Passenger를 포함해서 nginx를 컴파일해야만 시스템이 돌아가게 했던 기억도 납니다. 아마 10년 전쯤이었을까요, Docker로 전환하는 것은 개발자의 '내 홈 디렉터리에서는 잘 돌아가는데, 내 닷파일(dotfiles)은 악몽이다’라는 약점에 굴복하는 것이라고 @sam과 치열하게 논쟁했던 기억도 납니다. (그리고 저는 완전히 틀렸었죠!). '바이크 셰딩(bike-shedding)'이라는 말을 처음 들었을 때의 기억도 생생합니다. 그분의 말대로, 나는 모든 것을 기억합니다.

몇 년간 자리를 비운 후, 다시 Discourse 셀프호스팅으로 돌아온 이유는 휴스턴 지역 날씨 사이트의 네이티브 WordPress 댓글을 대체하기 위해서였습니다. 이 사이트는 평소에는 하루 약 1만 PV를 기록하지만, 허리케인 시에는 하루 약 200만 PV와 약 100만 명의 고유 방문자를 기록하기도 합니다. 우리는 수년간 WordPress의 네이티브 댓글 기능 때문에 고생해 왔지만, 지난 수요일부터 셀프호스팅된 Discourse로 전환하여 서비스 중입니다. (그것도 Graviton3 위에서요! 진심으로, 그냥 잘 돌아가고 훌륭합니다.)

제가 드디어 도달하려는 핵심은 이렇습니다. 2025년인 지금, 셀프호스터인 저는 여전히 수동으로 Docker 이미지 공간을 관리하고 있습니다. 프로덕션 환경에서 일주일도 채 안 된 시점에 /dev/root에 대한 이야기를 코드 스니펫으로 들려드리겠습니다:

[11:49:56] 0 ✓ (1.8ms)
root@discourse:/var/discourse # df -h
Filesystem       Size  Used Avail Use% Mounted on
/dev/root         30G   21G  9.6G  69% /
tmpfs            7.7G     0  7.7G   0% /dev/shm
tmpfs            3.1G  1.1M  3.1G   1% /run
tmpfs            5.0M     0  5.0M   0% /run/lock
efivarfs         128K  3.6K  125K   3% /sys/firmware/efi/efivars
/dev/nvme1n1p16  891M  109M  720M  14% /boot
/dev/nvme1n1p15   98M  6.4M   92M   7% /boot/efi
/dev/nvme0n1      32G  346M   30G   2% /var/discourse
tmpfs            1.6G   12K  1.6G   1% /run/user/1001
overlay           30G   21G  9.6G  69% /var/lib/docker/overlay2/5a649418bbfc064f488e895572eec1ace487a3eaa324fe1d8e3b395e6c5e3645/merged

[11:49:59] 0 ✓ (4.8ms)
root@discourse:/var/discourse # ./launcher cleanup
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y 
Total reclaimed space: 0B
WARNING! This will remove all images without at least one container associated to them.
Are you sure you want to continue? [y/N] y
Deleted Images:
untagged: discourse/base@sha256:3696bdf18652b5455bd33795ec3b8e0f201c17a04f0e0126fc0317ed821373cd
....

[여기서 아주 많은 줄들이 생략되었습니다]

....
Total reclaimed space: 12.43GB

[11:50:34] 0 ✓ (27.8s)
root@discourse:/var/discourse # df -h
Filesystem       Size  Used Avail Use% Mounted on
/dev/root         30G  6.9G   24G  23% /
tmpfs            7.7G     0  7.7G   0% /dev/shm
tmpfs            3.1G  1.1M  3.1G   1% /run
tmpfs            5.0M     0  5.0M   0% /run/lock
efivarfs         128K  3.6K  125K   3% /sys/firmware/efi/efivars
/dev/nvme1n1p16  891M  109M  720M  14% /boot
/dev/nvme1n1p15   98M  6.4M   92M   7% /boot/efi
/dev/nvme0n1      32G  346M   30G   2% /var/discourse
tmpfs            1.6G   12K  1.6G   1% /run/user/1001
overlay           30G  6.9G   24G  23% /var/lib/docker/overlay2/5a649418bbfc064f488e895572eec1ace487a3eaa324fe1d8e3b395e6c5e3645/merged

[11:55:28] 0 ✓ (3.3ms)
root@discourse:/var/discourse #

여러분을 사랑합니다. Discourse를 사랑합니다. 저는 이 제품에 완전히 매료되었고, 앞으로도 영원히 계속 사용할 의향이 있습니다.

하지만, 음… 도대체 _왜_일까요. 2025년인데도 제가 직접 여전히 launcher cleanup을 만지고 있어야 하는 이유는 무엇인가요? 왜 이미지 관리가 launcher의 내장 기능이 아닌가요?

다시 한번 말씀드리지만, 여러분을 사랑합니다. 저는 SCW에 Discourse를 선택한 이유는 여러분이 구축한 것에 대한 믿음과 그것을 사용하는 것을 사랑하기 때문입니다. 하지만… 제 가난한 AMI 부팅 볼륨의 절반이 기술적인 측면을 제가 제대로 이해하고 있다면 자동으로 관리될 수 있을 useless한 쓰레기로 가득 차 있다는 사실은 아쉬울 따름입니다.

불평하려는 의도는 아닙니다. 관리자 의자에서 몇 년간 떠나 있다가 다시 체크인을 해보는 것뿐입니다. AI 스팸 감지 기능과 AI 트리아지 기능을 정말 좋아합니다. 특히 기후 변화 관련 정치적 논쟁(지지 또는 반대)이 일상적인 날씨 포럼에서는 더더욱 그렇습니다. 모든 것에 감사드립니다 <3

리 씨, 다시 돌아오셨네요! :sunflower:

이번 주에도 제 셀프호스팅 사이트에서 같은 일이 발생했어요. 백업이 계속 실패했는데, 외출 중이라 노트북에 접근할 수 없어 일주일 정도 방치했어요. 돌아오자마자 클리너를 실행했는데 디스크 공간이 많이 확보되었고, 백업도 다시 정상적으로 실행되기 시작했어요.

안녕하세요, 다시 뵙게 되어 반갑습니다!

일부 이유는 '충분히 괜찮아서’입니다. 우리는 내부 호스팅에서 이 기능을 사용하지 않습니다. 컨테이너와 이미지를 자주 교체하기 때문에, 자체 호스팅 사이트와 비교했을 때 업데이트 주기가 훨씬 다르기 때문입니다.

또 다른 설명은, 런처와 Docker 양쪽 모두 데이터 삭제 일정에 대한 전체적인 책임을 지려 하지 않는다는 점입니다. 사용자 데이터 삭제 일정에 대한 제어 권한은 사용자에게 전적으로 있어야 합니다.

자체 호스팅 사이트에서 몇 가지 문제를 겪어 본 적이 있습니다. 정리 작업이 새로 구축해야 할 discourse 기반 환경까지 함께 정리해 버려서, 악순환에 빠지는 문제가 발생했습니다. 이런 일이 자동으로 실행되는 과정에서 발견되지 않으면, 원인을 파악하는 데 상당한 장애물이 될 수 있습니다.

간단한 제안으로, 스스로의 책임 하에 docker system prune 또는 launcher clean을 cronjob으로 실행해 보시는 것이 좋겠습니다. 효과가 있을지도 모르겠습니다.

가끔은 현재 실행 중인 유일한 컨테이너를 삭제할 수 있기 때문입니다.

모든 정상 작동 중인 컨테이너가 실행 중인 상태에서, 리빌드를 실행하기 전에 매번 실행하면 됩니다.

좋은 지적이네요. 때로는 단순한 답이 가장 좋은 답일 때가 있습니다. 감사합니다, 그렇게 하겠습니다!

cron으로 ./launcher cleanup를 실행할 때 어떻게 자동으로 '예’라고 응답할 수 있을까요? 제 경우 컨테이너는 큰 문제가 아니지만, 고아 이미지(orphaned images)는 문제입니다.

cron으로 실행할 이유는 없습니다. launcher로 이미지를 빌드할 때만 새 이미지가 생성되므로, 이미지를 빌드하기 전이나 후에만 실행하면 됩니다.

launcher의 프롬프트를 피하고 싶다면, 위에서 제안한 대로 docker 명령어를 사용할 수 있습니다. 여기 그 중 하나가 있습니다(원하는 동작인지 확인하기 위해 해당 명령어가 무엇을 하는지 문서를 참고하세요):

/usr/bin/docker image prune -a -f

그거 한번 확인해봐야겠다. 고마워.

오늘도 재빌드가 실패했는데, 빈 공간이 5GB 미만이었기 때문이야. cleanup은 문제를 해결해줬지만, 그건 좀 짜증나는 일이지. 하지만 이런 상황을 다시 보고 싶지는 않아.

여기서 도커를 얼마나 잘 모르는지 드러나네 :joy: 내가 제대로 이해했으면, 컨테이너에서 사용되지 않아서 삭제된 그 이미지들은 내가 늘 생각했던 것처럼 '그림’이라는 의미의 이미지가 아니었겠지 :face_with_peeking_eye: :rofl:

직접적인 답변은 echo y | launcher cleanup를 사용하여 "y"를 미리 전송할 수 있다는 것입니다.

간접적인 답변은, 실제 launcher cleanup(이후의 equivalent은)은 다음 두 명령과 동일하다는 것입니다:

docker container prune --force --filter until=24h
docker image prune --all --force --filter until=24h

그리고 제가 말씀하시는 프롬프트는 오래된 postgres 데이터 디렉토리를 삭제하기 위한 것입니다:

rm -rf /var/discourse/shared/standalone/postgres_data_old*

launcher에 대한 의존성을 제거하고 해당 명령을 직접 사용할 수 있습니다.

사실 제가 언급한 것은 ./launcher cleanup 명령을 실행할 때 나타나는 질문들입니다. 먼저 모든 중지된 컨테이너를 제거합니다. 그런 다음 적어도 하나의 컨테이너에 의해 사용되지 않는 모든 이미지를 삭제할지 여부를 제안하는데, 이 부분이 제 경우 공간을 확보해 주는 핵심입니다. 지난번에는 거의 40GB를 확보했습니다.

그래서 왜 jpg, png 같은 고아 이미지(orphan images)가 그렇게 많았는지 이해할 수 없어 꽤 혼란스러웠습니다. 하지만 여기서 말하는 이미지와 제가 언급한 이미지는 완전히 다른 종류이죠.

네, 저는 적어도 주 2회 재빌드(rebuild)를 수행합니다. 또는 최근 문제 있는 플러그인을 추적하던 당시에는 재빌드를 수십 번이나 수행하기도 했습니다.

매번 새 이미지가 생성되는지는 저도 잘 모르겠습니다.

모든 재빌드는 새로운 이미지를 생성하므로, 정리하지 않으면 이미지가 계속 쌓이게 됩니다.

현재 런처는 디스크 공간이 부족할 때 다른 명령을 실행하는 경우에만 정리하도록 프롬프트를 표시합니다.

스크립트로 실행 중이라면 이는 상당히 번거로울 수 있습니다. 스크립트는 응답을 기다리다가 멈춰 버리기 때문이죠(아마도 yes를 파이프하는 이유가 바로 그것일 것입니다). 저는 디스크의 남은 공간이 10GB 미만이면 항상 클리닝을 수행합니다.

아마도 나에게 도움이 될 잠재적인 우회책이 있을 것 같습니다. 다른 분들에게도 도움이 될 수 있을 것 같아 여기에 공유합니다.

/etc/docker/daemon.jsondata-root 설정을 추가하는 것을 고려하고 있습니다. 이렇게 하면 Docker가 이미지를 덜 중요한 위치에 배치하도록 강제할 수 있는지 확인해 보려는 것입니다. 이 서버에는 Discourse 이미지 외에 다른 것이 호스팅되지 않기 때문에, 부팅 볼륨이 터지는 것을 방지하기 위함입니다.

메타 사이트에서 이 주제에 대한 이전 스레드를 검색해 보면 몇 가지 결과가 나오지만, 실질적인 정보를 제공하지는 않습니다. 내 프로덕션 Discourse 인스턴스가 불타는 잔불 더미로 붕괴하기 전에, 이 방법이 실행 가능한지 물어보고 싶었습니다 :slight_smile:

저는 다른 접근 방식을 취하여 /var/lib/docker에 별도의 파일 시스템을 마운트했습니다.

제 경우, 매우 사이트 특이적인 이유로 /var/discourse/shared, /var/discourse/shared/data, /var/discourse/shared/app/uploads/default/original, /var/lib/docker 각각에 대해 별도의 파일 시스템을 선택했습니다. 하지만 단순히 /var/discourse를 별도의 파일 시스템으로만 사용하길 원한다면, /var/discourse/share/docker 디렉토리를 생성하여 /var/lib/docker에 바인드 마운트하는 것이 가능할 것입니다(물론 시스템이 안정된 상태에서 수행하고 필요한 경우 파일을 이동해야 합니다).

도커의 내부 구조를 건드리는 것보다 훨씬 좋은 아이디어네요. 감사합니다!!