2008/12/17

솔라리스의 '라이브 업그레이드' 기능을 활용한 실시간 운영체제 백업

솔라리스 10은 update 6 (2008년 11월) 버젼부터는 root filesystem으로 zfs를 지원하고 있습니다, 강력한 스냅샷 기능으로 가장 안정적인 운영체제 상태를 항상 유지할 수 있게 되면서, 사실 백업이 근본적으로 필요없게 되었다고 볼 수 있습니다. 물론, 단일 디스크로만 사용한다면 다스크 장애시 문제가 발생할 수 있으므로, zfs를 root 화일 시스템으로 사용하는 경우에는 반드시 미러링을 사용해야 합니다.

그런데, zfs가 아닌 이전 화일 시스템(ufs)를 선택했을 경우에는 다른 방법을 이용하여 운영체제 백업본을 디스크 상에 여러개 유지할 수 가 있습니다. 따라서, 디스크 하나에 장애가 났을 경우에 이미 구성된 백업 슬라이스를 이용하여 백업된 운영체제 이미지를 다시 붓거나 하는 일없이 바로 부팅할 수 있습니다.

이렇게 하기 위해서는 다음과 같은 구성을 필요로 합니다.

예를 들어, 160GB가 두개 설치되어 있는 서버가 있다고 합시다. 운영체제 이미지를 위해서는
20GB 이상이면 매우 충분하나, 추후 사용을 위해서 32GB 정도를 운영체제에 할당한다고
할 경우, 160GB의 디스크가 두개 있어서  디스크1에서 s0가 32G 공간으로 솔라리스 root가

설치되어 있고 나머지는 /home 등으로 사용되고 있다고 합시다.


디스크2도 같은 s0가 있어서 DISK1의 s0로 mirror되어 있고 나머지가 비어있다면,

디스크 2의 나머지 공간에 32G를 가지는 슬라이스를 몇개 더 만들어서 이 공간을

라이브 업그레이드용 이미지 공간으로 사용 가능합니다.



root(/) 디스크의 내용을 디스크2의 s3,s4,s5로 lucreate 해놓게 되면

디스크1이 장애가 났을 경우 디스크2의 위 슬라이스로 eeprom에서 바로 부팅할 수 있게 됩니다.

따라서, 별도로 백업했던 이미지를 다시 붓는 과정이 필요없게 되므로 긴급 장애에
빠르게 대응할 수 있습니다.

c0t0d0가 장애가 난 경우, c0t0d0s0와 c0t1d0s0이 미러링이 되어 있으므로, 별 문제없이 두번째
디스크로 유지가 되나, 만약을 위해서 운영체제 백업을 유지하기 위해서 위와 같이 별도의 부팅
가능한 운영체제 이미지본을 가질 수 있게 됩니다.

live upgrade를 이용하여 운영체제 백업 이미지를 구성하는 방법은 다음과 같이 실행하면 됩니다.

# lucreate  -m /:/dev/dsk/c0t1d0s3:ufs -n first_root_backup
이 경우 /, /usr, /var, /opt등이 나누어져 있는 경우에는 새 lu 이미지는 기존의 /usr, /var/를 공유해서

부팅할 수 있게 되나, 대개는 /(root)에 모든 서브 디렉토리가 포함되어 있기 때문에 위의 명령으로

단번에 백업합니다.



이렇게 실행하면 새로이 백업된  부팅 이미지의 이름으로 first_root_disk가 할당되며, 새로운 부팅 이미지

환경이  생성됩니다. 여기에 사용할 수 있는 디바이스로는 물리적인 디스크나 혹은 SVM에 의해서 주어지는

논리적 디스크에서만 생성됩니다. loopback file system이나, zfs의 데이타셋의 raw volume에서는 실행되지

않습니다.



정상적으로 생성이 되면 lustatus로 확인이 가능하며, 향후 부팅을 원하는 경우에는 luactivate로

가능하게 됩니다.



또한, 새로운 패치로 인해 어떤 일이 생길지 알 수 없다고 판단이 되면 위처럼 lucreate로 만들어진 슬라이스에 새로운 패치를 적용한 후에 부팅해 봄으로써 새패치로 인한 문제가 있는 지 없는 지를 파악할 수 있습니다. lu를 사용하는 경우에는 애플리케이션과 사용자 홈이 있는 파티션 영역은 /와 분리해서 독립적인 슬라이스에 구성하는 것이 바람직합니다.

2008/11/27

윤분투(Ubuntu), 오픈솔라리스(OpenSolaris), 프리비에스디(FreeBSD)의 성능 비교

흥미로운 성능 비교입니다.
성능 비교를 위해서 다음과 같은 것들을 비교했다고 하는 군요. Poronix Test Suite이라고 하네요. 아래와 같은 성능 테스트가 포함되어 있습니다.


7-Zip Compression
Gzip compression
GnuPG
BYTE Unix Benchmark
Tandem XML
Bork File Encryption
Java SciMark
Bonnie++
OpenSSL
Sunflow Rendering System

오픈 솔라리스도 비교적 좋은 성능을 보여주는 군요 특히 Java관련된 성능이 가장 잘나오는데, 그다지 놀랍다고 되어 있지는 않네요. Java가 썬거니.. 당연하지 하는 분위기.. ^^
아마도 대부분이 zfs의 우수한 성능 덕분에 생성된 결과로 보입니다.







[Phoronix] Ubuntu vs. OpenSolaris vs. FreeBSD Benchmarks

2008/11/25

IT에서 시스템 엔지니어라는 것..

제가 멘토링을 해준 한 학생이 어느날 저에게 와서 묻더군요.
시스템 엔지니어가 뭘 하고, 무슨 의미가 있는지...

그때 대답해준다고 편지 입니다.

----------------------

음.. 시스템 엔지니어라는 건 쉽게 비유하면

각 가정에서 냉장고를 사고 싶어해요. 그러면, 영업사원이 찾아가서 냉장고를 팔면 되겠지?

그런데, 영업사원이 전자제품은 전혀 모르는 가정 주부에게 냉장고에 대해서 많은 걸

얘기해주려고 하니 영업사원 한 사람이 아는 내용이 너무 적은게야.

그래서 영업사원 둘이되서 가정 주부에게 찾아가면 전달가능할 정보가 두배가 되므로

판매에 도움이 되겠지? 그런데, 굳이 영업이 둘인 경우보다는 냉장고 자체를 잘 아는

그 누군가가 같이 있으면 좋겠지 ? 그런 사람을 냉장고 엔지니어라고 가정해보자.



그대로 it에 비유할 수 있는데, 시스템의 경우에는 냉장고보다 훨씬 복잡하지. 그러니,

필연적으로 영업혼자 할 수가 없지. 더욱이 시스템은 하드웨어 그 자체보다 그 위에

수행되는 서비스(소프트웨어)가 더 복잡하지. 따라서, 엔지니어가 필수적이라고 할 수

있지. 그래서 IT에서는 엔지니어가 존재하게 되어 왔지. (더 큰 프로젝트에는 컨설턴트도
별도로 붙어서 정보 전달의 조직이 커지게 되지.)


요약하면, 궁극적으로 영업이든 시스템 엔지니어든 영업, 즉 판매를 촉진을 돕는

사람이라고 할 수 있고 (고장난 제품을 고쳐주는 엔지니어가 아니라면 영업이지 ^^; )



단순 영업은 그냥 친밀도와 가격으로 제품을 판매하려고 하는 반면에 시스템 엔지니어들은

첨단 기술과 신뢰로 제품을 판매하는 것이 다르다고 할 수 있지.

이런 시스템 엔지니어는 궁극적으로 IT 아키텍쳐 컨설턴트의 길을 가게 되고,

영업은 사장님의 길을 가게 되는 것이야.  IT 아키텍쳐 컨설턴트는 냉장고 하나 쓸때부터

냉장고를 1000개 만개 쓸때 어떻게 하면 적은 전기에 고효율에 수납을 편하게 할 수 있는 지등

전체를 고려하는 사람이 되는데, 위에 든 예와 비교하자면 냉장고를 팔지만  집 자체를

디자인하는 디자이너에 가까워진다고 할 수 있겠지.



그런데, 문제는 IT 컨설턴트가 소개해야할 기술들이 몇몇 없고(정말 냉장고처럼)

그걸 활용해서 사용하는 경우가 많지 않다면, 그냥 영업 사원의 조수로 전락하는 수가

생기게 되지. 그래서, 늘 최신 기술을 업데이트해야하고, 영업 사원보다는 장기적으로

전략적으로 생각하는 사고를 가져야 하는데, 쉽지 않지. 나이를 먹을 수록 몸이

피곤해질 수록, 매달리는 사람이 많을 수록(특히, 아이) 주변 상황이 피곤해질 수롤

자신을 업데이트 하는 것이 쉽지 않게 되거든.



시스템 엔지니어들은 첨단 기술로 명성과 존경을 받고, 그만한 금전적 대우를 받으면

할만한 일이긴 한데, 그렇지 않다면 정말 괴로운 일이라고 할 수 있겠지.

나도 늘 스스로 그런 사람인가 되묻곤 하지. ^^;



하지만, 뭘 선택하든 언제든지 바꿀 수 있는 기회가 있다는 것을 잊으면 안되고

오히려 중요한 것은 선택한 것에 집중을 해서 그쪽에서 일가견을 가진 사람이 되는 것이

더 중요하다는 것을 잊기 않길.

USB 3.0 세대

USb 3.0이 곧 나타난다고 하는 군요.
인터페이스가 바뀌게 되면, 대상을 이해하는 수준도 달라지게 되는 법인데
usb3.0이 이끌고 올 세상에서 솔라리스에 대한 이해도가 증가할 수 있을지 궁금하네요.

소스 : 스마트 쇼핑저널 버즈

2008/11/24

일부 프로세스에게만 자원 조정 및 관리를 하고 싶을때

자식 프로세스를 생성하는 쉘 스크립트가 있다고 하죠.
편의상 start.sh이라고 할때 start.sh은 여러개의 자식 프로세스를 실행할 수 있습니다.

이런 경우, start.sh을 실행하는 사용자의 기본 프로젝트의 정책과 달리 다른 정책으로 start.sh과 그 자식 프로세스들을 관리하고 싶은 경우가 있습니다. 예를들면, start.sh를 포함한 자식 프로세스는  1 core의 50%만 최대 사용할 수 있도록 제한을 두고 싶다면 일단, 1 core의 50%만을 사용하도록 정책이 선언되어 있는 프로젝트를 하나 만들어야 합니다.

솔라리스10에서 새로이 추가된 cpu-cap은 cpu 사용의 최대를 제안할 수 있습니다. 참고로, 기존에 제공하던 FSS와는 완전히 반대로 무조건 주어진 값(%) 이상을 CPU를 점할 수 없도록 하는 정책입니다.

#projadd -U admin -K "project.cpu-cap=(privileged,50,deny)" cap50
와 같이 cap50이라는 프로젝트를 만듭니다.

이제 start.sh을 실행하려고 하는데, 최대 cpu를 50%만 사용할 수 있도록 하게 하고 싶으면 다음과 같이 실행합니다.

#newtask -p cap50  start.sh


혹은
#newtask -p cap50 bash
#start.sh
과 같이 실행해도 됩니다.

실제로 CPU가 50%만 사용하는 지 확인을 하고 싶으면
#prstat -J
를 실행해서 Project 목록을 보도록 합니다.


실행을 했는데 사용중에 cpu 자원을 바꿔보고 싶으면 다음과 같은 방법을 사용합니다.
우선 task id를 확인합니다.
# ps -o taskid -p 8982
TASKID
 1039

해당 ID의 관심 자원 (여기서는 cpu-cap입니다)의 값을 확인합니다.
# prctl -P -i task 1039 | grep -i cap
project.cpu-cap privileged 50 - deny -

다음과 같은 방법으로 실시간 교체를 실행합니다.
#prctl -t privileged -n project.cpu-cap -r -v 200 -i task 1039


관심 대상의 자원에 따라, 위 prctl의 명령의 아규먼트는 약간 씩 다를 수 있습니다.

2008/11/15

IT 혁신의 사례

아마존 웹서비스의 S3가격입니다.
완전 가상화영역에서 사용자가 원하는 인프라스트럭쳐를 구성해서 제공해주는 서비스입니다.
리눅스/유닉스 영역에 오픈 솔라리스가 들어있습니다. 윈도우즈도 서비스를 제공해주고 있는데
리눅스/유닉스보다 25% 더 비싸군요.

그리고, 이렇게 완전히 자동화된 인프라스트럭쳐를 구축하려면 리눅스혹은 유닉스가 기초기술이라는 것이죠.
윈도우즈로는 불가능한 힘이 오픈솔라리스(유닉스) 리눅스에 있습니다.

중요한 것은 안팔리것 같았던 이 서비스가 매달 엄청난 수준의 이익을 낳기 시작했다는 것입니다.
더욱이, 계속 매출이 증가하고 있다는 것이죠.

책을 팔다가, 각종 잡다구니 제품을 팔더니, 컴퓨팅을 팔겠다고 생각하고 연구해서 이렇게 훌륭한
인프라스트럭쳐를 구성한 아마존의 개발자들은 정말 '혁신'의 대표적인 사례가 아닐까 싶습니다.

아마존은 약 2000명 정도의 개발자들을 유지하고 있다고 하는 군요. 마이크로소프트, 구글이래 가장 많은 웹 서비스 개발자를 보유한 곳인 것 같군요.

개인적으로 보면 솔라리스를 이용해서 이렇게 만드는 것이 그다지 어렵지 않은데, 우리 나라에서 이러한 서비스가 잘될까 하는 의문이 듭니다. 우리나라는 듀얼코어 인텔과 160GB 서버 호스팅이  한달에 10만원밖에 하지 않기 때문이죠.

Pricing


Pay only for what you use. There is no minimum fee. Estimate your monthly bill using AWS Simple Monthly Calculator.




Instances

























Standard Instances Linux/UNIX Windows
Small (Default) $0.10 per hour $0.125 per hour
Large $0.40 per hour $0.50 per hour
Extra Large $0.80 per hour $1.00 per hour



















