-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from dmytrostriletskyi/develop
- Loading branch information
Showing
14 changed files
with
436 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,3 +102,5 @@ venv.bak/ | |
|
||
# mypy | ||
.mypy_cache/ | ||
|
||
.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
sudo: required | ||
|
||
services: | ||
- docker | ||
|
||
before_install: | ||
- docker build -t heroku-load-balancer . -f Dockerfile | ||
- docker run -d -p 7979:7979 -v $PWD:/heroku-load-balancer -e PORT=7979 -e HEROKU_API_KEY=$HEROKU_API_KEY -e PIPELINE_IDENTIFIER='f64cf79b-79ba-4c45-8039-57c9af5d4508' --name heroku-load-balancer heroku-load-balancer | ||
|
||
script: | ||
- docker exec -it heroku-load-balancer bash -c "radon cc src -nb --total-average" | ||
- docker exec -it heroku-load-balancer bash -c "cat requirements.txt requirements-dev.txt | safety check --stdin" | ||
- docker exec -it heroku-load-balancer bash -c "bash <(curl -s https://linters.io/sort-requirements) requirements.txt requirements-dev.txt" | ||
- docker exec -it heroku-load-balancer bash -c "bash <(curl -s https://linters.io/isort-diff) src" | ||
- docker exec -it heroku-load-balancer bash -c "flake8 src" | ||
|
||
env: | ||
global: | ||
secure: I8Rr7rQOfSFkq+VjYFThFfbBE2YIs4d5y3f9kJEAbZ51p61QWu8h4Opu1/fXGPFA5hCOS2sd8hywj+wVAaavd4GUuJurYbUd4rGVhkpQDCSYlUVr2eGHXGC+JgpyZ14LKsPOAFIfdwumU4ZrmAgTmKuduhiXo/erQk2g086ivBjqUvjG/yRH3ZehlMVY1MU4QIOZa1JrWgG/XmXXIxaFbQpwIeNQw3Q5i10PcG+X+6Yoeg+IrJ4mKIExKzrwrBS3I/JEWh37TAB2AkQN8Ez2u8AktM8uAyKALxL4mThhr9sCsIjfrNHJOYENDKvzLM1Y0XDURSclrngsvp3ihOTo23JSXemBPzbxfP2jvhFV0nDePVq88fVIrhxFNw+Kd1Gvew6hFa1PftNq825eDfLv+oWD8o3J9SU8innqxF6TH5d/UfIkI+Z3ovciedL4Jy7/bGTIqKk5Zc04GylCfEGxm1YFotNpE1LYunMcmE6pJQvs35DWryhJw3ryGOI3CWQLXuSq8pbR3czJpPeZlisJ1pU7vh79x8lZmWsxsza5jYMvPAle6JgInnm8oYkvwNc4fi5u9Y4Tb9Q4QSIc3nX9o62mH1fu6F/N5lefe6xoRkTjz2xkOkwEHtiAFKrXimyI6E/wscOk5NgenMqp0cm4Sk5C3vlLnvCc79/zycCeIpw= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
FROM ubuntu:16.04 | ||
|
||
ENV LANG C.UTF-8 | ||
ENV LC_ALL C.UTF-8 | ||
|
||
RUN apt-get update && apt-get install -y \ | ||
software-properties-common \ | ||
nginx \ | ||
curl | ||
|
||
RUN add-apt-repository ppa:deadsnakes/ppa && apt-get update && apt-get install -y \ | ||
python3.6 \ | ||
python3.6-dev \ | ||
python3-pip \ | ||
python3-setuptools | ||
|
||
RUN ln -sfn /usr/bin/python3.6 /usr/bin/python3 && ln -sfn /usr/bin/python3 /usr/bin/python | ||
|
||
WORKDIR /heroku-load-balancer | ||
COPY . /heroku-load-balancer | ||
|
||
ENV PYTHONPATH="$PYTHONPATH:/heroku-load-balancer/src" | ||
|
||
RUN pip3 install -r /heroku-load-balancer/requirements.txt -r /heroku-load-balancer/requirements-dev.txt | ||
|
||
CMD /bin/bash -c "python3 src/entrypoint.py create-load-balancer --nginx-port=$PORT --heroku-api-key=$HEROKU_API_KEY --pipeline-identifier=$PIPELINE_IDENTIFIER" && mv nginx.conf /etc/nginx/nginx.conf && nginx -g 'daemon off;' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
Automatic (with no manual configuring) load balancer for your Heroku pipeline production applications. | ||
|
||
[![Release](https://img.shields.io/github/release/dmytrostriletskyi/heroku-load-balancer.svg)](https://github.com/dmytrostriletskyi/heroku-load-balancer/releases) | ||
[![Build Status](https://travis-ci.com/dmytrostriletskyi/heroku-load-balancer.svg?branch=develop)](https://travis-ci.com/dmytrostriletskyi/heroku-load-balancer) | ||
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) | ||
|
||
* [Getting started](#getting-started) | ||
* [What is a load balancer](#what-is-a-load-balancer) | ||
* [Motivation](#motivation) | ||
* [How to use](#how-to-use) | ||
* [How it works](#how-it-works) | ||
* [Development](#development) | ||
|
||
## Getting started | ||
|
||
### What is a load balancer | ||
|
||
A load balancer is a device that distributes network or application traffic across a cluster of servers. A load balancer | ||
sits between the client and the server farm accepting incoming network and application traffic and distributing the | ||
traffic across multiple backend servers. By balancing application requests across multiple servers, a load balancer | ||
reduces individual server load and prevents any one application server from becoming a single point of failure, | ||
thus improving overall application availability and responsiveness. | ||
|
||
![Illustation on how load balancer works](https://habrastorage.org/webt/iy/-s/vx/iy-svxvpqwnquwvciv7qm3pfm1u.png) | ||
|
||
### Motivation | ||
|
||
[Heroku](https://heroku.com) does not have `load balancer` paid feature to balancing your applications. It is the drawback | ||
comparing to [Digital Ocean](https://www.digitalocean.com/products/load-balancer) and [Amazon Web Services](https://aws.amazon.com/elasticloadbalancing/) | ||
which do have it. | ||
|
||
### How to use | ||
|
||
1. Press the button named `Deploy to Heroku` below. | ||
|
||
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/dmytrostriletskyi/heroku-load-balancer/tree/create-docs) | ||
|
||
2. Enter the name for the application which will host the load balancer. Choose the region and add to the pipeline if needed. | ||
|
||
<img src="https://habrastorage.org/webt/xq/rp/nl/xqrpnlgqh0-3o1kldfk2pflvtvy.png" width="900" height="440"> | ||
|
||
3. Visit the [Heroku account setting page](https://dashboard.heroku.com/account), find the `API Key` section, reveal the key and | ||
paste it to the `HEROKU_API_KEY` field. | ||
|
||
<img src="https://habrastorage.org/webt/v0/g7/wt/v0g7wtn1qltm8-_jpfdug4djkya.png" width="900" height="104"> | ||
|
||
3. Open the preferable pipeline and copy its identifier from the URL. On the screenshoot it is `f64cf79b-79ba-4c45-8039-57c9af5d4508` mentioned by red arrow at the top. | ||
|
||
<img src="https://habrastorage.org/webt/he/0j/c-/he0jc-ubwjfxajbn85lg_ysa14m.png" width="900" height="400"> | ||
|
||
4. Return to the deploying page, paste the identifier to the `PIPELINE_IDENTIFER` field. | ||
|
||
<img src="https://habrastorage.org/webt/zu/1v/wo/zu1vwo1y54o_efk9jqdrxpwpgeg.png" width="900" height="90"> | ||
|
||
5. Press the button named `Deploy app`. The process of deploying will start immediately as illustrated below. | ||
|
||
<img src="https://habrastorage.org/webt/0o/7l/k0/0o7lk0gv5lzp5ij14yepe17a4g4.png" width="900" height="340"> | ||
|
||
6. When build is finished, you can manage your application (rename, etc.) and view it (open URL in the browser). | ||
|
||
<img src="https://habrastorage.org/webt/wh/lo/sp/whlospuzvfmazjpdsrf52iduxf0.png" width="900" height="128"> | ||
|
||
7. To check if load balancer works properly, just open logs of each production back-end servers | ||
(`heroku logs --tail -a application-name` in the terminal), and send the request to the load balancer application. | ||
As the result, the load balancer will proxy your request to the each back-end server in round-robin method (one by one in order). | ||
|
||
<img src="https://habrastorage.org/webt/zm/bn/vj/zmbnvj7ztr3ho4y6xt6mfxt2qh4.png" width="900" height="480"> | ||
|
||
### How it works | ||
|
||
1. You specify pipeline's identifier (`PIPELINE_IDENTIFER`) to create load balancer for its applications in `production` stage. | ||
2. Through the [Heroku API](https://devcenter.heroku.com/categories/platform-api) using your `HEROKU_API_KEY`, URLs of applications are fetched. | ||
3. Then [configuration file for load balancing](http://nginx.org/en/docs/http/load_balancing.html) based on fetched URLS is created. | ||
4. And served by the [Nginx](https://nginx.org/en) in round-robin method (one by one in order). | ||
|
||
## Development | ||
|
||
Clone the project with the following command: | ||
|
||
```bash | ||
$ git clone https://github.com/dmytrostriletskyi/heroku-load-balancer.git | ||
$ cd heroku-load-balancer | ||
``` | ||
|
||
To build the project, use the following command: | ||
|
||
```bash | ||
$ docker build -t heroku-load-balancer . -f Dockerfile | ||
``` | ||
|
||
To run the project, use the following command. It will start the server and occupate current terminal session: | ||
|
||
```bash | ||
$ docker run -p 7979:7979 -v $PWD:/heroku-load-balancer \ | ||
-e PORT=7979 \ | ||
-e HEROKU_API_KEY='8af7dbb9-e6b8-45bd-8c0a-87787b5ae881' \ | ||
-e PIPELINE_IDENTIFIER='f64cf79b-79ba-4c45-8039-57c9af5d4508' \ | ||
--name heroku-load-balancer heroku-load-balancer | ||
``` | ||
|
||
If you need to enter the bash of the container, use the following command: | ||
|
||
```bash | ||
$ docker exec -it heroku-load-balancer bash | ||
``` | ||
|
||
Clean all containers with the following command: | ||
|
||
```bash | ||
$ docker rm $(docker ps -a -q) -f | ||
``` | ||
|
||
Clean all images with the following command: | ||
|
||
```bash | ||
$ docker rmi $(docker images -q) -f | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"name": "heroku-load-balancer", | ||
"description": "Automatic (with no manual configuring) load balancer for your Heroku pipeline production applications.", | ||
"repository": "https://github.com/dmytrostriletskyi/heroku-load-balancer", | ||
"env": { | ||
"HEROKU_API_KEY": { | ||
"description": "Heroku API key from the personal account.", | ||
"required": true | ||
}, | ||
"PIPELINE_IDENTIFIER": { | ||
"description": "Pipeline identifier for creating load balancer.", | ||
"required": true | ||
} | ||
}, | ||
"logo": "https://habrastorage.org/webt/w3/fp/ep/w3fpepqfjjtumhnyul4ymts-qm8.png", | ||
"keywords": ["nginx", "docker", "proxy", "balancing", "balancer"], | ||
"stack": "container" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
build: | ||
docker: | ||
web: Dockerfile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
add-trailing-comma==1.0.0 | ||
flake8-commas==2.0.0 | ||
flake8-comprehensions==2.1.0 | ||
flake8-docstrings==1.3.0 | ||
flake8-per-file-ignores==0.8.1 | ||
flake8-print==3.1.0 | ||
flake8==3.7.7 | ||
isort==4.3.20 | ||
pep8-naming==0.8.2 | ||
radon==3.0.3 | ||
safety==1.8.5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
click==7.0 | ||
requests==2.22.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
[isort] | ||
line_length=120 | ||
multi_line_output=3 | ||
include_trailing_comma=True | ||
force_grid_wrap=True | ||
combine_as_imports=True | ||
|
||
[flake8] | ||
max-line-length=120 | ||
ignore=D200, D413, D107, D100 | ||
per-file-ignores= | ||
*/__init__.py: D104, F401, D100, | ||
*/test_*: D205, | ||
src/constants.py: E501 | ||
|
||
[coverage:run] | ||
omit = | ||
*/.virtualenvs/*, | ||
*/virtualenv/*, | ||
|
||
*/__init__.py, | ||
tests/* |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
NGINX_LOAD_BALANCER_CONFIG_TEMPLATE = """events < | ||
worker_connections 4096; | ||
> | ||
http < | ||
gzip on; | ||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; | ||
proxy_ssl_server_name on; | ||
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; | ||
ssl_prefer_server_ciphers on; | ||
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; | ||
upstream main < | ||
{upstream_server_localhosts} | ||
> | ||
server < | ||
listen {port}; | ||
location / < | ||
proxy_pass http://main; | ||
> | ||
> | ||
{upstream_server_configs} | ||
> | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
""" | ||
Provide implementation of the command line interface. | ||
""" | ||
import click | ||
|
||
from src.heroku import ( | ||
GetHerokuPipelineProductionApplicationsUrls, | ||
HerokuApi, | ||
) | ||
from src.nginx import CreationNginxLoadBalancerConfigFile | ||
|
||
|
||
@click.group() | ||
def cli(): | ||
""" | ||
Command line interface root function. | ||
""" | ||
pass | ||
|
||
|
||
@click.command() | ||
@click.option( | ||
'--nginx-port', | ||
type=int, | ||
required=True, | ||
help='The port to the Nginx on.', | ||
) | ||
@click.option( | ||
'--heroku-api-key', | ||
type=str, | ||
required=True, | ||
help='The account\'s Heroku API key.', | ||
) | ||
@click.option( | ||
'--pipeline-identifier', | ||
type=str, | ||
required=True, | ||
help='Pipeline identifier to fetch applications for balancing.', | ||
) | ||
def create_load_balancer(nginx_port, heroku_api_key, pipeline_identifier): | ||
""" | ||
Create Nginx load balancer config file with pipeline's production applications URLs. | ||
""" | ||
heroku_api = HerokuApi( | ||
key=heroku_api_key, | ||
) | ||
|
||
get_heroku_pipeline_production_applications_urls = GetHerokuPipelineProductionApplicationsUrls( | ||
heroku_api=heroku_api, | ||
) | ||
|
||
pipeline_production_applications_urls = get_heroku_pipeline_production_applications_urls.by_pipeline_identifier( | ||
identifier=pipeline_identifier, | ||
) | ||
|
||
CreationNginxLoadBalancerConfigFile(port=nginx_port).with_urls(urls=pipeline_production_applications_urls) | ||
|
||
|
||
if __name__ == '__main__': | ||
cli.add_command(create_load_balancer) | ||
cli() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
""" | ||
Provide implements of the Heroku domain. | ||
""" | ||
import requests | ||
|
||
|
||
class HerokuApi: | ||
""" | ||
Implements Heroku API communicator. | ||
""" | ||
|
||
def __init__(self, key: str): | ||
""" | ||
Constructor. | ||
""" | ||
self.headers = { | ||
'Accept': 'application/vnd.heroku+json; version=3', | ||
'Authorization': f'Bearer {key}', | ||
} | ||
|
||
def fetch_pipeline_applications(self, identifier: str): | ||
""" | ||
Fetch a pipeline applications by its identifier. | ||
""" | ||
fetch_pipeline_applications_url = f'https://api.heroku.com/pipelines/{identifier}/pipeline-couplings' | ||
|
||
response = requests.get(fetch_pipeline_applications_url, headers=self.headers) | ||
response_json = response.json() | ||
|
||
return response_json | ||
|
||
def fetch_application(self, identifier: str): | ||
""" | ||
Fetch an application by its identifier. | ||
""" | ||
fetch_pipeline_applications_url = f'https://api.heroku.com/apps/{identifier}' | ||
|
||
response = requests.get(fetch_pipeline_applications_url, headers=self.headers) | ||
response_json = response.json() | ||
|
||
return response_json | ||
|
||
|
||
class GetHerokuPipelineProductionApplicationsUrls: | ||
""" | ||
Implement getting Heroku's pipeline production applications' URLs transaction. | ||
""" | ||
|
||
def __init__(self, heroku_api: HerokuApi): | ||
""" | ||
Constructor. | ||
""" | ||
self.heroku_api = heroku_api | ||
|
||
def by_pipeline_identifier(self, identifier): | ||
""" | ||
Get Heroku's pipeline production applications' URLs by pipeline identifier. | ||
""" | ||
production_applications_identifiers = [] | ||
|
||
for application in self.heroku_api.fetch_pipeline_applications(identifier=identifier): | ||
if application.get('stage') == 'production': | ||
application_identifier = application.get('app').get('id') | ||
production_applications_identifiers.append(application_identifier) | ||
|
||
production_applications_urls = [] | ||
|
||
for application_identifier in production_applications_identifiers: | ||
application = self.heroku_api.fetch_application(identifier=application_identifier) | ||
application_url = application.get('web_url') | ||
production_applications_urls.append(application_url) | ||
|
||
return production_applications_urls |
Oops, something went wrong.