Joyent의 Triton: /native는 우리 친구 이에요!

(Here’s the English version, easier to understand)
우리는 Docker 컨테이너를 Triton로 배포했고 다 잘 됐어요. 근데…갑자기 오늘 우리 앱가 먼제 있어요. 우리 앱은 느려요 아니면 작동하지 않아요. 그리고 컨테이너 안에 디버그 도구가 없어요. 그럼 우리는 뭐 할 거예요?

재부팅는 해결 아니에요

재부팅는 해결 아니에요

Triton의 컨테이너를 조금 탐에사하세요!

저는 다시 nginx 컨테이너를 트리톤에서 시작했어요.

#Switch to Triton
eval $(triton env)
# Start nginx on triton
docker run -d --name test -p 80 nginx

이 컨테이너를 시작 했을 떼 컨테이너 안에 `docker exec`로 shell을 사작해고 파일 시스템를 조금 보세요. 뭘 발견했어요?
roman@minty ~ $ docker exec -ti test /bin/bash
root@ae35cee7759b:/# ls -l

/native?

/native?

/native 뭐예요? 조금 더 보세요. 아마 유명한 Unix 프로그램을 잦아보세요? 아하! `awk`부터 `zcat`까지 다 있구나요! 그래서 /native가 다른 Unix예요? 맞아! /native는 Triton의 토박이 (native) 운영체제 이에요! 그리고 유용한 도구가 있어요!

탐사하세요.

탐사하세요.

/native에서 어랜 unix 도구가 사 살아요

/native에서 어랜 unix 도구가 사 살아요

/native를 사용하자!

이제는 /native기 있는걸 알아서 사용할 수 있어요. 먼저 나쁜 컨테이너 배포해요. 저는 ‘나쁜 컨테이너’ 예를 준비했어요. 소스 코드가 여기에 있어요. 이 ‘gamlerhart/waste-io’ 컨테이너를 시작하세요:

docker run -d --name waste-io-example gamlerhart/waste-io

