Bootsnap::CompileCache::PermissionError

여러분 안녕하세요. Docker로 최신 버전의 Discourse를 설치했습니다.
문제는 컨테이너를 재빌드한 후 컨테이너는 실행 중이고 앱 페이지에도 접근할 수 있지만, 응답으로 502 오류가 발생한다는 것입니다. 로그를 확인하거나 컨테이너 내부에서 rails c를 실행하려고 해도 다음 오류만 표시됩니다:
`permission_error’: bootsnap doesn’t have permission to write cache entries in ‘tmp/cache/bootsnap/compile-cache’ (or, less likely, doesn’t have permission to read ‘/usr/local/lib/ruby/2.7.0/set.rb’) (Bootsnap::CompileCache::PermissionError)

이 문제를 해결하려면 컨테이너 내부에서 chown -R discourse:discourse /var/www/discourse/tmp (정확히 말하면 /var/www/discourse/tmp/cache/bootsnap) 명령을 실행해야 하며, 이 작업을 즉시 수행하면 재시작 없이도 앱이 정상적으로 작동합니다. 매번 재빌드 후 수동으로 이 작업을 수행하는 것은 매우 번거롭습니다.

app.yml의 커스텀 명령 섹션에 위에서 언급한 chown -R discourse:discourse /var/www/discourse/tmp 명령을 사용하여 이 문제를 해결할 수 있을 것 같았지만, unfortunately 작동하지 않습니다. 명령이 실행되는 것 같기는 합니다(확인하기 위해 mkdir을 시도해 봤습니다) 하지만 파일 권한에는 영향을 미치지 않습니다.

여기서 문제가 무엇일 수 있으며 어떻게 해결할 수 있을까요? 파일 권한을 올바르게 변경하는 다른 방법이 있을까요?

이상하네요. Discourse 공식 표준 설치를 따랐나요?

물론입니다. 추가적인 조치는 취해지지 않았습니다.

container.yml 파일을 붙여넣어 주시겠어요? (비밀번호는 제외해 주세요)

네, 여기 있습니다:

## 이 파일은 올인원, 독립형 Discourse Docker 컨테이너 템플릿입니다
##
## 이 파일을 수정한 후에는 반드시 재빌드해야 합니다
## /var/discourse/launcher rebuild app
##
## 편집 시 *매우* 주의하세요!
## YAML 파일은 공백이나 정렬 실수에 *매우* 민감합니다!
## 필요 시 http://www.yamllint.com/에서 이 파일을 검증하세요

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Lets Encrypt(https)를 추가하려면 아래 두 줄의 주석을 해제하세요
  - "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"

## 이 컨테이너가 어떤 TCP/IP 포트를 노출해야 하는가?
## Discourse가 Apache나 nginx 같은 다른 웹서버와 포트를 공유하려면
## 자세한 내용은 https://meta.discourse.org/t/17247을 참조하세요
expose:
  - "80:80"   # http
  - "443:443" # https

params:
  db_default_text_search_config: "pg_catalog.english"

  ## db_shared_buffers는 총 메모리의 최대 25%로 설정하세요.
  ## 감지된 RAM 기반으로 bootstrap이 자동으로 설정하거나, 직접 덮어쓸 수 있습니다
  #db_shared_buffers: "256MB"

  ## 정렬 성능을 향상시킬 수 있지만, 연결당 메모리 사용량이 증가합니다
  #db_work_mem: "40MB"

  ## 이 컨테이너가 어떤 Git 리비전을 사용해야 하는가? (기본값: tests-passed)
  #version: tests-passed

env:
  LC_ALL: en_US.UTF-8
  LANG: en_US.UTF-8
  LANGUAGE: en_US.UTF-8
  # DISCOURSE_DEFAULT_LOCALE: en

  ## 동시에 처리할 수 있는 웹 요청 수는 얼마인가? 메모리와 CPU 코어 수에 따라 달라집니다.
  ## 감지된 CPU 기반으로 bootstrap이 자동으로 설정하거나, 직접 덮어쓸 수 있습니다
  #UNICORN_WORKERS: 3

  ## TODO: 이 Discourse 인스턴스가 응답할 도메인 이름
  ## 필수 항목입니다. Discourse는 IP 주소만으로는 작동하지 않습니다.
  DISCOURSE_HOSTNAME: 'example.com'

  ## 컨테이너가 위에서 지정된 호스트 이름(-h 옵션)과 동일한 이름으로 시작되도록 하려면
  ## 주석을 해제하세요 (기본값 "$hostname-$config")
  #DOCKER_USE_HOSTNAME: true

  ## TODO: 초기 가입 시 관리자 및 개발자로 지정할 쉼표로 구분된 이메일 목록
  ## 예: 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'test@mail.com'

  ## TODO: 새 계정을 검증하고 알림을 보내는 데 사용되는 SMTP 메일 서버
  # SMTP 주소, 사용자 이름, 비밀번호는 필수입니다
  # 경고: SMTP 비밀번호에 '#' 문자가 있으면 문제가 발생할 수 있습니다!
  DISCOURSE_SMTP_ADDRESS: smtp.mail.io
  #DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: 111
  DISCOURSE_SMTP_PASSWORD: 111
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (선택 사항, 기본값 true)
  #DISCOURSE_SMTP_DOMAIN: discourse.example.com    # (일부 제공업체에서 필수)
  #DISCOURSE_NOTIFICATION_EMAIL: noreply@discourse.example.com    # (알림을 보내는 주소)

  ## Lets Encrypt 템플릿을 추가했다면, 무료 SSL 인증서를 얻으려면 아래 주석을 해제하세요
  #LETSENCRYPT_ACCOUNT_EMAIL: me@example.com

  ## 이 Discourse 인스턴스의 http 또는 https CDN 주소 (pull로 구성됨)
  ## 자세한 내용은 https://meta.discourse.org/t/14857을 참조하세요
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com
  
  ## IP 주소 조회를 위한 maxmind 지리 정보 IP 주소 키
  ## 자세한 내용은 https://meta.discourse.org/t/-/137387/23을 참조하세요
  #DISCOURSE_MAXMIND_LICENSE_KEY: 1234567890123456

## Docker 컨테이너는 상태가 없으며(stateless), 모든 데이터는 /shared에 저장됩니다
volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log

## 플러그인은 여기에 설치합니다
## 자세한 내용은 https://meta.discourse.org/t/19157을 참조하세요
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git

## 빌드 후 실행할 사용자 지정 명령
run:
  - exec: echo "Beginning of custom commands"
  ## 첫 번째 가입 시 'From' 이메일 주소를 설정하려면 주석을 해제하고 변경하세요:
  ## 첫 가입 이메일을 받은 후, 해당 줄의 주석을 다시 활성화하세요. 한 번만 실행하면 됩니다.
  #- exec: rails r "SiteSetting.notification_email='info@unconfigured.discourse.org'"
  #- exec: chown -R discourse:discourse /var/www/discourse/tmp/cache/bootsnap  # 파일 권한을 수정하기 위해 시도했던 명령 예시
  - exec: echo "End of custom commands"

discourse-setup을 실행하지 않으셨죠? 주석 처리한 일부 항목들이 정의되었어야 합니다. 방금 해당 변경 사항을 적용했는데, 예상대로 작동하는지 확인하고 싶습니다.

Let’s Encrypt를 사용하지 않는 이유가 있을까요?

다만, 문제점에 대한 설명은 보이지 않습니다.

우선, 애플리케이션은 discourse-setup을 사용하여 구축되었습니다. 재구축을 몇 번 시도해 보았지만 긍정적인 결과를 얻지 못하자, /samples 디렉터리에서 사전 정의된 .yml 설정 파일 중 하나를 가져와 내 인증 정보로 수정했습니다.
왜 Let’s Encrypt를 사용하지 않느냐고 물었더니, 클라이언트 인증서를 사용하라고 했습니다. 하지만 제 이해로는 인증서는 이 문제와 무관한 것 같습니다. 인증서는 유효하며 이로 인한 문제가 없기 때문입니다.
문제는 프로젝트 레벨에 있는 것으로 보이며, 이는 구체적으로 bootsnap 캐시 파일 권한(아마도 Bootsnap 측의 문제일 수 있음)과 관련이 있는 것 같습니다.

감사합니다. 그게 문제가 될 가능성은 낮다고 동의합니다. 어제 몇 번 설치를 진행했는데 모두 정상적으로 작동했습니다. 질문자님의 문제에 대한 설명을 찾지 못하겠습니다.

저희도 동일한 문제를 겪고 있었고, Docker 컨테이너 내에서 다음 명령을 실행하면 문제가 해결된다는 것을 확인했습니다:

chown -R discourse:discourse /var/www/discourse/tmp

재시작 없이 문제가 해결되었습니다. 소유자 변경 전에 ‘tmp’ 디렉토리의 권한은 'discourse:www-data’에 할당되어 있었습니다. 향후에는 이러한 수동 작업이 매번 재빌드 후에 필요하지 않도록 수정되기를 바랍니다.

런처를 루트 권한으로 실행 중인가요?

네, @pfaffman, 맞습니다. 그게 문제인가요?

그렇지 않은 것 같습니다. 저는 항상 자체 호스팅 인스턴스를 root로 시작하는데, 그런 문제를 본 적이 없습니다.

chown을 사용하지 않으면 디렉토리의 소유자는 누구인가요? 최근에 다시 빌드된 컨테이너에서는 다음과 같이 보입니다:

# ls -l /var/www/discourse/tmp
total 36
lrwxrwxrwx 1 root      root         19 Mar  2 14:56 backups -> /shared/tmp/backups
drwxr-xr-x 1 discourse discourse  4096 Mar  2 14:57 cache
drwxr-xr-x 1 discourse discourse  4096 Mar  2 14:57 ember-rails
drwxr-xr-x 1 discourse root       4096 Mar  2 15:04 pids
lrwxrwxrwx 1 root      root         20 Mar  2 14:56 restores -> /shared/tmp/restores
drwxr-xr-x 2 discourse root       4096 Mar  2 14:56 sockets
drwxr-xr-x 2 discourse discourse 12288 Mar  2 15:02 stylesheet-cache

플러그인 클로닝 외에 app.yml에 사용자 정의 명령어나 훅을 추가했나요?

컨테이너를 실행한 후 /var/www/discourse/tmp 디렉터리의 소유자 사용자 및 그룹은 discourse:www-data였습니다. 이를 discourse:discourse로 재할당하면 문제가 해결되었습니다. 이전에 이런 현상은 발생하지 않았으며, 저는 이미 수개월간 discourse를 사용해 왔습니다. 어쨌든 간단한 해결책이었으며 우연히 발생한 것일 수 있으므로 더 이상 귀찮게 하지 않겠습니다. @gerhard 님, 정말 감사합니다!