Move from standalone container to separate web and data containers

이 과정이 안전한가요? 개발 환경에서는 멀티컨테이너 설정을 잘 사용할 수 있지만, 사람들이 구버전 컨테이너에 접근하는 동안 새로운 컨테이너가 부팅되어 DB 마이그레이션 단계를 실행하는 프로덕션 환경에서 이 방법을 사용하게 되면, DB 마이그레이션 단계가 끝난 후에도(단, 전체 부팅 과정이 완료되기 전에는) 구버전 컨테이너로 보내지는 요청들은 여전히 구버전 백엔드 로직을 사용하여 이전 버전에서 정의된 방식으로 데이터를 저장하게 됩니다.

이것이 Discourse 자체와 관련된 문제는 아니라는 점을 알고 있습니다(여러 레플리카가 있는 환경에서는 한 레플리카가 다른 레플리카보다 먼저 업데이트되는 경우 이러한 문제가 발생할 수 있으며, 업그레이드 전에 모두 중지하지 않는 한 이 문제는 발생할 수 있지만, HA(고가용성)를 원한다면 이는 아마도 해당되지 않을 것입니다) 하지만, 언급하신 과정은 일반적으로 안전한가요?

생각할 수 있는 한 가지 방법은 재빌드 사이의 DB 마이그레이션을 최소화하기 위해 항상 Discourse를 최신 상태로 유지하는 것입니다. 하지만 어떤 경우든 이는 이상적이지 않으며, 이 경우에도 문제가 발생할 수 있습니다.

멀티컨테이너 설정은 권장되는 방법 중 하나(단, 컨테이너 1개만 사용하는 표준 방식은 아님)로 보이기 때문에 안전할 것 같고, 제가 지나치게 걱정하는 것 같습니다.

프로덕션 사이트에서 이 방식이 잘 작동하는지(다른 컨테이너가 실행 중일 때 한 컨테이너에서 부팅을 수행하는 경우) 아시나요? 프로덕션 사이트에서 이미 이렇게 해본 분들의 피드백을 듣고 싶어서, 여러 번 재빌드한 후에도 잘 작동하는지, 주의할 점이 있는지 등을 알고 싶습니다. 말씀드렸듯이, 개발 환경에서는 잘 작동합니다.

다운타임을 전혀 없애고 싶다면 몇 가지 추가 단계를 수행해야 합니다. 새 컨테이너에서 "post-migrations"를 비활성화하고, 새 컨테이너를 완전히 배포한 후 post-migrations를 활성화하고, 다시 배포하면 됩니다. 이렇게 하면 구버전 코드가 더 이상 실행되지 않을 때까지 컬럼 삭제 마이그레이션이 실행되지 않도록 방지할 수 있습니다.

아직 이 과정에 대한 howto 문서는 없으며, 다음 링크에서만 문서화되어 있습니다:

다만 대부분의 포럼에서는 한 달에 1~3분의 다운타임 정도는 감수할 수 있습니다.

4개의 좋아요

네. 대부분은 실행 중인 컨테이너를 깨뜨리는 마이그레이션이 없습니다.

web_only.yml에서 마이그레이션을 비활성화한 상태로 두었다가, 새 컨테이너가 실행된 후에 다음과 같은 작업을 수행할 수도 있습니다.

 cd /var/www/discourse;SKIP_POST_DEPLOYMENT_MIGRATIONS=0 rake db:migrate 

이 명령을 실행 중인 컨테이너 내부에서 실행하면 됩니다.

3개의 좋아요

네, 프론트엔드 리버스 프록시 서버로 nginx를 사용할 경우 프로덕션에서 완벽하게 동작합니다.

저희는 다음과 같이 구성합니다.

세 개의 컨테이너:

  • Data (data.yml)
  • Socket1 (socket1.yml)
  • Socket2 (socket2.yml)

nginx config에서 단일 유닉스 도메인 소켓을 지정합니다. 예를 들어 다음과 같습니다:

location / {
        proxy_pass http://unix:/var/run/nginx.http.sock;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP $remote_addr;
 }

