Jeen - Yet anothere techlog

STFUAWSC

Better Than Ack

계속 쓰다보니 자연스레 손에 익은 이유도 있고, 여러 서버들에 ack 이 기본적으로 깔려 있지 않은 이유도 있습니다. 그것이 제게 있어서 ack 을 사용하지 않는 핑계가 되겠네요.

그런 현실중에 ag 를 소개하는 것은 조심스러울 따름입니다.

위 github 페이지에서 확인할 수 있는 내용대로,

  • ack 보다 3-5배 빠르고
  • 검색시에 .gitignore, .hgignore 를 참고하여 검색대상에서 제외해줍니다.
  • 그리고 ack 보다 한글자 더 적은 노력으로 검색할 수 있습니다. :–)

제 Github Pages Repo 안에 속한 1566 개의 파일을 대상으로 각각의 성능을 비교해봤습니다.

1
2
3
4
5
6
7
8
9
10
11
12
$ time ag Perl
……
……
source.old/_posts/2012-08-28-building-a-perl-project-on-travis-ci.markdown
11:`Travis CI`은 이전에 얘기를 들었을 때는 별  관심이 없었는데(처음에는 확실히 Perl 을 지원하지 않아서), 언제부턴가 Perl 을 지원한다는 얘기를 듣고 Perl 커뮤니티에서 움직임이 조금씩 있더라구요. `Dist::Zilla::TravisCI` 같은 모듈들도 나오는 걸로 봐서…
15: - [Travis CI - Building a Perl Project](http://about.travis-ci.org/docs/user/languages/perl/)
43:Perl may be copied only under the terms of either the Artistic License or the
44:GNU General Public License, which may be found in the Perl 5 source kit.
46:Complete documentation for Perl, including FAQ lists, should be found on
48:Internet, point your browser at http://www.perl.org/, the Perl Home Page.
……
ag Perl  0.07s user 0.14s system 128% cpu 0.164 total
1
2
3
4
5
$ time grep -R Perl .
……
./source.old/_posts/2012-12-26-a-usecase-of-app-fatpacker.markdown:  # ABSTRACT: Perl binding for Redis database
……
grep -R Perl .  1.13s user 0.09s system 77% cpu 1.573 total
1
2
3
4
5
6
$ time ack Perl
……
public/page/5/index.html
245:<a href="/2012/07/26/export-facebook-event-attending-list-perl-kr-teatime/">Export Facebook Event Attending List - Perl-kr TeaTime</a>
……
ack Perl  0.38s user 0.06s system 54% cpu 0.812 total

보시다시피 ag 의 위력은 대단합니다. 기본적으로 사용법도 ack 과 크게 다를 것도 없습니다.

문제는 기본적으로 사용할 수 있냐는 것이겠는데… 요즘 세상에는 Rex 같은 걸로 그냥 뭐 한번만 수고해주면 뭐 사용할 서버들에 일괄적으로 설치하는 것은 일도 아니지 않냐라고 그냥 스스로 수긍해버리면 될 것 같습니다.

Rex Common Practice

Parallelism 설정

한 손에 셀 수 있을 정도의 서버규모라면 사실 특별하게 병렬처리를 하느니 하는 것은 별로 고려하지 않아도 되겠습니다만, 그 이상일 경우에는 한 작업을 수행함에 있어서 수십대의 서버에 순차적으로 접속하고 작업하고하는 것을 지켜보는 것은 상당히 고역이죠. 이럴 경우에는 parallelism 을 지정합니다.

방법은 Rexfile 에 다음과 같이 설정합니다.

1
parallelism => 10;

이렇게 하면 한번에 10대의 서버에 대해서 병렬로 작업을 수행하게 됩니다. 어차피 작업은 원격지서버에서 하는 것이고, 로컬에서는 SSH 커넥션이 1개로 붙던 것들이 10개로 늘어나며, 물론 시간 또한 parallelism 설정갯수에 비례하게 됩니다. 하지만 로그는 이쁘게 남지는 않을 겁니다.

애시당초 CLI 옵션에 parallelism 옵션이 있었으면 좋겠는데…

Logging

일단 작업 기록은 꾸준히 남기는 것이 좋겠다라고 생각하는 바, 매 작업분기마다 로그를 남기도록 합니다. 하지만 매뉴얼에는 마땅히 로그에 관한 설명은 보이지 않는 것 같았습니다.

Rex 에서의 기본적인 로그를 쓰는 방법은 다음과 같습니다.

1
Rex::Logger::info("MSG"[, "TYPE"]);

로그 메시지는 당연히 필수이며, TYPE 은 사실 안써도 됩니다만, error/warn 두가지 인수를 넣을 수 있습니다. 에러 상황시 확실하게 눈에 띄게 해주는 정도(노랑색, 빨강색)입니다.

Parallelism 사용시에 병렬로 작업을 수행함으로 인해서 로그가 뒤죽박죽이 되기 마련인데, 이때 로그에 명기해주었으면 하는 것은 역시 각 서버의 호스트/IP입니다.

Rex::Logger 에서 다음과 같은 사용법을 통해서 해결할 수 있습니다.

1
2
$Rex::Logger::format = '%h - %D - %s';
# srv001 - 2012-04-12 18:35:12 - Installing package vim

각 포맷에 대한 자세한 설명은 위의 Metacpan 링크에서 확인할 수 있습니다.

CPAN 의존모듈 설치

애시당초 처음에 Rex 를 사용하는 이유가 여러대의 서버에 CPAN 모듈을 설치해야할 필요가 있었기 때문이었습니다. 하지만 사용구조상 일일이 모든 서버에 대해서 perlbrew 를 설치한다거나, 하는 호화(?)를 누릴 수는 없었습니다. perlbrew 를 꺼내는 것 자체가 애시당초 핀트에 어긋나버리는 얘기가 되어버리지만… –_–;

뭐 네, 방법은 간단합니다.

1
run("curl -L http://cpanmin.us | perl - --notest --mirror ftp://cpan.mirror.cdnetworks.com/CPAN/ --mirror-only Test::More LWP::UserAgent JSON Nagios::Plugin");

애시당초 Rex 의 접근은 root 로 이뤄지기 때문에 이와같은 설정으로 그냥 땜박질 할 수 있었지만, sudo 로 접근한다거나 하는 예외상황에서는 --sudo 옵션을 붙여주면 되겠습니다. 물론 root 로 접근했을 때 --sudo 옵션은 쓰면 오동작의 원인이 됩니다.

--notest 는 물론 수많은 서버가 동일 구성이며, 처음 커맨드로 때려보고 한방에 된다고 했을 때, 테스트가 불필요하다는 판단아래에서 집어넣었구요.

좀 더 속도개선을 노려본다면 --mirror 옵션을 통해서 CDNetworks 같은 CPAN Mirror 를 이용해보는 것도 괜찮을 것 같습니다. 눈에 띄는 속도향상을 체감할 수 있을 겁니다.

결론

어떤 일을 하기에 필요한 도구를 찾아서 사용하는 것이 일반적인 접근일텐데, 때로는 어떤 도구를 사용하기 위해서 일부러 일을 그쪽방향으로 맞추는 경우를 주변에서 많이 접하게 됩니다. 저는 어떻게해서 Rex 를 알고 사용하게 되었는지 이미 까먹었지만, 하는 일에 대한 최적의 선택이었다고 생각합니다.

Data::Printer vs Data::Dump(er)

여태껏 많은 Perl 코드에서 습관적으로 Data::Dumper 를 사용해왔습니다.

안되면 찍어봐라

라는 가장 기본적인 디버깅 방법론을 Data::Dumper 와 함께 해왔었죠. 물론 코어모듈이기때문에 별다른 모듈 설치없이 그냥 사용할 수 있는 점이 가장 큰 매력이었습니다.

단순한 데이터구조에서는 빛을 발하지만, 이런저런 데이터들이 서로 어우러지고 구겨지고 하는 구조에 한글과 같은 멀티바이트문자가 들어갔을 때는 사정없이 깨져버리는 문제가 있습니다. 물론 회피책도 있기는 하지만 매번 그러기도 쉽지가 않죠. 기존의 많은 Data::Printer 에도 언급되었다시피, DBIx::Class 오브젝트를 찍어보는 것에 매우 효과적입니다. DateTime 도 마찬가지 입니다. 이는 DBIx::ClassDateTimeData::Dumper 로 찍어본 사람이라면 누구나 느껴봤을 법한 것이죠.

아무튼 그래서 Data::Printer 를 사용하기 시작했습니다. 현재 회사에서는 이전과는 달리 웹개발에 메인으로 Perl 을 사용하지 않고 주로 여러가지 배치작업이나 스크래핑 작업 용도로 사용하고 있어서 주로 사용하는 모듈을 그냥 처음부터 집어넣고 시작하고 있습니다.

https://dl.dropboxusercontent.com/u/262117/blog-assets/screenshot-201304112.png

그리고 결정적으로 키와 값의 색 구분이라든가, 배열의 인덱스 번호를 좌르륵 이라든가, 한글이 안깨진다든가…

단점은 없는 게 아닙니다. 그러니까 좀 규모가 있는 데이터를 Data::Printer 로 덤프하면 그 나름대로 치장하는 시간이 좌르르륵 올라갑니다. 근데 뭐 그런 규모있는 데이터를 Data::Printer 로 뽑는 것 자체가 이상한 일이라 –_–;;

아 그리고 Data::Printerp 함수는 기본적으로 인자를 기본형만 받을 수 있습니다.

1
p [{ a => 1 }]

이라고 했을 때,

1
Type of arg 1 to Data::Printer::p must be one of [@$%&]

라는 에러메시지가 나옵니다.

아 그리고 Data::Printer 의 출력은 기본적으로 STDERR 로 나옵니다. 물론 뭐 하고 싶다면야 STDOUT 으로 지정할 수 있습니다.

Data::Printer 와 함께 깔끔한 디버깅을…