Ghost에서 AWS S3 Storage를 이미지 서버로 사용하기

보통 Ghost에서 글 쓰다가 이미지를 첨부 하고싶어 ![]()을 타이핑하면

요런 이쁜 업로드 창이 preview페이지에 떠서 Drag-and-Drop 같은 방식으로 쉽게 업로드 할 수 있다. 하지만 이미지는 블로그가 돌아가고 있는 로컬 서버안에 저장이 된다.

그런데 그런 방식에서 몇가지 단점이 보이면서 이미지는 다른 서버를 두어서 저장해두고 싶었다. 원래는 Dropbox에 저장하면서 public link를 매번 걸었는데, 별 문제는 없는데 그냥 맘에 안들다가 처음 S3를 이미지 서버로 사용할 수 있는 것을 이 블로그 에서 알게 되었다.

즉 하고자 하는 것은 다음과 같다.

s3-store-Custom-Storage-Module

복잡한 것 같지만, 쉽게 말하면 내가 글을 쓰면서 업로드하는 이미지들은 원래 Digital Ocean서버에 저장되던 것을 AWS S3로 업로드되도록 한다. 왜 이렇게 하냐면 우선

  1. Digital Ocean이 이미지를 로드하는 게 느리고, 용량제한이 있으니까
  2. 장기적으로 봤을 때 더 scalable하게 관리해주는 AWS S3가 좋아보이고 연습삼아 사용해보고 싶어서
  3. MarkDown을 많이 사용하면서 나의 모든 이미지 파일들을 쉽게 웹링크로 관리하고 싶어서

이다.

Digital Ocean은 사용하고자 한다면 제 리퍼럴 코드로 부탁드립니다. 저 링크로 가입시 10$ 드리며 결제시 저도 함께 25$ 를 받습니다.

그런데 나는 사실 AWS는 EC2밖에 안써봤고 웹지식이 주워들은 수준이라 애를 꽤나 먹었다. 처음에 참고한 블로그는 나와는 다른 것 같아서 고대로 따라하다가 이상해질까봐 포기했고 Extending Ghost Functionality with a Custom Storage Module를 많이 참고했다. 또 그렇다고 바로 되지는 않아서 전 과정을 공유한다.