그 다음, 실제 소켓으로 심볼릭 링크를 만들어 라이브로 만들 컨테이너를 선택합니다. 예를 들어 다음과 같습니다:

socket2 컨테이너를 라이브로 만들 경우:

ls -sf /var/discourse/shared/socket2/nginx.http.sock /var/run/nginx.http.sock

socket1에서 변경 사항을 적용하고 socket1을 라이브로 만들 경우:

cd /var/discourse
./launcher rebuild socket1
ls -sf /var/discourse/shared/socket1/nginx.http.sock /var/run/nginx.http.sock

참고로, socket1 컨테이너를 bootstrap only할 이유는 없습니다. 컨테이너는 자체 공유 디렉터리/볼륨을 통해 유닉스 도메인 소켓으로 노출되므로, 이 두 “웹 앱” 컨테이너는 동시에 실행할 수 있습니다:

  • Socket1: /var/discourse/shared/socket1/nginx.http.sock
  • Socket2: /var/discourse/shared/socket2/nginx.http.sock

TCP/IP 컨테이너 포트가 노출될 때 발생하는 "포트 바인딩 충돌"과 같은 문제는 없습니다. 이 때문에 프로덕션에서는 유닉스 도메인 소켓만 노출하고 TCP/IP 포트는 노출하지 않습니다.

원하시면 bootstrap를 수행할 수도 있습니다:

cd /var/discourse
./launcher bootstrap socket1
./launcher start socket1
ls -sf /var/discourse/shared/socket1/nginx.http.sock /var/run/nginx.http.sock

사용자 선택 사항이지만, 두 컨테이너를 동시에 실행하면 둘 다 sidekiq를 실행하고 예약된 작업을 수행하게 된다는 점을 염두에 두시기 바랍니다(저희 경험상). 따라서 저희는 가끔 두 컨테이너의 업로드 파일을 동기화합니다.

이 방법은 저희에게 완벽하게 동작하며, 웹 앱 컨테이너를 리빌드하고 라이브로 전환하는 데 기본적으로 거의 다운타임이 없습니다. 프로덕션에서는 다운타임을 매우 심각하게 받아들이며, 가능한 한 이를 피합니다.

참고:

위 방법은 솔루션의 웹 앱 부분에 대한 것이며, 데이터 컨테이너에 대한 것은 아닙니다. 데이터에 대해 유사한 솔루션을 만든 적은 없지만, 언젠가 시간을 내어 데이터 컨테이너를 위한 유사하지만(물론 다른) 솔루션(일종의 “두 개의 데이터 컨테이너, DB 동기화” 방식, 현재로서는 제 머릿속에서 완전히 미정)을 구축할 수도 있습니다.

따라서 저는 실제로는 이것과 정반대의 방식을 취하고 있습니다:

말씀드렸듯이, 개발 환경에서는 잘 동작합니다.

저는 일반적으로 개발 환경에서 이 구성을 하지 않습니다. 설정에 시간이 더 걸리고, 개발 환경에서는 사용자가 사이트를 방문하지 않으므로("나와 코드"만 존재하며, 라이브로 사용자나 봇이 사이트를 타격하지 않으므로) 약간의 다운타임은 괜찮기 때문입니다. 또한 데스크톱에서는 개발에 도커를 사용하지 않습니다**.

도움이 되셨으면 좋겠습니다.


**여기서 "개발"이란 소프트웨어(예: 플러그인) 개발을 의미하며, 단순히 Discourse 설치 환경을 "스테이징"하는 것을 말하는 것이 아닙니다. 후자는 "스테이징"이라고 부르고 "개발"이라고 하지 않습니다(정확히 구분하기 위해).

2개의 좋아요

감사합니다. 그 사실을 몰랐습니다. 아주 좋은 기능인 것 같고, 이전에 언급했던 같은 문제를 방지할 수 있을 것 같습니다(이미 존재하는 컬럼에 대한 변경된 로직을 방지하는 것은 아니지만, 그런 경우는 더 드물 것입니다).

