태그 보관물: AWS

일본어 RSS 번역해서 보기 – AWS Lambda 이용

저는 요즘 일본어 글을 보는 일이 많아졌습니다. 일본어라고는 히라가나 철자를 발음하는 수준이 전부지만 일본어로 된 좋은 글들이 많기 때문입니다. 다행히 일본어-한국어 번역기는 품질이 괜찮은 편이라 구글 번역기를 이용해서 내용을 이해하는 데 큰 무리가 없고 크롬을 이용하면 일본어 사이트인 경우 자동으로 한국어로 번역하도록 설정해서 볼 수 있지만 문제는 모바일입니다.

모바일 기기에서도 크롬을 사용할 수 있지만 사용하는 RSS 리더인 Feedly(, iOS, 안드로이드)는 모바일웹용 RSS는 제공하지 않고, 번역을 제공하는 다른 RSS 서비스나 앱을 찾을 수 없었습니다. 이러한 불편함을 해결하고자 일본어 RSS를 읽어서 제목만이라도 한국어로 번역해주면 RSS 목록에서 제목을 보고 관심 있는 글은 크롬으로 열어서 번역으로 보면 괜찮겠다고 생각했습니다. 제목만 한국어로 바꾼 새로운 RSS 파일을 생성하면 RSS 리더 서비스나 앱이 지원하지 않아도 번역된 내용을 볼 수 있는 장점이 있습니다.

RSS 번역 구조도

RSS 번역 구조도

일본어 RSS를 번역 후 새로운 RSS 파일을 만드는 건 어렵지 않은 일입니다. 번역 API를 사용해서 얻은 값을 파일에 다시 쓰기만 하면 되는 거니 까요. 문제는 서버를 어떻게 관리할 것 인지였습니다. 이런 간단한 프로그램을 위해 서버를 운영하는 건 비용이나 운영에 들어가는 시간이 아까웠습니다. 제가 원하는 조건은 다음과 같았습니다.

  • 서버 비용이 저렴 할 것
  • 따로 신경쓰지 않아도 잘 동작 할 것

서버 비용은 집에 남는 서버를 사용하거나 기존에 운영 중인 개인 서버를 사용하면 됩니다. 하지만 따로 신경 쓰지 않아도 잘 동작하는 서버라는 건 없었죠. 아무리 신경 쓰지 않는다고 해도 새로운 서버로 업그레이드하거나 정전, OS 업그레이드 등 다양한 상황이 발생할 수 있습니다.

그러던 와중에 생각난 것이 AWS Lambda입니다. Lambda는 소스코드만 올려두면 서버 설정 없이 AWS가 코드를 실행해주는 서비스입니다. 소스코드에 이상만 없으면 서버 관리할 필요 없이 알아서 동작하고, 비용도 실제 실행시간만큼만(100ms 단위) 지불되므로 제가 원했던 것처럼 몇 초 안에 실행이 끝나는 경우 별도의 서버를 운영하는 것보다 저렴합니다.

Lambda는 다양한 방법으로 코드를 실행할 수 있습니다. S3, API Gateway 요청, AWS IoT 이벤트 그리고 정해진 시간에 실행하는 방법 등이 있습니다. 정해진 시간에 코드가 실행되는 건 CloudWatch – Schedule을 선택 후 cron 문법에 맞는 문자열만 적으면 정해진 시간에 실행됩니다. CloudWatch가 뭔지 몰라도 상관없는 거죠.

Lambda 이벤트 소스 종류

Lambda 이벤트 소스 종류

Lambda CloudWatch - Schedule

Lambda CloudWatch – Schedule

Lambda Cron 표현식

Lambda Cron 표현식

제가 만든 RSS 번역하는 소스코드는 다음과 같습니다. 참고로 Lambda가 지원하는 개발 환경은 Node.js(v0.10.36), Java(Java 8), Python(Python 2.7) 인데 Java(자바)는 그냥 하기 싫었고 Python(파이썬)은 3이 아니라서 한 번도 해본 적 없는 Node.js로 만들었습니다. Node.js는 처음이라 제가 잘못 작성한 부분도 많겠지만 참고 봐주세요 ^^;