High CPU Instances Linux/UNIX Windows
Medium $0.20 per hour $0.30 per hour
Extra Large $0.80 per hour $1.20 per hour




Pricing is per instance-hour consumed for each instance type. Partial instance-hours consumed are billed as full hours.




For more information on Windows options including Windows with Authentication Services and Windows SQL Server, please click here.




Data Transfer




Internet Data Transfer




The pricing below is based on data transferred "in" and "out" of Amazon EC2.













Data Transfer In  
All Data Transfer $0.10 per GB

























Data Transfer Out  
First 10 TB per Month $0.17 per GB
Next 40 TB per Month $0.13 per GB
Next 100TB per Month $0.11 per GB
Over 150 TB per Month $0.10 per GB


Pricing for S3

Amazon EC2 running Linux/UNIX:

  • $0.10 per Small Instance (m1.small) instance-hour (or partial hour)
  • $0.40 per Large Instance (m1.large) instance-hour (or partial hour)
  • $0.80 per Extra Large Instance (m1.xlarge) instance-hour (or partial hour)
  • $0.20 per High-CPU Medium Instance (c1.medium) instance-hour (or partial hour)
  • $0.80 per High-CPU Extra Large Instance (c1.xlarge) instance-hour (or partial hour)


Amazon EC2 running Windows:

  • $0.125 per Small Windows Instance (m1.small) instance-hour (or partial hour)
  • $0.50 per Large Windows Instance (m1.large) instance-hour (or partial hour)
  • $1.00 per Extra-Large Windows Instance (m1.xlarge) instance-hour (or partial hour)
  • $0.30 per High-CPU Medium Windows Instance (c1.medium) instance-hour (or partial hour)
  • $1.20 per High-CPU Extra Large Windows Instance (c1.xlarge) instance-hour (or partial hour)


Amazon EC2 running Windows with SQL Server Standard:

  • $1.10 per Large Windows Instance and SQL Server Standard (m1.large) instance-hour (or partial hour)
  • $2.20 per Extra-Large Windows Instance and SQL Server Standard (m1.xlarge) instance-hour (or partial hour)
  • $2.40 per High-CPU Extra Large Windows Instance and SQL Server Standard (c1.xlarge) instance-hour (or partial hour)

--
Amazon Web Services

2008/10/30

솔라리스용 한글 폰트 (truetype fonts (english) to make Solaris more beautiful)

opensolaris는 기본적으로 많지 않은 폰트를 가지고 출시되었습니다.
그 허접하고 짜증나는 daewoo font는 여전히 들어있죠. 이 daewoo 폰트 때문에, 얼마나 많은 고객들이 Solaris에서 java가 더 이상하게 돈다는 오해를 낳기도 해왔습니다. 정말, 과감하게 지워버려야 합니다.

보다 깔끔한 환경을 원한다면, 화면 배경에서 테마를 변경해야 할 필요가 있는데, 사전에 충분한 폰트를 제공해놓는 것이 중요하겠죠.

영문 폰트는
http://web.nickshanks.com/typography/corefonts

에서 다소 받을 수 있습니다.

반면 한글폰트는 그 유명한 '은폰트' 셋트를 일단 다운 받으셔야 합니다.
http://ko.wikipedia.org/wiki/%EC%9D%80%EA%B8%80%EA%BC%B4
그리고, 무료로 공급하는 미려한 폰트가 있는데, 조선일보체와 한겨레체입니다. 각각 신문사에서 받을 수 있구요.

최근에는 다음에서도 '다음체' 폰트를 무상으로 제공합니다.
모든 폰트를 다운받아서 .ttf 형태의 화일로 푸신후

홈디렉토리의 .fonts 라는 서브 디렉토리 밑으로 모두 복사하신후, 로그아웃 하셨다가 로그인 하시면 됩니다.
대우 폰트는 반드시 지우시는 것이 좋습니다.(/usr/X11R6/lib/X11/fonts/misc/hangle*) 추악 그 자체입니다.

2008/10/28

소비자 제품의 Hype 사이클

opensolaris adoption - Google 이미지 검색

제목 그대로 '소비자 제품의 Hype 사이클'인데, 문득 솔라리스는 이 그래프에서 어느곳에 차지할까 하는 생각을 해봤습니다.

opensolaris package 지원 IPS 개념도

Redhat 5 V12n Scalability graph

Redhat 5 Enterprise Edition에서 테스트된 Full Virtualization(FV), Para Virtualization(PV), Native(Dom0)의 테스트 결과라고 하는 군요. Redhat도 XenSource기반일터인데, 솔라리스 xVM과 성능이 다소 차이가 있는 듯 해보이는 군요. FV성능이 너무 떨어지는 군요.

Google 이미지 검색결과: http://farm4.static.flickr.com/3269/2634449300_999df1a9a8.jpg

2008/10/20

dtrace를 이용한 프로세스의 화일(fd)당 write 패턴 분석

간혹 프로세스가 작동하는 것을 보면 프로세스가 오픈한 화일들에 대한 읽기/쓰기 패턴이 궁금할때가 있어집니다. 
예를 들면, 평소에 문제가 없었는데, DB로부터 데이타를 읽어와서 화면에 출력하는 어떤 경우 화면에 출력이 느려지는 경우가 있습니다.

이런 환경에서 이 프로세스가 오픈한 화일들을 대상으로 어떻게 작동하는 지를 볼 수 있다면 매우 편리할 것입니다.

솔라리스에서는 pfiles라는 유틸리티나 mdb를 이용하여 프로세스가 오픈한 화일들을 볼 수가 있습니다. 다음은, 이메일 클라이언트로 유명한 썬더버드라는 툴이 어떤 화일들을 오픈했는 지 확인하는 방법입니다.

#pfiles 1180
bhkim@vaio-bhkim:~/Desktop$ pfiles 1180  > /tmp/thunderbird.1180.pfiles
bhkim@vaio-bhkim:~/Desktop$ cat /tmp/thunderbird.1180.pfiles
1180:    /usr/lib/thunderbird/thunderbird-bin
  Current rlimit: 512 file descriptors
   0: S_IFCHR mode:0666 dev:301,0 ino:6815752 uid:0 gid:3 rdev:13,2
      O_RDONLY|O_LARGEFILE
      /devices/pseudo/mm@0:null
   1: S_IFIFO mode:0000 dev:299,0 ino:182 uid:0 gid:50 size:0
      O_RDWR
   2: S_IFIFO mode:0000 dev:299,0 ino:182 uid:0 gid:50 size:0
      O_RDWR
   3: S_IFDOOR mode:0444 dev:311,0 ino:42 uid:0 gid:0 size:0
      O_RDONLY|O_LARGEFILE FD_CLOEXEC  door to nscd[1156]
      /var/run/name_service_door
   4: S_IFSOCK mode:0666 dev:310,0 ino:50338 uid:0 gid:0 size:0
      O_RDWR|O_NONBLOCK FD_CLOEXEC
    SOCK_STREAM
    SO_SNDBUF(16384),SO_RCVBUF(5120)
    sockname: AF_UNIX
    peername: AF_UNIX /tmp/.X11-unix/X0
   5: S_IFIFO mode:0000 dev:299,0 ino:671 uid:101 gid:10 size:0
      O_RDWR
   6: S_IFIFO mode:0000 dev:299,0 ino:671 uid:101 gid:10 size:0
      O_RDWR
   7: S_IFREG mode:0644 dev:182,65544 ino:18136 uid:101 gid:10 size:0
      O_WRONLY|O_CREAT|O_TRUNC
      /export/home/bhkim/.thunderbird/5x1njpem.default/.parentlock
   8: S_IFIFO mode:0000 dev:299,0 ino:672 uid:101 gid:10 size:0
      O_RDWR|O_NONBLOCK
   9: S_IFIFO mode:0000 dev:299,0 ino:672 uid:101 gid:10 size:0
      O_RDWR|O_NONBLOCK
  10: S_IFIFO mode:0000 dev:299,0 ino:673 uid:101 gid:10 size:0
      O_RDWR|O_NONBLOCK
  11: S_IFIFO mode:0000 dev:299,0 ino:673 uid:101 gid:10 size:0
      O_RDWR|O_NONBLOCK
  12: S_IFREG mode:0644 dev:182,65544 ino:18139 uid:101 gid:10 size:140912
      O_RDWR
      /export/home/bhkim/.thunderbird/5x1njpem.default/abook.mab
  13: S_IFREG mode:0644 dev:182,65538 ino:37521 uid:0 gid:2 size:851050
      O_RDONLY|O_LARGEFILE
      /usr/lib/thunderbird/chrome/classic.jar
  14: S_IFREG mode:0644 dev:182,65538 ino:37527 uid:0 gid:2 size:837452
      O_RDONLY|O_LARGEFILE
      /usr/lib/thunderbird/chrome/en-US.jar
  15: S_IFSOCK mode:0666 dev:310,0 ino:31861 uid:0 gid:0 size:0
      O_RDWR FD_CLOEXEC
    SOCK_STREAM
    SO_SNDBUF(16384),SO_RCVBUF(5120)
    sockname: AF_UNIX
    peername: AF_UNIX /tmp/.iiim-bhkim/:0.0
  16: S_IFIFO mode:0000 dev:299,0 ino:674 uid:101 gid:10 size:0
      O_RDWR
  17: S_IFIFO mode:0000 dev:299,0 ino:674 uid:101 gid:10 size:0
      O_RDWR
  18: S_IFIFO mode:0000 dev:299,0 ino:675 uid:101 gid:10 size:0
      O_RDWR
  19: S_IFIFO mode:0000 dev:299,0 ino:675 uid:101 gid:10 size:0
      O_RDWR|O_NONBLOCK
  20: S_IFCHR mode:0644 dev:301,0 ino:78118918 uid:0 gid:3 rdev:149,1
      O_RDONLY
      /devices/pseudo/random@0:urandom
  21: S_IFSOCK mode:0666 dev:310,0 ino:33534 uid:0 gid:0 size:0
      O_RDWR|O_NONBLOCK FD_CLOEXEC
    SOCK_STREAM
    SO_SNDBUF(16384),SO_RCVBUF(5120)
    sockname: AF_UNIX
    peername: AF_UNIX /var/tmp/orbit-bhkim/linc-292-0-48e4789fdb523
  22: S_IFSOCK mode:0666 dev:310,0 ino:38996 uid:0 gid:0 size:0
      O_RDWR|O_NONBLOCK FD_CLOEXEC
    SOCK_STREAM
    SO_REUSEADDR,SO_SNDBUF(49152),SO_RCVBUF(49152)
    sockname: AF_INET 127.0.0.1  port: 41233
  23: S_IFSOCK mode:0666 dev:310,0 ino:31860 uid:0 gid:0 size:0
      O_RDWR|O_NONBLOCK FD_CLOEXEC
    SOCK_STREAM
    SO_REUSEADDR,SO_SNDBUF(16384),SO_RCVBUF(5120)
    sockname: AF_UNIX /var/tmp/orbit-bhkim/linc-49c-0-48e474737f48b
  24: S_IFSOCK mode:0666 dev:310,0 ino:31858 uid:0 gid:0 size:0
      O_RDWR|O_NONBLOCK FD_CLOEXEC
    SOCK_STREAM
    SO_REUSEADDR,SO_SNDBUF(16384),SO_RCVBUF(5120)
    sockname: AF_UNIX /var/tmp/orbit-bhkim/linc-49c-0-48e474737f48b
    peername: AF_UNIX
  25: S_IFREG mode:0644 dev:182,65538 ino:180840 uid:0 gid:2 size:1916121
      O_RDONLY|O_LARGEFILE
      /usr/lib/thunderbird/chrome/toolkit.jar
  26: S_IFREG mode:0755 dev:182,65538 ino:180947 uid:0 gid:2 size:790881
      O_RDONLY|O_LARGEFILE
      /usr/lib/thunderbird/chrome/ko.jar
  27: S_IFREG mode:0644 dev:182,65544 ino:70397 uid:101 gid:10 size:2400716
      O_RDONLY|O_LARGEFILE
      /export/home/bhkim/.thunderbird/5x1njpem.default/XUL.mfasl
  28: S_IFREG mode:0400 dev:182,65544 ino:18228 uid:101 gid:10 size:74694
      O_RDONLY|O_LARGEFILE
      /export/home/bhkim/.thunderbird/5x1njpem.default/extensions/{C0CB8BA3-6C1B-47e8-A6AB-1FAB889562D9}/chrome/quicknote.jar
  29: S_IFREG mode:0644 dev:182,65538 ino:180837 uid:0 gid:2 size:2712048
      O_RDONLY|O_LARGEFILE
      /usr/lib/thunderbird/chrome/messenger.jar
  30: S_IFREG mode:0644 dev:182,65544 ino:18131 uid:101 gid:10 size:32825
      O_RDWR
      /export/home/bhkim/.thunderbird/5x1njpem.default/panacea.dat
  31: S_IFIFO mode:0000 dev:299,0 ino:681 uid:101 gid:10 size:0
      O_RDWR|O_NONBLOCK
  32: S_IFREG mode:0644 dev:182,65544 ino:18143 uid:101 gid:10 size:1617
      O_RDWR
      /export/home/bhkim/.thunderbird/5x1njpem.default/ImapMail/mail-apac.sun.com/%EB%B0%9B%EC%9D%80%20%ED%8E%B8%EC%A7%80%ED%95%A7926b93e.msf
  33: S_IFREG mode:0644 dev:182,65544 ino:29639 uid:101 gid:10 size:1508
      O_RDWR|O_CREAT|O_TRUNC
      /export/home/bhkim/.thunderbird/5x1njpem.default/ImapMail/mail-apac.sun.com/%EB%B0%9B%EC%9D%80%20%ED%8E%B8%EC%A7%80%ED%95%A36b3cbd8.msf
  34: S_IFREG mode:0644 dev:182,65544 ino:38481 uid:101 gid:10 size:1965
      O_RDWR
      /export/home/bhkim/.thunderbird/5x1njpem.default/ImapMail/mail-apac.sun.com/%EB%B0%9B%EC%9D%80%20%ED%8E%B8%EC%A7%80%ED%95%A9ecefa66.msf
  35: S_IFIFO mode:0000 dev:299,0 ino:681 uid:101 gid:10 size:0
      O_RDWR|O_NONBLOCK
  36: S_IFREG mode:0600 dev:182,65544 ino:18120 uid:101 gid:10 size:98304
      O_RDWR FD_CLOEXEC
      /export/home/bhkim/.thunderbird/5x1njpem.default/cert8.db
  37: S_IFREG mode:0600 dev:182,65544 ino:18129 uid:101 gid:10 size:32768
      O_RDWR FD_CLOEXEC
      /export/home/bhkim/.thunderbird/5x1njpem.default/key3.db
  38: S_IFREG mode:0644 dev:182,65544 ino:18186 uid:101 gid:10 size:3554267
      O_RDWR
      /export/home/bhkim/.thunderbird/5x1njpem.default/ImapMail/mail-apac.sun.com/INBOX.msf
  39: S_IFSOCK mode:0666 dev:310,0 ino:25977 uid:0 gid:0 size:0
      O_RDWR|O_NONBLOCK
    SOCK_STREAM
    SO_SNDBUF(49152),SO_RCVBUF(49640)
    sockname: AF_INET 129.158.2.81  port: 36053
    peername: AF_INET 192.18.19.180  port: 993
  41: S_IFREG mode:0644 dev:182,65538 ino:180836 uid:0 gid:2 size:1575881
      O_RDONLY|O_LARGEFILE
      /usr/lib/thunderbird/chrome/comm.jar
  42: S_IFREG mode:0644 dev:182,65544 ino:117215 uid:101 gid:10 size:169599
      O_RDWR
      /export/home/bhkim/.thunderbird/5x1njpem.default/ImapMail/mail-apac.sun.com/Trash.msf
  43: S_IFREG mode:0644 dev:182,65544 ino:18126 uid:101 gid:10 size:0
      O_RDWR|O_CREAT|O_LARGEFILE
      /export/home/bhkim/.thunderbird/5x1njpem.default/urlclassifier2.sqlite
  44: S_IFREG mode:0644 dev:182,65544 ino:18126 uid:101 gid:10 size:0
      O_RDWR|O_CREAT|O_LARGEFILE
      /export/home/bhkim/.thunderbird/5x1njpem.default/urlclassifier2.sqlite
  45: S_IFSOCK mode:0666 dev:310,0 ino:61169 uid:0 gid:0 size:0
      O_RDWR|O_NONBLOCK FD_CLOEXEC
    SOCK_STREAM
    SO_SNDBUF(16384),SO_RCVBUF(5120)
    sockname: AF_UNIX
    peername: AF_UNIX /tmp/dbus-6tM2grR7lu
  46: S_IFREG mode:0644 dev:182,65544 ino:64775 uid:101 gid:10 size:375842
      O_RDWR
      /export/home/bhkim/.thunderbird/5x1njpem.default/ImapMail/imap.gmail.com/INBOX.msf
  47: S_IFSOCK mode:0666 dev:310,0 ino:25978 uid:0 gid:0 size:0
      O_RDWR|O_NONBLOCK
    SOCK_STREAM
    SO_SNDBUF(49152),SO_RCVBUF(49680)
    sockname: AF_INET 129.158.2.81  port: 54161
    peername: AF_INET 209.85.135.109  port: 993
  48: S_IFREG mode:0644 dev:182,65544 ino:65840 uid:101 gid:10 size:65031
      O_RDWR
      /export/home/bhkim/.thunderbird/5x1njpem.default/ImapMail/imap.gmail.com/Trash.msf
  49: S_IFSOCK mode:0666 dev:310,0 ino:13077 uid:0 gid:0 size:0
      O_RDWR
    SOCK_STREAM
    SO_SNDBUF(16384),SO_RCVBUF(5120)
    sockname: AF_UNIX
    peername: AF_UNIX /tmp/fam-bhkim/fam-
  50: S_IFREG mode:0644 dev:182,65544 ino:18130 uid:101 gid:10 size:238135
      O_RDWR
      /export/home/bhkim/.thunderbird/5x1njpem.default/history.mab
  51: S_IFREG mode:0644 dev:182,65544 ino:18176 uid:101 gid:10 size:813104
      O_RDWR
      /export/home/bhkim/.thunderbird/5x1njpem.default/ImapMail/mail-apac.sun.com/Sent-1.msf