답변 감사합니다. SKIP_POST_DEPLOYMENT_MIGRATIONS는 @riking님이 언급하신 것과 같아 보이며, 실행 중인 컨테이너에서 작업이 마이그레이션으로 인해 깨지는 것을 방지하려는 제 의도와 일치하는 것 같습니다.

설명해 주셔서 감사합니다. 두 컨테이너를 번갈아 가며 사용하는 것(하나는 실행 중이고 다른 하나는 부팅되도록)이 좋은 접근 방식인 것 같습니다.

제가 '개발 환경(dev environment)'이라고 한 것은 멀티컨테이너 설정을 테스트하기 위해 사용했던 원격 개발 환경을 의미했습니다(두 컨테이너를 같은 머신에 두거나, 서로 다른 머신에 두는 경우 모두 포함).

스테이징 환경이라면 동일한 플러그인을 가진 yml 파일과 백업된 프로덕션 데이터베이스를 사용하여 테스트할 것인데, '스테이징’이 아닌 '개발’이라고 한 이유는 그 때문입니다. 하지만 사실, 단순히 개발 환경을 설정하려는 경우 대부분의 경우 1컨테이너 방식을 사용할 것입니다.

2개의 좋아요

제가 보기에는 이 가이드는 다음과 같은 내용으로 구성되어 있습니다:

  • 백업
  • discourse_setup 2container를 실행하는 것과 동일한 결과를 내되, 더 많은 단계를 거쳐 완전히 새로운 discourse 인스턴스를 생성
  • 복원