```index.js
var http = require('http');
var xml = require('xml2js');
var DOMParser = require('xmldom').DOMParser;
var async = require('async');
var request = require('request');
var AWS = require('aws-sdk');
var s3 = new AWS.S3();

exports.handler = function(event, context) {
    var options = {
      hostname: 'b.hatena.ne.jp',
      path: '/hotentry/it.rss',
      headers: {
        'User-Agent': 'Mozilla/5.0'
      }
    }
    http.get(options, function(res) {
        res.setEncoding('utf8');
        var body = '';
        res.on("data", function(chunk) {
            body += chunk;
        });
        res.on('end', function() {
            var parser = new DOMParser({
                            locator:{},
                            errorHandler:function(level,msg){ }
                        })
            var doc = parser.parseFromString(body);
            var items = doc.getElementsByTagName('item');
            async.eachSeries(items, function iterator(item, callback) {
                var title = item.getElementsByTagName('title')[0]
                var form = {
                    q: title.textContent,
                    langpair: 'ja|ko',
                    de: 'myemailaddress'
                }
                request.post({ url:'http://api.mymemory.translated.net/get', form: form },
                    function (err, httpResponse, body) {
                        if (!err) {
                            var result = JSON.parse(body);
                            title.textContent = result.responseData.translatedText;
                        }
                        callback();
                    }
                )
            }, function done() {
                var xml = doc.toString();
                s3.putObject({
                    Bucket: 'seapy-static',
                    Key: 'rss/lambda_hatena_hotentry_it.xml',
                    Body: xml,
                    ACL: 'public-read',
                    ContentType: 'text/xml'
                }, function(err, data) {
                    if (err) console.log(err, err.stack);
                    else     console.log(data);
                    context.done(null, 'success');
                });
            });
        });
    }).on('error', function(e) {
        context.done('error', e);
    });
};
```

이 코드를 로컬 환경에서 실행하려면 좀 귀찮은데 이걸 해결하려면 lambda-local을 이용합니다.

번역 API는 구글 것을 사용하고 싶었지만, 유료라서 무료 범위에서 많은 단어를 제공하는 Mymemory를 사용했습니다. 필요한 모듈들은 npm을 이용해서 설치했고 해당 모듈들은 node_modules 폴더에 위치합니다.(aws-sdk는 로컬에서만 사용하고 실제 업로드 할 파일에서는 제외합니다. lambda 에서 이미 가지고 있거든요) 그리고 위의 코드와 node_modules 폴더를 하나의 압축파일로 만들어서 Lambda 서비스에 업로드 후 이벤트 소스를 CloudWatch – Schedule로 하고 20분에 한 번씩 실행하도록 하면 끝입니다. 진짜 끝이에요. 이제 20분 마다 일본어로 된 RSS를 한국어로 번역하고 S3에 저장합니다.

저는 서버가 어떻게 실행되는지 다운되지는 않았는지 업그레이드는 언제 할지 전혀 고민하지 않아도 되고 비용도 거의 무료로 사용합니다. AWS Lambda의 무료 범위가 꽤 넓거든요 ~

Lambda가 실행된 횟수와 실행시간은 Monitoring 탭에서 확인할 수 있고, 메모리 사용량이나 출력 등 자세한 로그는 CloudWatch에서 확인할 수 있습니다.

Lambda 모니터링

Lambda 모니터링

Lambda 로그

Lambda 로그

일본어 RSS의 제목을 번역한 새로운 RSS를 구독한 결과는 다음과 같습니다.

RSS 번역 결과 - Feedly 웹

RSS 번역 결과 – Feedly 웹

RSS 번역 결과 - Feedly 모바일

RSS 번역 결과 – Feedly 모바일

일부 번역이 안된 것들도 보이지만 주제를 아는 데 방해가 될 정도는 아닙니다. 문제는 “Mymemory Warning: You Used All Available Free Translations For Today.” 이라고 나오는 것들인데요. 제가 보는 하테나 IT 섹션 RSS의 업데이트 빈도가 너무 높아서 Mymemory 번역 API의 무료범위를 넘어가서 발생하는 문제입니다. 돈을 내면 쉽게 해결될 문제인데 저와 같은 목적으로 위의 RSS를 보고 싶은 사람들이 있다면 여러 명이 나눠서 내면 크게 비싸지 않을 것도 같습니다.

이번 개발을 하면서 느낀 건 2가지였습니다. 첫째는 Node.js 도 재미있다는 거였습니다. 이번 개발을 하기 전까지는 필요성을 못 느꼈는데 서버 운영하기 싫은 간단한 것들 만들 때 Lambda에 올릴 것들은 Node.js 배워서 해보면 재미있겠다는 생각이 들었습니다. 두 번째는 Serverless 트렌드가 일회성으로 끝나지 않고 앞으로 많은 비중을 차지하게 될 것이라고 느꼈습니다. 직접 해보니 특정 영역에서 확실한 장점이 보였습니다.

Lambda에 아쉬운 건 제가 좋아하는 Ruby(루비)를 아직 지원하지 않는다는 것입니다. 작년 AWS re:invent에서 Python 지원을 발표했으니 올해는 Ruby 지원을 발표하면 좋겠지만, Ruby는 아마 안될 거예요…

게시글의 아마존, 쿠팡, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

