• yundream
  • 2018-04-29 15:57:51
  • 2018-04-25 16:58:58
  • 78268

Contents

S3 static file server

S3에 있는 파일을 서비스하는 방법을 살펴보려 한다. AWS에서 제공하는 공식 golang sdk말고 좀 더 편하게 쓸만한게 없을까 찾아봤는데 못찾았다. 그냥 aws go sdk 쓰기로 했다.

구현을 위해서 아래와 같은 방법들을 고민했다.
  1. S3 Static Web Hosting
  2. EC2에 S3를 마운트해서 NginX로 파일 서비스
  3. EC2에 S3 SDK를 기반으로하는 웹 애플리케이션 서버 개발
당연히 1번, 보안이 필요하면 1번에 signed URL 적용하면 될 것이다. 하지만 어른의 사정으로 3번을 선택했다.

그 어른의 사정이라는 것은 이렇다. 이 다운로드 서버라는게 "FOTA" 서버인데, 여기에서 발생하는 트래픽에 대해서는 "과금예외"를 해줘야 했다. 그럴려면 IP를 특정할 수 있어야 한다. 결국 EIP를 기반으로 해서 만들 수 밖에 없었다.

Network Load Balancer 구성

ELB는 Classic Load Balancer, Application Load Balancer, Network Load Balancer 3가지 타입을 제공한다. Application Load Balancer(이하 NLB)는 OSI 7의 4번째 계층에서 작동하는 로드밸런서다. NLB를 사용하면 각 가용성존에 만든 서브넷에 EIP를 연결 할 수 있다. 대략 아래와 같은 구성을 가진다.

 NLB를 이용한 구성

EIP는 NLB에 붙는게 정확하겠으나 이해를 쉽게 하기 위해서 서브넷(Subnet)에 붙는 것으로 그림을 그렸다. 이제 NLB를 만들면 NLB의 endpoint domain이 만들어 질건데, 이 domain은 A 레코드에 EIP가 들어간다. 따라서 고정된 EIP로 서비스를 할 수 있게 된다.

NLB는 L4 레벨에서 작동하기 때문에, SSL 인증서를 offload 할 수 없다. HTTPS로 서비스하고 싶다면 SSL 인증서를 직접 관리해야 한다. ACM을 사용 할 수 없으므로 관리비용이 들어갈 수 있다. 요즘에는 Let's encrypt 같은 Free 인증서를 사용 할 수 있으니 큰 문제는 아니라고 할 수 있겠다.

AWS Go SDK

Gorilla 웹툴킷을 이용해서 다운로드 서비스를 개발하기로 했다. 사용하는 Go SDK 패키지는는 아래와 같다.
  • github.com/aws/aws-sdk-go/aws
  • github.com/aws/aws-sdk-go/aws/session
  • github.com/aws/aws-sdk-go/service/s3
  • github.com/aws/aws-sdk-go/service/s3/s3manager
코드는 아래와 같다.
package main

import (
    "fmt"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3"
    "github.com/aws/aws-sdk-go/service/s3/s3manager"
    "github.com/gorilla/mux"
    "net/http"
    "os"
)

var (
    downloader *s3manager.Downloader
)

func Downloader(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    filename := vars["file"]
    fmt.Println(filename)
    input := s3.GetObjectInput{
        Bucket: aws.String("www.joinc.co.kr"),
        Key:    aws.String(filename),
    }

    w.Header().Set("Content-Disposition", "attachment;filename="+filename)
    w.Header().Set("Content-Type", "application/octet-stream")
    buff := &aws.WriteAtBuffer{}

    n, err := downloader.Download(buff, &input)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    fmt.Println("Write success : ", n)
    w.Write(buff.Bytes())
}

func main() {
    sess, err := session.NewSession(&aws.Config{
        Region: aws.String("ap-northeast-2"),
    })
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1)
    }
    downloader = s3manager.NewDownloader(sess)

    r := mux.NewRouter()
    r.HandleFunc("/d/{file:.*}", Downloader)
    http.Handle("/", r)
    http.ListenAndServe(":5000", nil)
}
aws 패키지의 session.NewSession은 서비스 클라이언트에서 사용 할 수 있는 세션을 만든다. 이 메서드를 실행하면 ~/.aws/credentials 파일정보를 읽어서 로딩한다. 만약 AWS_SDK_LOAD_CONFIG 환경변수가 true로 설정돼 있다면 ~/.aws/config 파일도 읽는다.

s3manager는 S3로 부터 파일업로드 및 다운로드를 위한 유틸리티 메서드를 제공한다. NewDownloader 메서드는 S3에서 파일객체를 다운로드하기 위한 Downloader 인스턴스를 만든다.