S3 버킷 이름 변경 후, 기존 게시물 재처리해도 새 S3 CDN URL이 반영되지 않습니다

Discourse(2.7.0.beta1)를 설치하고 이전 Google Groups 게시글을 가져온 후, S3 버킷/키 설정(CDN URL은 제외)을 추가하고 다음을 실행했습니다.

rake uploads:migrate_to_s3

이 작업은 잘 진행되는 것처럼 보였습니다. 모든 이미지가 S3로 업로드되었고, Discourse는 자동으로 생성된 S3 URL을 통해 이미지에 접근하려고 했습니다. URL은 다음과 같은 형태였습니다.

https://ortus-discourse.s3.dualstack.us-west-2.amazonaws.com/original/1X/75747ca17a3ca01f298f836691e1990916bafccb.png

그 후 버킷 이름을 변경(ortus-discourse-uploads)하고, Cloudflare로 설정된 CNAME인

https://communitycdn.ortussolutions.com/

이라는 Cloudfront 배포를 그 앞에 설정했습니다. CNAME, Cloudfront, S3 모두 정상적으로 작동하고 있습니다. 다음과 같은 URL은 버킷 내 이미지가 올바르게 제공되고 있습니다.

https://communitycdn.ortussolutions.com/original/1X/75747ca17a3ca01f298f836691e1990916bafccb.png

그러나 문제는 Discourse가 버킷 이름 변경으로 인해 작동하지 않는 이전 도메인인 ortus-discourse.s3.dualstack.us-west-2.amazonaws.com에 완전히 고정되어 있다는 것입니다. 컨테이너를 재구축하거나 이전 게시글을 다시 생성(rebake)하는 횟수에 관계없이, Discourse가 새로운 CDN URL을 사용하도록 만드는 것이 불가능합니다. 이 문제에 대해 하루 종일 구글링을 했고, 다양한 설정으로 app 컨테이너 내에서 아마도 열두 번쯤 다시 생성(rebake)을 시도해 보았습니다. 모든 포럼 스레드에서는 동일한 조언(재구축 및 다시 생성)만 제시하고 있지만, 이는 작동하지 않습니다.

깨진 것은 게시글 내 이미지뿐만 아니라, <link rel="icon" type="image/png" href=""> 및 사이트 로고도 이전 도메인에 고정되어 S3 CDN URL을 사용하지 않습니다.

현재 S3 설정은 다음과 같습니다.

  DISCOURSE_S3_ACCESS_KEY_ID: '********'
  DISCOURSE_S3_SECRET_ACCESS_KEY: '******'
  DISCOURSE_BACKUP_LOCATION: 's3'
  DISCOURSE_ENABLE_S3_UPLOADS: true
  DISCOURSE_S3_BUCKET: 'ortus-discourse-uploads'
  DISCOURSE_S3_UPLOAD_BUCKET: 'ortus-discourse-uploads'
  DISCOURSE_S3_BACKUP_BUCKET: 'ortus-discourse-backups'
  DISCOURSE_S3_REGION: 'us-west-2'
  DISCOURSE_S3_CDN_URL: https://communitycdn.ortussolutions.com

  DISCOURSE_CDN_URL: https://community.ortussolutions.com

게시글에서 다음과 같이 이전 URL을 새로운 URL로 재매핑(remap)하는 것도 시도해 보았습니다.

rake posts:remap["ortus-discourse.s3.dualstack.us-west-2.amazonaws.com","communitycdn.ortussolutions.com"]

그러나 명령어는 0개의 게시글이 영향을 받았다고 보고했습니다.

env 변수나 DB 설정 어디에도 이전 버킷 이름인 ortus-discourse를 참조하는 것이 없으므로, Discourse가 어디서 그 값을 가져오는지 알 수 없습니다. 저는 Discourse 초보자이며 Ruby 개발자가 아니므로, app.yml, 관리자 UI, 그리고 포럼에서 찾은 rake 명령어의 출력 범위보다 더 깊이 살펴본 적이 없습니다.

Uploads 테이블에 업로드된 항목들의 값은 무엇인가요?

./launcher enter app
rails c
Upload.all.sample(10).pluck(:url)

@Falco 답변해 주셔서 감사합니다. 해당 명령어의 출력 결과는 다음과 같습니다.