많은 화일들을 오픈하는 군요.

일단 이정보를 확보한 상태에서, 다음과 같이  dtrace를 실행볼 수 있겠습니다.
#dtrace -q -n 'syscall::write:entry/pid==1180/{self->trace=timestamp;fd=arg0;}' -n 'syscall::write:return/self->trace/{delta=timestamp-self->trace;@[fd]=avg(delta/1000);printf("transfer size : %d[%d], response time:%d us\n",arg0,fd,delta/1000);self->trace=0;}'

예쁘게 스크립트용으로 정리를 해본다면,

#/usr/bin/dtrace

#option D quiet
syscall::write:entry
/pid==1180/
{
    self->trace=timestamp;
    fd=arg0;
}

syscall::write:return
/self->trace/
{
   delta=timestamp-self->trace;
   printf("transfer
size : %d[%d], response time:%d
us\n",arg0,fd,delta/1000);
   @[fd]=avg(delta/1000);
   self->trace=0;
}

뭐 이쯤 되지 않겠나 싶습니다.  출력을 위해서 세련되게 포맷팅은 하지 않았습니다.
아무런 작동을 하지 않고 있을때  출력은 대개 이렇군요.

transfer size : 1[9], response time:8 us
transfer size : 1[11], response time:6 us
transfer size : 1[9], response time:8 us
transfer size : 1[9], response time:8 us
transfer size : 44[4], response time:8 us
transfer size : 1[9], response time:8 us
transfer size : 1[9], response time:8 us
transfer size : 1[9], response time:8 us
^C
transfer size : 1[9], response time:7 us

               11                6
                9                7
                4                8

printf에서 첫번째 아규먼트는 write()한 크기이고, 두번째는 대상 fd 번호이고, 세번째는 write() 호출에 대한 turnaround time입니다. 따라서, 위 출력의 앞부분에서

transfer size : 44[4], response time:8 us
은 4번 fd에서 44바이트 쓰기를 했는데, 8 microsecond 걸렸다는 얘기로 보면 되겠죠.

위 출력의 최종부분은 각 fd에 대한 평균 turnaround time을 출력하는 부분입니다.
11번 fd에서 평균 6us, 9번 fd에서 7us, 4번 fd에서 8us 정도가 걸렸군요.

이 글 맨앞에 언급한 pfiles를 이용하여 각 fd가 어떤 화일인가를 측정해본다면 매우 가시적인 결과를 얻을 수 있습니다.
fd[4]   AF_UNIX /tmp/.X11-unix/X0 ; 음. X11도에 화면 출력을 위한 fd이군요.
fd[9]
S_IFIFO mode:0000 dev:299,0 ino:672 uid:101 gid:10 size:0 ; IFIFO는 내부 통신용이겠죠.
fd[11]
S_IFIFO mode:0000 dev:299,0 ino:673 uid:101 gid:10 size:0 ; 역시 내부 통신용이네요.

첨부 화일을 하나 다운받고 있는 와중에 dtrace를 떠봤습니다.
...
transfer size : 1[35], response time:2 us
transfer size : 1[35], response time:2 us
transfer size : 4096[54], response time:27 us
transfer size : 73[54], response time:31 us
transfer size : 1[9], response time:4 us
transfer size : 1[9], response time:4 us
transfer size : 1[9], response time:3 us
transfer size : 1[9], response time:3 us
transfer size : 1[9], response time:2 us
transfer size : 1[9], response time:3 us
transfer size : 1[11], response time:4 us
transfer size : 1[9], response time:2 us
transfer size : 4096[53], response time:48 us
transfer size : 4096[53], response time:36 us
transfer size : 4096[53], response time:35 us
transfer size : 4096[53], response time:38 us
transfer size : 4096[53], response time:37 us
transfer size : 4096[53], response time:39 us
transfer size : 1536[53], response time:40 us
transfer size : 1[9], response time:3 us
transfer size : 1[9], response time:2 us
transfer size : 1[9], response time:2 us
...
               11                2
               35                2
                9                3
                6                4
                4                5
               49               11
               39               21
               54               25
               53               28
위와 같은 정보가 나오네요. 53번 54번 fd가 나왔는데, 위에 화일에는 없는 fd이므로 첨부화일을 다운받으면서 만들어진 새 fd라고 보여집니다. 이럴때는 open을 새롭게 감시해서 어떤 화일이 새로 오픈 되었는지도 알 수 있겠죠.

위의 결과에서 각 fd가 write한 총합과 평균 응답시간을 합해서 출력을 하려면 다음과 같이 할 수 있습니다.
dtrace를 살짝 바꿔봅니다.

#dtrace -q -n 'syscall::write:entry/pid==1180/{self->trace=timestamp;fd=arg0;}' -n 'syscall::write:return/self->trace/{delta=timestamp-self->trace;@w[fd]=sum(arg0);@l[fd]=avg(delta/1000);printf("transfer size : %d[%d], response time:%d us\n",arg0,fd,delta/1000);self->trace=0;}' -n 'END{printa(@w,@l);}'

...(생략)
transfer size : 4096[53], response time:39 us
transfer size : 4096[53], response time:11 us
transfer size : 4096[53], response time:10 us
transfer size : 4096[53], response time:10 us
transfer size : 4096[53], response time:10 us
transfer size : 4096[53], response time:10 us
transfer size : 4096[53], response time:10 us
transfer size : 4096[53], response time:10 us
transfer size : 2079[53], response time:9 us
transfer size : 120[4], response time:11 us
transfer size : 8[4], response time:3 us
...(중략)


                6                2                4
               30               40            72036
               49               46               11
               38               73            73137
               35              346                2
                9              406                3
               11              533                2
               39              859               17
               54            14925               24
               53           461993               23
                4          1199979                5

4번 fd에서 가장 많은 쓰기를 하네요. 화면에 출력하기 바쁘단 얘기죠. 그렇지만, 매 쓰기당 평균 응답시간은 5us입니다. 그다지 느리지 않네요. 반면, 그위에 새로이 나타난 53,54번 fd는 쓰기의 양은 덜하지만 평균 쓰기 응답시간이 23us,24us입니다. 첨부 화일의 fd인데, 역시 disk(화일 시스템)이 느리다는 것을 의미합니다. 53번 화일은 총 460K 바이트정도 썼으며, 매 쓰기당 23us 걸렸네요. 갑자기 전부 몇번 write를 했는지 쓰기하는 총 걸린 시간이 얼마인지를 알 수 있으면 더 좋겠다는 생각이 드는 군요.

dtrace를 조금더 바꿔봅니다. 그리고 이번에는 '지운편지함'을 액세스 해봤습니다.

#dtrace -q -n 'syscall::write:entry/pid==1180/{self->trace=timestamp;fd=arg0;}' -n 'syscall::write:return/self->trace/{delta=timestamp-self->trace;@c[fd]=count();@w[fd]=sum(arg0);@t[fd]=sum(delta/1000); @l[fd]=avg(delta/1000);printf("transfer size : %d[%d], response time:%d us\n",arg0,fd,delta/1000);self->trace=0;}' -n 'END{printa(@c,@w,@t,@l);}'

transfer size : 216[4], response time:5 us
transfer size : 212[4], response time:4 us
transfer size : 112[4], response time:6 us
transfer size : 172[4], response time:6 us
transfer size : 6420[4], response time:11 us
transfer size : 1[9], response time:7 us
^C
               fd       total # of writes total size of write total elapsed  average tt
               30                5              813               78               15
               42                5            29654            52081            10416
               53               12              869              198               16
               35               77             5946              222                2
               11              141              187              334                2
                9              313              313             1022                3
                4             3025          1133659            15468                5

fd 42번의 값이 눈에 확띄는군요. 총 쓰기 시간이 52ms이고, 총 write 호출 회수는 5번, 각 호출당 응답시간은 10ms이네요.
42번이 뭘까요 ?
  42: /export/home/bhkim/.thunderbird/5x1njpem.default/ImapMail/mail-apac.sun.com/Trash.msf

아하, 지운 편지함의 메일박스 화일이군요.
결론적으로 본다면, 이 메일박스를 좀 더 빠른 디렉토리에 두면 시간은 아주 빨라지겠군요.

오픈소스 라이센스 관련한 판결문입니다.

최근 미국에서 있었던 오픈 소스 라이센스 관련 판결문입니다.

요약 : 오픈소스는 자유 다운, 편집, 재배포가 가능하나, 적용 및 재배포시 반드시 사용한 오픈소스의 원천을 언급해야 한다는 오픈소스 라이센스를 지키지 않는 경우, 오픈 소스 라이센스에 위배될 수 있다라는 판결입니다.

따라서, 오픈소스를 이용해서 모듈을 만드는 경우에는 누구의 어떤 오픈소스를 사용했는 지에 대한 언급(라이센스 화일)을 반드시 하란 얘기겠죠.

번역입니다.
오픈소스 소프트웨어 사용에도 지켜야 할 조건은 있다.

오픈소스 소프트웨어의 특징은 라이센스 사용자(Licensee)들이 오픈소스 소프트웨어의 소스 코드를 무상으로 제공받을 수 있고, 이를 자유롭게 검사, 편집, 개선, 배포할 수 있다는 점이다. 오픈소스 소프트웨어의 이러한 개방적인 성격으로 인하여 많은 사람들이 오픈소스 소프트웨어의 사용에는 어떠한 조건이나 의무도 지켜야 할 필요가 없다는 잘못된 믿음을 갖게 된 것 같다.  오픈소스 소프트웨어에 관한 (국내 Dutch) 판례법상 입장은 아직 명쾌하게 정의되지 않고 있었지만, 최근에 미국의 한 법원에서 흥미로운 판결이 내려졌다. 즉, 2008년 8월 13일, 미국 연방 순회법원 항소심에서 오픈소스 소프트웨어를 다룬 Jacobsen v. Katzer 사건(No. 2008-1001)과 관련하여 법원은 오픈소스 라이센스 계약서의 조건을 위반하는 행위를 하는 경우 저작권 침해로 인정될 수 있다는 취지의 판결을 내린 것이다.

1심 재판에서의 사건 개요:
1심 재판에서 원고인 Robert Jacobsen는 소송절차를 밟아, 피고인 Matthew Katzer(개인)와 Kamind Associates Inc.(회사)를 상대로 권리보호를 위한 가처분 결정을 청구하는 소송을 제기하였다. 이 소송은 원고가 오픈소스 라이센스의 변형인 Artistic License의 조건에 따라 출시한 모형 기관차 컨트롤 용 소프트웨어 패키지인 Java Model Railroad Interface (JMRI)라는 오픈소스 소프트웨어를 피고들이 침해하였다는 원고측의 주장에서 비롯되었다. Artistic License 계약 조건에 의하면 일반 대중들은 누구든지 이 프로그래밍 코드를 별도의 라이센스 비용을 지급하지 않고 무상으로 사용할 수 있다고 규정되어 있다. 피고들은 모형 기차 및 기타 취미용 응용장치들을 위한 상업용 소프트웨어를 설계하여 문제의 JMRI 소스 코드의 일부를 자신들의 상업용 소프트웨어 개발시 사용하고 포함시켰으면서 그 소스 코드는 제외하였고, 소스 코드의 원천도 밝히지 않았다.  이에 따라 원고인 Jacobsen은 피고들이 오픈소스 라이센스의 계약조건을 위반하였다고 주장하면서 소송을 제기하게 된 것이다.

1심 재판 과정에서, 원고의 상기 주장은 받아들여지지 않았고, 법원은 Artistic License 계약조건에 포함된 의무만이 계약상의 규정으로 간주될 수 있으며, 피고들의 의무위반 행위는 오로지 계약상 의무의 불이행을 구성할 뿐이라는 취지의 판결을 내렸었다. 1심 법원의 주장에 의하면, 오픈소스 라이센스 계약 조건은 그 내용이 너무 광범위하고  애매모호하여서 이를 위반하는 것이 계약상의 의무 위반에 추가하여 저작권까지 위반한다고 보기 어렵다는 것이다. 이에 원고인 Jacobsen측은 이 판결에 대한 불복하여 연방 순회법원 항소심에 항소하게 이르렀다.

연방 항소심 절차:
1심 법원에서와는 달리 연방 순회법원 항소심에서는 원고측의 주장을 인정하여 피고들이 원고의 오픈소스 소프트웨어를 위에 기재한 바와 같이 사용한 것은 계약 위반에 해당할 뿐만 아니라 저작권 침해로도 인정된다는 취지의 판결을 내렸다. 연방 순회법원 항소심은 원고인 Jacobsen측이 사용하고 있는 라이센스 조건의 내용에 의하면 그 프로그래밍 코드를 사용하는 사용자들이 지켜야 할 제반 조건들을 포함하고 판단함으로써 이러한 판결을 뒷받침하고 있다.  이는 곧 원고의 라이센스 조건은 계약상의 합의를 구성할 뿐만 아니라 Artistic License 조건은 완벽한 저작권 라이센스를 구성하며, 이러한 라이센스 조건을 위반하는 것은 이와 관련된 저작권을 침해한 것임을 내포하고 있다는 것과 다름없다. 해당 소스코드를 포함하지 않고 그 소스코드의 원천을 밝히지 않은 채 오픈소스 소프트웨어를 배포함으로써, 피고들은 원고인 Jacobsen의  저작권까지 침해한 것이라는 판결이 내려진 것이다.

결론
연방순회법원 항소심에서의 이번 판결은 해당 소스코드를 포함하지 않고 그 소스코드의 원천을 밝히지 않은 채 오픈소스 소프트웨어를 배포하는 것은 불법적이라는 판정을 내린 것이다. 이 점에서 이번 판결은 오픈소스 라이센스에 대한 법률적인 위치에 관한 중요한 확인이라는 점에서 그 의미가 크다고 하겠다. 이와 같이 라이센스 조건을 위반하게 되면 계약 조건을 위반하게 됨은 물론 경우에 따라 그 저변에 존재하고 있는 저작권의 침해로도 간주될 수 있다는 점에서 비넌 판결은 오픈소스 소프트웨어에 관한 저작권 보호의 중요한 획을 구성할 판결이라고 볼 수 있을 것이다. 결국 저작권 법에 따라, 저작권을 위반하는 경우에 저작권자는 침해자로부터 손해배상을 받을 수 있음을 물론이고, 해당 소프트웨어의 무단 배포를 금지하는 법원의 결정을 받아 낼 수 있게 된 것이다. 이는 오픈소스 소프트웨어에 대한 저작권의 효율적인 보호에 커다란 기여를 하게 될 것임에 틀림없다. 따라서, 이번 판결은 모든 오픈소스 커뮤니티 전체에게는 매우 중요하고도 고무적인 일이 아닐 수 없다.


원문
The Use of Open Source Software is Bound by Conditions Too

Characteristics of open source software are that the source code is freely available and that the licensee may inspect, use, improve, supplement and distribute the source code. Exactly the open character of open source software results in the belief that the use of open source software is not bound by any rules or conditions at all. Although the status of open source software has not been crystallized in (Dutch) case law yet, recently an interesting judgment was rendered in the United States. The Court of Appeals for the Federal Circuit ruled on 13 August 2008 in the /Jacobsen v. Katzer/ case, No. 2008-1001, that acting in violation of an open source license agreement may be considered to be a copyright infringement.

Proceedings in the First Instance:
In the first instance, the claimant Robert Jacobsen instituted preliminary relief proceedings against the defendants Matthew Katzer and Kamind Associates, Inc. The basis for this claim was an alleged infringement of Jacobsen's copyrights to the Java Model Railroad Interface (JMRI), a software package for model trains controllers that is made available by Jacobsen under the 'Artistic License', an open source license variant. Under the conditions of the Artistic License, the public may use the programming code without paying a license fee. The defendants design commercial software for model trains and other hobby applications, and have incorporated parts of the JMRI into their commercial software without including the source code and a reference to the source. Jacobsen has argued that the defendants have thus acted in violation with the applicable open source license conditions.

In the first instance, Jacobsen's claim was denied and the Court ruled that the obligations as included in the Artistic License were only to be considered contractual provisions, and that their violation would therefore only constitute a breach of contract. In the Court's opinion, the open source license was described too broadly and too vaguely to be able to constitute a copyright infringement in addition to the breach of contract. Jacobsen has appealed against this judgment to the Court of Appeals for the Federal Circuit.

The Appeal Proceedings:
As opposed to the Court, the Court of Appeals for the Federal Circuit has ruled that the use of the defendants does qualify as an infringement of the copyrights of Jacobsen in addition to the breach of contract. The Court of Appeals for the Federal Circuit has substantiated this judgment by ruling that the text of the licenses, as used by Jacobsen, does actually impose conditions of use on the users of the programming code. This implies that there were not only contractual agreements, but that the Artistic License constitutes a full copyright license, and that violation of the license conditions implies an infringement of the copyrights involved. By distributing the open source software without including the source code and without a reference to the source, the defendants have infringed Jacobsen's copyrights.

Conclusion:
The Court of Appeals for the Federal Circuit has ruled that the distribution of open source software without including the source code and without a reference to the source is unlawful. Thus this judgment constitutes an important confirmation of the legal status of open source licenses. By ruling that a violation of the license conditions entails that in addition to a breach of contract there is also an infringement of the underlying copyright, this judgment constitutes an important hold for the protection of copyrights to open source software. After all, under copyright law, if there is infringement, it is not only possible to obtain damages from the infringing party, but also to obtain a ban on dissemination. It is exactly this extra protection that can contribute to an effective protection of copyrights to open source software. Thus this judgment constitutes an important boost for the entire open source community.


하둡 분산 화일 시스템 개요

멀티스레드 머신에서의 자바 애플리케이션 튜닝

CoolThreads - CMT Tuning and Resources - Applications: Java Servers
CMT라고 불리우는 하드웨어 기반의 멀티스레드 서버에서의 자바 튜닝 방법을 다룬 링크입니다.
주요 제품별 보편적인 튜닝 기법을 제공합니다. apache/tomcat의 경우에는  IBM의 websphere와 가장 비슷하지
않나 생각이 됩니다. WebSphere를 위해서 제공하는 TCP option은 다른 경우에도 공통 적용해도 상당한 효과가 있습니다.

2008/10/13

아파치에서 톰캣을 부하 분산되도록 구성하는 방법

Apache 2.x + Tomcat 4.x + Load Balancing (or Private JVMs)

아파치에서 톰캣을 사용하는 경우 복수개의 톰캣 엔진을 이용하는 경우 부하 분산을 하는 방식을 다룬 방법입니다.
하나의 톰캣 엔진이 멀티스레드를 지원하기 때문에, 굳이 복수개의 엔진을 사용해야 하는 이유가 있을까 하고 생각이 들 수도 있습니다만,  단일 페이지에 대해서 다량의 웹 히트가 발생하게 되면, 동일 엔진으로 하여금 처리를 하게되는 현상이 발생합니다.

톰캣에서 이러한 경우 캐쉬를 이용해서 속도를 가속화하는 기능이 있는 것으로 알고 있습니다만, 만약 캐쉬를 하지 않는다면, 톰캣엔진은 동일 페이지를 여러개의 스레드가 실행되어야 하므로, 메모리 병목을 유도하게 됩니다. 특히, 썬마이크로시스템즈의 CMT와 같은 하드웨어 기반의 멀티스레드 장비(멀키코어 포함)에서는 효율이 급격히 떨어질 수 있게 됩니다.

따라서, 어쩔 수 없이 캐쉬를 사용하지 않는 경우이면서 복수 코어를 가진 프로세서(특히, CMT)를 사용해야 하는 경우라면 톰캣 엔진을 복수개로 올리는 것이 바람직한 접근입니다. 엔진이 분산됨으로써 메모리 병목을 원천차단할 수 있을테니까요.

특히, 위와 같은 apache + tomcat stack을 솔라리스용으로 사용하는 경우에는 사용하고자 하는 바이너리가 설치 플랫폼용으로 최적화된 컴파일이 되어 있는 지 확인하는 것이 매우 중요합니다. CMT용 바이너리는 여기에서 찾을 수 있습니다.

http://cooltools.sunsource.net/coolstack/

2008/09/20

오픈 솔라리스에서 존 설치 ( zone install on opensolaris )

오픈 솔라리스에서 컨테이너(존)을 설치해보았습니다.
컨테이너를 설치하려면 일단 컨테이너 구성을 해야 합니다.
#zonecfg -z web
을 이용해서 다음과 같이 구성했습니다.

bhkim@vaio-bhkim:/zone# zonecfg -z web export
create -b
set zonepath=/zone/web
set brand=ipkg
set autoboot=false
set ip-type=shared
add net
set address=10.10.0.1/24
set physical=yukonx0
end
여기서 흥미로운 것은 brand의 변수가 'native'가 아니라 'ipkg'이라는 점입니다. ipkg는 오픈 솔라리스가 사용하는 인스톨러 방식인 IPS를 위한 패키지를 의미하는데, 이점은 컨테이너 설치시에 필요한 이미지를 인터넷에서 다운로드 받겠다는 것을 의미합니다.

그리고, zonepath=/zone/web 인데, 사전에 만들어져 있다면, 반드시 ACL mode를 700으로 설정해야 합니다. 디렉토리가 없다면, 존 구성시 700을 가진 /zone/web 디렉토리를 자동적으로 구성합니다.

혹시나 해서 brand를 native로 바꾸어 보았습니다.
그리고, 아래 처럼 확인하고
bhkim@vaio-bhkim:~# zoneadm list -cv

ID NAME STATUS PATH BRAND IP
0 global running / native shared
- web configured /zone/web native shared

설치 시도해봅니다.
bhkim@vaio-bhkim:~# zoneadm -z web install

sh[1]: exec: /usr/lib/lu/lucreatezone: not found
안되는 군요. native는 lu(live upgrade)기술을 활용해서 글로벌존에 있는 패키지를 이미지화 해서 컨테이너가 있는 디렉토리로 옮겨주는데 아예 툴이 없습니다.

bhkim@vaio-bhkim:~# zoneadm list -cv
ID NAME STATUS PATH BRAND IP
0 global running / native shared
- web incomplete /zone/web native shared
설치시도에서 오류가 밸생해서 존의 상태가 incomplete로 남게됩니다. 따라서, 새로 설치를 다시 하려면 configured 모드로 돌아가야 하는데, 그렇게 만들기 위해서 uninstall을 실행합니다.

bhkim@vaio-bhkim:~# zoneadm -z web uninstall

Are you sure you want to uninstall zone web (y/[n])? y

성공적으로 uninstall 되었으면 다시 설치해봅니다.

bhkim@vaio-bhkim:~# zoneadm -z web install


Image: Preparing at /zone/web/root ... done.
Catalog: Retrieving from http://pkg.opensolaris.org:80/ ... done.
Installing: (output follows)
DOWNLOAD PKGS FILES XFER (MB)
SUNWcsl 22/52 4681/7830 79.66/209.30


설치가 진행되고 있군요. 인터넷이 연결되어 있지 않으면 위 URL에 접속할 수 없으므로, 설치가 되지 않습니다. 좋은 점도 있긴 합니다만 인터넷이 없으면 설치하기가 어려우므로 다소 불편할 수도 있겠습니다.

다음은 설치가 완료된 상태입니다.
bhkim@vaio-bhkim:~# zoneadm -z web install

Image: Preparing at /zone/web/root ... done.
Catalog: Retrieving from http://pkg.opensolaris.org:80/ ... done.
Installing: (output follows)
DOWNLOAD PKGS FILES XFER (MB)
Completed 52/52 7830/7830 209.30/209.30

PHASE ACTIONS
Install Phase 12880/12880

Note: Man pages can be obtained by installing SUNWman
Postinstall: Copying SMF seed repository ... done.
Postinstall: Working around http://defect.opensolaris.org/bz/show_bug.cgi?id=681
Postinstall: Working around http://defect.opensolaris.org/bz/show_bug.cgi?id=741
Done: Installation completed in 648.009 seconds.

Next Steps: Boot the zone, then log into the zone console
(zlogin -C) to complete the configuration process


설치가 끝났으므로, 부팅을 해봅니다. 그리고 콘솔에 로그인해봅니다.

bhkim@vaio-bhkim:~# zoneadm -z web boot
zoneadm: zone 'web': Unable to set route for interface lo0 to ��� ��x�
zoneadm: zone 'web':
bhkim@vaio-bhkim:~# zlogin -C web
[Connected to zone 'web' console]


You did not enter a selection.
What type of terminal are you using?
1) ANSI Standard CRT
2) DEC VT100
3) PC Console
4) Sun Command Tool
5) Sun Workstation
6) X Terminal Emulator (xterms)
7) Other
Type the number of your choice and press Return: 6
Creating new rsa public/private host key pair
Creating new dsa public/private host key pair
Configuring network interface addresses: yukonx0.


