Running a load testing Go utility using Docker for Mac
5th November 2017
I’m playing around with Zeit Now at the moment (see my previous entry) and decided to hit it with some traffic using Apache Bench. I got this SSL handshake error:
simonw$ ab -n 10 -c 2 'https://json-head.now.sh/'
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking json-head.now.sh (be patient)...SSL handshake failed (1).
140735278280784:error:14077438:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert internal error:/Library/Caches/com.apple.xbs/Sources/libressl/libressl-1.60.1.2.1/libressl/ssl/s23_clnt.c:541:
SSL handshake failed (1).
Some brief Googling turned up this thread on Stack Overflow, which suggested trying hey as an alternative. Hey is a load testing utility written in Go, and the installation instructions are as follows:
go get -u github.com/rakyll/hey
Unfortunately, I don’t have a current Go environment set up on this laptop—I have Go 1.6, but Hey calls for at least Go 1.7.
Rather than work through upgrading my Go environment, I decided to see if I could get this tool working using Docker for Mac.
We recently switched to Docker for Mac for running our development environments at work, and having worked through various iterations of Docker over the past few years Docker for Mac offers by far the most pleasant developer experience. You download the installer, run it, and now docker info
in a terminal will reveal a fully functioning Docker environment. Couldn’t be simpler.
But how to use it to run a one-off tool written in Go? This article on the official Docker blog gave me everything I needed to know.
First step: run the go get
command in a brand new Docker container, like so:
docker run golang go get -v github.com/rakyll/hey
This runs the go get
command in a new instance of the official golang container. If you’ve never used the container before, Docker will download everything it needs before executing the rest of the command.
Once this command finishes, we have a container with the Go program compiled and installed in it. But how to run it?
We can “commit” the container to freeze it into a new image that bakes in the command. Here’s how to do that:
docker commit $(docker ps -lq) heyimage
The nested docker ps -lq
command outputs the container ID. The outer docker commit
command then creates a new image freezing those latest changes.
Having frozen the container, we can run the command like this:
docker run heyimage hey -n 10 -c 2 'https://json-head.now.sh/'
And the command runs, exactly as if I’d installed it without using Docker at all.
simonw$ docker run heyimage hey -n 10 -c 2 'https://json-head.now.sh/'
Summary:
Total: 0.9778 secs
Slowest: 0.6794 secs
Fastest: 0.0564 secs
Average: 0.1954 secs
Requests/sec: 10.2266
Response time histogram:
0.056 [1] |∎∎∎∎∎∎
0.119 [7] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
0.181 [0] |
0.243 [0] |
0.306 [0] |
0.368 [0] |
0.430 [0] |
0.493 [0] |
0.555 [0] |
0.617 [0] |
0.679 [2] |∎∎∎∎∎∎∎∎∎∎∎
Latency distribution:
10% in 0.0588 secs
25% in 0.0653 secs
50% in 0.0868 secs
75% in 0.6792 secs
90% in 0.6794 secs
Details (average, fastest, slowest):
DNS+dialup: 0.1221 secs, 0.0000 secs, 0.6109 secs
DNS-lookup: 0.0981 secs, 0.0000 secs, 0.4906 secs
req write: 0.0001 secs, 0.0000 secs, 0.0001 secs
resp wait: 0.0727 secs, 0.0561 secs, 0.0904 secs
resp read: 0.0004 secs, 0.0001 secs, 0.0012 secs
Status code distribution:
[200] 10 responses
One last puzzle: the above command worked for load testing externally hosted URLs, but I also wanted to try running it against a web server running on port 8000 on my Mac itself. Running hey
against http://localhost:8000/
didn’t work inside the container. Instead, I ran ipconfig getifaddr en0
to find the local network IP address of my Mac and then ran hey
against that IP address (thanks again, Stack Overflow):
simonw$ docker run heyimage hey -n 100 -c 10 'http://10.0.0.12:8000/'
Summary:
Total: 0.2481 secs
...
For me, this use-case illustrates a huge part of the value of Docker: it lets you execute tools written in basically anything without having to pollute your laptop with environment junk.
Running commands against files
Update: 9th November 2017
I decided to use this technique to try out this Go minify tool by Taco de Wolff. Building the tool into a container used the same pattern:
docker run golang go get -v github.com/tdewolff/minify/cmd/minify
docker commit $(docker ps -lq) minify
Running the command this time is a bit harder, because it needs access to files on my filesystem. I can give it access by mounting my current directory as part of the docker run
command, like so:
docker run -v `pwd`:/mnt minify minify /mnt/all.css
Running this minifies the contents of the all.css
file in my current directory and outputs the result to standard out. If I want to save it I can redirect it to a file like so:
docker run -v `pwd`:/mnt minify minify /mnt/all.css > all.min.css
More recent articles
- Qwen2.5-Coder-32B is an LLM that can code well that runs on my Mac - 12th November 2024
- Visualizing local election results with Datasette, Observable and MapLibre GL - 9th November 2024
- Project: VERDAD - tracking misinformation in radio broadcasts using Gemini 1.5 - 7th November 2024