Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slow_cooker version load test script #56

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 16 additions & 119 deletions workloads/tech-demo/deploy-k8s.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
{
"userId": "william",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this field.

"name": "tech-demo",
"region": "us-east-1",
"files": [
{
"fileId": "tech_demo_load_test",
"path": "\/home\/ubuntu\/locust_file\/locustfile.py"
},
{
"fileId": "tech_demo_core_site_xml",
"path": "\/home\/ubuntu\/core-site.xml"
Expand Down Expand Up @@ -53,15 +50,7 @@
"id": 2
},
{
"task": "locust-master",
"id": 1
},
{
"task": "locust-slave",
"id": 1
},
{
"task": "locust-slave",
"task": "slow-cooker",
"id": 1
},
{
Expand Down Expand Up @@ -154,7 +143,7 @@
"mountPath": "\/var\/run\/command.sock",
"name": "command-sock"
},
{
{
"mountPath": "\/sys\/fs\/cgroup",
"name": "cgroup"
}
Expand Down Expand Up @@ -829,22 +818,22 @@
"kind": "Deployment",
"metadata": {
"labels": {
"app": "locust-master"
"app": "slow-cooker"
},
"name": "locust-master",
"namespace": "hyperpilot"
"name": "slow-cooker",
"namespace": "default"
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "locust-master"
"app": "slow-cooker"
}
},
"template": {
"metadata": {
"labels": {
"app": "locust-master"
"app": "slow-cooker"
}
},
"spec": {
Expand All @@ -857,124 +846,32 @@
}
},
"args": [
"--master",
"--host=http:\/\/goddd.default:8080",
"-f",
"\/locust_file\/locustfile.py"
"-mode",
"server",
"-server-port",
"8081"
],
"imagePullPolicy": "Always",
"image": "hyperpilot\/locust",
"name": "locust-master",
"image": "hyperpilot\/slow_cooker:tech_demo",
"name": "slow-cooker",
"ports": [
{
"containerPort": 8089,
"containerPort": 8081,
"hostPort": 8089,
"protocol": "TCP"
},
{
"containerPort": 5557,
"hostPort": 5557,
"protocol": "TCP"
},
{
"containerPort": 5558,
"hostPort": 5558,
"protocol": "TCP"
}
],
"volumeMounts": [
{
"mountPath": "\/locust_file",
"name": "home-ubuntu-locust-file"
}
]
}
],
"volumes": [
{
"hostPath": {
"path": "\/home\/ubuntu\/locust_file\/"
},
"name": "home-ubuntu-locust-file"
}
]
}
}
}
},
"family": "locust-master",
"family": "slow-cooker",
"portTypes": [
1
]
},
{
"deployment": {
"apiVersion": "extensions\/v1beta1",
"kind": "Deployment",
"metadata": {
"labels": {
"app": "locust-slave"
},
"name": "locust-slave",
"namespace": "hyperpilot"
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "locust-slave"
}
},
"template": {
"metadata": {
"labels": {
"app": "locust-slave"
}
},
"spec": {
"containers": [
{
"args": [
"--loglevel",
"DEBUG",
"--slave",
"--master-host=locust-master",
"-H",
"http:\/\/goddd.default:8080",
"-f",
"\/locust_file\/locustfile.py"
],
"imagePullPolicy": "Always",
"image": "hyperpilot\/locust",
"name": "locust-slave",
"resources": {
"requests": {
"cpu": "512.0m",
"memory": "512Mi"
}
},
"volumeMounts": [
{
"mountPath": "\/locust_file",
"name": "home-ubuntu-locust-file"
}
]
}
],
"volumes": [
{
"hostPath": {
"path": "\/home\/ubuntu\/locust_file\/"
},
"name": "home-ubuntu-locust-file"
}
]
}
}
}
},
"family": "locust-slave"
},
{
"deployment": {
"apiVersion": "extensions\/v1beta1",
Expand Down
198 changes: 198 additions & 0 deletions workloads/tech-demo/load-test/slow_cooker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
"""Slow cooker load testing."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this file being used with the demo?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wrap this file into Docker, and it can launch by tech-demo-ui (i've send a PR to tech-demo-ui).
or run locally by this command:
python slow_cooker.py -c hi_lo_config.json --slow-cooker-host http://<endpoint.elb.aws.slow-cooker>:<port> --host http://<endpoint.goddd.k8s.dn>:<port>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where can we find this docker image? Can you add the Dockerfile into the images folder in tech-demo?

import requests
import random
import datetime
import uuid
import time
import sys
from urlparse import urljoin
from multiprocessing import Pool
import json
import argparse
import os

LOCATIONS = ["SESTO", "AUMEL", "CNHKG", "JNTKO", "NLRTM", "DEHAM"]
QPS = 20
CONCURRENCY = 1
INTERVAL = "1s"
HISTOGRAM_WINDOW_SIZE = "1s"
TOTAL_REQUESTS = 200
TARGET_HOST = "http://goddd.default:8080"
# Please replace slow_cooker with real host
SLOW_COOKER_HOST = "http://aef2018a95c0811e7a4d102472bbd1c4-577574532.us-east-1.elb.amazonaws.com:8089"
STATUS_COMPLETE = "finished"


class StaticTasks(object):
"""Load tasks."""

def __init__(self, target_host):
"""Initialize Tasks."""
self.target_host = target_host

def _pick_random_location(self):
return LOCATIONS[random.randint(0, 5)]

def create_route_delete_cargo(self):
"""Book new cargo -> route cargo -> delete cargo."""
add_days = random.randint(0, 15)
deadline = datetime.datetime.utcnow() + \
datetime.timedelta(days=add_days)

data = json.dumps({
"origin": self._pick_random_location(),
"destination": self._pick_random_location(),
"arrival_deadline": deadline.isoformat() + "Z"
})
scenario = [
{
"url_template":
urljoin(self.target_host,
"/booking/v1/cargos"),
"method": "POST",
"data": data,
"drain_resp": "tracking_id"
},
{
"url_template":
urljoin(self.target_host,
"/booking/v1/cargos/:tracking_id"),
"method": "GET"
},
{
"url_template":
urljoin(self.target_host,
"/booking/v1/cargos/:tracking_id"),
"method": "DELETE"
}
]
return scenario

def list_cargos(self):
"""List cargo."""
return [
{
"url_template":
urljoin(self.target_host,
"/booking/v1/cargos"),
"method": "GET"
}
]

def list_locations(self):
"""List locations."""
return [
{
"url_template":
urljoin(self.target_host,
"/booking/v1/locations"),
"method": "GET"
}
]


def run_benchmark(*args):
"""Run benchmark.
arg[0][0]: slow cooker host
arg[0][1]: scenario
arg[0][2]: load {qps: duration_in_second}
load is a list which contains dict qps:duration
"""
one_second = 1000000000
for qps, duration in args[0][2].iteritems():
id = uuid.uuid1()
response = requests.post(
url=urljoin(args[0][0],
"/slowcooker/benchmark"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can let slow cooker support sending multiple scenarios, and allow slow_cooker to choose internally how each scenario should be ran and with what weight?

json={
"runId": id.__str__(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runId is now part of the URL instead

"qps": qps,
"concurrency": CONCURRENCY,
"totalRequests": qps * duration * one_second,
"interval": one_second,
"loadTime": "1s",
"url": "http://localhost:8080",
"scenario": args[0][1],
"headers": {"Content-Type": "application/json"}
}
)
# TODO: interval means nothing in benchmark
if response.ok:
done = False
sys.stdout.write("task {} is running ".format(id))
while not done:
status = requests.get(
url=urljoin(args[0][0],
"/slowcooker/benchmark/{}".format(id)))
if status.ok:
if json.loads(status.content)["State"] == STATUS_COMPLETE:
done = True
# query status per second
sys.stdout.write(".")
time.sleep(1)

sys.stdout.write(" complete\n")
sys.stdout.flush()
else:
print response.text
response.raise_for_status()


# Run benchmark
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("-c", "--config", type=str,
required=False, default="config.json",
help="hi-lo config")
args = parser.parse_args()
if os.path.isfile(args.config):
print args.config
with open(args.config, 'r') as json_data_file:
try:
params = json.load(json_data_file)
except ValueError as e:
print "Main:ERROR: Error in reading configuration file %s: %s" % (args.config, e)
sys.exit(-1)
else:
print "Main:ERROR: Cannot read configuration file ", args.config
sys.exit(-1)

return params


def main():
"""Main function."""
params = parse_args()
tasks = StaticTasks(TARGET_HOST)

pool = Pool()

# Edit load here
lo = params["low_count"]
lo_duration = params["low_duration_seconds"]
hi = params["high_count"]
hi_duration = params["high_duration_seconds"]

args = [
(SLOW_COOKER_HOST, tasks.create_route_delete_cargo(), {lo: lo_duration, hi: hi_duration}),
(SLOW_COOKER_HOST, tasks.list_cargos(), {lo: lo_duration, hi: hi_duration}),
(SLOW_COOKER_HOST, tasks.list_locations(), {lo: lo_duration, hi: hi_duration})]
while True:
try:
pool.map_async(run_benchmark, args).get(0xffff)
except KeyboardInterrupt as e:
# pool.close()
# pool.join()
pool.terminate()
pool.join()
print "Interrupted"
sys.exit(0)


if __name__ == "__main__":
main()

# runner.run_benchmark(tasks.create_route_delete_cargo(), {300: 45, 700: 75})

# runner.run_benchmark(tasks.list_cargos(), {300: 45, 700: 75})
# runner.run_benchmark(tasks.list_locations(), {300: 45, 700: 75})