HAProxy로 프론트된 상태에서 ./launcher rebuild appN 실행 후 일시적 503 오류 발생

저는 두 개의 web_only 컨테이너, mail-receiver 컨테이너, Postgres 및 Redis를 Docker 네트워크에 구성하여 테스트용 포럼을 만들었습니다. 두 웹 컨테이너의 nginx 앞에는 HAProxy가 배치되어 있었습니다. 시스템은 잘 작동했지만, 두 웹 컨테이너 중 하나를 재빌드할 때마다 503이 반환되는 짧은 다운타임이 항상 발생했습니다.

다행히 완화할 수 있는 우회 방법을 찾았지만, 완벽하지는 않습니다.


app1을 재빌드하기 전에 다음을 실행하세요.

echo "disable server be_discourse/app1" | socat stdio /run/haproxy/admin.sock

그리고 app1의 재빌드 과정이 완료된 후 다음을 실행하세요.

echo "enable server be_discourse/app1" | socat stdio /run/haproxy/admin.sock

반대로 app2를 재빌드하려면 다음을 실행하세요.

echo "disable server be_discourse/app2" | socat stdio /run/haproxy/admin.sock

그리고 app2의 재빌드 과정이 완료된 후 다음을 실행하세요.

echo "enable server be_discourse/app1" | socat stdio /run/haproxy/admin.sock

이는 /etc/haproxy/haproxy.cfgbe_discourse 관련 설정과 일치하도록 작성되었습니다. 아직 설정 파일을 코드 블록으로 붙여넣지는 않겠지만, 많은 분들이 원하신다면 공유할 수도 있습니다.

따라서 추가된 명령어들은 컨테이너가 다운되어 503이 발생할 것이라는 것을 HAProxy가 인지하기 전에 트래픽을 우회하도록 지시함으로써 503 오류를 완화합니다.


대신에 에러 페이지를 생성하는 것도 가능하지만, 저는 그쪽으로 큰 성과를 보지 못했고 더 많은 조사가 필요하다고 느낍니다. 그러나 이는 다운타임 문제를 실제로 해결하지는 못합니다.

503 에러가 발생하면 다음 서버로 재분배하는 것은 왜 하지 않나요?

좋네요! 그게 좋을 것 같습니다. 그리고 Sidekiq에 대한 추천 사항이 있나요? discourse_docker/samples at main · discourse/discourse_docker · GitHub 에서 Sidekiq를 전혀 볼 수 없어서, 다음 변경 사항을 yml 파일 중 하나에 적용했습니다.


app1.yml

## Remember, this is YAML syntax - you can only have one block with a name
run:
  - exec: echo "Beginning of custom commands"
+  - exec: rm -f /etc/service/sidekiq/down
  ## If you want to configure password login for root, uncomment and change:

app2.yml

## Remember, this is YAML syntax - you can only have one block with a name
run:
  - exec: echo "Beginning of custom commands"
+  - exec: bash -lc 'mkdir -p /etc/service/sidekiq && touch /etc/service/sidekiq/down'
  ## If you want to configure password login for root, uncomment and change:

여러 개의 웹 컨테이너를 실행 중인 경우, Sidekiq를 어디에서 실행할지에 대해 추가로 고려해야 할 점이 있습니다.

503 에러는 웹 요청에만 영향을 미치지만, Sidekiq가 중단되면 사이트는 백그라운드 작업(이메일, 다이제스트, 배지, 웹훅)을 조용히 처리하지 못하게 됩니다. 이를 깔끔하게 처리하는 방법 중 하나는 Sidekiq에 전용 컨테이너를 할당하고 웹 노드에서는 비활성화해 두는 것입니다.

Discourse는 내부적으로 runit을 사용합니다: /etc/service/*의 각 서비스는 해당 디렉터리 안에 down이라는 이름의 파일이 없는 한 시작됩니다. 필요한 제어는 이것만으로 충분합니다.

전용 Sidekiq 워커의 예시는 다음과 같습니다:

worker.yml — Sidekiq 전용
templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/sshd.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"

expose: []   # HTTP 포트 없음

env:
  DISCOURSE_DB_HOST: data
  DISCOURSE_REDIS_HOST: data
  DISCOURSE_HOSTNAME: "forum.example.com"
  # 기존 시크릿 / SMTP 설정과 일치하도록 설정

hooks:
  after_code:
    - exec:
        cd: $home
        cmd:
          # 웹 서비스 비활성화
          - mkdir -p /etc/service/puma  && touch /etc/service/puma/down
          - mkdir -p /etc/service/nginx && touch /etc/service/nginx/down
          # Sidekiq 활성화
          - rm -f /etc/service/sidekiq/down

run:
  - exec: echo "Starting Sidekiq worker container"

웹 컨테이너(app1/app2)에서는 반대로 해야 합니다. Puma와 Nginx는 활성화된 상태로 유지하되, 다음을 추가합니다:

hooks:
  after_code:
    - exec:
        cd: $home
        cmd:
          - mkdir -p /etc/service/sidekiq && touch /etc/service/sidekiq/down

이 구성을 사용하면 HAProxy는 웹 컨테이너만 로드 밸런싱하고, Sidekiq는 워커 컨테이너에서 계속 실행됩니다(.\launcher rebuild <yml 이름>). 이렇게 하면 웹 노드를 재빌드하거나 교체할 때 백그라운드 작업이 중단되지 않습니다.

이 내용이 끝없는 대화로 이어지는 것 같아 #installation:hosting 채널로 옮겼습니다. Support 채널에서는 이런 식의 대화를 피합시다.