이 컨테이너 문제 있잖아요. 무슨 문제 있는 것 잦아보세요. 우리는 다시 `docker exec`로 컨테이너가 연락해요. 그럼 `iostat’ 사용 해보면 `iostat’ 없다고요. 근데 우리는 /native 사용할 수 있어요. /native의 PATH에 더하고 `iostat’ 사용 할 수 있어요:
roman@minty ~ $ docker exec -ti waste-io-example /bin/bash
root@3bd53e4a7fe4:/#
root@3bd53e4a7fe4:/# iostat -M 1
bash: iostat: command not found
#Oh...damn...iostat would tell us if our IO is sane.
#Ah.../native to the rescue
root@3bd53e4a7fe4:/# iostat -M 1
tty ramdisk1 sd0 sd1 sd2 cpu
tin tout Mps tps serv Mps tps serv Mps tps serv Mps tps serv us sy dt id
0 236 0 0 0 0 0 0 2 148 0 2 42 1 4 8 0 89
0 1214 0 0 0 0 0 0 7 3583 0 0 0 0 9 14 0 77
0 2100 0 0 0 0 0 0 7 3531 0 5 157 0 7 17 0 77
0 941 0 0 0 0 0 0 7 3640 0 0 0 0 7 14 0 79
view raw iostat.sh hosted with ❤ by GitHub
.
아하!! 우리 컨테이너가 IO 많이 하고 있어요. 근데 우리는 더 알고 싶어요. 흠~~~….’tracing’ 하고 싶어요. ‘dtrace’ 하자요!. 맞아! dtrace가 /native에 있어요. 저는 dtrace프로 아니에요. 근데 인터넷 안내 많아요 (dtrace.org, guide, 예가).
우리는 ‘lx-sycall’-프로브 (LinuX-syscall) 사용할 거에요. `-ln lx-syscall:::`가 lx-syscall 프로브를 명단해요. -n `syscall::: { @num[execname,pid,probefunc] = count() }`는 프로브 시작하고 프로그램과 프로세스 아이디와 sycall 이름 그룹 해요. 프로브를 Cntl+C 그만하세요.
#linux-syscalls aka lx-syscalls
root@3bd53e4a7fe4:/# dtrace -ln 'lx-syscall:::'
ID PROVIDER MODULE FUNCTION NAME
1549 lx-syscall sys32 inotify_init1 return
1550 lx-syscall sys32 preadv entry
1551 lx-syscall sys32 preadv return
1552 lx-syscall sys32 pwritev entry
# Start tracing the syscalls. Grouped by execname, pid and syscallname (probefunc)
root@3bd53e4a7fe4:/# dtrace -n 'syscall::: { @num[execname,pid,probefunc] = count() }'
dtrace: description 'lx-syscall::: ' matched 1352 probes
^C
node 23210 fsync 11368
node 23210 ioctl 11368
node 23210 open 11368
node 23210 pwrite64 11368
node 23210 close 11370
node 23210 unlink 11370
node 23210 epoll_wait 30332
node 23210 read 30332
node 23210 write 30332
node 23210 futex 180019

우리는 node-프로세스의 파일 시스템 write, read, open unlink하고 futex 많이 봐서 이 node 프로세스 나빠졌어요. 근데 이 프로세스가 뭐 하고 있어요? 무슨 파일 사용하고 있어요? 우리는 더 dtrace 프로브 하자! `x-syscall::open:entry` open-syscall만 보고 `execname==”node”`로 node-프로그램만 봐요. 고리고 `@num[copyinstr(arg0)] = count()`로 파일 그룹 해요:
root@3bd53e4a7fe4:/# dtrace -n 'lx-syscall::open:entry/execname=="node"/ { @num[copyinstr(arg0)] = count() }'
dtrace: description 'lx-syscall::open:entry' matched 2 probes
^C
/tmp/io-file-0.00023033588805110305 1
/tmp/io-file-0.00029973264092197915 1
/tmp/io-file-0.001082331117725488 1
/tmp/io-file-0.0017028727466004145 1
/tmp/io-file-0.00224423432995402 1
/tmp/io-file-0.002446706340221372 1
/tmp/io-file-0.002880551536430742 1

아…임시의 파일을 많아요. 흠…node-프로그램 안에 어디에 이 임시의 파일 만들어요? Dtrace의 ‘jstack/ustack’ 우리는 스택 추적를 받을 수 있어요:
root@3bd53e4a7fe4:/# dtrace -n 'lx-syscall::open:entry/execname=="node"/ { @num[jstack()] = count() }'
dtrace: description 'lx-syscall::open:entry' matched 2 probes
^C
libc.so.6`open+0x10
libc.so.6`sysconf+0x54c
node`_ZN2v88internal20MarkCompactCollector31NumberOfParallelCompactionTasksEil+0x46
node`_ZN2v88internal20MarkCompactCollector23EvacuatePagesInParallelEv+0xea
node`_ZN2v88internal20MarkCompactCollector29EvacuateNewSpaceAndCandidatesEv+0x67
node`_ZN2v88internal20MarkCompactCollector14CollectGarbageEv+0x29
node`_ZN2v88internal4Heap11MarkCompactEv+0x6d
node`_ZN2v88internal4Heap24PerformGarbageCollectionENS0_16GarbageCollectorENS_15GCCallbackFlagsE+0x4b0
node`_ZN2v88internal4Heap14CollectGarbageENS0_16GarbageCollectorEPKcS4_NS_15GCCallbackFlagsE+0x142
node`_ZN2v88internal4Heap15HandleGCRequestEv+0x84
node`_ZN2v88internal10StackGuard16HandleInterruptsEv+0x31c
node`_ZN2v88internal18Runtime_StackGuardEiPPNS0_6ObjectEPNS0_7IsolateE+0x45
0x2bd84b30961b
0x2bd84b44e1f9
0x2bd84b4490eb
0x2bd84b44281b
0x2bd84b30d157
0x2bd84b442615
0x2bd84b337f84
0x2bd84b322922
node`_ZN2v88internal12_GLOBAL__N_16InvokeEPNS0_7IsolateEbNS0_6HandleINS0_6ObjectEEES6_iPS6_S6_+0xb3
node`_ZN2v88internal9Execution4CallEPNS0_7IsolateENS0_6HandleINS0_6ObjectEEES6_iPS6_+0x61
node`_ZN2v88Function4CallENS_5LocalINS_7ContextEEENS1_INS_5ValueEEEiPS5_+0x139
node`_ZN2v88Function4CallENS_5LocalINS_5ValueEEEiPS3_+0x41
node`_ZN4node9AsyncWrap12MakeCallbackEN2v85LocalINS1_8FunctionEEEiPNS2_INS1_5ValueEEE+0x149
node`_ZN4nodeL5AfterEP7uv_fs_s+0x146
node`uv__work_done+0xa5
node`uv__async_event+0xab
node`uv__async_io+0xa3
node`uv__io_poll+0x3a0
node`uv_run+0x156
node`_ZN4node5StartEiPPc+0x468
libc.so.6`__libc_start_main+0xf5
2
libpthread.so.0`open+0x2d
node`worker+0xa1
node`uv__thread_start+0x19
libpthread.so.0`start_thread+0xc4
62043

아…운이 없어요. open-syscall가 node의 이벤트 프로브에 나와요 (node를 dtrace로 알아보기 다른 주제이에요) . 근데 우리는 node를 프로그램가 `tmp/io-file-` 파일 마들기 알아고 이제 아마 프로그램 버그 문제를 풀 수 있어요.
셜록 'dtrace' 홈즈

셜록 ‘dtrace’ 홈즈

dtrace 잘 아세요?

dtrace 잘 알면 많는 프로브 못 잦아요? 그럼 Triton  컨테이너 안에 컨테이너만 볼 수 있어서 dtrace 프로보 덜 많아요. 그리고 우리는 Joyent의 클라우드 스비스 ‘global-zone’ 들어가면 못 해요.  개인 Triton가 있으면 할 수 있어요. 근데 이 글 끝냈어요. ^.^끝

Tagged on: , ,