root@discourse-app:/var/www/discourse# rails c
Upload.all.sample(10).pluck(:url)
[1] pry(main)> Upload.all.sample(10).pluck(:url)
=> ["//ortus-discourse.s3.dualstack.us-west-2.amazonaws.com/original/1X/52b3aff4e63a7e38bef42d469bafd1ed7c1cc1a2.png",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/9f90374a280a4681332bcd2191b8de43462f8776.png",
 "//ortus-discourse.s3.dualstack.us-west-2.amazonaws.com/original/1X/29691fba566fc998a966aa93859753e3cf0b8528.cfc",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/6ae912ced40d60adc1356c1d7acf144b0fa0985a.jpeg",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/4dfe5b48fc8cb5d79880d70355c34d7ed02be812.png",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/897c4b4e755c1c8e93224a27187dc631a02e4388.png",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/59886f322e6834b567d473138108fab6e0f33764.png",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/e3e3429d63155cf0d850e161846d187bc6f273ea.jpeg",
 "//ortus-discourse.s3.dualstack.us-west-2.amazonaws.com/original/1X/1b701869b4b235daa8d6a9a7728766f3b4e69814.txt",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/c83aaee941462d47ef91f0c6448257d07487b231.png"]
[2] pry(main)>

실제로 구버킷을 참조하는 파일이 꽤 많이 있습니다.

필요한 리맵은 다음과 같습니다:

./launcher enter app
rails c
DbHelper.remap("ortus-discourse.s3.dualstack.us-west-2.amazonaws.com", "ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com")

이러한 작업은 되돌릴 수 없으므로, 실행 전에 반드시 백업을 받아 두시기 바랍니다.

@Falco 다시 한번 감사합니다. remap을 두 번 실행해 보았습니다.

DbHelper.remap("ortus-discourse.s3.dualstack.us-west-2.amazonaws.com", "communitycdn.ortussolutions.com")
DbHelper.remap("ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com", "communitycdn.ortussolutions.com")

이미지를 다시 빌드한 결과 사이트 헤더 로고 같은 문제는 해결되었습니다. 이제 게시물 이미지를 수정할 수 있는지 확인하기 위해 모든 게시물을 다시 백킹(rebake)하고 있지만, 시간이 좀 걸릴 것 같습니다.


백킹이 완료될 때까지 기다리는 동안, 여기서 정확히 무슨 일이 있었던 것일까요? Discourse의 버그로 인해 업로드된 파일들이 구 URL에 머물러 있고, 새 URL로 전환되지 않은 것일까요?

CDN URL 변경에 대해 논의하는 이 게시물과 같은 글들을 읽었으나, 거기서는 게시물 내 문자열 치환(이것은 효과가 없었습니다)만 언급했을 뿐 DBUtil remap에 대해서는 전혀 언급하지 않았습니다. How do I change the legacy CDN URLs of images in posts?

s3로 가져오기(import) rake 스크립트를 다시 실행해 보기도 했는데, 에러가 발생했습니다(죄송합니다, 당시 에러 내용을 메모하지 않았습니다).

처음부터 다시 시작할 수 있는지 확인하기 위해 migrate_from_s3 rake 스크립트를 거의 실행하려 했으나, 실행하면 데이터베이스가 손상된다는 여기 포럼 게시물을 발견하여 실행하지 않았습니다.

무엇을 다르게 해야 했는지, 혹은 이 문제를 해결해 줄 포럼 게시물이 무엇이었는지 막막합니다. (여기에 게시하기 전에 스스로 해결하려고 정말 열심히 노력했습니다!)

불행히도 재처리(rebake)가 게시물에 내장된 이미지 문제를 해결하지 못한 것 같습니다. 흥미로운 점은 오래된 게시물을 선택해 편집하면 이미지가 다음과 같은 형태로 표시됩니다:

![COMMANDBOXERROR.png|1169x984](upload://yTDVQSa4wbIeLGEZvE7muXe8sAJ.png)

하지만 게시물을 열람할 때는 다음 주소를 가리키는 큰 빈 공간만 보입니다:

https://community.ortussolutions.com/images/transparent.png

이것은 비교적 최근 발생한 변화입니다. 어제까지는 대부분의 이미지가 단순히 잘못된 이전 S3 URL을 가리키고 있었지만, 어젯밤이나 오늘 어느 시점부터 투명 PNG가 나타났습니다.

음, 제가 답글에서 말씀드린 내용과는 다릅니다 :face_with_raised_eyebrow:

uploads 테이블에는 S3 위치를 기대하며, 마크다운 쿡 과정에서만 CDN으로 대체됩니다.

업로드 테이블에 CDN을 넣었는데, 이는 소프트웨어가 보통 수행하는 방식이 아닙니다.

죄송합니다. OP를 충분히 꼼꼼히 읽지 않아서 Cloudflare 배포에 대한 언급을 놓쳤고, 제 실제 URL을 사용하도록 조정해야 한다는 점을 인지하지 못했다고 생각했습니다. 원치 않는 URL이 실제로 데이터베이스 어디에 저장되어 있다는 사실도 몰랐습니다. 처음에 그게 의도한 것인지 먼저 확인하고 답글을 달 뻔했지만, 제가 해야 할 일이 분명해 보였기 때문입니다.

걱정 마세요. S3 URL로 다시 매핑하는 것은 쉽습니다. 이건 새로운 Discourse 설치 환경이고 업로드된 콘텐츠가 모두 같은 위치에 있으므로 전환하기가 쉽습니다.

이전 게시물에서 제가 제기한, 무엇이 잘못되었는지에 대한 질문은 여전히 유효합니다.

Object Storage 버킷을 변경하는 것을 지원하지 않는다는 것이 그 이유라고 생각합니다.

여기서 '지원’이란 변경해도 정상적으로 동작할 것을 기대할 수 없고, 이를 위한 문서화된 가이드나 미리 패키징된 rake 태스크가 없다는 뜻입니다. 따라서 버킷을 변경해야 하는 상황이 발생하면 데이터베이스를 직접 수정해야 합니다.

알겠습니다, 충분히 납득이 됩니다. 제가 큰 문제를 일으킨 부분은 S3 버킷 이름을 변경한 후, 관리자 화면에서 버킷 이름만 업데이트하면 필요한 모든 것이 처리될 것이라고 가정했던 것입니다. 버킷 이름을 편집할 때 관리자 UI에 경고 메시지가 표시되면 도움이 될 것 같습니다. (어떤 식으로든 도움이 될 것 같아서 관리자 UI의 설정으로 시작하다가 나중에 환경 변수 사용으로 전환했습니다.) 업로드 후 버킷 이름을 변경하면 문제가 발생한다는 것은 분명하지 않았습니다.

도메인을 올바른 AWS S3 도메인으로 다시 매핑했습니다.

DbHelper.remap("communitycdn.ortussolutions.com", "ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com")
[1] pry(main)> Upload.all.sample(10).pluck(:url)
=> ["//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/fc05f9be9b783479819fec68b1d8e493110007a4.cfc",
 "/images/d-logo-sketch-small.png",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/50f0d6f260cdb4ef91e29023d92b46df096ab34e.cfc",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/65d80cddc6dc15b9a4d1b9e9d88cc9a8928c5316.png",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/a86aa2a12183428f3289caa95787ea16f22e2e4d.png",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/9f76b5238b147a60c8ad5f65bd7fa4bb6b58d852.png",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/6a4c9b992e6cd8a15ddeaec0d158ebd473164525.zip",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/3a532dec6390d5087ed6154fc0335c2c0f1ea543.zip",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/93420848249ecea2261d405e46f7f450cc02a3af.txt",
 "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/d67c39e06ce624b9deb7625dd041d21cffd96df9.png"]

앱을 다시 빌드했고 이제 게시물을 다시 베이킹(재처리)하고 있습니다. 37번째 시도가 성공을 가져다줄지 지켜보겠습니다 :slight_smile:

모든 업로드에 올바른 S3 버킷 URL이 설정되어 있고, 컨테이너도 재빌드했으며, 모든 3만 개의 게시물을 다시 처리(re-bake)했습니다. 하지만 여전히 모든 이미지 자리에 다음이 표시됩니다.

/images/transparent.png

게시물을 편집하면 여전히 다음과 같은 내용이 표시됩니다:

![COMMANDBOXERROR.png|1169x984](upload://yTDVQSa4wbIeLGEZvE7muXe8sAJ.png)

흥미로운 점은 zip 파일과 같은 다른 첨부 파일은 이제 정상적으로 작동한다는 것입니다.

기존 게시물에 내장된 이미지가 정상적으로 작동하도록 하기 위해 누락된 부분이 있을까요?

yTDVQSa4wbIeLGEZvE7muXe8sAJ에 해당하는 Upload 객체의 속성을 출력해 주시겠습니까?

해당 작업을 하고 싶지만, 이 부분은 도움이 필요합니다. 저는 개발자이지만 Ruby 개발자는 아닙니다. 무작위로 선택된 업로드의 모든 속성을 다음과 같이 출력하는 방법은 찾았습니다:

[17] pry(main)> Upload.all.sample(1)
=> [#<Upload:0x00005633230f8af0
  id: 353,
  user_id: 273,
  original_filename: "helloWorldF.zip",
  filesize: 50542,
  width: nil,
  height: nil,
  url: "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/3a532dec6390d5087ed6154fc0335c2c0f1ea543.zip",
  created_at: Wed, 30 Dec 2020 18:46:31 UTC +00:00,
  updated_at: Wed, 30 Dec 2020 18:46:31 UTC +00:00,
  sha1: "3a532dec6390d5087ed6154fc0335c2c0f1ea543",
  origin: nil,
  retain_hours: nil,
  extension: "zip",
  thumbnail_width: nil,
  thumbnail_height: nil,
  etag: nil,
  secure: false,
  access_control_post_id: nil,
  original_sha1: nil,
  animated: nil,
  verification_status: 1>]

특정 레코드를 찾는 방법에 대한 ROR Active Record 참고 자료를 찾았지만, yTDVQSa4wbIeLGEZvE7muXe8sAJ과 비슷해 보이는 데이터는 찾을 수 없습니다. 해당 업로드 레코드를 어떻게 찾나요?

오, 좋은 소식입니다. 업로드(Upload) 기록을 찾는 방법에 대해 조사하던 중, base62 문자열을 sha1 해시로 변환하는 방법을 알려주는 이 게시물을 발견했습니다.

이 게시물에서는 투명.png만 표시되는 이미지가 ‘tombstone(무덤)’ 처리되었다고 언급하고 있었습니다. 그 정확한 의미를 완전히 파악하지는 못했지만, 이미지가 손상된 상태에서 야간 프로세스가 실행되어 사용되지 않는 것으로 표시된 것으로 추정됩니다. 저는 다음 명령을 실행할 수 있었습니다.

rake uploads:recover_from_tombstone

그리고 모든 내장 이미지가 복구되어 이제 제 S3 CDN을 가리키고 있습니다!

참고로, yTDVQSa4wbIeLGEZvE7muXe8sAJ 문자열로 시작하는 업로드 기록을 찾은 방법을 공유합니다.

[14] pry(main)> Base62.decode("yTDVQSa4wbIeLGEZvE7muXe8sAJ").to_s(16)
=> "f49428d6af35d7e0414408ccb65e7316f5003215"
[15] pry(main)> Upload.where( "original_filename ilike '%f49428d6af35d7e0414408ccb65e7316f5003215%'" )
=> [#<Upload:0x000056313aa91fe8
  id: 899,
  user_id: 549,
  original_filename: "f49428d6af35d7e0414408ccb65e7316f5003215.png",
  filesize: 25514,
  width: 1169,
  height: 984,
  url: "//ortus-discourse-uploads.s3.dualstack.us-west-2.amazonaws.com/original/1X/f49428d6af35d7e0414408ccb65e7316f5003215.png",
  created_at: Tue, 12 Jan 2021 23:01:35 UTC +00:00,
  updated_at: Tue, 12 Jan 2021 23:01:36 UTC +00:00,
  sha1: "f49428d6af35d7e0414408ccb65e7316f5003215",
  origin: nil,
  retain_hours: nil,
  extension: "png",
  thumbnail_width: 594,
  thumbnail_height: 500,
  etag: "6977f35ddbf39a4399dc76f92a5079d4",
  secure: false,
  access_control_post_id: nil,
  original_sha1: nil,
  animated: nil,
  verification_status: 1>]

다시 한번 도움을 주셔서 감사합니다, @Falco. 저는 디스커스(Discourse)를 처음 사용하지만, 여러분은 매우 인내심 있고 도움을 주시는 분임을 입증하셨습니다 :+1: