mongodb expire data

로그인 처리를 위해서 특정 시간동안 데이터를 저장할 필요가 생겼다.

기존에 사용경험이 있었던 Redis를 이용할지를 고민하던 중에, 혹시 요즘 많이 쓰고 있는 Mongodb에도 혹시 특정 시간동안만 저장하는 기능이 있을까 해서 찾아보았다.

검색 키워드로는 "mongodb expire data"를 이용했고 바로 첫번째로 검색 되었다.

사용방법은 인덱스를 생성할때 날짜 필드에 expireAfterSeconds 를 추가하면된다.

# 1시간뒤에 삭제되는 데이터 구조 생성
db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 3600 } );

# 사용법 
db.log_events.insert( {
   "createdAt": new Date(),
   "logEvent": 2,
   "logMessage": "Success!"
} );

createAt 이라는 필드 명에는 날짜 형식이 들어가고 해당 날짜를 기준으로 expireAfterSeconds 의 초 만큼 지나가면 데이터가 삭제 된다.

nodejs에서 mongodb를 사용할때는 다음과 같이 스키마 구조를 생성하면 된다.

new Schema({ createdAt: { type: Date, expires: 3600 }});

참고 자료

Posted by lahuman

댓글을 달아 주세요

AWS EC2 서버 DISK 크기 늘리기

EC2의 디스크는 탄력적 볼륨이라고 하여 DISK의 크기를 가변적으로 조절 할수 있다.

이번에 EC2 인스턴스의 DISK 용량을 확장하였는데, WEB GUI 기반으로 볼륨 수정 요청을 하고 하루가 지나도 용량의 변경이 없었다.

구글에 검색을 해보니, "파일 시스템을 확장 해야 디스크 용량이 확장되다"고 한다.

먼저 lsbkl 명령어를 이용해서 인스턴스의 볼륨을 확인한다.

[ec2-user ~]$ lsblk
NAME          MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme1n1       259:0    0  30G  0 disk /data
nvme0n1       259:1    0  16G  0 disk
└─nvme0n1p1   259:2    0   8G  0 part /
└─nvme0n1p128 259:3    0   1M  0 part
  • 루트 볼륨 /dev/nvme0n1에는 /dev/nvme0n1p1라는 파티션이 있다. 루트 볼륨에 새 크기인 16GB가 반영되는 동안 파티션의 크기에 원래 크기인 8GB가 반영되기 때문에 파일 시스템을 확장하려면 먼저 파티션 크기를 늘려야 한다.
  • 볼륨 /dev/nvme1n1에는 파티션이 없습니다. 볼륨 크기에 새 크기 30GB가 반영됩니다.

루트 볼륨에서 파티션을 확장하려면 다음 growpart 명령을 사용한다.

[ec2-user ~]$ sudo growpart /dev/nvme0n1 1

lsbkl 명령어를 이용해서 인스턴스의 볼륨의 크기가 반영 되었는지 확인한다.

[ec2-user ~]$ lsblk
NAME          MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme1n1       259:0    0  30G  0 disk /data
nvme0n1       259:1    0  16G  0 disk
└─nvme0n1p1   259:2    0  16G  0 part /
└─nvme0n1p128 259:3    0   1M  0 part

예제: ext2, ext3 또는 ext4 파일 시스템 확장

df -h 명령어로 볼륨에 대한 파일 시스템 크기를 확인한다.

[ec2-user ~]$ df -h
Filesystem       Size  Used Avail Use% Mounted on
/dev/xvda1       8.0G  1.9G  6.2G  24% /
/dev/xvdf1       8.0G   45M  8.0G   1% /data

resize2fs 명령어를 이용해서 각 볼륨에서 파일 시스템을 확장한다.

[ec2-user ~]$ sudo resize2fs /dev/xvda1
[ec2-user ~]$ sudo resize2fs /dev/xvdf1

마지막으로 볼륨에 대한 파일 시스템의 크기를 확인하면 된다.

[ec2-user ~]$ df -h
Filesystem       Size  Used Avail Use% Mounted on
/dev/xvda1        16G  1.9G  6.2G  12% /
/dev/xvdf1        30G   45M  8.0G   1% /data

참고

Posted by lahuman

댓글을 달아 주세요

Nginx source를 이용한 컴파일 설치

오랜만에 Nginx를 설치하고 sub_filter를 사용하였는데 동작을 하지 않는다.