AWS 서울 vs 도쿄 리전 속도 및 가격 비교

드디어 한국에도 AWS 리전(region)이 생겼습니다 ^^

그동안 한국에서 AWS 사용하시는 분들은 대부분 일본 도쿄에 있는 리전을 사용했었는데요. 다운로드 속도 같은건 괜찮았지만 지연속도나 가끔 특정 인터넷 업체에서 접속이 안되는등의 문제가 있었습니다. 하지만 이제 서울에 리전이 생긴만큼 이런 문제들은 없어질것이라고 생각합니다.

그런 의미에서 기존에 한국 서비스에서 많이 사용하던 도쿄 대비 서울이 과연 얼마나 속도가 빨라졌을까 궁금해서 비교해보기로 했습니다.

가장 많이 사용되는 S3와 EC2의 지연시간, 다운로드 속도에 대해 비교를 진행했습니다.(참고로 저는 KT의 기가 인터넷을 사용하고 있고 공유기는 기가를 지원하지만 ASUS 공유기 문제로 테스트에 사용한 노트북과 공유기는 2.5 Ghz 로 연결하고 있습니다)

S3

S3 속도 비교는 저용량부터 고용량 파일 4개를 업로드/다운로드 하는 방법으로 진행했습니다.

테스트에 사용한 파일의 크기는 374KB, 1.5MB, 100MB, 1.5GB 였고, 스토리지 옵션은 Standard Storage, 서버 암호화 안함, Public Access 설정이었습니다.

  • 도쿄
    • 4분 16초, 4 MB/s ~ 10MB/s 사이. 평균 6 MB/s
  • 서울
    • 2분, 10 MB/s ~ 16MB/s 사이. 평균 13 MB/s

파일 업로드 속도에서 2배 차이로 서울이 빨랐습니다. 도쿄의 경우 특정 시간대에는 1 MB/s도 안나오는 경우가 있었는데 그것까지 감안하면 서울의 속도는 정말 빠릅니다.

Multipart Upload 기능을 이용한 업로드 테스트

+ 2016년 1월 11일 내용추가된 단락입니다.

S3에서는 대용량 파일을 업로드할때 여러개의 파일로 나눠서 올리는 Multipart Upload 기능이 있습니다. 이를 이용해서 올리는 경우에도 차이가 많이 나는지 테스트 해봤습니다. 테스트에는 aws cli 프로그램을 이용했고 명령어는 다음과 같았습니다.

time aws s3 cp --region ap-northeast-1 100mb2.MOV s3://tokyo-bucket/100mb2.MOV
time aws s3 cp --region ap-northeast-2 100mb2.MOV s3://seoul-bucket/100mb2.MOV

결과는 다음과 같습니다.

  • 100 MB
    • 도쿄 : 16초
    • 서울 : 11초
  • 1.5 GB
    • 도쿄 : 2분 48초
    • 서울 : 2분 30초

1.5 GB에서 반반의 확률로 일본이 속도가 5초정도 빠른경우가 있었습니다. 지금 도쿄가 속도가 빠른시간인지 모르겠으나 속도 차이가 많이 나지 않네요. 주의할점은 이전 섹션의 대용량 테스트와 테스트 시점이 다르기 때문에 서로 뭐가 빠르다고 이야기 할 수 없습니다.(참고로 이 테스트는 다른 테스트와 다르게 기가 인터넷이 아닌 KT 일반 인터넷에서 진행됬습니다.)

같은 환경에서 이전 섹션의 테스트처럼 브라우저에서 업로드한경우 서울 리전 기준 3분이 걸렸습니다. multipart upload가 조금더 빠르네요

저용량 다운로드 테스트

크롬 개발자 도구의 네크워크 탭에서 정보를 확인했으며 3번씩 테스트 하고 지연시간(Latency), 전체 시간을 기록했습니다. 다운로드에 사용한 URL은 HTTPS라서 HTTP보다 조금 느리다는것 감안하시기 바랍니다.

  • 도쿄
    • 374 KB
      • 233ms, 462ms
      • 217ms, 554ms
      • 198ms, 569ms
    • 1.5 MB
      • 245ms, 693ms
      • 239ms, 747ms
      • 215ms, 811ms
  • 서울
    • 374 KB
      • 95ms, 142ms
      • 55ms, 109ms
      • 49ms, 113ms
    • 1.5 MB
      • 79ms, 201ms
      • 69ms, 234ms
      • 51ms, 251ms

지연시간의 개선은 물론 다운로드 속도도 빨라져서 374 KB의 경우 4배정도 빠르고, 1.5 MB의 경우도 3배정도는 빨라졌습니다.

대용량 다운로드 테스트

