|
| 1 | +# Selenium Grid on Fargate |
| 2 | + |
| 3 | +This project shows how to use [CloudFormation](https://aws.amazon.com/cloudformation/) and [ECS Fargate](https://aws.amazon.com/fargate/) to deploy a private [Selenium Grid](https://github.com/SeleniumHQ/docker-selenium) with a Chrome node. |
| 4 | + |
| 5 | +## Why |
| 6 | + |
| 7 | +In many companies, people write e2e / UI / regression tests for their web-apps. Some solutions like Browserstack or SauceLabs provide a "public" Selenium Grid for running your tests against different environments. However, such as any paid service, it comes with a cost. The more VM instances you need, the more expensive it becomes. |
| 8 | + |
| 9 | +Assuming you have a small subscription to a service like Browserstack, you might be interested in reserving your VMs for your principal branches (`develop` or `master` or release branches following your branching strategy) and use a private Selenium Grid to run the tests for your other branches. |
| 10 | + |
| 11 | +## How |
| 12 | + |
| 13 | +The project is splitted into 5 stacks. Each stack takes care of a specific aspect of the infrastructure. The 5 domains are the following: |
| 14 | +* Network: creates a VPC for the ECS Cluster with 3 subnets, 2 public ones and 1 private one. It creates also a internet gateway and NAT gateway for the subnets. It finally adds a internet-facing load-balancer to route traffic to the Selenium Hub. |
| 15 | +* IAM: creates all the required IAM roles for the ECS, Cloudwatch and application auto-scaling. |
| 16 | +* Serice discovery: creates a AWS ServiceDiscovery namespace and register the routes for the hub and the Chrome node. |
| 17 | +* Selenium Grid: creates the ECS cluster and the tasks for the hub and Chrome as well as their services. |
| 18 | +* Auto scalers: adds AWS Application auto-scaler the nodes to increase the amount of instances in case of heavy work load. |
| 19 | + |
| 20 | +Illustration: |
| 21 | + |
| 22 | + |
| 23 | + |
| 24 | +## Using it |
| 25 | + |
| 26 | +### Infrastructure |
| 27 | + |
| 28 | +Before anything else, make sure that you have a valid AWS account (more info [here](https://aws.amazon.com/free/)). |
| 29 | + |
| 30 | +Create a user in IAM for programmatic usage and export the related `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` in your terminal. |
| 31 | + |
| 32 | +Once it's done, run the following commands in the specified order: |
| 33 | + |
| 34 | +```bash |
| 35 | +$> aws cloudformation create-stack --stack-name grid-network --template-body="$(cat ./modules/network.yaml)" && aws cloudformation wait stack-update-complete --stack-name grid-network |
| 36 | +$> aws cloudformation create-stack --stack-name grid-iam --template-body="$(cat ./modules/iam.yaml)" --capabilities CAPABILITY_IAM && aws cloudformation wait stack-update-complete --stack-name grid-iam |
| 37 | +$> aws cloudformation create-stack --stack-name grid-service-discovery --template-body="$(cat ./modules/service-discovery.yaml)" && aws cloudformation wait stack-update-complete --stack-name grid-service-discovery |
| 38 | +$> aws cloudformation create-stack --stack-name grid --template-body="$(cat ./modules/selenium-grid.yaml)" && aws cloudformation wait stack-update-complete --stack-name grid |
| 39 | +$> aws cloudformation create-stack --stack-name grid-auto-scalers --template-body="$(cat ./modules/auto-scalers.yaml)" && aws cloudformation wait stack-update-complete --stack-name grid-auto-scalers |
| 40 | +``` |
| 41 | + |
| 42 | +After all the deployments are made, go to your AWS console. Navigate to `EC2` then `LOAD BALANCING` then `Load Balancers`. Finally, click on the load-balancer named `SeleniumGrid`, you should see the DNS name in the screen below the list of load balancers. Save the DNS somewhere, it will be re-used for the example. |
| 43 | + |
| 44 | +### Front-End |
| 45 | + |
| 46 | +Now that our infrastructure is up and running, let's see what's needed for running our tests. For this example, we will use [Webdriver I/O](https://webdriver.io/) as shown in the example folder of this repository. |
| 47 | + |
| 48 | +The first thing is to define our capabilities for Chrome. Here is an example: |
| 49 | + |
| 50 | +```javascript |
| 51 | +// wdio.conf.js |
| 52 | +exports.config = { |
| 53 | +// code before... |
| 54 | + capabilities: [{ |
| 55 | + browserName: 'chrome', |
| 56 | + 'goog:loggingPrefs': { browser: 'ALL' }, |
| 57 | + 'goog:chromeOptions': { |
| 58 | + args: ['--no-sandbox', '--disable-setuid-sandbox', '--headless', '--whitelisted-ips', '--disable-dev-shm-usage'], |
| 59 | + }, |
| 60 | + }], |
| 61 | +// ... code after |
| 62 | +}; |
| 63 | +``` |
| 64 | + |
| 65 | +The options passed to the Chromedriver are required. The most important ones are: |
| 66 | +* `--headless`: tells the Chromedriver to start in headless mode |
| 67 | +* `--whitelisted-ips`: tells the Chromedriver to bind `0.0.0.0` instead of `127.0.0.1` because of how networking works within Fargate |
| 68 | +* `--disable-dev-shm-usage`: tells the Chromedriver to not to look for extra resources in `/dev/shm` (aka _shared memory_) |
| 69 | +* `--no-sandbox` and `--disable-setuid-sandbox`: very bad practice - could potentially be removed |
| 70 | + |
| 71 | +When your configuration is ready, let `wdio` know where to look for the Selenium Hub. To do so, you can either use the following configuration (make sure to replace `randominteger` and `my-aws-region` by the correct values): |
| 72 | + |
| 73 | +```javascript |
| 74 | +// wdio.conf.js |
| 75 | +exports.config = { |
| 76 | +// code before... |
| 77 | + hostname: 'SeleniumGrid-randominteger.my-aws-region.elb.amazonaws.com', |
| 78 | + port: 4444, // default port |
| 79 | + path: '/wd/hub', // default path |
| 80 | +// ... code after |
| 81 | +}; |
| 82 | +``` |
| 83 | + |
| 84 | +or provide some options to the `wdio` cli as follows: |
| 85 | + |
| 86 | +```bash |
| 87 | +$> npx wdio --hostname SeleniumGrid-randominteger.my-aws-region.elb.amazonaws.com |
| 88 | +``` |
| 89 | + |
| 90 | +A complete example can be found [here](./example). |
| 91 | + |
| 92 | +## Yet to be done |
| 93 | + |
| 94 | +* No support for Firefox |
| 95 | +* No CloudWatch alerts defined |
| 96 | +* Stacks could be nested |
| 97 | +* Distribute the template through a (public?) S3 bucket |
| 98 | +* Make a better diagram |
| 99 | +* Add CI (GitHub Actions for the win) |
0 commit comments