PowerGSLB is a simple DNS based Global Server Load Balancing (GSLB) solution.
- Main features
- Database diagram
- Class diagram
- Web based administration interface
- Installation on CentOS 7
- Health checks
- Building PowerGSLB RPM packages
- Using PowerGSLB Docker image
- Building PowerGSLB Docker image
- Install using VirtualEnv
- Nginx as reverse proxy
- Checks
- LB mode
- Tests
- Quick installation and setup
- Written in Python 2.7
- Built as PowerDNS Authoritative Server Remote Backend
- Web based administration interface using w2ui
- HTTPS support for the web server
- DNS GSLB configuration stored in a MySQL / MariaDB database
- Master-Slave DNS GSLB using native MySQL / MariaDB replication
- Multi-Master DNS GSLB using native MySQL / MariaDB Galera Cluster
- Check status and data stored in local Redis instance Redis
- Modular architecture
- Multithreaded architecture
- Systemd status and watchdog support
- Extendable health checks:
- ICMP ping
- TCP connect
- HTTP(s) request
- Arbitrary command execution
- Fallback if all the checks failed
- Weighted (priority) records
- Per record client IP / subnet persistence
- DNS GSLB views support
- All-in-one Docker image
yum -y install epel-release
yum -y update
yum -y install python2-pip
pip install pyping
VERSION=1.7.1
yum -y --setopt=tsflags= install \
"https://github.com/AlekseyChudov/powergslb/releases/download/$VERSION/powergslb-$VERSION-1.el7.noarch.rpm" \
"https://github.com/AlekseyChudov/powergslb/releases/download/$VERSION/powergslb-admin-$VERSION-1.el7.noarch.rpm" \
"https://github.com/AlekseyChudov/powergslb/releases/download/$VERSION/powergslb-pdns-$VERSION-1.el7.noarch.rpm"
sed -i 's/^password = .*/password = your-database-password-here/g' /etc/powergslb/powergslb.conf
cp /etc/pdns/pdns.conf /etc/pdns/pdns.conf~
cp "/usr/share/doc/powergslb-pdns-$VERSION/pdns/pdns.conf" /etc/pdns/pdns.conf
yum -y install mariadb-server
sed -i '/\[mysqld\]/a bind-address=127.0.0.1\ncharacter_set_server=utf8' /etc/my.cnf.d/server.cnf
systemctl enable mariadb.service
systemctl start mariadb.service
systemctl status mariadb.service
mysql_secure_installation
VERSION=1.7.1
mysql -p << EOF
CREATE DATABASE powergslb;
GRANT ALL ON powergslb.* TO powergslb@localhost IDENTIFIED BY 'your-database-password-here';
USE powergslb;
source /usr/share/doc/powergslb-$VERSION/database/scheme.sql
source /usr/share/doc/powergslb-$VERSION/database/data.sql
EOF
systemctl enable powergslb.service pdns.service
systemctl start powergslb.service pdns.service
systemctl status powergslb.service pdns.service
yum -y install bind-utils
dig @127.0.0.1 example.com SOA
dig @127.0.0.1 example.com A
dig @127.0.0.1 example.com AAAA
dig @127.0.0.1 example.com ANY
Open URL https://SERVER/admin/.
- Default username: admin
- Default password: admin
Health checks are configured in the "Monitors" sidebar section in JSON format.
Supported check types:
type | description |
---|---|
exec | arbitrary command execution |
icmp | ICMP ping |
http | HTTP request |
tcp | TCP connect |
General parameters for all checks:
parameter | description |
---|---|
type | check type |
interval | interval between checks |
timeout | check timeout |
fall | number of failed checks to disable record |
rise | number of successful checks to enable record |
parameter | description |
---|---|
type | exec |
args | command to execute and arguments |
Example:
{"type": "exec", "args": ["/etc/powergslb/powergslb-check", "%(content)s"], "interval": 3, "timeout": 1, "fall": 3, "rise": 5}
parameter | description |
---|---|
type | icmp |
ip | endpoint IP address |
Example:
{"type": "icmp", "ip": "%(content)s", "interval": 3, "timeout": 1, "fall": 3, "rise": 5}
parameter | description |
---|---|
type | http |
url | endpoint URL |
Example:
{"type": "http", "url": "http://%(content)s/status", "interval": 3, "timeout": 1, "fall": 3, "rise": 5}
parameter | description |
---|---|
type | tcp |
ip | endpoint IP address |
port | endpoint port number |
Example:
{"type": "tcp", "ip": "%(content)s", "port": 80, "interval": 3, "timeout": 1, "fall": 3, "rise": 5}
You should always create RPM packages in a clean environment and preferably on a separate machine!
Please read How to create an RPM package.
yum -y update
yum -y install @Development\ Tools
VERSION=1.7.1
curl "https://codeload.github.com/AlekseyChudov/powergslb/tar.gz/$VERSION" -o "powergslb-$VERSION.tar.gz"
rpmbuild -tb --define "version $VERSION" "powergslb-$VERSION.tar.gz"
Upon successful completion you will have three packages
~/rpmbuild/RPMS/noarch/powergslb-$VERSION-1.el7.noarch.rpm
~/rpmbuild/RPMS/noarch/powergslb-admin-$VERSION-1.el7.noarch.rpm
~/rpmbuild/RPMS/noarch/powergslb-pdns-$VERSION-1.el7.noarch.rpm
For quick setup, you can pull all-in-one Docker image from docker.io.
VERSION=1.7.1
docker pull docker.io/alekseychudov/powergslb:"$VERSION"
docker run -it --name powergslb --hostname powergslb docker.io/alekseychudov/powergslb:"$VERSION"
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' powergslb
docker exec -it powergslb bash
docker stop powergslb
For systemd to run in Docker container the following SELinux boolean should be enabled.
semanage boolean --modify --on container_manage_cgroup
To create an all-in-one Docker image.
VERSION=1.7.1
docker build -f docker/Dockerfile --build-arg VERSION="$VERSION" \
--force-rm --no-cache -t powergslb:"$VERSION" https://github.com/AlekseyChudov/powergslb.git
> virtualenv powergslb
> cd powergslb
> source bin/activate
> git clone https://github.com/AlekseyChudov/powergslb.git
> cd powergslb
> pip install -r requirements.txt
> sudo ln -s $(pwd)/powergslb/powergslb.service /etc/systemd/system/powergslb.service
> python setup.py install ; sudo service powergslb restart
Example:
upstream powergslb {
server localhost:8080;
}
server {
listen 80;
server_name powergslb.local;
return 301 https://$host$request_uri;
}
server {
server_name powergslb.local;
# SSL configuration
#
listen 443 ssl;
listen [::]:443 ssl;
#
# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
#
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
#include snippets/snakeoil.conf;
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
location / {
proxy_pass http://powergslb;
proxy_set_header Host $host;
proxy_set_header Referer "";
proxy_set_header X-Remotebackend-Real-Remote $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
}
error_log /var/log/nginx/powergslb_error.log;
access_log /var/log/nginx/powergslb_access.logd;
}
Checks are implemented in src/powergslb/monitor/.
Supported types are :
- icmp
- tcp
- http
- https
- command execution
type: the type of check interval: timeout: fall: rise:
Specific Arguments:
- ip: the endpoint IP
Example:
{"type": "icmp", "ip": "%(content)s", "interval": 3, "timeout": 1, "fall": 3, "rise": 5}
Specific Arguments:
- ip: the endpoint IP
- port: the endpoint port
Example:
{"type": "tcp", "ip": "%(content)s", "port": 9200, "interval": 3, "timeout": 1, "fall": 3, "rise": 5}
Specific Arguments:
- url: the url to check
Optionnal Arguments:
- headers: add specififc request headers (default to empty dictionnary {})
- store: if True store returned value in timeserie database (default to False)
Example:
{"type": "http", "url": "http://%(content)s/status", "headers": {}, "interval": 3, "timeout": 1, "fall": 3, "rise": 5, "store": False}
Specific Arguments:
- args: the command to execute
Optionnal Arguments:
- store: if True store returned value in timeserie database (default to False)
Example:
{"type": "exec", "args": ["/etc/powergslb/powergslb-check", "%(content)s"], "interval": 3, "timeout": 1, "fall": 3, "rise": 5}
Specific Arguments:
- url: the url to check
Optionnal Arguments:
- secure: check certificate '(default to True)
- headers: add specififc request headers (default to empty dictionnary {})
- store: if True store returned value in timeserie database (default to False)
Example:
{"type": "https", "url": "https://%(content)s/ping?fmt=json", "secure": False,"headers": {"Host": "test.local", "Accept": "text/html"}, "interval": 3, "timeout": 1, "fall": 3, "rise": 5, "store": False}
Records with highest weigth are send, DNS LB is only efficient on A records. On CNAME only first record will always be selected.
Client IP and record content are compared to a topology map in order to extract a region. If both region match, only these records will be selected.
Based on topology map like :
_lb_topology_map = {
'region1': [ '10.10.0.0/16' ],
'region2': [ '10.15.0.0/16' ],
...
}
If client IP is 10.10.0.1 and possible records are 10.10.0.10 and 10.15.0.10, only 10.10.0.10 will be sent. If 10.10.0.10 is detected as down, 10.15.0.10 will then be send.
Filter records using topology map and then, apply priority loadbalancing method.
One record is sent randomly based on available records respective weight.
Record probability = [record weight] / sum( [record weight] )
Mix both topology and weighted round robin (only for A records).
Use response timeseries data in order to select the record with the lowest check response.
As response measurements are stored on a local Redis Database on each GSLB servers, the lowest data is not always relevant. Typically, timseries data based on response time or network time are biased by the GSLB server position compared to the monitored endpoints.
This kind of information are relevant only if client DNS selection is based on minimal round trip time or if Timseries store information that are independent of the GSLB position (like Load, CPU, bandwith...).
You can set the client IP at the value you want using the http header X-Remotebackend-Real-Remote.
clients IPs: 10.10.0.1 and 10.15.0.1 t.example.net: 2 A records 10.10.0.10 and 10.15.0.10
Client 10.10.0.1 receives 10.10.0.10:
curl --silent -H 'X-Remotebackend-Real-Remote: 10.10.0.1' -H 'host: powergslb.local' http://127.0.0.1:8080/dns/lookup/t.example.net/ANY | jq .
{
"result": [
{
"content": "10.10.0.10",
"qtype": "A",
"qname": "t.example.net",
"ttl": 10
}
]
}
Client 10.15.0.1 receives 10.15.0.10:
> curl --silent -H 'X-Remotebackend-Real-Remote: 10.15.0.1' -H 'host: powergslb.local' http://127.0.0.1:8080/dns/lookup/t.example.net/ANY | jq .
{
"result": [
{
"content": "10.15.0.10",
"qtype": "A",
"qname": "t.example.net",
"ttl": 10
}
]
}
If checks ara actives, when an IP is failing, the other IP will be delivered to every clients.
Client IP: 192.168.0.1 wrr.example.net: A records 10.10.0.10 with a weight of 1 and 10.15.0.10 with a weight of 2
> curl --silent -H 'X-Remotebackend-Real-Remote: 192.168.0.1' -H 'host: powergslb.local' http://127.0.0.1:8080/dns/lookup/wrr.example.net/ANY | jq .
{
"result": [
{
"content": "10.15.0.10",
"qtype": "A",
"qname": "wrr.example.net",
"ttl": 10
}
]
}
Client will receive 10.15.0.10 two times more than 10.10.0.10.