원인은 http_sub_module이 설치 되지 않아서 발생하는 문제이다.
nginx에 http_sub_module을 설치하기 위해서는 꼭 source compile 설치를 해야 한다.

NGINX 컴파일에서 컴파일 방법에 대하여 잘 정리가 되어 있다.

다음은 내가 사용한 명령어만 정리 하였다.

# 최초 update & 필수 gcc g++ 설치
$> sudo apt-get update
$> sudo apt-get install gcc g++;

# Nginx 설치 디렉토리 설정
$> mkdir nginx_source
$> cd nginx_source/

# Nginx 1.15.10 다운
$> wget https://nginx.org/download/nginx-1.15.10.tar.gz
$> tar xvf nginx-1.15.10.tar.gz

# Nginx 설치를 위한 모듈들 설치 START
$> wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.43.tar.gz
$> tar xvf pcre-8.43.tar.gz

$> wget https://www.openssl.org/source/openssl-1.0.2r.tar.gz
$> tar -xzvf openssl-1.0.2r.tar.gz

$> wget http://zlib.net/zlib-1.2.11.tar.gz
$> tar xvf zlib-1.2.11.tar.gz
# Nginx 설치를 위한 모듈들 설치 END

# 실행 계정 생성
$> useradd --shell /usr/sbin/nologin www-data

$> cd nginx-1.15.10/
# 컴파일 옵션 설정 **--with-http_sub_module** 내가 추가한 옵션이다.
$> sudo ./configure --with-http_sub_module --with-zlib=../zlib-1.2.11 --with-pcre=../pcre-8.43 --with-openssl=../openssl-1.0.2r --with-http_ssl_module --with-debug  --prefix=/usr/local/nginx --user=www-data --group=www-data
$> sudo make;
$> sudo make install;


# Nginx init 스크립트 처리
$> sudo wget https://raw.github.com/JasonGiedymin/nginx-init-ubuntu/master/nginx -O /etc/init.d/nginx;
$> sudo chmod +x /etc/init.d/nginx;
$> sudo update-rc.d -f nginx defaults
$> service nginx status

이렇게 하고 nginx.conf에 proxy pass 설정을 한다.

location /test/{ 
    proxy_pass http://lahuman.github.io/; 
}

이 경우 기본적으로 주소에 http://server/test 를 호출 하면 http://lahuman.pe.kr 내용을 확인 할 수 있다. 다만 해당 컨텐츠의 링크를 확인하면 절대 경로로 표출 되어 동작이 제대로 되지 않는다.

rewrite 와 sub_filter를 이용하여 내부 컨텐츠의 경로를 변경 하자
내부 컨텐츠의 경로를 변경하는 방법으로 sub_filter를 이용할 수 있다.

location /test/ {
        rewrite ^/test(/.*)$ $1 break;
        proxy_pass http://lahuman.pe.kr/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_redirect    off;
        proxy_set_header Accept-Encoding "";
        sub_filter_types *;
        sub_filter_once off;
        sub_filter '(?i)src=[\"\']/' 'src="/test/';
        sub_filter '(?i)href=[\'\"]/' 'href="/test/';
        sub_filter '/doc4sm/' '/test/doc4sm/';
    }

오랜만에 설정을 하였다.

설정은 늘 힘들다...

참조 링크

Posted by lahuman

댓글을 달아 주세요

pupperteer를 이용한 화면 캡처 기능 개발

이번에 개발하는 기능중에 화면을 캡처해서 다운로드를 제공해야 하는 기능이 있다.

처음 시도한 방법은 html2canvas을 이용한 방법 이었는데... 실패 했다.

화면에 보여주는 부분은 잘 캡쳐가 되지만, 스크롤에 숨겨진 부분이 캡쳐가 되지 않았다.

하루를 날리고, 다음으로 시도한 방법은 pupperteer를 이용해서 요청이 오면 내부에서 새로운 페이지를 띄우고 해당 페이지를 로딩해서 캡쳐 하는 방식이다.

만든 코드는 다음과 같다.

router.post('/capture', async (req, res, next) => {
  const file_name = `${shortid.generate()}.png`;
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.setViewport({ width: 1366, height: 768, });
  await page.goto(CAPTURE_URL+Buffer.from(JSON.stringify(req.body)).toString('base64'), {waitUntil: 'domcontentloaded'});
  await page.waitFor(1000);
  await page.waitForSelector('#endarea');
  await page.screenshot({fullPage: true, path: `public/images/${file_name}`});
  await browser.close();
  res.json({image_url:`/images/${file_name}`})
});