─ Host Name for yukonx0:1 ──────────────────────────────────────────────────────

Enter the host name which identifies this system on the network. The name
must be unique within your domain; creating a duplicate host name will cause
problems on the network after you install Solaris.

A host name must have at least one character; it can contain letters,
digits, and minus signs (-).


Host name for yukonx0:1 web











────────────────────────────────────────────────────────────────────────────────
F2_Continue F6_Help

;정상적으로 떴군요. ^_^

매우 흥미로운 것은 컨테이너를 만들기 전에 컨테이너 저장될 디렉토리를 미리 만들어놨었는데(아래)
...
rpool/zone 110K 37.3G 20K /rpool/zone
rpool/zone/mysql-db1 18K 37.3G 18K /zone/mysql-db1
rpool/zone/mysql-db2 18K 37.3G 18K /zone/mysql-db2
rpool/zone/mysql-mgm 18K 37.3G 18K /zone/mysql-mgm
rpool/zone/mysql-sql 18K 37.3G 18K /zone/mysql-sql
rpool/zone/web 18K 37.3G 18K /zone/web


설치하고 나니 /zone/web 디렉토리를 사라져 버리고 안보이는 군요. 정상인 것인지 다소 궁금합니다.
아마도 보안이유로 마운트 포인트에서 없앤것이 아닌가 싶은데, 그렇다고 zfs list에서 조차 보이지 않는 것은 좀 의아하군요.

