Jay @pfaffman 님, 안녕하세요. 이 ‘두 컨테이너’ 주제에 대해 이 글과 다른 글들, 그리고 Sam 님의 글까지 감사드립니다.
질문드립니다:
말씀하신 대로 data용 컨테이너 하나와 web-only용 컨테이너 하나를 설정하는 것을 시도해 왔는데, macOS에서 이를 구동하는 과정에서 여러 가지 문제가 발생하고 있습니다.
하지만 Mac이나 Ubuntu에서 이 '두 컨테이너 구성’을 디버깅하는 것에 대해 걱정하기 전에, 우리가 이 일을 하는 것이 올바른 이유인지 먼저 확인하고 싶습니다.
'두 컨테이너 방식’을 시도하려는 이유는 웹 앱을 재구축할 때(예: 플러그인 설치 시) 사이트가 다운되지 않도록 하기 위해서입니다. 또한, 자체 개발한 플러그인을 수정할 때, 변경 사항이 작동하도록 하는 유일한 방법이 재구축인 경우가 있다는 것을 알게 되었습니다(이것은 다른 날 이야기할 내용입니다). 또한, 제 마음에 드는 ‘빠르고 친근한’ 웹 개발 환경을 구축하는 것도 고군분투하고 있지만, 그것도 또 다른 주제입니다.
따라서 제 질문은, ‘두 컨테이너’ 구성이 웹 앱의 웹 전용 부분이 재구축될 때 다운타임을 현저히 줄여주는지 여부입니다.
이것이 이 문제를 생각하는 올바른 방식이 맞죠, 그렇죠?
플러그인을 설치하거나 수정할 때, ‘web-only’ yml 파일만 재구축하면 되고 data yml 파일은 재구축하지 않아도 되는 것인가요?
우리는 LAMP 포럼 배경에서 왔기 때문에, 플러그인 변경은 대부분 라이브 사이트에서 런타임으로 이루어집니다(실수를 하지 않는 한 다운타임이 없습니다). 또한, VueJS 웹 앱에서 데스크톱에서 빌드한 후 새 앱을 업로드하고 제자리에 배치하면 거의 다운타임 없이 업그레이드/업데이트가 가능했습니다. 그러나 Discourse에서는 다운타임이 발생하는데, 우리는 이를 원하지 않습니다(몇 초라도).
‘두 컨테이너’ 솔루션이 (1) 앱을 재구축할 때(플러그인, 코드 수정 등) 또는 (2) 전체 백업에서 복원할 때 다운타임에 있어 상당한 개선 효과를 보이는가요?
우리는 Discourse를 프로덕션 환경에서 실행하고 거의 제로 다운타임으로 변경 사항을 적용하는 방법을 찾고 있지만, LAMP이나 VueJS 앱(예를 들어)에서처럼 쉽게 할 수 있는 방법을 아직 찾지 못했기 때문에 이 질문을 하면 또 '마구잡이로 비판’을 당할 것 같은 기분이 듭니다.
네. 새 컨테이너가 빌드되는 동안 기존 웹 컨테이너는 계속 실행됩니다. 따라서 다운타임은 새 웹 서버를 시작하는 데 걸리는 시간이며, 이는 보통 1분 미만이지만, 이를 제로 다운타임(Zero-downtime)이라고 부르지는 않습니다. 제로 다운타임을 원한다면, 구 컨테이너를 종료하기 전에 새 컨테이너가 완전히 시작되고 작업을 처리할 수 있도록 해주는 리버스 프록시를 앞에 배치해야 합니다. (그리고 새 컨테이너를 위한 데이터베이스 마이그레이션이 구 컨테이너에 문제를 일으키는 경우, 다른 복잡한 절차를 거치지 않는 한 거기서도 다운타임이 발생합니다.)
제(아직은 제한적인) 이해를 바탕으로 한 이 다소 미친(?) 아이디어에 대해 어떻게 생각하시나요:
다음 튜토리얼에 따라 프론트엔드에서 nginx를 리버스 프록시로 설정합니다:
그런 다음 discourse_docker(스탠드얼론)이 설정된 두 개의 디렉터리/인스턴스를 만듭니다. 예를 들어:
/var/discourse1
/var/discourse2
이 두 인스턴스 모두에서 discourse_docker(스탠드얼론)을 서로 다른 소켓을 경청하도록 설정하며, 각 인스턴스에서 다음 템플릿을 수정합니다:
- "templates/web.socketed.template.yml"
즉, 요약하자면, 우리는 조용한 시간에 프로덕션 환경을 다시 빌드하여 다른 컨테이너에서 서로 다른 소켓(nginx.https.sock2)을 경청하도록 실행하므로 소켓 충돌이 발생하지 않습니다. 이를 통해 스탠드얼론 모드에서도 빌드할 수 있으며(두 컨테이너, 즉 data와 web-only의 필요성을 제거하는 것이 목표입니다).
예를 들어(논의/설명용), discourse1의 web.socketed.template.yml에서:
그러나 discourse 템플릿이 마법을 부리게 하는 대신, 우리는 단순히 /etc/nginx/conf.d/discourse.conf에서 소켓을 수동으로 전환하고 nginx를 재시작합니다. 따라서 web.socketed.template.yml 템플릿에서 replace: 지시문을 제거하게 됩니다.
이 제안된(아마도 미친) 구성에서는, 두 개의 스탠드얼론 컨테이너가 서로 다른 소켓(충돌 없음)을 경청하도록 하고, nginx를 원하는 소켓에 연결하도록 구성한 후 nginx를 재시작하면 됩니다.
이는 명확하고 쉬우며, 라이브 인스턴스에서 새 게시글이 없는 느린 기간 동안 유용할 수 있습니다. 단일 discourse 인스턴스(앱)당 두 개의 컨테이너(data와 web-only)의 복잡성을 원하지 않거나 필요로 하지 않는 사람들에게는 특히 그렇습니다.
물론, 데이터 관점에서 가장 견고한 구성, 특히 완벽함을 추구하는 바쁜 사이트의 경우, “두 컨테이너” 솔루션이 될 것입니다. 왜냐하면 우리는 data와 web-only 인스턴스(이제 두 개의 서로 다른 소켓, sock와 sock2를 경청)를 원할 것이기 때문입니다.
nginx 프론트엔드를 갖춘 "두 컨테이너 솔루션"에서 "표준 구성"은 두 개의 web-only 컨테이너가 동일한 소켓을 경청하도록 하는 것이므로, 두 컨테이너는 동시에 실행할 수 없습니다. 하지만(예를 들어) 서로 다른 소켓을 경청하도록 한다면, 두 컨테이너는 동시에 실행할 수 있고, 우리는 단순히 nginx 설정 파일(과 nginx 재시작)을 사용하여 두 컨테이너 사이를 전환할 수 있습니다.
이것이 올바른 이해인가요?
(천천히 하지만 확실히) 이해하기 시작하고 있는 건가요?
감사합니다!
후속 노트: 제 데스크톱 Mac 중 하나에서 “두 컨테이너” 구성이 작동하는 것을 확인했습니다:
설치 시 유일한 주의사항은, 스크립트가 어떤 이유에서인지 생성하지 않는 다음 디렉터리를 수동으로 생성하고(소유권과 권한을 설정) 해야 한다는 것입니다:
이 접근 방식에 대해 궁금한 점이 하나 있습니다: 이 설정에서 Rebuild는 어떻게 진행해야 하나요?
@pfaffman님이 추가하신 2개 컨테이너 구성을 처음부터 적용한다고 가정하면, "app"이 두 개, 즉 "data"와 "web_only"가 있게 됩니다.
모든 Rebuild 작업과 관련 처리는 “web_only” 컨테이너로 향하지만, “data” 컨테이너에 대해 별도로 무언가를 해야 하는지, 아니면 "web_only"의 부트스트랩이 이를 처리해 주는 건지 궁금합니다. (테스트를 해보고 있는데, data 컨테이너는 어떤 순간에도 내려가지 않는 것 같아서요.)
현재 socket1(여러분이 'web-only’라고 부르는 것)을 실행 중이라고 가정해 보겠습니다. 그리고 무언가를 재빌드하고 테스트하고 싶다고 합시다. socket1이 실행되는 동안 socket2를 재빌드합니다. 각 컨테이너가 유닉스 도메인 소켓을 사용하므로, 공유 소켓이 서로 다른 파일(위치)에 있기 때문에 두 컨테이너는 동시에 실행될 수 있습니다.
재미로, bash 로그인 프로필에 몇 줄의 코드를 추가하여 서버에 로그인할 때마다 어떤 소켓(컨테이너)이 실행 중인지 항상 알려주도록 했습니다:
Last login: Fri May 15 09:39:39 2020 from 159.192.33.138
srw-rw---- 1 root docker 0 May 5 07:38 /var/run/docker.sock
lrwxrwxrwx 1 root root 44 May 15 09:16 /var/run/nginx.http.sock -> /var/discourse/shared/socket/nginx.http.sock
도움이 되길 바랍니다.
제 생각에는(일반적으로) 이것이 “추천하는 방법”(그리고 프로덕션 환경에서 스테이징이나 개발 환경이 아닌 제 “기본 표준”)이지만, 이것은 제 개인적인 의견이며 YMMV(결과가 다를 수 있음)입니다. 설정에 약간 더 많은 노력이 필요하지만, 프로덕션 환경에서는 그만한 가치가 있다고 생각합니다.
주의사항:
템플릿이 덮어쓰기되어 문제가 될 수 있으므로, 원래의 yml 파일 사본을 안전한 곳에 보관하는 것이 좋습니다. 따라서 우리는 “모든 yml 파일 저장”, “먼저 pull”, 그리고 “yml 파일 확인” 순서로 진행한 후(과도한 신중함으로 인해) bootstrap을 실행하는 경향이 있습니다.
그러면 이 방식에서는 웹 인스턴스를 두 개 두고, 유휴 상태인 인스턴스를 동일한 데이터로 테스트하는 것이군요? 흥미롭네요. 제가 이 ‘공간을 절약하려는’ 전략을 완전히 정리하는 대로 꼭 한번 시도해 보겠습니다
정말 감사합니다. 글을 처음부터 끝까지 읽었는데, 작은 질문이 하나 있습니다. 여기서 'data’를 의미하신 건 맞나요? (즉, web_only 대신 data를 다시 빌드한다는 의미로, 글에 나와 있는 내용처럼요?) 아니면 제가 놓치고 있는 다른 트릭이 있는 걸까요(예: 큰 업그레이드가 있을 때 두 가지를 합쳐서 실행하고 다시 분리하는 방식 등).
자세한 설명 감사합니다. 테스트 환경에서 두 개의 "앱 컨테이너"를 Data One에 연결해 보았는데, 아주 잘 작동했습니다.
하지만 PRD에는 적용할 수 없습니다. PRD에서 Data 컨테이너를 재빌드할 수 없고, 문제를 해결하기 전에 아무것도 변경하고 싶지 않기 때문입니다. discourse@discourse FATAL: terminating connection due to administrator command 오류로 인해 작업이 중단됩니다.