요청이 들어오면 해당 페이지로 넘어온 파라메터를 다시 전달해서 화면을 캡쳐 한다.

await page.screenshot({fullPage: true, path: public/images/${file_name}});이 부분에 보이는 부분을 전체 화면으로 캡쳐를 해서 저장을 하는 기능을 수행한다.

저장된 이미지를 웹에서 다운로드를 하는 코드는 다음과 같다.

function forceDownload(url, fileName){
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.responseType = "blob";
        xhr.onload = function(){
            var urlCreator = window.URL || window.webkitURL;
            var imageUrl = urlCreator.createObjectURL(this.response);
            var tag = document.createElement('a');
            tag.href = imageUrl;
            tag.download = fileName;
            document.body.appendChild(tag);
            tag.click();
            document.body.removeChild(tag);
        }
        xhr.send();
    }

이와 같이 실행하면 fileName으로 다운로드가 진행된다.

인터넷과 함께면 난 못하는게 없다.

참고자료

Posted by lahuman

댓글을 달아 주세요

MongoDB aggregate 사용

다음과 같은 형식의 여러 ROW의 데이터를 하나의 데이터로 표현 하고 싶었다.

# DATA
{
    "_id" : ObjectId("5c864b1e1cd4038eed5b5633"),
    "link_tag" : [ 
        "#핫앤쿨", 
        "#마스크팩", 
        "#홈케어", 
        "#리뉴메디", 
        "#젊줌마", 
        "#예뻐지는시간"
    ],
    "name" : "홈케어",
    "text" : "젊어지는시간\n. .\n\n#핫앤쿨 #마스크팩 #홈케어\n#리뉴메디 #젊줌마 #예뻐지는시간",
    "owner" : "10961953345",
    "shortcode" : "Bu3eX2iHIFG",
    "display_url" : "https://scontent-icn1-1.cdninstagram.com/vp/c330b55401b96aca266acca202386b42/5D08D97D/t51.2885-15/e35/54446467_181108306201409_5180562059450456961_n.jpg?_nc_ht=scontent-icn1-1.cdninstagram.com",
    "is_video" : false,
    "accessibility_caption" : "Image may contain: 1 person",
    "writed_date" : ISODate("2019-03-11T11:44:00.000Z"),
    "hash" : "086e2b6a65b823c887ef1afc19734f67",
    "createdAt" : ISODate("2019-03-11T11:48:46.276Z"),
    "updatedAt" : ISODate("2019-03-11T12:22:43.532Z"),
    "__v" : 3
}

/* 2 */
{
    "_id" : ObjectId("5c864b1e1cd4038eed5b5632"),
    "link_tag" : [ 
        "#토니슈어", 
        "#다낭까지와서", 
        "#이러고있다", 
        "#간절한걸어떡함", 
        "#플라즈마", 
        "#갈바닉", 
        "#너네둘만믿는다", 
        "#동안피부", 
        "#얼굴마사지기", 
        "#피부개선", 
        "#탄력", 
        "#피부미용기기", 
        "#미용", 
        "#홈케어", 
        "#피부미인", 
        "#데일리", 
        "#뷰티", 
        "#살균", 
        "#셀프", 
        "#피부질환", 
        "#플라즈마", 
        "#트러블케어", 
        "#피부", 
        "#피부과", 
        "#선물", 
        "#물광피부", 
        "#보습", 
        "#안티에이징", 
        "#led마스크", 
        "#미백", 
        "#콜라겐"
    ],
    "name" : "홈케어",
    "text" : "토니느님~ 제 피부를 10년전으로 돌려주세요\n\n제발요....♡ .\n\n#토니슈어 #다낭까지와서 #이러고있다 #간절한걸어떡함 #플라즈마 #갈바닉 #너네둘만믿는다 #동안피부 가즈앙~ \n#얼굴마사지기 #피부개선 #탄력 #피부미용기기 #미용 #홈케어 #피부미인 #데일리 #뷰티 #살균 #셀프 #피부질환 #플라즈마 #트러블케어 #피부 #피부과 #선물 #물광피부 #보습 #안티에이징 #led마스크 #미백 #콜라겐",
    "owner" : "10698447319",
    "shortcode" : "Bu3e2n_jrrE",
    "display_url" : "https://scontent-icn1-1.cdninstagram.com/vp/97d840dbdea55753d2a976302f90002b/5D065CE9/t51.2885-15/fr/e15/s1080x1080/53176269_319394038719034_3474616797099883761_n.jpg?_nc_ht=scontent-icn1-1.cdninstagram.com",
    "is_video" : false,
    "accessibility_caption" : "Image may contain: outdoor and water",
    "writed_date" : ISODate("2019-03-11T11:48:12.000Z"),
    "hash" : "c9a418d8295391c1c83553f0316d1127",
    "createdAt" : ISODate("2019-03-11T11:48:46.266Z"),
    "updatedAt" : ISODate("2019-03-11T12:22:43.527Z"),
    "__v" : 3
}