설치이후
bhkim@vaio-bhkim:/zone# zfs list
NAME USED AVAIL REFER MOUNTPOINT
rpool 39.0G 37.3G 57K /rpool
rpool@install 17K - 55K -
rpool/ROOT 6.67G 39.6G 18K /rpool/ROOT
rpool/ROOT@install 15K - 18K -
rpool/ROOT/opensolaris 6.67G 39.6G 4.04G legacy
rpool/ROOT/opensolaris@install 503M - 2.22G -
rpool/ROOT/opensolaris@091108 102M - 3.87G -
rpool/ROOT/opensolaris@091608 19.6M - 3.80G -
rpool/ROOT/opensolaris/opt 1.96G 39.6G 1.77G /opt
rpool/ROOT/opensolaris/opt@install 112K - 3.60M -
rpool/ROOT/opensolaris/opt@090308 191M - 1.73G -
rpool/ROOT/opensolaris/opt@091608 1009K - 1.77G -
rpool/export 30.0G 37.3G 19K /export
rpool/export@install 15K - 19K -
rpool/export/home 12.2G 55.1G 11.8G /export/home
rpool/export/home@090308 404M - 10.9G -
rpool/zone 90K 37.3G 18K /rpool/zone
rpool/zone/mysql-db1 18K 37.3G 18K /zone/mysql-db1
rpool/zone/mysql-db2 18K 37.3G 18K /zone/mysql-db2
rpool/zone/mysql-mgm 18K 37.3G 18K /zone/mysql-mgm
rpool/zone/mysql-sql 18K 37.3G 18K /zone/mysql-sql

아마도 해달 화일 시스템인 /zone/web을 언마운트하고 /rpool/zone 밑의 서브 디렉토리로 생성한 것처럼 보이는 군요.
왜 그랬을까요 ? 흠...

이렇게 되도, 작동에는 별 문제 없으나, 가용성에는 매우 불편해질 수 있겠습니다. 만약 rpool/zone/web이 그대로 남아있다면, 모두 설치 이후에 해당 화일 시스템의 스냅샷을 설정하게되면, 클로닝 기능을 이용하여 빠르게 복구할 수 있을텐데요.. 그렇게 안된다는 점이 매우 안타깝네요.

새로 만들어진 존에 로그인해봅니다.
bhkim@vaio-bhkim:~# zlogin -C web
[Connected to zone 'web' console]

web console login: root
Last login: Fri Sep 19 10:32:28 on pts/4
Sep 19 10:40:01 web login: ROOT LOGIN /dev/console
Sun Microsystems Inc.   SunOS 5.11      snv_98  November 2008
-bash-3.2# pwd
/root
-bash-3.2# uname -a
SunOS web 5.11 snv_86 i86pc i386 i86pc

헉.. 무지 놀라운 사실이 보이는 군요. 새로 설치된 존에서의 배너에서는  snv_98로 나타나는 군요. 햐..
글로벌 존은 snv_86이거든요. ipkg 브랜드에 의해서 인터넷에 있는 최신 패키지들이 내려온 것으로 보입니다.
그러나 uname -a에서는 글로벌존과 같은 넘버가 나타나죠. 글로벌존의 라이브러리로 호출되기 때문인 것으로 예상됩니다. 호환성의 문제만 없다면 괜찮겠습니다만... 서브 존과 글로벌 존이 다른 라이브러리 환경을 가지는 것처럼 보일 수 있어서 고쳐져야 할 것 같습니다.

2008/09/12

오픈 솔라리스에서의 (커널 포함 전체) 이미지 업데이트

오픈 솔라리스 정식 발매 CD를 설치하게 되면 빌드가 86임을 알 수가 있습니다.
화면 상단의 메뉴에 있는 시스템->관리->패키지 매니저를 통해서 이미지 전체를 업데이트할 수 가 있는데, 업데이트를 하고 났더니, 부팅을 하지 않더군요.

그 이유는 커널은 업데이트가 되어 있는데, GRUB은 변경된 MBR의 위치를 인지하지 못해서 발생하는 일입니다.
다음은 전체 이미지 업데이트하는 법과 추후 GRUB 업데이트 하는 과정을 포함합니다.
사용하는 빌드가 96이후 이면 일단 다음과 같은 사전 작업을 준비하셔야 합니다.

$
BUILD=`uname -v | sed s/snv_//` ; build 숫자를 저장합니다.
$echo $BUILD ; 빌드 확인
$pfexec pkg refresh ; 전체 패키지 갱신한번 하고
$pfexec pkg install
SUNWipkg at 0.5.11-0.$BUILD ; 설치된 빌드의 최신 패키지 모듈을 일단 설치하고

모두 정상적으로 되었으면, 이제 전체 패키지를 업데이트합니다.
$ pfexec pkg install entire at 0.5.11-0.$BUILD ; 전체 빌드를 설치합니다.

정상적으로 이루어졌으면, 다음과 같이 해서 오픈 솔라리스 이미지를 업데이트합니다.
$pfexec pkg image-update

정상적으로 설치가 모두 되었는 지 확인해보기 위해서 다시 재부팅을 해봅니다.

만약 $BUILD가 93이전이면 다음과 같은 작업으로 진행하도록 합니다. (live upgrade 방식입니다)

$ pfexec beadm create opensolaris- ; 새로운 솔라리스 이미지가 설치될 새 부팅환경(BE)를 만들어줍니다. "opensolaris-"로 이름을 주게 되면, 자동적으로 그 뒤에 정수가 붙어서 이름이 지어집니다. opensoalris-1, opensolaris-2와 같이 만들어집니다.
$ pfexec beadm list ; 만들어진 부트환경(be)을 나열합니다. 가장 최근에 만들어진 부트환경을 확인합니다.
$ pfexec beadm mount opensolaris-1 /mnt ; 가장 최근에 만들어진 새부트환경을 /mnt로 마운트합니다.
$ pfexec pkg -R /mnt image-update ; 마운트한 /mnt를 대상으로 오픈 솔라리스 이미지 업데이트를 실행합니다.
$ pfexec beadm activate opensolaris- ; 다음 부팅시 새 부팅환경(BE)으로 부팅하도록 활성화를 시켜줍니다.

만약, 빌드넘버가 86이면 여기에서 GRUB을 수동적으로 업데이트해주어야 합니다. 86보다 크고 93이전이면 그냥 재부팅을 해서 정상적으로 새부팅환경으로 부팅하는 지 확인하면 됩니다.

다음은 빌드가 86인 경우 재부팅하기 전에 GRUB을 업데이트하는 법입니다.
$ pfexec /mnt/boot/solaris/bin/update_grub -R /mnt

재부팅을 해보아서, 정상적으로 재부팅되는 지 확인합니다.

Reference : http://mail.opensolaris.org/pipermail/opensolaris-help/2008-August/011864.html

2008/09/10

오픈솔라리스의 패키지 시스템 사용

오픈솔라리스에서는 IPS라는 패키징 방식을 사용합니다.

이 패키징 방식은 기존의 썬 솔라리스가 사용하는 SYSV 방식의 패키징 방식에서 데비안 리눅스가 사용하는 방식으로 변경된 것이 주요 특징이라고 할 수 있습니다. 데비안 리눅스의 창시자인 이안 머독이 썬에와서 처음으로 만들어낸 아웃풋이라고 할 수 있겠죠.

IPS 패키징은 늘상 발생하는 업데이트를 지속적이고 꾸준히 선택적으로 업데이트해야 하는 오픈소스 세계에서의 요구사항을 반영한 것이라고 볼 수 있습니다. 일면에서는 윈도우즈의 패치 업데이트와도 다소 비슷한 면이 있습니다만, 윈도으즈는 새로운 기능을 담은 애플리케이션에 대해서는 서비스팩으로 별도로 제공하고, 실시간 업데이트는 패치만을 취급한다는 것이 솔라리스의 IPS 패키징과 다소 다르다고 할 수 있습니다.

솔라리스의 패키징은 모든 모듈에 대한 늘 새로운 버젼이 제공되어지는데, 이러한 새로운 버젼은 패치와 기능을 포함하고 있습니다. 따라서, 패치를 위해서도 IPS를 사용하고, 버젼업을 위해서도 IPS를 사용합니다.

최초 오픈 솔라리스를 설치하게 되면 필요한 모든 패키지가 설치되어 있지는 않습니다. 특히, 2008년 5월 opensolaris 버젼에는 한글 입력기에 버그가 있는데, opensolaris가 설치되면 일단 iiim 패키지를 먼저 업데이트(설치)할 필요가 있습니다.
그 다음에는 openoffice 정도가 필요할 것이구요.

개발을 위해서라면  gcc관련 개발 패키지들을 설치해야 할 것입니다. 개발을 위해서는 한가디 더 신경써야 할 것이 있는데, 오픈 솔라리스에서는 개발툴만 다운/설치한다고해서 개발할 수 있지는 않습니다. 오픈솔라리스는 데스크탑 수준의 사용자 수준환경만을 위해서 최소한의 패키지만을 가지고 있기 때문에, 개발을 위한 헤더화일들을 가지고 있지 않습니다. 따라서, 이것 저것 포팅을 하기 위해서는 다소 이헤더 저헤더가 없다고 나오는 일을 만나게 됩니다.

설치 이후 화면 상단의 상태줄의 메뉴에서 GUI로 되어 있는 패키지 관리자를 불러 올 수 있긴 합니다만, 이 패키지 관리자는 아직 명령어 툴인 pkg의 모든 기능을 제대로 반영하고 있지 않습니다. 따라서, 당분간(2008년 9월 현재)은 명령어 툴인 pkg를 사용하는 것이 바람직할 것으로 보입니다.

예를 들어서 컴파일시 Xlib.h가 없는 것으로 나타났다면 다음과 같이 화일을 찾습니다.
#pkg search -r Xlib.h
INDEX      ACTION    VALUE                     PACKAGE
basename   file      opt/csw/lib/perl/csw/Tk/pTk/Xlib.h pkg:/IPSpmtk@0.5.11-2.6
basename   file      opt/csw/include/IV-X11/Xlib.h pkg:/IPSivtools@0.5.11-2.6
basename   file      usr/X11/include/X11/Xlib.h pkg:/FSWxorg-headers@0.5.11-0.79
basename   file      usr/X11/include/X11/Xlib.h pkg:/FSWxorg-headers@0.5.11-0.75
basename   file      usr/X11/include/X11/Xlib.h pkg:/SUNWxwinc@0.5.11-0.94
basename   file      usr/X11/include/X11/Xlib.h pkg:/SUNWxwinc@0.5.11-0.86
basename   file      usr/X11/include/X11/Xlib.h pkg:/SUNWxwinc@0.5.11-0.86
basename   file      usr/X11/include/X11/Xlib.h pkg:/SUNWxwinc@0.5.11-0.93
basename   file      usr/X11/include/X11/Xlib.h pkg:/SUNWxwinc@0.5.11-0.91
basename   file      usr/X11/include/X11/Xlib.h pkg:/SUNWxwinc@0.5.11-0.90
basename   file      usr/X11/include/X11/Xlib.h pkg:/SUNWxwinc@0.5.11-0.89
basename   file      usr/X11/include/X11/Xlib.h pkg:/SUNWxwinc@0.5.11-0.95
basename   file      usr/X11/include/X11/Xlib.h pkg:/SUNWxwinc@0.5.11-0.96

