In the first part I told about simple approaches to build scalable Selenium cluster without writing any line of code. In this part we’re going to dive into more subtle Selenium questions:
All new tools described in the first part are in fact smart thin proxies that redirect user requests to real Selenium Hub and Nodes. However when you think a bit more questions arrive:
One may try to use hardware servers having one Selenium Hub and a lot of Selenium Nodes for different browsers. Seems reasonable but in fact is far from being useful:
The simplest way to always have the same quantity of nodes connected to the same hub is to launch both hub and nodes on the same virtual machine. If you use one machine per browser version that becomes an elementary school task to calculate total number of available browsers. You can easily add and remove virtual machines containing compatible version of Selenium node and browser. This is what we recommend when working with Selenium cluster in cloud with static quantities of each version available.
But what should be present inside such virtual machine except Selenium Hub and Selenium Node to work smoothly?
xvfb-run -l -a -s '-screen 0 1600x1200x24 -noreset' java -jar \ selenium-server-standalone.jar -role node <...other args>
Notice that Xvfb is only needed for Selenium node process.
#!/bin/bashapt-get -y install linux-sound-base libasound2-devapt-get -y install alsa-utils alsa-ossapt-get -y install --reinstall linux-image-extra-`uname -r` \modprobe snd-dummyif ! grep -Fxq "snd-dummy" /etc/modules; thenecho "snd-dummy" >> /etc/modulesfiadduser $(whoami) audio
As you could probably notice Selenium is a Java application. You need to have Java Virtual Machine (JVM) installed on your system to run Selenium. The smallest Java installation package called JRE is about 50 megabytes. Selenium JAR for the latest version 3.0.1 adds 20 more megabytes. Then consider operating system size, all required fonts, browser distribution size and you easily add up to several hundreds of megabytes. Although disk storage is now relatively cheap — we can do better. Selenium 2.0 and 3.0 series are also called Selenium WebDriver. This is because different browsers support is implemented using so called webdriver binaries. Here’s how it works:
Having said that — is not it a bit expensive to spend hundreds of megabytes for a simple proxy? A year ago the answer was definitely no because there was no such binary for Firefox — the most widely used Selenium browser. It was Selenium server responsibility to start Firefox process, upload an extension to it and proxy requests to the port opened by this extension. During the last year the situation has changed. Starting from Firefox 48.0 Selenium interacts with browser by using a standalone driver called Geckodriver. That means that at least for the majority of desktop browsers we can safely remove Selenium Server and proxy requests directly to driver binaries.
In previous sections I described how we can organize a Selenium cluster using virtual machines in cloud. In this approach virtual machines are always running and thus spending your money. Also total number of hosts for concrete browser version is limited and this can lead to free browsers exhaustion during peak loads. I have heard about working and even patented sophisticated solutions that prelaunch and warm up a pool of virtual machines according to current load to always have free browsers. That works but can we do better? The main issue with hypervisor virtualization is the speed. It can take several minutes to launch a new virtual machine. Let’s think a bit more — do we need a separate operating system for every browser? The answer is no — we only need filesystem and network isolation working fast. This is where container virtualization comes into play. For the moment containers work mostly under Linux but as I said Linux easily covers 80% of most popular browsers. Containers start in seconds and go down even faster.
What should we place inside container? — Almost the same stuff as we do for virtual machine: browser binary, fonts, Xvfb. For the old Firefox version we still need to have Java and Selenium server but for Chrome, Opera and latest Firefox we can use driver binary as container main process. Using minimalistic Linux distribution such as Alpine we can deliver extremely small and lightweight containers.
Actually the most popular and well-known container platform is Docker. Selenium developers provide a set of prebuilt Docker containers to launch Selenium server standalone or Selenium Grid in Docker environment. Unfortunately to create a cluster you need to start and stop these containers manually or using some automation tool like Docker Compose. This is already better than installing Selenium from packages but it would be better to get a lightweight daemon with the following behavior:
During the years of using standard Selenium server on a large scale we understood that it’s an overhead to use JVM and fatty Selenium JAR for proxying requests. So we were searching for a lighter technology. Finally we chose the Go programming language aka Golang. Why is it better for our purposes?
We did not find a good name for this new Go daemon. This is why it’s called just Selenoid. To start working with Selenoid do three simple steps:
{"firefox": {"default": "latest","versions": {"48.0": {"image": "selenoid/firefox:48.0","port": "4444"},"latest": {"image": "selenoid/firefox:latest","port": "4444"}}},"chrome": {"default": "53.0","versions": {"53.0": {"image": "selenoid/chrome:53.0","port": "4444"}}}}
Like in GridRouter XML file you specify available browser versions. But Selenoid starts containers on the same machine or using a remote Docker API so there’s no need to enter host names and regions. For each browser version you need to provide container name, version and port that container listens to.
$ selenoid -limit 10 -conf /etc/selenoid/browsers.json
By default it starts on port 4444 as if it was Selenium Hub.
Our tests show that Docker containers even with standard Selenium server inside start in a few seconds. What you get instead — is a guaranteed memory and disk state. The browser state is always like after fresh installation. More than that you can install Selenoid on a cluster of hosts having the same set of desired browsers stored as Docker images. This gives you a big Selenium cluster that automagically scales according to browser consumption. If current requests need more Chrome sessions — more containers are launched. When there’s no Chrome requests — all containers go down and free place for other browser requests.
To deliver better load distribution Selenoid automatically puts requests exceeding sessions limit to wait queues and processes them when some sessions on the same host end. But Selenoid is not just a container management tool. It allows you to start on demand not only containers but also any driver binaries instead. The main use case for this feature is replacing Selenium Server on Windows. Selenoid in that case will start IEDriverServer binary thus economizing memory consumption and avoiding some proxying errors in Selenium Server.
Do you remember that GridRouter is also a Java application? We did an effort and created a lightweight Go implementation called Go Grid Router (or simply ggr). What are the benefits?
When combined with Selenoid it allows to create scalable and robust clusters like this:
In this part, I told you about the bleeding edge technologies that can be used to organize Selenium cluster in modern way:
In the next part I am going to show how to start using solutions described in this part in popular cloud platforms.
À bientôt…