blob: ea4c7ff173809692c36d2aba0476f416458ddea4 [file] [log] [blame] [view] [edit]
Multi-Master Configuration
==========================
This document describes how to setup multiple Gerrit master
servers using shared back-ends: shared DB and shared git
repos. With multiple Gerrit masters it is possible to
mitigate server load by allowing users to access a server
which has more free resources, and it is also possible to
provide higher availability by allowing service to be
transferred to any remaining masters when a master fails.
Running a multi-master setup is inherently complicated and,
depending on the configuration and external hardware and
software available, it is possible to configure a multi-
master setup with very different levels of service.
Supported configuration options are outlined below.
Sharing Git Repositories
------------------------
A single site multi-master arrangement generally
consists of pointing all the masters to the same copy of
the git repositories via a shared file system.
### Simple Shared Host Test Setup ###
The simplest example of this is to host two gerrit servers
on the same host and to point the two servers at the same
directory where the git repos are located. This is likely
only useful as an example for testing, since running two
gerrit servers on the same host is unlikely to provide any
additional load balancing or to improve availability very
much. But this may be useful for those interested in
understanding how a multi-master setup works since it is
very easy to setup.
### Sharing via a Network File System ###
Running each master on a separate physical host provides a
load balancing improvement. A shared file system such as
NFS is likely the best option for this arrangement,
although any JGit supported shared file system will work.
To share the git repos, initialize the Gerrit instances as
shown below:
```
$ java -jar {path/}gerrit.war init -d <site1>
# ...
*** Git Repositories
***
Location of Git repositories [git]: /path/to/nfs/git
# choose any location on the network shared file system
for git repository location. NOTE: the location of the
git repository must be shared by all servers. If you
use the default, [git], then the repository will be
located on this server in 'site1/git'. It is
recommended that you avoid the default and specify the
full path to the git repository, even if it is on this
server. The full path can then be used when setting up
the other servers.
# ...
```
```
$ java -jar {path/}gerrit.war init -d <site2>
# ...
*** Git Repositories
***
Location of Git repositories [git]: /path/to/nfs/git
# choose the same git repository location that the first
instance uses
# ...
```
Sharing The Database
--------------------
All servers must use the same database to ensure users
have access to the same data. Therefore PostgreSQL or MySQL
should be used as the database. The default, embedded H2
db, cannot be used since multiple servers cannot connect to
the same H2 database in embedded mode.
To share the database, initialize the Gerrit instances as
shown below:
```
$ java -jar {path/}gerrit.war init -d <site1>
# ...
*** SQL Database
***
Database server type [H2/?]:
# choose anything except "h2" server type for database
# choose database options, username, and password based
on your setup
# ...
```
```
$ java -jar {path/}gerrit.war init -d <site2>
# ...
*** SQL Database
***
Database server type [H2/?]:
# choose same options as for the first instance for sql
database, since we want all instances to use the same
database.
# ...
```
Cache Coherency
---------------
Gerrit uses caches to speed up data access. Each server
uses its own local cache which means data in these
caches is not shared with the other servers. While
incoherent caches across servers will not result in data
consistency issues at the database or git layers (i.e. it
will not corrupt your data), it can result in many user
facing issues.
Generally speaking, the user facing issues result in an
unpleasant user experience, for example, new projects
created on one server do not show up on the other servers.
However, in some cases cache coherency issues may be
considered to have security implications. For example,
there is a large delay before a group membership change on
one server shows up on the other servers. This delay can
result in outdated ACLs being applied to certain users.
Some caches of note are:
* accounts: important details of an active user, including
their display name, preferences, known email addresses,
and group memberships
* projects: project description records
* groups_members: group membership information
* sshkeys: unpacked versions of user SSH keys, so the
internal SSH daemon can match against them during
authentication
### Brute Force Cache Coherency ###
The simplest solution to ensure cache coherency is to
disable the caches so each server is forced to get
up-to-date data from the database and repos on every
request. Obviously this approach has a performance
impact on masters and should only be used if your
benefits of having multiple masters outweighs this
performance loss.
To disable the caches add the following lines to each
server's config, `<site>/etc/gerrit.config`:
```
[cache "accounts"]
memoryLimit = 0
diskLimit = 0
[cache "projects"]
memoryLimit = 0
diskLimit = 0
[cache "groups_members"]
memoryLimit = 0
diskLimit = 0
[cache "sshkeys"]
memoryLimit = 0
diskLimit = 0
```
Restart all servers for the config changes to take effect.
```
$ ./<site1>/bin/gerrit.sh restart
$ ./<site2>/bin/gerrit.sh restart
```
Web Sessions
------------
Web sessions are a special case of caches. The web session
caches are authoritative and the sessions are not stored
anywhere else. So with a standard gerrit it is not possible
to share web sessions across masters.
### Non-Shared Web Sessions ###
Since a web session is identified using the hostname,
it is possible to simply have different sessions for each
master server. With such a setup, logging in or out of one
master does not log the user in or out of the other masters.
The masters simply appear as different sites to users (but
the back-end data is still the same). Naturally, without
shared web sessions, no automatic load balancing, or fail
over is available for these sessions.
Non-shared web sessions does not provide an ideal solution;
users must manually switch between the servers to load
balance or fail over. Mostly this does not provide a very
satisfying user experience, however, there are cases where
such a solution still provides an overall improvement over
using a single master.
One way to improve the experience is to point each user to
a per user master and attempting to split users evenly
across servers. This may work well when there is a natural
split in your users: perhaps to split users from a remote
site, or automation users off to their own server. Such
splits might happen without a multi-master solution anyway
in many situations by using independent masters. Lacking
an alternative it is worth asking if the non-shared web
sessions approach might be an improvement over independent
master. See [rationale](#rationale) for other reasons why
you might want to use this setup.
HTTP Access
-----------
Each server must listen to a different http ip:port
combination. In order to load balance or fail-over users
to different masters, they must get distributed across
masters.
### Separate Host URLs ###
If web sessions are not shared across masters, different
host URLs must be used for each master and http load
balancing and fail over must be done manually. Using a
different host URL will distribute users based on the
initial URL they choose to access the master. Subsequent
accesses will be to the same master.
While separate host URLs does not provide a great http
user experience, see [rationale](#rationale) for why you
might want to do this absent any other solutions.
```
$ java -jar {path/}gerrit.war init -d <site1>
# ...
*** HTTP Daemon
***
Listen on address [*]: server1
Listen on port [8080]: port1
```
```
$ java -jar {path/}gerrit.war init -d <site2>
# ...
*** HTTP Daemon
***
Listen on address [*]: server2
Listen on port [8080]: port2
# choose a different <ip>:<port> combination for HTTP
daemons than what the first instance uses (if this
instance is on another server, you can still use the
defaults)
# ...
```
### Same Host URLs and Load Balancing ###
NOTE: Shared web sessions must be setup first.
Accessing masters on different servers using the same host
URL requires using a load balancer. By connecting to the
masters through a load balancer, the users will see only one
hostname (the load balancer's), and thus will have just one
session. Any standard load balancer can be used.
The load balancer's front-end http address should be made
different from that of any master. Configure the load
balancer's back-end with the http addresses of all the
masters. To have the masters direct clients to connect to
the load balancer's http address, add the following lines to
each master's config, `<site>/etc/gerrit.config`:
```
[gerrit]
canonicalWebUrl = http[s]://<ip>:<port>
# http address of the load balancer
```
Restart all servers for the config changes to take effect.
A sample setup using HAProxy is given below:
```
global
daemon
pidfile /var/run/haproxy.pid
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind <ip>:<http_port>
# NOTE: users should connect over http to
<ip>:<http_port>, which should be the same as the
gerrit.canonicalWebUrl parameter in the
'gerrit.config' files
default_backend http-servers
backend http-servers
server server1 <server1_ip>:<server1_http_port>
server server2 <server2_ip>:<server2_http_port>
```
See [Using HAProxy](#HAProxy) for how to start and stop HAProxy.
SSH Access
----------
As with http, each server must listen to a different ssh
ip:port combination.
```
$ java -jar {path/}gerrit.war init -d <site1>
# ...
*** SSH Daemon
***
Listen on address [*]: server1
Listen on port [29418]: port1
```
```
$ java -jar {path/}gerrit.war init -d <site2>
# ...
*** SSH Daemon
***
Listen on address [*]: server2
Listen on port [29418]: port2
# choose a different <ip>:<port> combination for SSH
daemons than what the first instance uses (if this
instance is on another server, you can still use the
defaults)
# ...
```
To prevent users from getting a security warning when
connecting over ssh, have all masters use the same ssh-rsa
host key by copying '\<site\>/etc/ssh_host_rsa_key' and
'\<site\>/etc/ssh_host_rsa_key.pub' from any one master to
the others.
### Load Balancing ###
In order to load balance or fail-over users to different
masters they must be distributed across masters. However,
since ssh sessions are not persistent across connections,
any standard ssh load balancer can be used to distribute ssh
connections across the available masters.
The load balancer's front-end ssh address should be made
different from that of any master. Configure the load
balancer's back-end with the ssh addresses of all the
masters. To have the masters direct clients to connect to
the load balancer's ssh address, add the following lines to
each master's config, `<site>/etc/gerrit.config`:
```
[sshd]
advertisedAddress = <ip>:<port> # ssh address of the
load balancer
```
Restart all servers for the config changes to take effect.
A sample <haproxy_config> file for HAProxy:
```
global
daemon
pidfile /var/run/haproxy.pid
defaults
mode tcp
retries 3
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend ssh-in
bind <ip>:<ssh_port>
# NOTE: users should connect over ssh to
# <ip>:<ssh_port>, which should be the same as the
# sshd.advertisedAddress parameter in the
# 'gerrit.config' files
default_backend ssh-servers
backend ssh-servers
option redispatch
server server1 <server1_ip>:<server1_ssh_port> check
server server2 <server2_ip>:<server2_ssh_port> check
```
See [Using HAProxy](#HAProxy) for how to start and stop HAProxy.
###<a id="rationale"> Rationale</a>
The different host URL setup is valuable if you mainly
care to load balance ssh traffic and don't care which http
master your users hit. Gerrit http traffic is generally
very light compared to Gerrit ssh traffic (unless git over
http is used). This can be used as a simple upgrade path
for slaves allowing them to be used as masters for ssh
data. This is also useful for load balancing anonymous
git http traffic since it does not require a session. If
you choose to use multi-masters only for ssh, you want to
set your canonical URL to point to the single http master
so that change upload messages created by each master
point to the correct http URL.
###<a id="HAProxy">Using HAProxy</a>
For more information go [here][http://haproxy.1wt.eu/].
Check if the HAProxy config file is valid:
```
$ sudo haproxy -f <haproxy_config> -c
```
Start HAProxy:
```
$ sudo haproxy -f <haproxy_config>
```
HAProxy can be stopped using "sudo kill \<haproxy_pid\>".
The HAProxy PID can be found using "ps -e | grep haproxy".
If you are using the example config file, the PID can also
be found in '/var/run/haproxy.pid'. To reload a new
configuration with minimal service impact and without
breaking existing sessions, run:
```
$ sudo haproxy -f haproxy.cfg -sf <haproxy_pid>
```