왜 깨끗한 종료(clean shutdown) 후, 그리고 개별 containers/*.yml 파일에서 두 개의 새 컨테이너를 시작하기 전에 /var/discourse/shared/standalone/{postgres,redis}*를 /var/discourse/shared/data로 이동하거나 복사하지 않는 것일까요? 백업/복원은 모든 데이터를 이동하는 데 정말 무거운 방식처럼 보이며, 프로세스에 불필요하게 몇 시간을 추가합니다. 제가 놓치고 있는 명백한 무언가가 있는 걸까요?

방금 테스트용 discourse에서 이 프로세스를 테스트했고, 기왕에 하는 김에 redis도 분리해서 모든 기반을 커버하고 있는지 확인했습니다. 수정: 설명을 새 토픽으로 이동했습니다:

사이트는 백업/복원 사이클 없이도 정상적으로 작동하는 것 같습니다. 제가 확인해야 할 비명백한 무언가가 있을까요?

상당히 큰 규모의 discourse에 대해서도 동일한 프로세스를 수행했고, 정상적으로 작동하고 있습니다. 프로덕션 환경에서는 새 web_only 컨테이너를 app으로 명명하기로 결정했는데, 그러면 손가락이 평소대로 올바른 작업을 수행하도록 유지할 수 있기 때문입니다. 새로운 container/*.yml 파일을 작성한 후, 전체 마이그레이션에 걸린 다운타임은 12분으로, 백업/복원 사이클을 거쳤을 때보다 훨씬 빨랐습니다.

그냥 그렇게 하려면 조금 더 많은 작업과 시스템 동작 원리에 대한 이해가 필요합니다. 백업 복원은 더 확실합니다.

1개의 좋아요

아마도 우리는 서로 다른 의견을 존중하기로 하자는 말인 것 같네요. 여러 컨테이너를 운영할 수 있을 만큼 능력이 있는 사람이라면 몇 가지 명령어를 실행하는 건 어렵지 않을 거라고 생각해요. 그리고 그 명령어들이 여기저기 bin/rails c를 실행하고 루비 명령어를 입력하는 것보다 타이핑하기가 더 어렵거나, 여러 컨테이너를 사용하는 데 필요한 기술보다 의미 있게 더 많거나 다른 기술을 요구한다고 생각하지 않거든요. :smiling_face: 하지만 여기 댓글 속에 묻어두기보다는 내용을 별도의 새 게시글로 옮기겠습니다.

1개의 좋아요

그건 타당한 주장입니다. 아마도 해당 절차를 더 무섭게 보이게 하면, 관련 기술이 없는 사람들이 시도해 보지 않도록 할 수 있겠죠.

…그리고 만약 실수를 저지른다면, 마이그레이션 전에 백업할 대안은 없습니다! 위 링크로 연결된 내 글에서 그 점을 분명히 전달했기를 바랍니다! :smiling_face:

1개의 좋아요

여기가 바로 논리의 허점입니다. ./discourse-setup에 2container를 추가하는 것에는 어떠한 능력 평가 기준도 포함되어 있지 않습니다. 이러한 주제들을 보고 어떤 비밀 요령이 있거나 "이렇게 해야 한다"고 가정하여 단순히 두 개의 컨테이너를 실행하는 사람들이 많습니다.

Postgres 12 관련 주제는 추가된 복잡성에 대한 경각심을 줄 수 있는 사례가 되어야 합니다. 백업을 상태 간 단계로 사용하면 사용자가 단일 파일 이름을 변경하는 것만으로 단일 컨테이너로 되돌릴 수 있지만, 폴더를 이동하기 시작하면 그 단순성은 사라집니다.

2개의 좋아요

@Stephen 당신의 주장에는 결함이 있습니다: 멀티 컨테이너 설명서에는 업데이트에 대한 책임을 지고 그 작동 원리를 이해해야 한다는 경고가 가득하며, 위쪽에 있는 긴 설명서는 너무 난해해서 그걸 본 사람은 누구나 결국 포기할 가능성이 큽니다. 제 글 Migrate quickly to separate web and data containers 을 읽어보시고, 이 글이 따라가기 어려워 어려움을 겪을 사람들을 겁먹게 만들지 않거나, 문제가 생겼을 때 백업의 필요성과 백업으로 되돌릴 수 있는 능력을 강조하지 못한다고 말씀해 보세요!

더 나은 성능의 서버로 마이그레이션(보안 패치 적용을 위해)한 직후 ./launcher rebuild app 명령을 실행했을 때, 사이트가 터무니없이 오랜 시간 동안 중단된 상태였고, 그 중 상당 부분이 컨테이너의 postgres 부분을 재빌드하는 데 소요되었다는 사실을 알게 되었을 때 매우 불쾌했습니다. 그때 저는 2container 문서와 이 문서를 발견했는데, 복원하는 데 걸리는 4시간의 다운타임을 피하기 위해 ./launcher rebuild app으로 인한 긴 다운타임을 계속 감수하며 마이그레이션을 다시 하고 싶지 않았습니다. 다소 유능한 사람으로서, 이 구성이 사실상 숨겨져 있다는 사실에 오랫동안 매우 짜증을 냈습니다.

postgres 12 관련 토픽은 훌륭한 참고 자료입니다. postgres 컨테이너만 두 번 재빌드하면 될 상황에서, 사람들이 앱 전체를 여러 번 재빌드해야 하기 때문에 오히려 더 많은 다운타임을 겪게 되거든요. 6일 자동 삭제 기능 때문에 전체 스레드를 다 읽지는 못했지만, 거기서 무능한 멀티 컨테이너 배포가 주요 문제이거나 심지어 문제가 된다고 보는 것은 저에게 전혀 자명하지 않습니다.

(죄송합니다. 여기서는 때때로 "모든 사용자는 무능하다"는 태도에 조금 지칠 때가 있습니다.)

2개의 좋아요

여러분께는 이해가 안 되실 수 있지만, Support 채널에서 6~7년 동안 사람들을 도와온 우리에게는 롤백 전략이 항상 중요했습니다.

버그는 테스트를 통과한 코드에도 섞여 들어오기 마련이고, 가끔은 rubygems의 레이트 리미트가 리빌드에 영향을 주기도 하며, GitHub조차도 가끔 불안정한 상황이 발생했습니다. 이러한 이유만으로, 단순히 파일 이름을 변경하고 ./launcher start app을 실행하는 것보다 더 어렵게 만드는 상태 변경에는 어떤 가치도 있다고 보지 않습니다.

위험 감수 성향이 다르신 분들은 다른 방법을 선택하시면 됩니다. 하지만 평소 문제를 해결하는 데 도움을 주는 사람들에게는 현재의 가이드가 잘 작동합니다.

3개의 좋아요

제가 쓴 프로세스를 제대로 읽지 않은 것 같은데요, 마치 복원할 수 있는 기능을 강조하지 않은 것처럼 말씀하고 계시니까요. 한 번 읽어보시고, 제 지시 사항에 대한 정확한 진술로 수정하신 후 다시 오시기 바랍니다. 현재로서는 제가 쓴 내용을 읽을 생각조차 하지 않고 제게 설명을 덧붙이는 것 같아 불쾌합니다.

다만, 저는 이 게시글 위에 추가적인 경고 문구를 여러 개 더 추가했습니다. 이 게시글은 5년 동안 이 마이그레이션 프로세스에 대한 공식적인 안내 장소로 유지되어 왔으며, 기존에 있던 경고 문구보다 훨씬 더 많은 경고가 포함되어 있습니다.

2개의 좋아요

우선, 모험가 여러분 :sweat_smile: , 이 "정글"을 우리가 아닌 여러분이 정리해 주시려니 정말 감사합니다. 며칠 안 남으면 저도 그 발자취를 따라갈 것 같네요…
multisite.yml 파일에는 어떤 내용이 들어 있나요?

안녕하세요.

최근 VPS에 셀프호스팅 Discourse 포럼을 설치했습니다. 업로드 파일과 백업 모두 Wasabi에 저장하고 있으며, 모든 것이 Linode에서 호스팅되고 있습니다.

standalone 템플릿을 사용했는데, 다른 소프트웨어와 비교했을 때 설치가 신선한 바람과 같았습니다. 정말 훌륭했습니다. 순수한 즐거움이었습니다. 모든 오픈소스 프로젝트가 Discourse만큼 설치와 설정에 신경을 썼으면 좋겠습니다.

여기서 어려운 점이 있습니다. 인터넷에 접근할 수 없는 RFC-1918 주소로만 접근 가능한 별도의 서버에서 실행 중인 전용 PostgreSQL이 있습니다. 이 데이터베이스를 Discourse에 사용하고 싶습니다. 웹/애플리케이션 서버와 같은 서버에서 데이터베이스 서버를 실행하는 것을 크게 선호하지 않기 때문입니다.

즉, standalone 데이터베이스를 분리하여 전용 데이터베이스 클러스터로 이전하는 방법이 있을까요?

제가 할 일은 Discourse 데이터베이스의 pgdump를 수행하고, 전용 데이터베이스로 이동하여 복원한 다음, 복원/가져오기 후 모든 테이블에 대해 postgres vacuum analyze를 실행하고, 마지막으로 Discourse 애플리케이션을 네트워크를 통해 새 데이터베이스로 연결하는 것뿐이라고 가정하고 있습니다.

하지만 데이터베이스 자격 증명이 어디에 저장되어 있는지 찾지 못하겠습니다. app.yml을 확인했는데 데이터베이스 항목이 보이지 않고, ../templates/ 폴더를 살펴봐도 yml 파일 중 어디에도 데이터베이스 자격 증명이 있는 것 같지 않습니다.

제가 하려는 것이 실제로 가능한 일일까요?

1개의 좋아요

자격 증명 설정 과정을 설명하는 이 가이드를 따라갈 수 있습니다:

3개의 좋아요

훌륭합니다! 제가 찾고 있던 바로 그 내용이었어요. 정말 감사합니다!

이제 nginx가 CloudFlare 뒤의 실제 IP를 인식하도록 해야 하는데…… :wink:

내장된 PostgreSQL 데이터베이스에 기본적으로 인증 정보가 없는 경우, 스탠드얼론 컨테이너에서 pgdump를 실행하여 데이터베이스를 덤프하는 방법이 있나요?

1개의 좋아요

기본 Discourse 백업은 tarball에 포함된 pg_dump이므로, 이를 사용할 수 있습니다.

덤프를 파이프를 통해 전송하거나 다른 형식을 사용하는 등 커스텀 작업을 수행하려면 언제든지 다음을 사용할 수 있습니다:

ssh root@forum
cd /var/discourse
./launcher enter app
su postgres
psql # 또는 pg_dump
2개의 좋아요