하여튼 결과적으로 이미지 업로드를 S3로 우회시킨다면 ![]() 에서 이미지 업로드 완료 시 ![](/content/images/2017/01/image.png)에서 ![](https://s3.ap-northeast-2.amazonaws.com/<버켓이름>/2017/image.png) 로 S3로 업로드가 되며 링크가 바뀐다!

주의

  1. 기존 로컬 이미지는 S3로 업로드 되지않는다.
  2. 기존 로컬 이미지에 영향이 없어야하는데 뒤늦게 확인해보니 이미지가 로컬에 있어도 S3로 접근하는 에러가 난다 ㅠㅠ 따라서 지금은 처음 세팅하는 블로그에 해당

먼저 정리하면

  1. AWS S3 버킷 생성. bucket, region 기억.

    • 파일 업로드 해보고 Make Public해보면 파일 접근하는 URL 기억.
      예를 들어, https://s3.ap-northeast-2.amazonaws.com/tmmsexy/
  2. AWS IAM으로 S3에 업로드 권한 가지는 계정 생성. accessKeyId, secretAccessKey 기억.

  3. Ghost에서 S3 설정

mkdir -p content/storage/ghost-s3 	# 폴더만들고
cd content/storage/ghost-s3			# 이동
npm install ghost-s3-storage		# module 설치
npm install aws-sdk-promise
vim index.js						# 없으면 만들어줘야하는 파일
  • index.jscode 복붙

  • Ghost 홈폴더에서 ./configure를 아까 알던 값으로 채움. 예를 들어,

storage: {
  active: 'ghost-s3',
    'ghost-s3': {
      accessKeyId: 'XXXXXXXXXXXXXXXXXXXX',
      secretAccessKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
      bucket: 'tmmsexy',
      region: 'ap-northeast-2',
      assetHost: 'https://s3.ap-northeast-2.amazonaws.com/tmmsexy/'
    }
},
  • npm start —production, 블로그에서 이미지 업로드시 S3로 업로드 되는 것을 확인!

여기서 부터는 헤딩 과정을 상세히 서술.

AWS S3

당연하게도 Amazon Web Service(AWS) 계정이 필요하다. 만약 S3가 잘 뭔지 모르겠으면 AWS Korea에서 공유한 자료 AWS 시작하기 및 Amazon S3 살펴보기 (윤석찬) 를 참고하자. 참고로 S3도 요금이 있는데 난 아직도 얼마가 과금이 드는지 몰라서 바들바들하긴한데 그렇게 안많겠지?

우선 슬라이드 자료처럼 24번쨰 슬라이드를 참고해서 버킷을 만든다. 아, Region은 Seoul로 한다.

그리고 심심풀이로 직접 파일을 업로드해보고 Make Public해보면 대충 감이 온다. 아무튼 이렇게 S3 버킷을 만들면 이걸로 외부에서 업로드가 가능하게 해줘야하는데 그건 31번째 슬라이드의 AWS Identity and Access Management (IAM) 부분이다.

이 부분의 과정을 통해 S3를 이용하는 AWS(AmazonS3FullAcess 권한을 가진 Policy를 부여)의 유저를 추가해주고 그 Access Key IDSecret Access Key를 적어둔다. 당연히 이 두 개는 외부로 노출되면 안된다.

그것만 되면 이제 Ghost 서버에서 필요한 모듈을 설치해야한다.

Ghost에서 S3 Storage 세팅

Ghost 홈폴더에서 다음을 실행

mkdir -p content/storage/ghost-s3 	# 폴더만들고
cd content/storage/ghost-s3			# 이동
npm install ghost-s3-storage		# module 설치
vim index.js						# 없으면 만들어줘야하는 파일

많은 곳에서 index.js에서

'use strict';
module.exports = require('ghost-s3-storage');

를 복붙 해주라하는데, 난 그렇게 해주면 안되었다. 뭐 일단은 여기까지 따라하고, 다시 Ghost 홈폴더에서 ./configure를 수정해준다.

storage: {
    active: 'ghost-s3',
    'ghost-s3': {
        accessKeyId: 'aws_access_key_here',
        secretAccessKey: 'aws_secret_key_here,
        bucket: 's3_bucket_name',
        region: 'bucket_region'
        assetHost: 's3_acces_url'
    }
},

위 code를 우리는 npm start —production으로 Ghost를 활성화 시키므로 config = { 요기 }안에 production: {요기}의 끝부분에 넣어주면 된다. 내 code는 다음 처럼 생겼다.

storage: {
	active: 'ghost-s3',
    	'ghost-s3': {
        	accessKeyId: 'XXXXXXXXXXXXXXXXXXXX',
            secretAccessKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
            bucket: 'tmmsexy',
            region: 'ap-northeast-2',
            assetHost: 'https://s3.ap-northeast-2.amazonaws.com/tmmsexy/'
	}
},

accessKeyID, secretAcessKey는 아까 IAM에서 AWS 유저 추가해줄때 기억해둔 것, 그리고 S3에서 만든 bucket이름, 그리고 regeion은 서울을 뜻하는 ap-northeast-2를 넣어준다. 난 바보같이 그냥 Seoul했는데 그게 아니였다. 앞서 S3에 파일을 올려보고 Make Public해보면 https://s3.ap-northeast-2.amazonaws.com/<버켓이름>/2017/image.png 요런 형식이었는데, 이곳의 ap-northeast-2가 서울을 뜻한다.

assetHost는 사실 CDN까지 따져서 넣어주면 좋은데 솔직히 내가 CDN도 잘모르고, 그것을 해주는 Amazon CloudFront 설정이 헬 같아서 넘어같다. 이 블로그는 내수용 블로그다

Amazon CloudFront는 .html, .css, .php 및 이미지 파일과 같은 정적 및 동적 웹 콘텐츠를 최종 사용자에게 더 빨리 배포하도록 지원하는 웹 서비스입니다. CloudFront는 엣지 로케이션이라고 하는 데이터 센터의 전 세계 네트워크를 통해 콘텐츠를 제공합니다. CloudFront를 통해 서비스하는 콘텐츠를 사용자가 요청하면 지연 시간이 가장 낮은 엣지로 라우팅되므로 콘텐츠 전송 성능이 뛰어납니다.

그냥 쉽게 이해해서 외국에서 접속할때 빠르게 이미지를 띄어준다는 것 같은데 난 깔끔 포기. 설정하고 싶으면 앞서 참고했다는 여기의 뒷부분을 참고.

여튼 설정이 다 되었다 생각하지만 아까 index.js가 나는 문제가 되어 npm start —prudction 실행시 다음과 같은 에러가 뜬다.

> ghost@0.11.4 start /var/www/ghost
> node index

WARNING: Ghost is attempting to use a direct method to send email.
It is recommended that you explicitly configure an email service.
Help and documentation can be found at http://support.ghost.org/mail.


ERROR: Your storage adapter does not inherit from the Storage Base.

 Error
    at Error.IncorrectUsage (/var/www/ghost/core/server/errors/incorrect-usage.js:3:18)
    at Object.getStorage (/var/www/ghost/core/server/storage/index.js:54:15)
    at setupMiddleware (/var/www/ghost/core/server/middleware/index.js:131:44)
    at /var/www/ghost/core/server/index.js:188:9
    at tryCatcher (/var/www/ghost/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/var/www/ghost/node_modules/bluebird/js/release/promise.js:510:31)
    at Promise._settlePromise (/var/www/ghost/node_modules/bluebird/js/release/promise.js:567:18)
    at Promise._settlePromise0 (/var/www/ghost/node_modules/bluebird/js/release/promise.js:612:10)
    at Promise._settlePromises (/var/www/ghost/node_modules/bluebird/js/release/promise.js:691:18)
    at Promise._fulfill (/var/www/ghost/node_modules/bluebird/js/release/promise.js:636:18)
    at PromiseArray._resolve (/var/www/ghost/node_modules/bluebird/js/release/promise_array.js:125:19)
    at PromiseArray._promiseFulfilled (/var/www/ghost/node_modules/bluebird/js/release/promise_array.js:143:14)
    at Promise._settlePromise (/var/www/ghost/node_modules/bluebird/js/release/promise.js:572:26)
    at Promise._settlePromise0 (/var/www/ghost/node_modules/bluebird/js/release/promise.js:612:10)
    at Promise._settlePromises (/var/www/ghost/node_modules/bluebird/js/release/promise.js:691:18)
    at Async._drainQueue (/var/www/ghost/node_modules/bluebird/js/release/async.js:133:16)
    at Async._drainQueues (/var/www/ghost/node_modules/bluebird/js/release/async.js:143:10)
    at Immediate.Async.drainQueues [as _onImmediate] (/var/www/ghost/node_modules/bluebird/js/release/async.js:17:14)
    at processImmediate [as _immediateCallback] (timers.js:383:17)

걍 구글링 해보면 깃헙 이슈가 뜬다. 요약하자면 아까 index.js

사용한 index.js를 다음의 code로 대체하고 또 npm install aws-sdk-promise를 한다.

안하면 나오는 에러가 ERROR: Cannot find module 'aws-sdk-promise'

다시 npm install —production으로 제대로 블로그 서버가 돌아가게 되고, S3로 이미지가 업로드 되는 것을 확인하였다.

후기

그런데, 난 결국 다른 MarkDown 프로그램으로 글을 작성하기 때문에 Ghost에서 바로바로 local image를 업로드하기 귀찮게 되었고 안쓸듯 하다

엉엉 내 노력, 하지만 좋은 걸 배웠으니 됬어.

  1. 참고로, S3 이미지 업로드 방식으로 바꿨지만 이미 local에 있는 이미지들을 그대로 남는다.

  2. 결국 이제 Digital Ocean 서버를 쓸 필요가 없어졌다. 한번 꺼졌다 키면 데이터가 날아가는 Heroku의 무료 계정에 이미지 서버는 S3로 하면 되기 때문에, 참고. 나중에 해봐야겠다.