이와 같은 결과가 나오면, 우측에서 어떤 패키지에 내가 원하는 헤더가 있는 지를 알수가 있게 됩니다.
위의 결과로 보면, SUNWxwinc라는 패키지가 필요하므로 관련 패키지를 설치(업데이트)합니다.
#pkg install SUNWxwinc

관련 헤더 화일들이 모두 설치되었다면, 다시 컴파일을 하시면 됩니다.

참고로 SUNW는 썬에서 제공되는 패키지이며, 그외 IPS나 CSW등 다른 대문자로 시작하는 것은 오픈솔라리스의 기본 리포지터리인 opensolaris.org가 아닌 다른 제3의 리포지터리에서 제작되고 배포되는 패키지입니다.

2008/09/06

오픈 솔라이스 패키지 리포지터리

오픈 솔라리스를 쓰다보니, 패키지 매니저를 자주 쓰게 되는데, 아직은 리눅스 만큼 풍부한 패키지를 가지고 있지 않더군요. 특히, 썬 솔라리스에는 있었는데, 오픈 솔라리스에는 없는 제품들을 opensolaris.org repository에서 찾을 수 없을 때는 다소 당황스럽기 까지 합니다.

그래서, opensolaris.org이외의 repository를 찾아보았습니다.
아하, 예상되로 몇개 있더군요.
http://blogs.sun.com/partnertech/entry/public_available_opensolaris_repositories

특히, 썬 프리웨어 소프트웨어들과 블라스트웨어 목록이 있다는 것이 매우 인상적입니다.
빨리 받아봐야 겠네요.

2008/07/22

ufs의 disk quota 동작

커널 모듈인 ufs는 화일 시스템에 쓰기 행위가 발생할 때 disk quota를 확인하는 과정을 밟습니다. 이러한 과정은 quotaon/off와는 관계없이 실행되는데, quota on이 되어 있으면 많은 것을 실행하는 것이고, quotaoff인 경우에는 빠르게 리턴하는 것이죠.

다음은 ufs_write시 일어나는 ufs 모듈내의 흐름입니다. 더 하부 모듈인 genunix, unix와 관련된 다른 모듈은 캡쳐하지 않았습니다. dtrace로 캡쳐했으며 캡쳐한 방법은 다음과 같습니다.

#dtrace -F -n 'io:::start/uid==31523 && args[1]->dev_statname =="cmdk0"/{trace(timestamp);self->trace=1;}' \
-n 'fbt:ufs:ufs_write:entry/self->trace/{trace(timestamp);self->ufs=1;}' \
-n 'fbt:ufs::/self->trace&&self->ufs/{trace(timestamp);}' \
-n 'fbt:ufs:ufs_write:return/self->trace&&self->ufs/ {trace(timestamp);self->trace=0;self->ufs=0;exit(1);}'



결과는 아래 그림과 같다.

그림에서 보듯이, ufs_write가 발생할때 chkdq라는 것을 지나가는데, chkdq()는 아래와 같은 내용을 가진다.
ufs quota는 inode quota와 data quota를 동시에 가지는데, 아래는 ufs_write()를 통해서 data block의 write가 일어난 경우를 잡은 경우이기 때문에 data quota만 체크한다.

그런데, 중요한것은 chkdq()에서 무엇을 하느냐 인데, 아래 코드에서 볼 수 있는 바와 같이,