원하는 결과 데이터

[
    { "tag" : "#미백", "count" : 3},
    { "tag" : "#물광", "count" : 4},
    { "tag" : "#보습", "count" : 10},
    ...
]

구글을 검색하니 $push명령어를 찾았다.

내가 처리한 Query는 다음과 같다.

db.hashtag.aggregate([
    { $match: { $and: [{ name: "테그명" }, { "createdAt": { $gte: (new Date()), $lt: (new Date()) } }] } },
    {$unwind: "$link_tag"},
    {$group:{_id:null, clrs: {$push: {name: "$link_tag", count:1}}}},
    {$unwind: "$clrs"},
    {$group:{_id:"$clrs.name", size: {$sum:"$clrs.count"}}},
    { $sort: { size:-1} },
    { $limit : 5 },
    {$project: {_id:1, size:1 }
  ]);

이렇게 처리 하면 다음과 같은 결과를 받을 수 있다.

[
    { _id:"#물광", size:15 },
    { _id:"#홈케어", size:13 }
    { _id:"#대일리", size:12 }
]

다음은 매 시간별로 수집된 데이터를 counting 하는 query 이다.
내가 원하는 결과는 다음과 같다.

[
    {"date": "2018-03-14 10", "count":30},
    {"date": "2018-03-14 11", "count":20},
    {"date": "2018-03-14 12", "count":33},
    {"date": "2018-03-14 13", "count":34}
]

다행이 이번 QUERY는 한방에 처리 하였다.

db.hashtag.aggregate([
    { $match: { $and: [{ name: "태그명" }, { "createdAt": { $gte: (new Date()), $lt: (new Date()) } }] } },
    {
      $group:
      {
        _id:
        {
          hour: { $hour: "$createdAt" },
          day: { $dayOfMonth: "$createdAt" },
          month: { $month: "$createdAt" },
          year: { $year: "$createdAt" }
        },
        value: { $sum: 1 },
        date: { $first: "$createdAt" },
        sortDate: { $first: "$createdAt" }
      }
    },
    {
      $project:
      {
        date:
        {
          $dateToString: { format: "%Y-%m-%d %H", date: "$date" }
        },
        sortDate: 1,
        value: 1,
        _id: 0
      }
    },
    { $sort: { sortDate: 1 }}
  ])

분명히 많은 기능을 Mongodb에서 제공해주고 있는데, 잘 쓰지 못하고 있다...

노력하자.

참고 자료

Posted by lahuman

댓글을 달아 주세요

puppeteer를 설치하고 구동하기

설치는 아주 간단하다. npm 을 이용해서 설치 하면 바로 되는데, 문제는 설치후 기동하면 오류가 난다.

# 설치
npm install --save puppeteer

# 실행
node app.js

# error 발생

에러 메시지에서는 puppeteer troubleshooting를 보라고 하지만, 내게 도움되는 내역이 없었다.

구글의 도움을 받아 찾은 puppeteer not working on Ubuntu 16.04 but works on 14.04에서 다음 라이브러리를 설치하라는 메시지를 확인하였다.

sudo apt-get install libpangocairo-1.0-0 libx11-xcb1 libxcomposite1 libxcursor1 libxdamage1 libxi6 libxtst6 libnss3 libcups2 libxss1 libxrandr2 libgconf2-4 libasound2 libatk1.0-0 libgtk-3-0

설치를 하고 나서 실행하면 문제 없이 잘된다.

그러하다~

참고 주소

Posted by lahuman

댓글을 달아 주세요

cert, key 파일을 pem 파일로 변환 하기

nginx에서 사용하던 인증서를 nodejs에서 바로 사용을 하기 위해 pem 파일 형식으로 변경 해야하는 일이 생겨서 검색을 해보았다.
파일 형석의 변환은 다음과 같이 쉽게 할 수 있다.

# key 변경
openssl rsa -in server.key -text > private.pem
# crt 변경
openssl x509 -inform PEM -in server.crt > public.pem

추가 팁] nodejs 에서 https 설정

