Grinder는 Console, Agent로 나뉜다. 하나의 Agent안에 여러 Worker가 돌 수 있다.
grinder.org에서 zip파일을 다운로드 받고, 풀고, 실행한다. Project구조를 다양하게 잡을 수 있다.
이 글을 따라하면 간단한 HTTP 부하테스트를 시작할 수 있다.
Grinder 홈페이지의 제안이 있지만 여기서는 AWS에서 spot instance를 worker로 돌리면서 개인노트북에서 Console을 돌리려고 하니 조금 다르게 잡아보자.
BASE를 정해두고, BASE 아래에 capistrano-script, grinder-script, grinder-engine 디렉토리를 만든다.
BASE=$HOME/my-grinder-project
mkdir -p $BASE/{capistrano-script,grinder-script,grinder-engine}
먼저 AWS에 spot instance 1+N개 만든다. 1대는 SSH Remote Port Forwarding을 할 서버로만 사용하고 나머지 N대는 Grinder Worker로 사용한다.
capistrano-script 디렉토리에는 그 spot instance에 접속해 grinder를 설치하고 Agent(자바 Class이름은 net.grinder.Grinder)를 띄우는 capistrano task를 만든다.
먼저 Capistrano 2.15.x를 설치하고[1]
cd $BASE/capistrano-script
echo -n 'source "https://rubygems.org"\ngem "capistrano", "~>2.15.0"\n"' > Gemfile
bundle
bundle exec capify .
config/deploy.rb를 이렇게 고친다. app에 grinder worker로 사용하려고 만든 spot instance N개의 DNS를 입력한다.
role :app, *["ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com",
"ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com"]
default_run_options[:pty] = true
task :download_grinder do
run <<-EOF
curl -o /tmp/grinder-3.11-binary.zip http://central.maven.org/maven2/net/sf/grinder/grinder/3.11/grinder-3.11-binary.
sudo unzip /tmp/grinder-3.11-binary.zip -d /opt/ && \
cd /opt && sudo ln -s grinder-3.11 grinder
EOF
end
task :check_files do
run <<-EOF
cd /opt/grinder && ls -al lib
EOF
end
task :upload_files do
upload "grinder.properties", "/opt/grinder/grinder.properties"
upload "simple_app.py", "/opt/grinder/simple_app.py"
end
task :run_workers do
run "cd /opt/grinder && java -cp lib/grinder.jar net.grinder.Grinder"
end
이렇게 하고 bundle exec cap download_grinder upload_files run_workers 를 실행하면
worker가 capistrano-script 디렉토리에 있는 grinder.properties를 바탕으로 돌게 된다.
grinder.properties는 이렇게 적는다. 여기의 consoleHost에는 SSH Remote Port Forwarding용으로 생성해둔 ec2 instance의 DNS 주소를 적는다. grinder worker들이 이 서버의 6372포트로 접속해야 하니 AWS의 security group에서 port도 열어준다.
grinder.useConsole=true
grinder.consoleHost=ec2-xxx-xxx-xxx-xxx-first.ap-northeast-1.compute.amazonaws.com
grinder.consolePort=6372
# Console로 접속이 안 될 때는 이 설정이 먹는다.
grinder.processes=1
grinder.threads=3
grinder.run=9999
grinder.script=simple_app.py
이렇게 해서 돌리면 useConsole=true기 때문에 simple_app.py를 돌리는 것이 아니라 console로 접속하려고 시도하고 접속이 되면 console에서 명령을 기다린다. 그리고 console에서 명령을 내리면 실행된다. 이렇게 하는 것이 realtime으로 상황을 볼 수 있어서 조금 더 편했다.
AWS에서 돌리지 않고 내 Desktop에서 Console을 돌린 다음 공유기에서 Port를 열어서 Console로 접속시킬 수도 있겠지만 그러지 말고 SSH Remote Port Forwarding을 써서 AWS의 서버로 접속하고 그 연결을 다시 내 Desktop의 Console로 보내는 과정을 진행하고 있다.
SSH Remote Port Forwarding용으로 만든 spot instance에서 /etc/ssh/sshd_config에서 GatewayPort yes로 바꾸고 적용시킨다. sshd를 재시작한다.
vi /etc/sshd/config
/etc/init.d/sshd restart
아까전에 적었지만 다시 한 번 말하면 그 instance의 security group에서 6372 포트도 열어준다. 실수하기 쉽다.
그러고 나서 Desktop에서 ssh -N -R 6372:localhost:6372 ec2-xxx-xxx-xxx-xxx-first.ap-northeast-1.compute.amazonaws.com 를 실행한다.
그러면 woker에서 grinder.consoleHost에 적어둔 ec2-xxx-xxx-xxx-xxx-first:6372에 접속하려고 시도하고
ssh의 remote port forwarding이 그 요청을 다시 Desktop에서 localhost로 resolve하는 서버의 6372로 보낸다.
이 상황에서 cap run_workers를 실행시키면 자연스럽게 내 local에 Console이 6372포트에 안 떠 있으니 접속을 할 수 없다고 에러를 내면서 simple_app.py를 실행시키려고 할 거다. 그렇지 않고 화면에 아무 진행상황이 안 보이고 대기하는 듯 보이면 domain을 잘 못 적었거나
Console을 띄워보자.
desktop에서 unzip grinder-3.11.zip && mv grinder-3.11 grinder-engine 하고
cd $BASE/grinder-script; java -cp $BASE/grinder-engine/lib/grinder.jar net.grinder.Console 하면
15~30초쯤 시간이 흐른다음 java swing application이 하나 눈에 보인다.
이 앱이 Console이고 여기서 worker로 파일을 보내고 worker가 properties에 적힌대로 일을 시작하도록 할 수 있다. 'Distribute Files' 버튼을 누르면 $BASE/grinder-script 디렉토리에 있는 모든 파일을 woker들로 보낸다. Console에서 Edit도 할 수 있지만 그렇게 편하지 않으니 외부에서 하고 내용확인만 Console에서 하는 정도가 편하다. Distribute Files가 활성화가 안 되어 있을 때도 있으니 Console안에서 파일을 열었다가 닫았다가 하면 좋다.
'Start'를 시작하면 도는 것이 보일 거다.
돌릴 때 착오가 꽤 있을 수 있는데 'Agent'가 잘 붙었는지 확인하고 도는 동안에는 TPS만 볼 것이 아니라 Result 팬에서 'Error'와 'Response Error'를 눈여겨 보는 것이 좋다. 'Error'는 직접 만든 jython script에서 나는 error를 의미하고 하고 'Response Error'는 HTTP Repsonse가 200이 아닌 것을 의미할 것 같다.
grinder-script안에 둘 sample1.properties 를 이렇게 만들자. 이 properties를 Console에서 별표가 되게 만들고 distribute files를 누르고 start 하면 여기 설정된 대로 woker들이 돌아간다. 멈추기 전까지는 계속 돌리는 설정이다.
grinder.script=sample.py
grinder.processes=4
grinder.threads=30
grinder.processIncrement=1
grinder.processIncrementInterval=60000
grinder.runs=0
#grinder.duration=600000
grinder.jvm.arguments=-Xms256m -Xmx1024m -verbose:gc -XX:+PrintGCDetails
이 정도 sample로 충분할 때가 많다.
from net.grinder.script import Test
from net.grinder.script.Grinder import grinder
from net.grinder.plugin.http import HTTPRequest
test1 = Test(1, "Request resource")
request1 = HTTPRequest()
test1.record(request1)
class TestRunner:
def __call__(self):
result = request1.GET("http://some-elb-dns-name.ap-southeast-1.elb.amazonaws.com")
#print result.getStatusCode()
if result.getStatusCode() != 200:
grinder.statistics.forLastTest.success = 0
여기까지 해서 desktop에 console을 돌리고 AWS의 SpotInstance에 woker를 돌리고 그 worker들이 console에 접속할 수 있게 SSH Tunnel (Remote Port Forwarding)을 만들고 woker를 돌리고 console에서 명령을 내릴 수 있는 상태를 만들었다.
이제 sample.properties안에서 processes 숫자와 threads 숫자를 바꿔가며 시험한다.
주의할 것은 가장 낮은 thread 숫자로 꽤 오래 시험해 보는 것이 좋다. 한 5분 10분 돌리다보면 낮은 thread 숫자에서도 잘 안 돌때가 많다.
어떻게 해석하나?
grinder의 woker에서 GC가 일어나기에 그럴 수도 있겠지만 내 경우는 아니었다. 조심하자. 대부분 부하 대상시스템에서 정말 응답을 주지 않고 있는 것이라 보는 것이 낫겠다.
적어도 3분~5분은 돌려야 혹시라도 있을 부하의 대상이 되는 시스템의 TCP 스택의 제한 (netfilter)을 파악할 수도 있겠다.
XXX 언제 어디선가 response time의 90% percentile 값이 사용자 경험을 측정하는데 적당한 metric이라는 이야기를 들었다.
XXX 95% percentile 값도 적당한 metric이 아니라는 이야기도 들었다.
XXX SHOW ME SOURCE
XXX 아직 잘 모른다. XXX 만드는 것과 손으로 돌리는 것 사이의 Trade-Off가 얼마나 될까?
http://spin.atomicobject.com/2013/03/15/red-green-performance-testing/
Red를 isolation된 시스템에서 grinder로 재현하고 시스템을 수정하고 Green이 된 것을 확인한다. grinder 설정방법에 대해 다룬다.
Linux Performance
J2ee Performance Testing
이 책이 Grinder에 대해 다룬 유일한 책인가 보다.
[1] capistrano3를 쓰는 것도 좋겠다.