<br /><br /><span style="font-family: courier new;font-size:85%;" >124 int<br />125 chkdq(struct inode *ip, long change, int force, struct cred *cr,<br />126         char **uerrp, size_t *lenp)<br />127 {<br />128         struct dquot *dqp;<br />129         uint64_t ncurblocks;<br />130         struct ufsvfs *ufsvfsp = ip->i_ufsvfs;<br />131         int error = 0;<br />132         long abs_change;<br />133         char *msg1 =<br />134 "!quota_ufs: over hard disk limit (pid %d, uid %d, inum %d, fs %s)\n";<br />135         char *msg2 =<br />136 "!quota_ufs: Warning: over disk limit (pid %d, uid %d, inum %d, fs %s)\n";<br />137         char *msg3 =<br />138 "!quota_ufs: over disk and time limit (pid %d, uid %d, inum %d, fs %s)\n";<br />139         char *msg4 =<br />140 "!quota_ufs: Warning: quota overflow (pid %d, uid %d, inum %d, fs %s)\n";<br />141         char *errmsg = NULL;<br />142         time_t now;<br />143<br />144         /*<br />145          * Shadow inodes do not need to hold the vfs_dqrwlock lock.<br />146          */<br />147         ASSERT((ip->i_mode & IFMT) == IFSHAD ||<br />148             RW_LOCK_HELD(&ufsvfsp->vfs_dqrwlock));<br />149         ASSERT(RW_WRITE_HELD(&ip->i_contents));<br />150<br />151         if (change == 0)<br />152                 return (0);<br />153         dqp = ip->i_dquot;<br />154<br />155         /*<br />156          * Make sure the quota info record matches the owner.<br />157          */<br />158         ASSERT(dqp == NULL || ip->i_uid == dqp->dq_uid);<br />159<br />160 #ifdef DEBUG<br />161         /*<br />162          * Shadow inodes and extended attribute directories<br />163          * should not have quota info records.<br />164          */<br />165         if ((ip->i_mode & IFMT) == IFSHAD || (ip->i_mode & IFMT) == IFATTRDIR) {<br />166                 ASSERT(dqp == NULL);<br />167         }<br />168         /*<br />169          * Paranoia for verifying that quotas are okay.<br />170          */<br />171         else {<br />172                 struct dquot *expect_dq;<br />173                 int mismatch_ok = 0;<br />174<br />175                 /* Get current quota information */<br />176                 expect_dq = getinoquota(ip);<br />177                 /*<br />178                  * We got NULL back from getinoquota(), but there is<br />179                  * no error code return from that interface and some<br />180                  * errors are "ok" because we may be testing via error<br />181                  * injection.  If this is not the quota inode then we<br />182                  * use getdiskquota() to see if there is an error and<br />183                  * if the error is ok.<br />184                  */<br />185                 if (expect_dq == NULL && ip != ufsvfsp->vfs_qinod) {<br />186                         int error;<br />187                         struct dquot *xdqp;<br />188<br />189                         error = getdiskquota((uid_t)ip->i_uid, ufsvfsp, 0,<br />190                             &xdqp);<br />191                         switch (error) {<br />192                         /*<br />193                          * Either the error was transient or the quota<br />194                          * info record has no limits which gets optimized<br />195                          * out by getinoquota().<br />196                          */<br />197                         case 0:<br />198                                 if (xdqp->dq_fhardlimit == 0 &&<br />199                                     xdqp->dq_fsoftlimit == 0 &&<br />200                                     xdqp->dq_bhardlimit == 0 &&<br />201                                     xdqp->dq_bsoftlimit == 0) {<br />202                                         mutex_enter(&xdqp->dq_lock);<br />203                                         dqput(xdqp);<br />204                                         mutex_exit(&xdqp->dq_lock);<br />205                                 } else {<br />206                                         expect_dq = xdqp;<br />207                                 }<br />208                                 break;<br />209<br />210                         case ESRCH:     /* quotas are not enabled */<br />211                         case EINVAL:    /* error flag set on cached record */<br />212                         case EUSERS:    /* quota table is full */<br />213                         case EIO:       /* I/O error */<br />214                                 mismatch_ok = 1;<br />215                                 break;<br />216                         }<br />217                 }<br />218<br />219                 /*<br />220                  * Make sure dqp and the current quota info agree.<br />221                  * The first part of the #ifndef is the quick way to<br />222                  * do the check and should be part of the standard<br />223                  * DEBUG code. The #else part is useful if you are<br />224                  * actually chasing an inconsistency and don't want<br />225                  * to have to look at stack frames to figure which<br />226                  * variable has what value.<br />227                  */<br />228 #ifndef CHASE_QUOTA<br />229                 ASSERT(mismatch_ok || dqp == expect_dq);<br />230 #else /* CHASE_QUOTA */<br />231                 if (expect_dq == NULL) {<br />232                         /*<br />233                          * If you hit this ASSERT() you know that quota<br />234                          * subsystem does not expect quota info for this<br />235                          * inode, but the inode has it.<br />236                          */<br />237                         ASSERT(mismatch_ok || dqp == NULL);<br />238                 } else {<br />239                         /*<br />240                          * If you hit this ASSERT() you know that quota<br />241                          * subsystem expects quota info for this inode,<br />242                          * but the inode does not have it.<br />243                          */<br />244                         ASSERT(dqp);<br />245                         /*<br />246                          * If you hit this ASSERT() you know that quota<br />247                          * subsystem expects quota info for this inode<br />248                          * and the inode has quota info, but the two<br />249                          * quota info pointers are not the same.<br />250                          */<br />251                         ASSERT(dqp == expect_dq);<br />252                 }<br />253 #endif /* !CHASE_QUOTA */<br />254                 /*<br />255                  * Release for getinoquota() above or getdiskquota()<br />256                  * call when error is transient.<br />257                  */<br />258                 if (expect_dq) {<br />259                         mutex_enter(&expect_dq->dq_lock);<br />260                         dqput(expect_dq);<br />261                         mutex_exit(&expect_dq->dq_lock);<br />262                 }<br />263         }<br />264 #endif /* DEBUG */<br />265<br />266         /*<br />267          * Shadow inodes and extended attribute directories<br />268          * do not have quota info records.<br />269          */<br />270         if (dqp == NULL)<br />271                 return (0);<br />272         /*<br />273          * Quotas are not enabled on this file system so there is nothing<br />274          * more to do.<br />275          */<br />276         if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {<br />277                 return (0);<br />278         }<br />279         mutex_enter(&dqp->dq_lock);<br />280         if (change <>dq_flags |= DQ_MOD;<br />282                 abs_change = -change;   /* abs_change must be positive */<br />283                 if (dqp->dq_curblocks <>dq_curblocks = 0;<br />285                 else<br />286                         dqp->dq_curblocks += change;<br />287                 if (dqp->dq_curblocks <>dq_bsoftlimit)<br />288                         dqp->dq_btimelimit = 0;<br />289                 dqp->dq_flags &= ~DQ_BLKS;<br />290                 TRANS_QUOTA(dqp);<br />291                 mutex_exit(&dqp->dq_lock);<br />292                 return (0);<br />293         }<br />294<br />295         /*<br />296          * Adding 'change' to dq_curblocks could cause an overflow.<br />297          * So store the result in a 64-bit variable and check for<br />298          * overflow below.<br />299          */<br />300         ncurblocks = (uint64_t)dqp->dq_curblocks + change;<br />301<br />302         /*<br />303          * Allocation. Check hard and soft limits.<br />304          * Skip checks for uid 0 owned files.<br />305          * This check used to require both euid and ip->i_uid<br />306          * to be 0; but there are no quotas for uid 0 so<br />307          * it really doesn't matter who is writing to the<br />308          * root owned file.  And even root cannot write<br />309          * past a user's quota limit.<br />310          */<br />311         if (ip->i_uid == 0)<br />312                 goto out;<br />313<br />314         /*<br />315          * Disallow allocation if it would bring the current usage over<br />316          * the hard limit or if the user is over his soft limit and his time<br />317          * has run out.<br />318          */<br />319         if (dqp->dq_bhardlimit && ncurblocks >= (uint64_t)dqp->dq_bhardlimit &&<br />320             !force) {<br />321                 /* If the user was not informed yet and the caller      */<br />322                 /* is the owner of the file                             */<br />323                 if ((dqp->dq_flags & DQ_BLKS) == 0 &&<br />324                     ip->i_uid == crgetruid(cr)) {<br />325                         errmsg = msg1;<br />326                         dqp->dq_flags |= DQ_BLKS;<br />327                 }<br />328                 error = EDQUOT;<br />329                 goto out;<br />330         }<br />331         if (dqp->dq_bsoftlimit && ncurblocks >= (uint64_t)dqp->dq_bsoftlimit) {<br />332                 now = gethrestime_sec();<br />333                 if (dqp->dq_curblocks <>dq_bsoftlimit ||<br />334                     dqp->dq_btimelimit == 0) {<br />335                         dqp->dq_flags |= DQ_MOD;<br />336                         dqp->dq_btimelimit = now +<br />337                             ((struct ufsvfs *)ITOV(ip)->v_vfsp->vfs_data)<br />338                             ->vfs_btimelimit;<br />339                         if (ip->i_uid == crgetruid(cr)) {<br />340                                 errmsg = msg2;<br />341                         }<br />342                 } else if (now > dqp->dq_btimelimit && !force) {<br />343                         /* If the user was not informed yet and the     */<br />344                         /* caller is the owner of the file              */<br />345                         if ((dqp->dq_flags & DQ_BLKS) == 0 &&<br />346                             ip->i_uid == crgetruid(cr)) {<br />347                                 errmsg = msg3;<br />348                                 dqp->dq_flags |= DQ_BLKS;<br />349                         }<br />350                         error = EDQUOT;<br />351                 }<br />352         }<br />353 out:<br />354         if (error == 0) {<br />355                 dqp->dq_flags |= DQ_MOD;<br />356                 /*<br />357                  * ncurblocks can be bigger than the maximum<br />358                  * number that can be represented in 32-bits.<br />359                  * When copying ncurblocks to dq_curblocks<br />360                  * (an unsigned 32-bit quantity), make sure there<br />361                  * is no overflow.  The only way this can happen<br />362                  * is if "force" is set.  Otherwise, this allocation<br />363                  * would have exceeded the hard limit check above<br />364                  * (since the hard limit is a 32-bit quantity).<br />365                  */<br />366                 if (ncurblocks > 0xffffffffLL) {<br />367                         dqp->dq_curblocks = 0xffffffff;<br />368                         errmsg = msg4;<br />369                 } else {<br />370                         dqp->dq_curblocks = ncurblocks;<br />371                 }<br />372         }<br />373<br />374         if (dqp->dq_flags & DQ_MOD)<br />375                 TRANS_QUOTA(dqp);<br />376<br />377         mutex_exit(&dqp->dq_lock);<br />378         /*<br />379          * Check for any error messages to be sent<br />380          */<br />381         if (errmsg != NULL) {<br />382                 /*<br />383                  * Send message to the error log.<br />384                  */<br />385                 if (uerrp != NULL) {<br />386                         /*<br />387                          * Set up message caller should send to user;<br />388                          * gets copied to the message buffer as a side-<br />389                          * effect of the caller's uprintf().<br />390                          */<br />391                         *lenp = strlen(errmsg) + 20 + 20 +<br />392                             strlen(ip->i_fs->fs_fsmnt) + 1;<br />393                         *uerrp = (char *)kmem_alloc(*lenp, KM_NOSLEEP);<br />394                         if (*uerrp != NULL) {<br />395                                 /* errmsg+1 => skip leading ! */<br />396                                 (void) sprintf(*uerrp, errmsg+1,<br />397                                     (int)ttoproc(curthread)->p_pid,<br />398                                     (int)ip->i_uid, (int)ip->i_number,<br />399                                     ip->i_fs->fs_fsmnt);<br />400                         }<br />401                 } else {<br />402                         /*<br />403                          * Caller doesn't care, so just copy to the<br />404                          * message buffer.<br />405                          */<br />406                         cmn_err(CE_NOTE, errmsg,<br />407                             (int)ttoproc(curthread)->p_pid,<br />408                             (int)ip->i_uid, (int)ip->i_number,<br />409                             ip->i_fs->fs_fsmnt);<br />410                 }<br />411         }<br />412         return (error);<br />413 }<br /></span></pre><span style="font-family: courier new;font-size:100%;" >흥미로운 것은 솔라리스 10에 새로이 생긴 zfs에서는 어떻게 quota를 사용할까 ?<br />zfs는 metadata에 대한 quota를 사용하지 않는다, 기본적으로 화일의 수를 할당된 공간과 별도로 체크하는 것이 의미가 없기 때문이다. 따라서, 또한, zfs는 dataset layer에서 블럭을 할당할때마다 used byte가 할당이 되며, 모든 블럭은 객체지향적으로 상속되므로, uber block은 하위 블럭의 총합에 대한 것을 늘 유지하게 되므로 quota check하는 것이 매우 단순하다.<br /><br /></span><pre><span style="font-family: courier new;font-size:85%;" ><span style="font-size:100%;">아래가 dataset에서 quota를 check하는 모듈이다. 매우 단순함을 알 수 있다.<br />코드의 길이만 봐도 ufs의 경우에는 300라인에 해당하지만, zfs의 경우에는<br />40여라인밖에 되지 않는다. 아래의 모듈에서는 위 ufs용 모듈인 chkdq()와는 달리<br />사용자 id를 기준으로 비교하는 라인도 없다.<br /><br />코드의 길이가 중요한 이유는 data를 쓰기하는 경우에, 솔라리스는 mutex_lock()을 설정하는데<br />코드의 길이가 길게 되며, lock이 잡히는 시간이 증가하게 되므로, 병목의 발생 가능성이 증가하게<br />된다.<br /><br />따라서, zfs는 quota() 기능 사용에 따른 오버헤드가 매우 작을 수 있음을 예측할 수 있다.<br />반면, 이런 오해가 있을 수도 있다. ufs는 단일 화일 시스템을 여러 사용자를 기준으로 사용 공간을<br />나누어 줄 수 있는데, zfs는 불가능한 것이 아닌가?<br /><br />그러나, 이런 오해는 ufs와 zfs의 근본적인 차이를 이해하지 못해서 발생하는 것이다. zfs는 모든 것이<br />pool에 기반하기 때문에 사용자별로 별개의 zfs를 만들어서 할당할 수 있는 볼륨기능이 통합되어 있다.<br />따라서, 단일 화일시스템을 사용자별로 쪼개고 붙이고할 필요가 없다. 그냥 필요한 공간만큼 할당만<br />하면 된다.  혹여나 모 사용자에게 너무 많이 할당했다면, 반납하고 필요한 사용자에게 할당하면 된다.<br /><br />반면, ufs는 볼륨 매니저에서 사용자별로 쪼개놓지 않으면 하나의 단일 화일 시스템에서 여러 사용자용<br />쿼타를 설정해야하므로 매우 불편할 뿐 아니라, 다른 화일 시스템의 사용자에게 남는 공간을 떼어줄 수도<br />없다.<br /><br />어떤 화일 시스템을 사용해야 하는가? 관리의 포인트를 중히 여긴다면 선택의 여지가 없다. 두말할 필요없이<br />zfs라고 할 수 있겠다. zfs 기반으로 서비스를 제공하는 순간 많은 사람들이 편하게 된다는 점을 염두에<br />두어야 한다. 서비스 업체들은 자사 엔지니어가 스토리지 관리때문에 고객들에게 뺏기는 시간이 혁신적으로<br />짧아진다는 것을 알아야한다. 왜냐하면, 시간은 곧 돈이기 때문이다.</span><br /><br /><br /><br />2736 int<br />2737 dsl_dataset_check_quota(dsl_dataset_t *ds, boolean_t check_quota,<br />2738     uint64_t asize, uint64_t inflight, uint64_t *used, uint64_t *ref_rsrv)<br />2739 {<br />2740         int error = 0;<br />2741<br />2742         ASSERT3S(asize, >, 0);<br />2743<br />2744         /*<br />2745          * *ref_rsrv is the portion of asize that will come from any<br />2746          * unconsumed refreservation space.<br />2747          */<br />2748         *ref_rsrv = 0;<br />2749<br />2750         mutex_enter(&ds->ds_lock);<br />2751         /*<br />2752          * Make a space adjustment for reserved bytes.<br />2753          */<br />2754         if (ds->ds_reserved > ds->ds_phys->ds_unique_bytes) {<br />2755                 ASSERT3U(*used, >=,<br />2756                     ds->ds_reserved - ds->ds_phys->ds_unique_bytes);<br />2757                 *used -= (ds->ds_reserved - ds->ds_phys->ds_unique_bytes);<br />2758                 *ref_rsrv =<br />2759                     asize - MIN(asize, parent_delta(ds, asize + inflight));<br />2760         }<br />2761<br />2762         if (!check_quota || ds->ds_quota == 0) {<br />2763                 mutex_exit(&ds->ds_lock);<br />2764                 return (0);<br />2765         }<br />2766         /*<br />2767          * If they are requesting more space, and our current estimate<br />2768          * is over quota, they get to try again unless the actual<br />2769          * on-disk is over quota and there are no pending changes (which<br />2770          * may free up space for us).<br />2771          */<br />2772         if (ds->ds_phys->ds_used_bytes + inflight >= ds->ds_quota) {<br />2773                 if (inflight > 0 || ds->ds_phys->ds_used_bytes <>ds_quota)<br />2774                         error = ERESTART;<br />2775                 else<br />2776                         error = EDQUOT;<br />2777         }<br />2778         mutex_exit(&ds->ds_lock);<br />2779<br />2780         return (error);<br />2781 }</span><br />

2008/07/11

Unix history


Unix의 정신을 이어받은 제품들의 히스토리 그림입니다. 소스는 어디인지 모르겠네요.

2008/07/08

추격자 : %sys의 범인은 누구일까 ?

솔라리스 뿐만 아니라, 유닉스 시스템을 이용하다 보면 간혹 사용자의 cpu% 사용율 보다, sys%의 사용율이 높은 경우가 있습니다. 이런 경우에 도대체 왜 sys% 높은지 답답할 때도 있습니다. 심한 경우에는 sys%가 높음에도 내 애플리케이션의 응답 시간이나 총처리량이 나쁜 경우에는 더욱 답답하죠.

운영체제 입장에서 sys%는 커널에서 동작되는 시간의 백분율입니다. 커널은 사용자 애플리케이션에서 호출하는 시스템 콜을 서비스하기 위해서 각종 서비스 모듈들과 관련 장치 드라이버, 내부 캐쉬, 커널 코어등의 코드(함수)들의 모음인데 s사용자가 단순히 open()을 했다고 해도, 커널 입장에서는 specfs, 관련 화일 시스템(ufs, zfs ... ), 각종 디바이스 드라이버등이 실행되어 집니다. 따라서, 어떤 계층에서 문제가 발생하는 지 알기가 쉽지 않았다고 할 수 있습니다. 커널내의 함수들이 유기적으로 작동되니까요.

그렇지만, 솔라리스 10이 생기면서 이 부분을 거의 정확하게 집어낼 수 있게되었습니다.

일단, 평상시에 어떤 커널 모듈이 많이 호출되는 지만 알면, 관련 패치가 있는 지를 쉽게 찾아볼 수 있습니다. 다음은 가장 바쁜 커널 모듈을 찾는 dtrace 스트립트입니다. 바쁘다는 관점은 두가지가 있습니다. 호출 횟수가 많은 것과 호출 당 처리 시간이 긴 것 두가지가 있을 수 있습니다. 이 두가지의 데이타를 dtrace는 한번에 관찰할 수 있습니다.

다음은 이것을 위한 dtrace script입니다. 커널 모듈에 대한 모든 콜을 트랩해서 호출 횟수와 호출당 소요 시간을 5초간 저장했다가 보여주는 역할을 합니다.

#dtrace -q -n 'fbt:::entry{self->trace=timestamp;}' -n 'fbt:::return/self->trace/{@c[probemod]=count();@e[probemod]=sum(timestamp-self->trace);}' -n 'tick-5s{printa(@c,@e);clear(@c);clear(@e);trunc(@c);trunc(@e);}'

...
mm 448 1677093
net80211 473 1608838
sha1 480 902546
fifofs 1315 7142051
specfs 1344 8330793
ip 1359 10275445
ufs 1439 6542160
usba 1542 5783685
skge 2335 12904313
pciehpc 2748 6571366
uhci 3221 14217584
nvidia 3260 61999111
TS 3347 12659840
ehci 3650 11595263
pcie_pci 5753 46414892
npe 13172 89669520
pcplusmp 110784 269213239
genunix 331691 1106575148
unix 500814 82226651922614
acpica 1929748 42631


제 랩탑에서 돌려본 예입니다. acpi module이 가장 많이 호출되죠. 반면에 총 처리 시간이 가장 긴 모듈은 unix 모듈입니다. 솔라리스 코어니 당연한 것이겠죠. sys%는 일정 시간동안 cpu를 점유한 커널 모듈의 시간이므로 어떤 모듈을 볼 것인지 상위 3~4개 정도 보면 직감이 나타나게 됩니다. 문제가 있는 시스템의 경우에는 요. 우측 두개의 숫자를 스프레드 시트를 이용해서 계산을 해보면, 호출당 처리 시간도 나오게 됩니다.

5초가 데이타가 나오므로, 연속적으로 저장하게 되면, 챠트도 형성할 수 있게 됩니다. 오픈솔라리스.org에 있는 chime이라는 툴을 사용하면, 챠트도 볼 수 있습니다.

위의 경우는 커널 모듈별만 계산했는데, 특정 모듈이 의심이 된다고 여기게 되면, 모듈내 어떤 함수에 문제가 있었는 지도 알 수 있습니다. 위에서 acpi 모듈이 의심이 간다면, apci 의 함수를 볼 수 있습니다.

#dtrace -q -n 'fbt:acpica::entry{self->trace=timestamp;}' -n 'fbt:acpica::return/self->trace/{@c[probefunc]=count();@e[probefunc]=sum(timestamp-self->trace)}' -n 'tick-5s{printa(@c,@e);clear(@c);clear(@e);trunc(@c);trunc(@e);}'

...
AcpiUtStatusExit 41842 174456493
AcpiFormatException 42079 189609876
AcpiUtGetMutexName 54557 112240926
AcpiNsGetNextValidNode 93576 205688515
AcpiNsGetNextNode 196392 872796601
AcpiUtDebugPrint 259500 507489364
AcpiUtTrackStackPtr 364434 750592053


acpica 모듈에서는 AcpiUtTrackStackPtr()가 호출당 처리 시간이 긴 것 같군요.

이런 방법으로 의심이 되는 모듈을 추적해볼 수 있습니다.

한편으로는 애플리케이션 관점에서 어떤 시스템콜 - 즉, 커널에게 어떤 서비스를 요청하는 지를 조사해볼 필요가 있습니다.
사용자 pid가 1234라고 했을때,
예전에 이런 목적으로 truss 를 사용했었습니다.
#truss -c -p 1234

dtrace는 이것보다 훨씬 더 화려한 출력을 제공합니다.

#dtrace -n 'syscall:::entry/pid==1234/{@[probemod,probefunc]=count()}'
를 사용하게 되면 pid 1234가 실행할때 발생하는 시스템 콜을 모듈별, 함수별로 보관했다 호출 횟수를 보여줌으로 어떤 서비스를 많이 쓰게 될 지 추측할 수 있게됩니다.

혹은

#dtrace -n 'syscall:::entry/pid==123/{self->sys=1}' \
-n 'fbt:::entry/self->sys/{@[probemod]=count();self->trace=timestamp;}'

와 같이 하게되면, pid 1234가 시스템 콜을 요청할 때, 작동되는 커널의 모듈들의 호출 회수를 저장했다가 보이게 할 수도 있습니다. 즉, 해당 애플리케이션에 의한 sys% 사용을 정확하게 잡아낼 수도 있습니다. 이 앞에서 썼던 timestamp 방식을 도입하면, 호출당 소요 시간도 추정할 수 있겠죠.


2008/07/02

솔라리스 10 서버 성능 모니터링에 관한 방안 제안



솔라리스10에서 시스템에서 발생할 성능 관련 문제를 감지하기 위한 자동화된 모니터링 환경 구축을 위해서는 다음과 같은 구조를 권고합니다.



성능 관련해서 저장해야할 정보는

1) 성능에 영향을 주는 장애가 발생하나 를 일단 봐야 합니다.