대용량 파일은 크롬에서 진행하기 힘들어서 curl 명령어를 이용해 전체 다운로드 소요시간, 평균 다운로드 속도를 100 MB 파일은 3번, 1.5 GB는 1번 시도해서 얻은 결과값입니다. URL에는 저용량과 마찬가지로 HTTPS입니다.

  • 도쿄
    • 100 MB
      • 12s (8078 KB/s)
      • 18s (5162 KB/s)
      • 13s (7115 KB/s)
    • 1.5 GB
      • 4m 9s (6631 KB/s)
  • 서울
    • 100 MB
      • 6s (14.6 MB/s)
      • 7s (12.8 MB/s)
      • 7s (12.4 MB/s)
    • 1.5 GB
      • 1m 45s (15.3 MB/s)

대용량 파일에서는 속도 및 시간이 2배 빨리진것을 확인할 수 있습니다.

가격 비교

전체적으로 2배이상 빨라진 S3의 스토리지 및 네트워크 가격 비교입니다.

  • 스토리지
    • 도쿄 : 1 GB당 $0.033 ~ $0.0302
    • 서울 : 1 GB당 $0.0314 ~ $0.0287
  • 요청 요금(GET 요청만 비교)
    • 도쿄 : GET 요청 1만건당 $0.0037
    • 서울 : GET 요청 1만건당 $0.0035
  • 데이터 전송 요금
    • 도쿄 : 1 GB당 $0.140 ~ $0.120
    • 서울 : 1 GB당 $0.126 ~ $0.108

S3는 스토리지보다 전송요금 때문에 많이 나오기 마련인데 도쿄보다는 저렴합니다. 그래도 미국이나 유럽보다 2배정도 비싸네요 ㅜㅜ

S3 뿐만 아니라 서울은 도쿄보다 가격이 조금 저렴하게 측정되었습니다.

EC2

EC2 비교는 Ruby로 만들어진 간단한 sinatra 웹 어플리케이션 대해 wrk로 요청을 테스트 했습니다. 인스턴스 타입은 서비스에 많이 사용되는 m4.large, OS는 Ubuntu 14.04 를 사용했습니다.

단순히 ping 테스트만 해봤을때 지연시간은 도쿄가 40ms, 서울이 6ms 정도 였습니다.

응답시간 및 요청수 비교

`wrk http://xxx.xxx.xxx.xxxx:8080` 명령어를 사용한 스레드 한개에 대한 결과 입니다. (참고로 wrk에 옵션없이 실행했을때 2개의 스레드로 10개의 커넥션을 가지고 10초동안 테스트가 진행되었습니다.)

  • 도쿄
    • 평균 응답 시간 89.81ms
    • 초당 요청수 55.65
  • 서울
    • 평균 응답 시간 36.88ms
    • 초당 요청수 135.90

응답 시간에서 2배의 개선이 확인됩니다. 이 수치는 어플리케이션 상황에 많이 달라질수 있습니다.

가격비교

테스트에 사용한 m4.large Linux 기준입니다.

  • 온 디맨드
    • 도쿄 : 시간당 $0.174, 30일 기준 $125.28
    • 서울 : 시간당 $0.165 , 30일 기준 $118.8
  • 1년계약 전체 선결제
    • 도쿄 : $799
    • 서울 : $758

서울에는 현재 스팟 인스턴스가 없습니다

데이터 전송 비용

EC2에서 인터넷으로 데이터 송신하는데 들어가는 비용입니다.

  • 도쿄 : 1 GB당 $0 ~ $0.120
  • 서울 : 1 GB당 $0 ~ $0.108

그외 비용

  • Elastic IP : 동일
  • ELB 시간당
    • 도쿄 : $0.027
    • 서울 : $0.025
  • ELB가 처리한 GB 단위 데이터당 요금 : 동일

정리

서울 리전이 생기면서 네트워크 속도가 2배 이상 빨라진걸 확인할 수 있었습니다. 마음 같아서는 지금이라도 당장 이전하고 싶지만 대부분 저처럼 1년 혹은 3년단위로 계약해서 사용하기 때문에 계약 기간이 끝나기전까지는 옮기지 못할것으로 보입니다.

미국은행 계좌가 있는경우는 AWS 마켓플레이스를 통해 EC2 선결제한것을 사용자 끼리 사고 팔수 있지만 저는 미국은행 계좌가 없거든요 ㅜㅜ

어차피 리전을 이동하는게 쉬운일은 아니라서 지금부터라도 새로 생성되는 서버는 서울에 두고 천천히 옮겨가는게 좋겠지요 ^^ 리전 이동에 대해서는 “AWS CLOUD Advanced 2. AWS를 활용한 글로벌 아키덱처 운용 전략” 영상이 도움이 될것입니다.

참고정보

게시글의 아마존, 쿠팡, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^