nodejs에서 https 사용을 위해서는 https 모듈을 추가로 설치 해야 한다.

이후 소스내에 다음과 같이 인증서를 options을 추가 하면 된다.

const https = require('https');
const fs = require('fs');
const options = {
  ca: fs.readFileSync('인증서경로/ca-bundle.pem')
  key: fs.readFileSync('인증서경로/domain_xxxxx.key.pem')
  cert: fs.readFileSync('인증서경로/domain_xxxxx.crt.pem')
};
https.createServer(options, (req, res) => {
  res.writeHead(200);
  res.end('hello world\n');
}).listen(443);

참 쉽죠?

참고 주소

Posted by lahuman

댓글을 달아 주세요

MongoDB backup & restore

간단하게 몽고 디비 백업과 복구를 해보겠다.

먼저 백업 명령은 다음과 같다

# 결과로 디렉터리가 생성 된다.
# 특정 collection 명이나 DB 명을 Backup 할 수 있다.
$> mongodump --collection words --db TEST --out ./

다음은 복원 하는 방법이다.

# 특정 컬렉션만 복원 할 수 있으며 --drop  옵션을 추가 하면 삭제하고 복원 한다.
$> mongorestore --host 172.30.10.1 --port 27017 --db TEST --collection words --username tester --password 'password'  ./TEST/words.bson --drop

가끔씩 쓸일이 생긴다.

Posted by lahuman
TAG mongodb

댓글을 달아 주세요

AWS LAMBDA 를 개발시 알면 좋은 팁

  1. Lambda를 API Gateway를 통해서 호출 하도록 설정 할때 매핑 템플릿 을 사용하면 IP 등의 정보를 얻을 수 있다.

  2. Lambda에서 RDS에 접근하기 위해서는 VPC를 설정해야 한다.
  3. VPC가 설정된 Lambda는 외부 인터넷에 접근 할 수 없다.
  4. VPC가 설정된 Lambda는 내부 private IP만 접근 할 수 있다.

다시 사용할 일이 올지는 모르겠지만, 재미있었다.

신기술은 늘 어렵다. 근데 써보고 나면 별거 없다.

참고 자료

Posted by lahuman

댓글을 달아 주세요

80 port에 대하여 Root가 아닌 다른 계정으로 서비스 하기

보안 취약점으로 인해 서비스에 대한 기동을 root가 아닌 다른 계정으로 하라는 지침이 내려왔다.

하지만 1024 포트 이하에 대한 서비스 권한은 root만 가지고 있다. 기존에 했던 방법은 계정을 생성하고 root와 같은 그룹으로 묶어서 처리한 기억이 있다.

이번에 찾은 방법은 authbind라는 라이브러리를 이용 하는 방법이다.

예제는 pm2를 이용하였는데, 아마 생각은 다른 프로그램 역시 동작 할것이다.

우선 다음 명령어를 이용하여 authbind를 설치 하고 %user% 는 사용하려는 계정으로 바꿔서 진행하면 된다.

$> sudo apt-get install authbind
$> sudo touch /etc/authbind/byport/80
$> sudo chown %user% /etc/authbind/byport/80
$> sudo chmod 755 /etc/authbind/byport/80

그리고 변경해야 하는 내용은 사용하려는 서비스의 기동을 다음과 같이 하면 된다.

$> authbind --deep %명령어% 

일일이 치기 귀찮다면 다음과 같이 alias를 사용자 프로파일에 추가한다.

# ~/.bash 또는 ~/.zshrc
$> vi ~/.bash
# 가장 하단에 다음 내역 추가 
alias 명령어='authbind --deep 명령어'

# 프로파일 재반영
$> source ~/.bash

pm2를 예를 들면 다음과 같다.

# ~/.bash 또는 ~/.zshrc
$> vi ~/.bash
# 가장 하단에 다음 내역 추가 
alias pm2='authbind --deep pm2'

# 프로파일 재반영
$> source ~/.bash

pm2의 경우 update를 authbind를 이용하여 해야 한다.

$> authbind --deep pm2 update

추가적인 방법으로는 포트 포워딩을 이용하여 처리하는 방법도 있다.

참고자료


Posted by lahuman

댓글을 달아 주세요