92호. 사이트맵 삽질기 (feat. Vapor) free

2019-12-11

안녕하세요. 어제 예고해드린 대로 오늘은 사이트맵 작업을 할 때 Vapor에서 발생한 이슈를 정리해서 말씀드리고자 합니다. 사이트맵이라는 특정한 상황을 예로 들었지만 서버리스 아키텍처에서 공통으로 발생할 수 있는 이슈이니 한 번 쯤 미리 봐두면 나중에 도움이 되실 수도 있을 것 같아요.


문제


사이트맵 파일을 public 디렉터리에 자동으로 생성하는 코드를 추가하고 로컬에서 잘 되는 걸 확인한 후 Vapor에 배포했습니다. 매일 한 번씩 갱신하도록 해두었기 때문에 스케쥴링도 잘 동작하나 볼 겸 하루를 기다렸습니다. 그런데 다음 날 확인해보니 파일이 생성되지 않았어요.


혹시나 하고 직접 커맨드를 실행해봤습니다. 그랬더니 에러가 발생하더군요.



file_put_contents(/var/task/public/sitemap.xml): failed to open stream: Rea  
d-only file system

읽기 전용 시스템이기 때문에 파일을 만들어 저장할 수 없다는 메시지였습니다.


말 그대로 서버리스니까 실행되는 애플리케이션에는 파일을 저장할 수가 없는 것이죠. 생각해보면 당연한 건데 Vapor가 뭔가 다 알아서 처리해줄 거라고 기대한 면도 있습니다. ㅎㅎ


참고로 그럼 다른 정적 파일들은 어떻게 된 것이냐고 물으실 수 있을 것 같은데, 미리 준비된 정적 파일은 Vapor에 배포할 때 S3에 올라가고 Vapor가 자동으로 연결해줍니다. 지금 이슈는 애플리케이션에서 파일을 만들 때 발생하는 것입니다.


해결


다들 아시다시피 public 디렉터리에 파일을 만들면 따로 뭘 안 해줘도 {도메인}/파일명으로 바로 정적 파일을 제공할 수 있습니다. 하지만 지금은 public 디렉터리에 파일을 저장할 수 없는 상황입니다. 그래서 파일을 S3에 저장하고, 라우트에서 이를 받아와서 응답해주는 방식으로 해결했습니다.


// app/Console/Commands/GenerateSitemap.php

public function handle()
{
SitemapGenerator::create(config('app.url'))
->writeToFile('/tmp/sitemap.xml');

Storage::disk('s3')->put('/web/sitemap.xml', file_get_contents('/tmp/sitemap.xml'));
}

SitemapGenerator에 파일을 저장할 스토리지를 설정하는 옵션이 있다면 바로 S3로 설정해서 올릴 수 있었을 텐데, 아쉽게도 그런 옵션은 없어서 로컬에 파일을 생성한 후 이를 S3로 전송하는 방식을 사용했습니다.


잠깐, 아까는 로컬에 파일을 만들 수 없다고 하지 않았나요? 네 맞습니다. 지금처럼 어쩔 수 없이 파일을 만들어야 하는 상황이 생기기 때문에 Vapor에서는 /tmp 디렉터리만 예외적으로 파일 생성을 허용합니다. SitemapGenerator를 이용해서 파일을 저장하는 위치를 주의해서 봐주세요. /public/sitemap.xml이 아니고 /tmp/sitemap.xml에 저장하고 있습니다.


다음으로 S3에 저장한 사이트맵 파일을 서빙하도록 라우트를 작성했습니다.


// app/routes/web.php

Route::get('/sitemap.xml', function () {
return response(
Storage::disk('s3')->get('web/sitemap.xml'), 200, ['Content-Type' => 'application/xml']
);
});

이렇게 애플리케이션이 만든 사이트맵 파일을 S3에 올려두고 제공하는 방식으로 해결했습니다.


요약



  • Vapor는 서버리스라 애플리케이션에서 만든 파일을 로컬에 저장하도록 작성하면 안 된다. 외부 스토리지에 저장해야 한다.

  • 임시로 파일을 저장할 필요가 있을 때는 /tmp를 활용할 수 있다.


Vapor는 라라벨 애플리케이션을 서버리스로 만들어주는 서비스입니다. 그래서 Vapor로 동작하는 라라벨 애플리케이션은 서버리스인 반면 로컬은 그냥 라라벨이죠. 여기서 실수할 가능성이 싹트는 것 같습니다. 로컬에서 잘 동작하는 것만 확인하고 Vapor로 배포하면 제대로 동작하지 않을 가능성이 있습니다. 실서버에서는 서버리스로 동작한다는 점을 조금은 염두에 두고 코드를 작성해야 할 것 같아요.


1일 1식 라라벨 92호

2019년 12월 11일


이현석

메쉬 코리아 개발자. 바쁜 팀장님 대신 알려주는 신입 PHP 개발자 안내서를 쓰고, 클린 아키텍처 인 PHP를 번역했습니다. 2020년에 출간될 Laravel Up & Running 2nd Edition을 번역하고 있습니다.