2) 컴포넌트 장애가 없다는 가정하에서 어떤 형태의 부하가 발생하고 어느 컴포넌트에 부하가 많이 몰리는 지를 축적하는 것이 중요합니다.



1)은 fmstat과 prtdiag, dmesg 를 주기적으로 보관하는 것이 중요합니다. 특히 fmstat는
cpu/memory/io component의 장애를 실시간 보고해주는 것이므로, 관리하는 것이 바람직합니다. 모니터링시에는
syslogd가 만들어주는 메세지 /var/adm/messages와 함께 일치된 시간상으로 관리하는 것이 바람직합니다.




$pfexec fmstat 1
module ev_recv ev_acpt wait svc_t %w %b open solve memsz bufsz

cpumem-retire 0 0
0.0 0.2 0 0 0 0 0 0
disk-transport 0 0 0.0 348.9 0 0 0 0 32b 0

eft 0 0 0.0 2.0 0 0 0 0 1.4M 0

fmd-self-diagnosis 1 0 0.0 0.1 0 0 0 0 0 0

io-retire 0 0 0.0 0.1 0 0 0 0 0 0

snmp-trapgen 0 0 0.0 0.1 0 0 0 0 32b 0

sysevent-transport 0 0 0.0 434.5 0 0 0 0 0 0

syslog-msgs 0 0 0.0 0.1 0 0 0 0 0 0

zfs-diagnosis 2 2 0.0 7.4 0 0 0 0 0 0

zfs-retire 0 0 0.0 0.1 0 0 0 0 0 0



2) 장애난 컴포넌트가 없는 경우에 서비스에 의해서 발생하는 부하에 의하여 서비스 응답이 장애성으로 판단되는 경우가 있는데
이러한 경우를 향후 추척하기 위해서는 3가지의 데이타를 축적하는 것이 바람직합니다. 시스템에서는 서비스를 위해서 사용되는 것은

CPU, memory, io(block io), net(streaming io)로 분류될 수 있겠습니다.

CPU와 메모리 추적을 위해서는 vmstat를 축적하시는 것이 바람직하며, 네트웍의 대역폭 조사를 위해서는 첨부되어 있는 netsum과 같은 유틸리의 값을 저장해놓는 것이 좋습니다.

동시에 io(블럭IO)를 위해서 예전에는 sar를 제안했습니다만, 솔라리스10에서는 보다 더 세련된 dtrace를 이용하여 실감나는 추적을 할 수 있습니다.


링크된 netsum은 다음과 같은 출력을 지원합니다. (플랫폼별로 실행화일이 다릅니다.)


$netsum -A -I wpi0 -i 5

Name inc/s out/s ipkts/s opkts/s ierr oerr Bi/pkt Bo/pkt Time

wpi0 20.4 B 16.0 B 0.2 0.2 0 0 102 80 14:24:18

wpi0 953.5 B 36.3 B 2.6 0.4 0 0 367 91 14:24:23

wpi0 0.0 B 0.0 B 0.0 0.0 0 0 0 0 14:24:28

이런 기록으로 네트웍 인터페이스에 대한 기록을 유지할 수가 있습니다.

그외 디스크 사용의 패턴에 관한 데이타를 저장해두고, 가장 빈번한 사용이 되는 disk가 어딘지를평소에 기록해놓는 것이 좋습니다. 솔라리스10 이전에는 이러한 기록이 매우 힘들었으나, 솔10에서는 dtrace 스크립트를 통해서 아주 훌륭한 데이타를 볼 수 가 있습니다.


다음은 hotspot.d라는 DTraceToolkit 0.99에 포함된 스크립트를 약간 수정한 스크립트입니다.
실행은 #dtrace -s hotspot.d 처럼 실행하며, 스크립트화해서 cron에 등록해서 백그라운드로
실행하시면 됩니다.



실행결과는 대략 다름과 같습니다. 이러한 데이타를 통하여 어떤 디스크가 어떻게 사용되고 있는 지를 파악할 수 있습니다.


혹은 io 패턴을 축적해놓는 것도 도움이 될 수 있는데, 역시 DTraceToolkit에 들어있는 iopattern이라는 스크립트를 이용하여 관련 정보를 기록해놓으면 크게 도움이 될 수 있습니다. (아래 링크 참조)



iopattern 이라는 스크립트는 다음과 같은 내용이 나오게 됩니다. 디스크의 패턴에 따라서, 향후 서비스 둔화 지점 파악과 스토리지 아키텍쳐를 어떤 방향으로 가져가야 할 지에 대한 기초 데이타를
제공하게 됩니다.









그외, DtraceToolkit에 내장된 여러 스크립트를 이용하게 되면, 매우 정확하고 정밀한 서버 관련 데이타를 축적할 수 있습니다.

DtraceToolkit : http://opensolaris.org/os/community/dtrace/dtracetoolkit/


솔라리스에서 사용자의 명령어 활동을 기록할 수 있을까 ? (CLI logging)

사용자 활동에 관한 로그는 기본적으로는 강제로 규정하게 하려면, 쉘 레이어에서 강제화 해야 하는데 현재 솔라리스에 들어있는 쉘은 강제적으로 활성화하는 방안이 없습니다.



현재 사용자의 터미널 활동을 로깅할 수 있는 방안으로는

1) 사용자의 쉘의 .profile이나 .login에서 로그인 시점에 script들이 자동으로 실행되어 자동으로 스크립트가 저장되도록 하는 방법. 사용자의 쉘 프로화일 파일 안에 다음과 같이 추가합니다.

...

exec script -a `date '+%m%d%y:%H%M'`.log




=> 사용자가 로그인하면 자동으로 저장되고, ctrl-d 혹은 logout하면 스크립트 내용이 저장됩니다.

매우 소극적 방법이고, 실제 사용자가 로그아웃할때까지 내용이 화일에 저장되지 않습니다. 또한, 사용자가
.profile을 변경해버리면 소용이 무용지물이 될 수 있습니다. 또한 쉘에 따라서 이런 구성이 지원 될 수도 있고, 안될 수도
있습니다.



2) dtrace를 이용하여 실시간 모든 쉘의 행위를 감시할 수 있습니다.

=> 매우 적극적인 방법입니다. 모든 쉘을 죄다 감시하게 되므로, 불필요한 데이타도 컬렉션 될 수 있습니다.
DtraceToolkit에 포함된 shellsnoop 이라는 dtrace 스크립트를 이용하면, 현재 실행되고 있는 대부분의
쉘(sh,ksh,bash,csh,tcsh,zsh)의 입출력을 실시간으로 감시가 가능합니다. 물론, 사용자는 알 수 없습니다.
커널단에서 syscall을 hooking하여 쉘의 read,write를 캡쳐해서 출력해주는 유틸리티입니다. 매우 강력합니다.
첨부 화일 참고



3) 쉘 자체가 log 기능을 가지고 있는 쉘을 사용자 쉘로 지정하는 방법이 있습니다.

솔라리스가 제공하는 기본 쉘들은 이런 기능을 가지고 있지 않으며, 오픈 소스 중에서 사용하여야 합니다.

대표적으로 수도쉘등이 있습니다.

다음 링크를 참고하십시요. http://www.egbok.com/sudoscript/sudoshell.1.html

2008/06/30

버추얼박스에서 게스트와 호스트간의 공유 방식

버추얼 박스 상에서는 다양한 호스트/게스트가 올라올 수 있기 때문에, 각 조합에 따른 호스트/게스트 간의 폴더 공유 방식이 달라질 수 있습니다. 다음은 몇가지 경우에 대해서 구성가능한 매트릭스를 만들어봤습니다.

다음의 테이블에서 첫번째 컬럼에는 호스트 운영체제의 목록이며, 각 컬럼은 호스트에 따라 게스트별 사용이 권고되는 공유 화일 시스템입니다.

아래 테이블의 경우, 호스트와 게스트가 동시에 솔라리스와 리눅스 중 어느 한쪽인 경우에는 호스트에서 NFS를 구성하고 게스트에서 NFS를 공유하는 방식이 가장 추천되는 방법입니다. 물론, 솔라리스의 경우 내장형 CIFS나 혹은 Samba를 이용해서 CIFS를 서비스할 수 있으며, 리눅스 역시 Samba를 이용하여 CIFS를 서비스할 수 있으므로, 이러한 방법으로도 공유를 할 수 있습니다.

H \ G
Solaris Linux Windows
Solaris NFS NFS(CIFS) CIFS
Linux NFS(CIFS) NFS CIFS
Windows CIFS CIFS CIFS

기술적으로 버츄얼 박스는 게스트가 윈도우즈 계열인 경우 내부적으로 VboxSharedFolderFS이라는 가상의 화일시스템 서비스를 제공하여 호스트가 공유한 폴더를 마치 네트웍 공유 폴더인 것처럼 노출하여 윈도우즈 게스트가 호스트의 공유폴더를 cifs로 마운트할 수 있도록 제공해줍니다. 하지만, 게스트가 비 윈도우즈 계열인 경우에는 이러한 기능을 쓰지 않고 호스트나 게스트가 이미 내장하고 있는 공유 화일 시스템인 NFS/CIFS(samba)를 이용하여 네트웍 공유를 사용하면 됩니다. vbox에서 NAT 네트웍 구성을 가지도록 게스트OS를 설치하면 게스트운영체제는 default gateway로 10.0.2.2를 가지게 되는데, 이 IP가 곧 호스트 서버이기도 합니다.

따라서, 호스트서버에서 export된 nfs나 cifs(samba)는 이 IP를 이용하여 vbox 내부 게스트들에게 마운트(Map Network drive...)되어 질 수 있게 됩니다.

각 운영체제별 공유 디렉토리 생성 및 제공 방안은 구글링해보시기 바랍니다.

참고로 솔라리스는 /etc/dfs/dfstab에 예제 라인을 복사한후 원하는 디렉토리로 변경하고, 커멘트를 없애고 저장한 후에 nfs 서비스를 시작 혹은 재시작 하도록 하면 됩니다.

#svcadm enable -r nfs/server
-----> nfs/server와 연관된 다른 서비스도 모두 시작됩니다. 이미 서비스가 시작되어 있는 경우 restart를 하시면 됩니다.

공유하고자 하는 디렉토리가 공유 서비스 중인지 확인하기 위해서는 다음과 같이 실행해봅니다.
#share

명령어 결과로 원하는 내용이 나오면, 버추얼 박스 게스트에서 마운트할 수 있는 지 확인할 수 있습니다. 게스트가 솔라리스인 경우에는

#dfshares 10.0.2.2
와 같이 공유가능한 지 확인할 수 있으며, nfs automount가 활성화되어 있는 경우에는

#cd /net/10.0.2.2
처럼 그냥 일반 디렉토리로 접근하면 공유된 화일 시스템을 보실 수 있게 됩니다. 노틸러스에 북마크해놓으면 늘 자동으로 접근할 수 있게 됩니다.

2008/06/25

웹2.0 시대로 진입하는 문 : zembly.com

zembly.com이라는 사이트를 가보게 되었습니다.

간단하게 얘기하면 lightweight code들이 잔뜩 모여진 wiki로 보여집니다.

백과사전이 아니라 백과 웹 애플리케이션 사이트를 만들겠다는 것으로 보입니다.



iGoogle에서 필요한 컨텐츠 모듈을 작성, 선택해서 제 화면을 만들듯이,



내가 사용하는 웹서비스(현재는 주로 소셜 서비스와 iPhone정도가 있네요)용 플러그인용
코드(widget)를 여러 사람이 참여해서 만들게 하고, 내가 사용하는 웹서비스에 장착해서 사용할 수
있도록 하는 것입니다. iGoogle과 다른 점은 iGoogle code는 iGoogle에서만 사용가능한 코드를
개발하는 것인데, zembly.com은 facebook, meebo, iphone등 나름 미국에서 상위의 서비스를
타겟하고 있다는 점입니다.


예전 코드 개발 사이트의 관점에서 보면 sourceforge.net + wikipedia + web2.0 services 라고

볼 수도 있겠습니다.


비즈니스 관점에서 바라보면, 웹서비스 업자들은 모든 기능을 자신이 개발하지

않아도 되고, zembly안에서 선점을 하게 되면 서비스 우위를 점할 수 있는 점이 있습니다.

따라서, 웹서비스 업자들에게는 매우 높은 관심을 받을 것으로 예상됩니다. 상황에 따라서,

웹서비스 업자들은 선점효과를 위해서 간이개발자들의 코드를 돈주고 살수도 있을 것으로

보입니다. 혹은 서비스 최종 이용자들에게 아이템을 팔듯이 이 간이 모듈을 대행 판매해주고

중개 수수료를 받을 수도 있습니다.



간이 모듈 개발자들 입장에서 보면 웹서비스를 자기의 마음대로 개인화(customization)를 할 수 있을 뿐 아니라,

웹서비스 사용자들에게 판매할 수도 있습니다. 물론, 서비스 업자들이 중개를 할 수도 있고, 직접

팔수도 있으리라 봅니다.



최종 사용자의 입장에서 보면 엄청나게 풍부한 기능들이 속출하게 되므로, 개인화 기능을 넘어서게

되면, 그룹 혹은 엔터프라이즈 기능으로 사용할 수 있는 수준으로 성장할 수 있는 잠재성을

볼 수 있게 됩니다. 그렇다면, 필요한 기능만을 위해서 비용을 지불할 수 있는 웹서비스를

기대해볼 수 있겠습니다.



이 프로젝트가 잘 진행되면 엄청난 후폭풍이 있을 수 있습니다. 진정한 mash-up이 가능한 웹서비스

세계가 도래할 수 있구요. 썬에서 예측했던 redshift 세상이 좀 더 빨리 구현될 수도 있겠습니다.

(SOA가 구현된 세상일 수도 있구요) 결론적으로 아이디어는 아주 훌륭한 것 같습니다.



기술적으로는 이런 서비스(?)가 가능하려면, 현존하는 모든 웹서비스들이 open API에 기반하여야

합니다. 그래야만, 외부에서 작성된 웹서비스를 pluggable하게 장착해서 사용할 수 있게 됩니다.