From ca29ae406817e9da87aaf1efedcb2914e6f80b8c Mon Sep 17 00:00:00 2001 From: Grant Young Date: Tue, 9 Nov 2021 14:09:01 +0000 Subject: [PATCH 01/23] Expand Database Load Balancing docs --- .../img/db_load_balancing_postgres_stats.png | Bin 21543 -> 0 bytes .../database_load_balancing.md | 211 +++++++----------- .../img/db_load_balancing_postgres_stats.png | Bin 0 -> 99288 bytes 3 files changed, 75 insertions(+), 136 deletions(-) delete mode 100644 doc/administration/img/db_load_balancing_postgres_stats.png rename doc/administration/{ => postgresql}/database_load_balancing.md (64%) create mode 100644 doc/administration/postgresql/img/db_load_balancing_postgres_stats.png diff --git a/doc/administration/img/db_load_balancing_postgres_stats.png b/doc/administration/img/db_load_balancing_postgres_stats.png deleted file mode 100644 index 8b311616e7b600ed5191a13821faf25eaafbdf97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21543 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 WxDU$~IxsLWFnGH9xvX - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1283) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.0. > - [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60894) from GitLab Premium to GitLab Free in 14.0. - -Distribute read-only queries among multiple database servers. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334494) for Sidekiq in GitLab 14.1. ## Overview -Database load balancing improves the distribution of database workloads across -multiple computing resources. Load balancing aims to optimize resource use, -maximize throughput, minimize response time, and avoid overload of any single -resource. Using multiple components with load balancing instead of a single -component may increase reliability and availability through redundancy. -[_Wikipedia article_](https://en.wikipedia.org/wiki/Load_balancing_(computing)) - -When database load balancing is enabled in GitLab, the load is balanced using -a simple round-robin algorithm, without any external dependencies such as Redis. +With Database Load Balancing, read-only queries can be distributed across +all PostgreSQL nodes to increase performance. -In the following image, you can see the load is balanced rather evenly among -all the secondaries (`db4`, `db5`, `db6`). Because `SELECT` queries are not -sent to the primary (unless necessary), the primary (`db3`) hardly has any load. +This functionality is provided natively in relevant GitLab components where +it can be configured to balance the load round robin, without any external dependencies. ![DB load balancing graph](img/db_load_balancing_postgres_stats.png) -## Requirements - -For load balancing to work, you need at least PostgreSQL 11 or newer, -[**MySQL is not supported**](../install/requirements.md#database). You also need to make sure that you have -at least 1 secondary in [hot standby](https://www.postgresql.org/docs/11/hot-standby.html) mode. +### Requirements -Load balancing also requires that the configured hosts **always** point to the -primary, even after a database failover. Furthermore, the additional hosts to -balance load among must **always** point to secondary databases. This means that -you should put a load balancer in front of every database, and have GitLab connect -to those load balancers. +Note the following requirements for Database Load Balancing: -For example, say you have a primary (`db1.gitlab.com`) and two secondaries, -`db2.gitlab.com` and `db3.gitlab.com`. For this setup, you need to have 3 -load balancers, one for every host. For example: +- A HA Postgres setup that has 1 or more secondary nodes replicating the primary. +- Each Postgres node can be connected with the same credentials and on the same port. -- `primary.gitlab.com` forwards to `db1.gitlab.com` -- `secondary1.gitlab.com` forwards to `db2.gitlab.com` -- `secondary2.gitlab.com` forwards to `db3.gitlab.com` +Additionally the following is recommended but not required: -Now let's say that a failover happens and db2 becomes the new primary. This -means forwarding should now happen as follows: +- PgBouncer configured on each PostgreSQL node to pooling all load balanced connections. + - For external PostgreSQL setups, a different database pooler could be used as desired. -- `primary.gitlab.com` forwards to `db2.gitlab.com` -- `secondary1.gitlab.com` forwards to `db1.gitlab.com` -- `secondary2.gitlab.com` forwards to `db3.gitlab.com` +## Configuring Database Load Balancing -GitLab does not take care of this for you, so you need to do so yourself. +Database Load Balancing can be configured in one of two ways: -Finally, load balancing requires that GitLab can connect to all hosts using the -same credentials and port as configured in the -[Enabling load balancing](#enabling-load-balancing) section. Using -different ports or credentials for different hosts is not supported. +- Hosts - A list of PostgreSQL hosts. (recommended) +- Service Discovery - A DNS record that returns a list of PostgreSQL hosts. -## Use cases +Refer below to each section for more details. -- For GitLab instances with thousands of users and high traffic, you can use - database load balancing to reduce the load on the primary database and - increase responsiveness, thus resulting in faster page load inside GitLab. - -## Enabling load balancing +### Hosts +Configuring with a list of hosts is straight forward For the environment in which you want to use load balancing, you'll need to add the following. This balances the load between `host1.example.com` and `host2.example.com`. -**In Omnibus installations:** - 1. Edit `/etc/gitlab/gitlab.rb` and add the following line: ```ruby @@ -85,39 +56,7 @@ the following. This balances the load between `host1.example.com` and 1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. ---- - -**In installations from source:** - -1. Edit `/home/git/gitlab/config/database.yml` and add or amend the following lines: - - ```yaml - production: - username: gitlab - database: gitlab - encoding: unicode - load_balancing: - hosts: - - host1.example.com - - host2.example.com - ``` - -1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect. - -### Load balancing for Sidekiq - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334494) in GitLab 14.1, load balancing for Sidekick is enabled by default. - -Sidekiq jobs mostly write to the primary database, but there are read-only jobs that can benefit -from the use of Sidekiq load balancing. -These jobs can use load balancing and database replicas to read the application state. -This allows to offload the primary database. - -For Sidekiq, we can define -[data consistency](../development/sidekiq_style_guide.md#job-data-consistency-strategies) -requirements for a specific job. - -## Service Discovery **(PREMIUM SELF)** +### Service Discovery **(PREMIUM SELF)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/5883) in GitLab 11.0. @@ -131,25 +70,23 @@ and an A record containing the IP addresses of your secondaries. To use service discovery you need to change your `database.yml` configuration file so it looks like the following: -```yaml -production: - username: gitlab - database: gitlab - encoding: unicode - load_balancing: - discover: - nameserver: localhost - record: secondary.postgresql.service.consul - record_type: A - port: 8600 - interval: 60 - disconnect_timeout: 120 +1. Edit `/etc/gitlab/gitlab.rb` and add the following line: + +```ruby +gitlab_rails['db_load_balancing'] = { 'discover' => { + 'nameserver' => 'localhost' + 'record' => 'postgresql-ha.service.consul' + 'record_type' => 'A' + 'port' => '8600' + 'interval' => '60' + 'disconnect_timeout' => '120' + } +} ``` -Here, the `discover:` section specifies the configuration details to use for -service discovery. +1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. -### Configuration +#### Configuration The following options can be set: @@ -183,26 +120,62 @@ Some nameservers (like [Consul](https://www.consul.io/docs/discovery/dns#udp-bas queried over UDP. To overcome this issue, you can use TCP for querying by setting `use_tcp` to `true`. -## Balancing queries +### Handling Stale Reads **(PREMIUM SELF)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3526) in GitLab 10.3. + +To prevent reading from an outdated secondary the load balancer checks if it +is in sync with the primary. If the data is determined to be recent enough the +secondary is used, otherwise it is ignored. To reduce the overhead of +these checks we only perform these checks at certain intervals. + +There are three configuration options that influence this behavior: + +| Option | Description | Default | +|------------------------------|----------------------------------------------------------------------------------------------------------------|------------| +| `max_replication_difference` | The amount of data (in bytes) a secondary is allowed to lag behind when it hasn't replicated data for a while. | 8 MB | +| `max_replication_lag_time` | The maximum number of seconds a secondary is allowed to lag behind before we stop using it. | 60 seconds | +| `replica_check_interval` | The minimum number of seconds we have to wait before checking the status of a secondary. | 60 seconds | + +The defaults should be sufficient for most users. Should you want to change them +you can specify them in `config/database.yml` like so: + +```yaml +production: + username: gitlab + database: gitlab + encoding: unicode + load_balancing: + hosts: + - host1.example.com + - host2.example.com + max_replication_difference: 16777216 # 16 MB + max_replication_lag_time: 30 + replica_check_interval: 30 +``` + +## Implementation Details + +### Balancing queries Read-only `SELECT` queries balance among all the secondary hosts. Everything else (including transactions) executes on the primary. Queries such as `SELECT ... FOR UPDATE` are also executed on the primary. -## Prepared statements +### Prepared statements Prepared statements don't work well with load balancing and are disabled automatically when load balancing is enabled. This should have no impact on response timings. -## Primary sticking +### Primary sticking After a write has been performed, GitLab sticks to using the primary for a certain period of time, scoped to the user that performed the write. GitLab reverts back to using secondaries when they have either caught up, or after 30 seconds. -## Failover handling +### Failover handling In the event of a failover or an unresponsive database, the load balancer tries to use the next available host. If no secondaries are available the @@ -214,7 +187,7 @@ operation is retried up to 3 times using an exponential back-off. When using load balancing, you should be able to safely restart a database server without it immediately leading to errors being presented to the users. -## Logging +### Logging The load balancer logs various events in [`database_load_balancing.log`](logs.md#database_load_balancinglog), such as @@ -236,37 +209,3 @@ For example: ```json {"severity":"INFO","time":"2019-09-02T12:12:01.728Z","correlation_id":"abcdefg","event":"host_online","message":"Host came back online","db_host":"111.222.333.444","db_port":null,"tag":"rails.database_load_balancing","environment":"production","hostname":"web-example-1","fqdn":"gitlab.example.com","path":null,"params":null} ``` - -## Handling Stale Reads **(PREMIUM SELF)** - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3526) in GitLab 10.3. - -To prevent reading from an outdated secondary the load balancer checks if it -is in sync with the primary. If the data is determined to be recent enough the -secondary is used, otherwise it is ignored. To reduce the overhead of -these checks we only perform these checks at certain intervals. - -There are three configuration options that influence this behavior: - -| Option | Description | Default | -|------------------------------|----------------------------------------------------------------------------------------------------------------|------------| -| `max_replication_difference` | The amount of data (in bytes) a secondary is allowed to lag behind when it hasn't replicated data for a while. | 8 MB | -| `max_replication_lag_time` | The maximum number of seconds a secondary is allowed to lag behind before we stop using it. | 60 seconds | -| `replica_check_interval` | The minimum number of seconds we have to wait before checking the status of a secondary. | 60 seconds | - -The defaults should be sufficient for most users. Should you want to change them -you can specify them in `config/database.yml` like so: - -```yaml -production: - username: gitlab - database: gitlab - encoding: unicode - load_balancing: - hosts: - - host1.example.com - - host2.example.com - max_replication_difference: 16777216 # 16 MB - max_replication_lag_time: 30 - replica_check_interval: 30 -``` diff --git a/doc/administration/postgresql/img/db_load_balancing_postgres_stats.png b/doc/administration/postgresql/img/db_load_balancing_postgres_stats.png new file mode 100644 index 0000000000000000000000000000000000000000..22cd3d7025ed641ae42160562b03776525c34dba GIT binary patch literal 99288 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

EM|2oy??+xX1(&mMSEPkGSFF)mYyRH7! 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 O1_n=8KbLh*2~7aJB+=;r literal 0 HcmV?d00001 -- GitLab From 6d0f52eb44c6f1e309dcddb5ff8fae442be6897c Mon Sep 17 00:00:00 2001 From: Grant Young Date: Tue, 9 Nov 2021 15:02:03 +0000 Subject: [PATCH 02/23] Add new database diagram --- .../postgresql/replication_and_failover.md | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index 01fe4bf64baa1e..f55a32a5b4e391 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -25,7 +25,44 @@ replication and failover requires: - A minimum of three `Consul` server nodes. - A minimum of one `pgbouncer` service node, but it's recommended to have one per database node. An internal load balancer (TCP) is required when there is more than one `pgbouncer` service node. -![PostgreSQL HA Architecture](img/pg_ha_architecture.png) +```plantuml +@startuml database_load_balancing +card "**Internal Load Balancer**" as ilb #9370DB + +together { + card "**GitLab Rails / Sidekiq**" as gitlab #32CD32 +} + +collections "**Consul** x3" as consul #e76a9b + +card "Database" as database { + card "**PGBouncer**\n//Consul//" as pgbouncer1 #4EA7FF + card "**PGBouncer**\n//Consul//" as pgbouncer2 #4EA7FF + card "**PGBouncer**\n//Consul//" as pgbouncer3 #4EA7FF + + card "**PostgreSQL** //Primary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_primary #4EA7FF + card "**PostgreSQL** //Secondary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary1 #4EA7FF + card "**PostgreSQL** //Secondary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary2 #4EA7FF + + pgbouncer1 -[#4EA7FF]-> postgres_primary + pgbouncer2 -[#4EA7FF]-> postgres_primary + pgbouncer3 -[#4EA7FF]-> postgres_primary + postgres_primary .[#4EA7FF]-> postgres_secondary1 + postgres_primary .[#4EA7FF]-> postgres_secondary2 +} + +gitlab -[#32CD32]-> ilb +gitlab .[#32CD32]-> postgres_primary +gitlab .[#32CD32]-> postgres_secondary1 +gitlab .[#32CD32]-> postgres_secondary2 + +ilb -[#9370DB]-> pgbouncer1 +ilb -[#9370DB]-> pgbouncer2 +ilb -[#9370DB]-> pgbouncer3 + +consul .[#e76a9b]-> database +@enduml +``` You also need to take into consideration the underlying network topology, making sure you have redundant connectivity between all Database and GitLab instances @@ -467,6 +504,8 @@ Refer to your preferred Load Balancer's documentation for further guidance. ### Configuring the Application nodes +This + Application nodes run the `gitlab-rails` service. You may have other attributes set, but the following need to be set. -- GitLab From 3fa775130dab5dfdff83c1ba2048a6a79af30f46 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Tue, 9 Nov 2021 15:11:51 +0000 Subject: [PATCH 03/23] Tweak diagram further --- .../postgresql/img/pg_ha_architecture.png | Bin 18308 -> 0 bytes .../postgresql/replication_and_failover.md | 1 + 2 files changed, 1 insertion(+) delete mode 100644 doc/administration/postgresql/img/pg_ha_architecture.png diff --git a/doc/administration/postgresql/img/pg_ha_architecture.png b/doc/administration/postgresql/img/pg_ha_architecture.png deleted file mode 100644 index 5d2a4a584bfe8ef098628c7897a26b355d5f351c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18308 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%0D9x4RKvAO0#S+^5Q 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 Uz8M@}n?X7~UHx3vIVCg!0O(Q!Q2+n{ diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index f55a32a5b4e391..6275808900f585 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -49,6 +49,7 @@ card "Database" as database { pgbouncer3 -[#4EA7FF]-> postgres_primary postgres_primary .[#4EA7FF]-> postgres_secondary1 postgres_primary .[#4EA7FF]-> postgres_secondary2 + postgres_secondary1 .[hidden]u-> postgres_primary } gitlab -[#32CD32]-> ilb -- GitLab From df57466255e21c84dd67268884035eebac88ddd6 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Tue, 9 Nov 2021 16:55:00 +0000 Subject: [PATCH 04/23] Fix broken links --- doc/administration/postgresql/database_load_balancing.md | 4 ++-- doc/development/licensed_feature_availability.md | 2 +- doc/development/merge_request_performance_guidelines.md | 6 +++--- doc/development/scalability.md | 2 +- doc/development/sidekiq_style_guide.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md index 87affc7e1be00e..64b8c5fd85e7f1 100644 --- a/doc/administration/postgresql/database_load_balancing.md +++ b/doc/administration/postgresql/database_load_balancing.md @@ -54,7 +54,7 @@ the following. This balances the load between `host1.example.com` and gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.example.com'] } ``` -1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. +1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. ### Service Discovery **(PREMIUM SELF)** @@ -190,7 +190,7 @@ without it immediately leading to errors being presented to the users. ### Logging The load balancer logs various events in -[`database_load_balancing.log`](logs.md#database_load_balancinglog), such as +[`database_load_balancing.log`](../logs.md#database_load_balancinglog), such as - When a host is marked as offline - When a host comes back online diff --git a/doc/development/licensed_feature_availability.md b/doc/development/licensed_feature_availability.md index 10e6d717a18144..424173e128a7d7 100644 --- a/doc/development/licensed_feature_availability.md +++ b/doc/development/licensed_feature_availability.md @@ -29,7 +29,7 @@ project.feature_available?(:feature_symbol) ## Restricting global features (instance) However, for features such as [Geo](../administration/geo/index.md) and -[Load balancing](../administration/database_load_balancing.md), which cannot be restricted +[Load balancing](../administration/postgresql/database_load_balancing.md), which cannot be restricted to only a subset of projects or namespaces, the check is made directly in the instance license. diff --git a/doc/development/merge_request_performance_guidelines.md b/doc/development/merge_request_performance_guidelines.md index cbf3c09b28b336..74b1d5680b0966 100644 --- a/doc/development/merge_request_performance_guidelines.md +++ b/doc/development/merge_request_performance_guidelines.md @@ -160,10 +160,10 @@ query. This in turn makes it much harder for this code to overload a database. ## Use read replicas when possible -In a DB cluster we have many read replicas and one primary. A classic use of scaling the DB is to have read-only actions be performed by the replicas. We use [load balancing](../administration/database_load_balancing.md) to distribute this load. This allows for the replicas to grow as the pressure on the DB grows. +In a DB cluster we have many read replicas and one primary. A classic use of scaling the DB is to have read-only actions be performed by the replicas. We use [load balancing](../administration/postgresql/database_load_balancing.md) to distribute this load. This allows for the replicas to grow as the pressure on the DB grows. By default, queries use read-only replicas, but due to -[primary sticking](../administration/database_load_balancing.md#primary-sticking), GitLab uses the +[primary sticking](../administration/postgresql/database_load_balancing.md#primary-sticking), GitLab uses the primary for some time and reverts to secondaries after they have either caught up or after 30 seconds. Doing this can lead to a considerable amount of unnecessary load on the primary. To prevent switching to the primary [merge request 56849](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56849) introduced the @@ -187,7 +187,7 @@ Internally, our database load balancer classifies the queries based on their mai - Sidekiq background jobs After the above queries are executed, GitLab -[sticks to the primary](../administration/database_load_balancing.md#primary-sticking). +[sticks to the primary](../administration/postgresql/database_load_balancing.md#primary-sticking). To make the inside queries prefer using the replicas, [merge request 59086](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59086) introduced `fallback_to_replicas_for_ambiguous_queries`. This MR is also an example of how we redirected a diff --git a/doc/development/scalability.md b/doc/development/scalability.md index fdae66b7abc800..bd999e7a9ba34c 100644 --- a/doc/development/scalability.md +++ b/doc/development/scalability.md @@ -124,7 +124,7 @@ the read replicas. [Omnibus ships with Patroni](../administration/postgresql/rep #### Load-balancing GitLab EE has [application support for load balancing using read -replicas](../administration/database_load_balancing.md). This load balancer does +replicas](../administration/postgresql/database_load_balancing.md). This load balancer does some actions that aren't traditionally available in standard load balancers. For example, the application considers a replica only if its replication lag is low (for example, WAL data behind by less than 100 MB). diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md index e28a328888d1d7..c7fd2f6d669702 100644 --- a/doc/development/sidekiq_style_guide.md +++ b/doc/development/sidekiq_style_guide.md @@ -574,7 +574,7 @@ of reading a stale record is non-zero due to replicas potentially lagging behind When the number of jobs that rely on the database increases, ensuring immediate data consistency can put unsustainable load on the primary database server. We therefore added the ability to use -[database load balancing for Sidekiq workers](../administration/database_load_balancing.md#load-balancing-for-sidekiq). +[database load balancing for Sidekiq workers](../administration/postgresql/database_load_balancing.md#load-balancing-for-sidekiq). By configuring a worker's `data_consistency` field, we can then allow the scheduler to target read replicas under several strategies outlined below. -- GitLab From aa357950eacd23a8b310556d4137457c19fe14d5 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Wed, 10 Nov 2021 13:07:43 +0000 Subject: [PATCH 05/23] Set DB Load Balancing as recommended in PG docs Also remove no longer recommended minimal setup --- .../postgresql/database_load_balancing.md | 46 ++++- .../img/db_load_balancing_postgres_stats.png | Bin 99288 -> 0 bytes .../postgresql/replication_and_failover.md | 168 +++++------------- doc/development/sidekiq_style_guide.md | 2 +- 4 files changed, 92 insertions(+), 124 deletions(-) delete mode 100644 doc/administration/postgresql/img/db_load_balancing_postgres_stats.png diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md index 64b8c5fd85e7f1..f49e84aed1c616 100644 --- a/doc/administration/postgresql/database_load_balancing.md +++ b/doc/administration/postgresql/database_load_balancing.md @@ -15,10 +15,50 @@ info: To determine the technical writer assigned to the Stage/Group associated w With Database Load Balancing, read-only queries can be distributed across all PostgreSQL nodes to increase performance. -This functionality is provided natively in relevant GitLab components where +This functionality is provided natively in GitLab Rails and Sidekiq where it can be configured to balance the load round robin, without any external dependencies. -![DB load balancing graph](img/db_load_balancing_postgres_stats.png) +```plantuml +@startuml database_load_balancing +card "**Internal Load Balancer**" as ilb #9370DB + +together { + card "**GitLab Rails / Sidekiq**" as gitlab #32CD32 +} + +collections "**Consul** x3" as consul #e76a9b + +card "Database" as database { + card "**PGBouncer**\n//Consul//" as pgbouncer1 #4EA7FF + card "**PGBouncer**\n//Consul//" as pgbouncer2 #4EA7FF + card "**PGBouncer**\n//Consul//" as pgbouncer3 #4EA7FF + + card "**PostgreSQL** //Primary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_primary #4EA7FF + card "**PostgreSQL** //Secondary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary1 #4EA7FF + card "**PostgreSQL** //Secondary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary2 #4EA7FF + + pgbouncer1 -[#4EA7FF]-> postgres_primary + pgbouncer2 -[#4EA7FF]-> postgres_primary + pgbouncer3 -[#4EA7FF]-> postgres_primary + postgres_primary .[#4EA7FF]-> postgres_secondary1 + postgres_primary .[#4EA7FF]-> postgres_secondary2 + postgres_secondary2 .[hidden]u-> postgres_primary +} + +gitlab -[#32CD32]-> ilb +gitlab .[#32CD32]-> postgres_primary +gitlab .[#32CD32]-> postgres_secondary1 +gitlab .[#32CD32]-> postgres_secondary2 + +ilb -[#9370DB]-> pgbouncer1 +ilb -[#9370DB]-> pgbouncer2 +ilb -[#9370DB]-> pgbouncer3 + +consul .[#e76a9b]-> pgbouncer1 +consul .[#e76a9b]-> pgbouncer2 +consul .[#e76a9b]-> pgbouncer3 +@enduml +``` ### Requirements @@ -84,7 +124,7 @@ gitlab_rails['db_load_balancing'] = { 'discover' => { } ``` -1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. +1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. #### Configuration diff --git a/doc/administration/postgresql/img/db_load_balancing_postgres_stats.png b/doc/administration/postgresql/img/db_load_balancing_postgres_stats.png deleted file mode 100644 index 22cd3d7025ed641ae42160562b03776525c34dba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99288 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

EM|2oy??+xX1(&mMSEPkGSFF)mYyRH7! z>?>1_U$Oo^`@XG)x7dFDc#VT6J5+k-@cg~LHE3hl&X!{vzOH_2^u~Ps((KvGd}r?} z`y8ssH$5|W`O>J66$Oqfmv`B+Fsyg|{w~~#wf4-6>vM``tSVHiFUh!nZtjue^7WTm zUkRjJdgh-iuw2RVXxZ$%iRSlf4)3&?$v=Hl>giLCypyF&Ux{895BqMpIek@-UBong zNvW)V^V{Vob_%QSth*sD_GiKMgXTw+H-+EKDK9-Z$)Wynulb2yX7*oK`|eI+cedj( zDtq&~=whq5o2vSg$s#%t0a>eBmvro6_?S0A&0`wpTf^Vp&{m>Xv7DfBb%B&#(Oz z^LCAR_=`ft`nng6nRj+EVipO_iITJ??+H?|0B62kX*T3&I-z^061$onYMc z?N;^;6BX?#f9|eS5!m_j*=$+EpR?Q67v@ep@$cG?!lLPI{P$|V+ZN@0e%5y(wmR1S z`oFD)>oaW9ew#cwvXTwLV_KbaO~3$>N-dH(o?QK2&RAO?zCP}9 zrO~-C)k!he6a8&oefwEo|Fiwg{q6DdYF`yrCZ=?k^d~>?b&h#)X|uoWvu&1ZEcb2a zd!=9dD^CCNGM_1LZ||G<|M%zn#yO7u-yJSpG^klqF?qtNs5?@Jk#-lcXOr~NfW%y+zf_xsMz zzYd<6HZO0z)#7!&ua>?4kI|JaE}cPop-pp^#Oy4}E?knOcd_n7@82i3S9i+UR0Qa~ zE1KCO^Ks9X%*zKQ+yDRl-tKf;-DVJzcT1WpT;Mpgxfc8C}ODog5~VzP@&vmpOmRLG$}c z>Q|LMe7*i!#!$-q8}G|2JA<`~m<$hQ8#*{o|E8_zuu=Z|v;K!s`MvWO=$)TZ{IYO~ zm*j5+7QU5h#JcCc5>=eTeRD_QGt5Bb=!;TH!V34+x>cbTAv)fxZZnq*<2Y*$)e+TrIccQ1M^7P6?(63G5XfO_`U|B6ok&R+V90+A{5c^Sqi@nVQSQWjDFXo16$= zexC8OfbxcgEe3M)7ZiNHXH&7^LFJ9j>Fb|m+XzSPO?vA7N^z&jm)qwBxK9^;m|w7F zu9sWWrtB)t9 z=Qh5x0-j}aZ^?tJOCCmN1!j&lm&^BvpSoYSp<-f8MWOi0+Y1(!?2TFMZL(^?_pN_j z*NJK_W4k!#mt6ge_x~@l{Hotopt^tU{6#bVz1seNdFO@uC+F9F&)#ow^~<*1?@Hf) zzIl21srUcBghy_BQv34eZ!6zPdJ6hBze`qaOaJ7)W#g}aElE=Ao-XZ}WTG-@dvg<0 zO_`F$3vG7(cl>%>Pow|5`&T_>hK*W$&A*w`wCwz|uB=hre&5C*^tSQe$M^efHACK2 z{h3(5R9kU>`F+N9k6tzH+#Jly8}zhO^w=%)IBDJbydUyAL)OpTX!ZVS|0~to8|vm( zg}&P1m7yJ8^I}y^?!7G+pKAOp`?$ZQr}*Ww>}o`KDnp)(sKX& zoj;cyT*tB@-tXmujMPf=9G{PJTQ0V|THgM5g-LuxV*2)B2wlw|r-_w^O2 zL;n{9sD1cTe12-WYVI8McRn4Q_quQA?LVCBrq`*i{{GeWkS3=jtJnY6EuHpUThQSH zo5B^N??xpr-oBFDaeiW3t5@8Ih$Wns*1mp48XLb9^~teYs#M6l=&zZuAmr+e3$eS) zcDp{glyhf;U+u6rhWlXX?`LV*F>C@I{6BN$QGUe9&wo_vvYohUOQ~qUp zAf4;{5FZI$nW%mA9!NI1wi%Vl;{~dIV-DTCR)cNGQ?T6~jtIKMqO`IH& ztlBB7SUB70gvZ*bt#`ld&cFO>`HQHjSE?Vhy43Ic|8cIA{@w(G{r|Vum)ukOdTNsEzR%3huqu9D zosDXbe2gH+Y;mnkQ+2}+KD}Y{?MuPJ3k-?P`g@JS*1fpsE%)>J{hG@=E-#rl`OCAu zBeH>0*kb-ndAj%0DQ!@3aOG6Ydh4fBt@8qAeyN+?vMMxsTTbZrkkrZ__W!57G*0u_ zy)Ac1;NmvFjj9i=1SO5rdc1Gced6rBT=P*ib4BQ)fP|eL$|v=7<*UPH8eIw!-VvVB z=MpeY*PB!K)upFGzIwNH!(%Ro$>!^&RZLS*Aahy7cwH-|yaxYhza;JmE z`Rg&4kJt96y}41CH_y;_rq$84`D?e{^6WZ4Rl7gu&X$w4971aKPXCo_zN}oPHqSig zhQqZrKO-i;+h2Ix^T(}6s@_l5SqGh%kuCG^{EG1St3p-2Tb*tszl*jw{^i#0_w#nA zUVC%%(}%EnP{rYz;zfUKAmx}MrJJa3vKNNaYYk4;; z>c-PY-ujn6ZQ#$?=M`A_WM%r@>ijF+hqrF<_0lkY9KT-dbG~UfkGt6R#{Js zxwj!OI9zgd*4E8eFU&Aq=yX)TEiqd-Cj5b)H0rTzGqtUm>d^-(%i+ zElHavW|=p4tqr^L%Vpb&c~=&tcD-{9Wa$#&k*JvbXLL;nzT4! zq0;gj3rZv>ZLI%O5w~Q=E*C4i;9U8N2iNrqZ`HR<St>baklbawk!yZ{P1!&)2@~bJc*u)%>r4 z@cs7+KYtgd-e_t0d8F|EbbCIhoi$ETA1n?$TmNforAXyEXS*$y#n*IVrzMph@cw#b zvVY4Sn<>-lUM>|}*TL}nfmCBbW2|*==hDBM54Uk%JtlDMtj*ttiH{f0sWESF;*8_C zE>-f_;d?^j&9c3U?7X{fPBc8F^8X+I{^@&Oi8`KW|M~a*{|RiyeaVhfHuZkFzB*if z``*8XkiLKc%Q+RMh6+wrbETN~#*(VY+7ydyqw+k@&$Vy z?uuJC>H6F6f3zM2q@S+{w^jXL^7_X<_V*8JfBj@EifP;br%OaT?oi~d|6hKyKY4t0 zW$$J|>oZRdd56sGGrv|Pb;GksnW=esrP zIWPB!t({R?bvfnia*i;4<>Sf`FJI69wRdvWSLq$w*6+9SitBzbwP3F5&-1GvtF_r3 zka!~%`@YND>Xde}dd9^SHt%kmtU12>+l%1Csq4fNmEtvGPHDLR*uVcz?5a@v+b>gJ z>^g7%d5O>5FMcaD<0pJqovf+)r=9)(D;w{Y^Sn|8xlZ!QXUd)&SsV~J_4D3Y=O@fu zKQX!2z0h^1OW?T`0g`W4Z_SQnvR>%^-f#A!L*>7|$_s4tcB(zK;`@>M<*IuGeSYic z+P)D!bolz`*#c@=fs-xd{7-NyhP8#BkDPzp6#OdWsHTv?|L56t zCy5&i4w~=2bY$YMZ>JBWEuW-ollQQxorlSDz8bs{eqNSQXvWoFFFl--PRh^Spb`Vr%Ed&%0PWZ_5Tf`w44ixdlfE-?Ue6Jrz82;;*l-P8OfH zeZ1zGO3(JoGJs~vqQlG-7_wz*=%o0z1X6auuyET+{Mb9mL+Is>i?YI;!Jp)fdanHU->MTB^ys+f z)U98Hgtwi1Vfo)s`R4nZ%QzJew14yH(OZ6xb6>)I_q!?j`z(|~T{pH^*);d^zc3H+ zn3Vo2m@7UeU1mc6nhDlhe3Y)<)7$w&>93rZvbzU+rUs(S2N(SZ&T}{BNMHNWEVV|HR|MpXVHLK5@S1xt=bKxw&bQcKEsw2T|>?5U(j3a=+%Azp>V_EP7J#`q1>}=jPtlO`qj+ za;EX}kW~?iS<^*NO`p#AcfIg2S^unSYaZTuc5?C^!(+GC$IsV~-zTt6?(rhm;A63y zkKN46dAQ?OuJFuF^EaiQ@7m?LIn8&;`Kz{z*YU5*xV>y@Okw5q2aA@Ro30TT(^7w#b#(;&VF-5WAU+~Hx*Ush7Km2=WE8_1pU2Ez4{U>&L|15f1 zSN4A6p;qqWHcKjA|2_7Az3+jf-ixB6-QwKwHOJ}}*N3bQ3*{2kktmmcvG~Q=^tqz3 z`xi|vkl_+JG2iX`XR8I*fBxAj$~k!lbN7V@%P$rw^Qi3Y*4z2QDf7aDc??EHkB)H8 zv`L*+^Yd!d_utDLnJ>o}wDZYM(+s{p^{cd*#sSro3kMjDs^8f}ZcJKwyUqUUs<4Tx zcf~6T-zrhDs8E6m@k`}%tRgw5yI=~aF?^zYH>^)c67yF{jK zs#+*=BHrQKySw7WPs_67Y5 ztUt!x-I2L@kLA0yA+=jF?*`2>&wkZ$@{sHMlkDEpmPWm=U%(T8?WvGvMU>*b-TCu< zW|@R;%f0_@7SGPg3DL**rJb3jyE?r7%{1q#J{ikLyH8HibXgR*nC;RUPvPYW_O@>< ze?Gjh@WQ^wYxzxWY%f1vrTz5HtE+S7RepNn`SZ!;^H1)+G`@T5wEkH0G&RtyBFYz+}+I4lpr-vhFYBT{Z_K&-%lFFTeLohR>wD58^LN!NW7Z|%{qHWVjSiRm z_s83>vvrHjw^wB!M>WMY;Vub-foff>D21=r#mOBKl$~qHg#9k{7=u% z>zniLa*UHP4Epxwrt}-V;Lp9%rX@#G&d%~rdD(t?Y4P!Ie2=eIy}QtO_~|O`-IkL+ zJp18Ul<=SN<<-r*OD^Rqn6&fDPm8cfxWoT_$=3YaZm+Jb)tz7eacRPidpnD#_f)0! ze0g2nJE#8NPNTPXB`<|?=GYy5aQ36{p?2Z#<=d3h#a}SW+f-bLoNBpgYc~H5W|oq? zJ^l7Sul%(uc`+e5_s@^I=Y9u^54o~@zAf``$;*GJ?)8`)p}FE9-r^>#pURsHA$^p=X`g5dtG`d_I$3* znS$(XSvQ?dP0@TSC$Ktf?Zms)L4D`>U+pQLz5B6E$&U$p_E&V~+@6;7@Z;;hUoLg) zPm;Y~|30rO&&?)oP#+2@QX$M4$u z({-b_t4*C@z_ushMb`Us9v^S@%>U4Lr~13BpE-}ZddvNV`bGEl{@YeEQ)rf-Y|IsI z7ZWi)3E!XZS=XPne12uh`s-`UkFeE#f56Xv_mzB|#ge$qOn&W7-w$?A5UsjktX z@lM(-&#QD!OM-7j^`e~n^TI5@@cPf|T9fnp>&%%&j6!N{P3KQOfAsdo!ns%0h958W z_H*J@b#3Jmn-o*!B)IM2lUF^zW&ap^h+h7D-uk-XomHXQ++wAAe-<2Y`@H)5(wd(| z=dxQ|l_qV;s%+f!l5f?P>4oMepT_E!CTlKJ;Zm&WVrQGy@6^g|dD=5O^wVO`gG{Wl z+-mL3bNbbKrCoEn*crUNJbAxZ#*jiV$AT&Qz8#yrFoWfbXvu{9ZMnsN z%>td3ihK@l`5jx{>o%*T{Nd5O#2fO5HU)%K`a9cjiE1seT$Jj}cK*EG??aI{IV;~? z*f+Q4lO=KSo@vBxPROHILsed4k}-q_b7K<-zjCD_o6QU&KA$Q3$+Pfjuv0| zzRPY?`ujM=*abc>FG<}qJ-gNW+5AJRHVgVaXI*rtveBZA+wIqXn@=qk8K6}_=&>VwwTcUQOX)(cyuD0bK=PPS*STNl;A^q|7O+W7M_q}cw=h#1mzp2$$L*ur=QJGf@88(QC&so0E{CH|_)W?m7)Pq;O zI(ppm(A%#oHx)nn*~Yii@~qwtjtz(I8!VU~JZ&bwxX>)V)zi}_8rA-K^sw-J>~?SM zuS@nUU2}tbp-aogpS2U4n~S9NzFytU)Ghz-5XW)_^Q-Ivd7=8OqRXG2wx48~LvB{$z=l74*L@eWCG{v}$WTCcYxtm2;69^&77@`;bO8;4mJYzDpus>~NW`DZ#d3?zX$Dl`FvR`pG>n0p|0 zb(rsRpRnsE*iRfUmRhz|e*wepW53w7ZQCwe=kLF&dnnlG;oQ!JKOgGvc~TT!asBP} zwl@~nL^^(|lJ2n*yf*)LLpQO99 z`1!=YiGoT`%ASPU>dsr>Ke@fxT{CKl&l)kz(L*9=Nx>^FVHJ7BH`$+`={9YW zw>x+Jcwc#9+LPx$9`o^+R(ad{C0t#Z%)W9#oPWH83#v`_b1-B`A1HKyR6*C zJ+n!IcVFQG?+N^y3OhK>-<-ZBCf2|F!L?|%-E}{lLH)=b_fH;pldZb!_SLywMwQF^ z-ro85!Y1PHn-dck_s!TaXLje0Q^i+`LgI?2%}}z5snvN?#(VJ7jHS!#uRGq5?~*R~ zQ*rP^Ozj%}4e9OsALxi4`Q~;jKSKQ5Wmu=jLyGAI_fFHM1yx_O^mw|z+yC6q5}EAq zbg%CV!}tFdI_DK7-d8!+?!8(}e8;z(+4}W%;&(5GEEe0du=xJIzeeTvn3i*2;1?_T z!u=`cT4v&c1#v?6zMucOeYMc)D&`bsoz>YYY;h}&%Ig%e_HxYs-txXHZ#w6_MZUNC zDn0I9S}MQH>)81>8=rMtXgq(lcf%>&M|ZxxyzBk-O0&_|yOI}YxyJUL^FHzGiRSxd zS0U>8*57p}W;Y!D z=_Po_z>cqfw#ucGkJxyxbIyD6c4ki4 z<@MQ0ZC~Z*u&)eL`);094BetA5^a)ZU~%qo)no;`?J7a)PhKs!vb)<$v`Eil{gv}` zY{NgTJ}vffowJQ_oJHCm(FBFnX}qbj$2Hxi1W+A!rH~+`eE%UiEcM!78dN7 zRmpMS!QSa-`%P=OGBpEIwv%1;wW9h&73IH|`LD}}?zy!kQ+2CI@|u7*;ziy{ zviun;x9aYC{f75@RX7l7MJ(FwwRCX#!L{Ep^hJ35NN>-%?WfO7(OTGYm6dhP$&Gz# zPc8<9a?A4iySse}E1J5)ZbhB6=dSp|ofE3hK37ms7pXs#@%K_k;KJA1m&zWVp5*4o zanb9F@FBC8qHF#!cxnU&e2|!6?#P*-ud(XlsZUkns^VSgJ96BOBsRS+S+wK#!g>Ys ziB8L$UVndA+G)SR^?rGu%*Vafb-y2oxOpiFhVs=+s&4+3`PuVC>xZX5G_1KR8%$b- z)ldBiTOX&J^g~G8^hnEx4ds672PS$yI>{;5yg-1jG zY?%)q-Q82^E%oP{s^I3btj>pS)q(moYa}a5Dgszk!`I25XJ^^qF;VH9sJGBH`ISlx z3}z=hT^vKIm$-a;uk@ArkzoSac; zaGc;|++waQz@fx-!qinkRb`8_%^tx?AAU5mm+$&|&_4CN*5kT&7i`${txM$&8@e{0 z$ak2-8hg0@!r2oSAIxpP_~XPyEjE%Hm>!_^8k3O+`z!UQ~2* ziR-;N(bFHa)6G`wq|leMpPts~DD!-L@IUVAdL_Wo@x52fZW(>aKlfV4 zG|pY=lcVPZWl5J6wg-4_xsZ&svWDD zSoe5d*OYFdd+hsnYVQC0&DwUQ=E)}&i5`=V?eeW_>tEQtC3i{clxM3~FJW4JpeEGi zbmsn=ImfD{4{g6$pRk`hyD7w=Z^4gCwNJbQ+ILU%puGbE>|M=<921CwJ?;5y^Ts`Ic7bniqYt(_Rbj-r4c@>~nJl@ip4hOLvAWJ3mKN z^{XOVTFiqAmOU~S6%NO;?aoe!X48&-_s(hNj+xDqe9m-yJ>pdOukvi{k9q5lIeywF zo)>Xvm4MxyixX!0+yA|@zR%Lv|NlfQg^B-T{-$w;2A!~6_{LOd&3d^^YorFQFk{ae z-dEz{hmP7`3dp~Ax8_p9zeBBg=H4OOo!-B=`&w(cLz0H-ES7%url2mx8TlCjA4T3f z9{-m6wB=Rsp7_~jnU6}e{e5mc6Ec-bc07K8Gjrv_C#KLs84rsqBPMGOy}=uzi+bkh8Rz^dD9=VwPtJoRrc%e`Cq?dP6{XAOML zhp$n{t-f->pU>#Ij7-_vUxuI5YIb|-c^w&@miB0(%kpS~TD->atM zEj#1rq$@^#*?Q&S<|^jW{{^$Eg-=acA>ieG_jxte1;-;h>bM1Yg`$$pP_r^W%s$cyj2L;pGk1j~< z{w&Kk!M1~M-jo}YHoXs?Xw|!S;aXGJpy4HjhIcHIjvJ57IAuNY)7Oaaf?qhgemzKZ z?02;L{ii$N3hV7e(fP}#_cmJBJmv*Pedi z;Q6>*V1~rf^_yS+eE(kQr?COyWj2b_5PAO-B9YXxaSRh zIl&|=WzHL$=e0YS9E@_@5wzxq(|%n`#ftcX+i`2fx#f>=PWy56t3-DFbY&`uBxirK+ z6dcyx#qjpfba!jJB9o+-rWd#U)|~Ecm3K^-|M-I$oJ*So+LEPmCSR=l;#so8BQfgY zv3VD3Q&V<(JpU76_2pa6Q%g>!$vmn3+^lZXB^zf1NcQxLC)%!9evMV+yQsEOd(=k# zZ_?d72O1vVJrMKa<#8Aq}-pl&0cuM()K0C4l7=Y_`B)Lm-n~Z{3jQ# zZTp_NA6|I72zGpzQ(@7WqTDZ+m&(+@kyQ#Lqb>js#`-f)? zRZUb*8voT)wNCsN{3zwO87w*SFgib|=LHXY?=M^QrUK>|*i`(R9*3$!yUr9(ntOgj z?_F4X4mybagH^Dy#2bFgddw zeGEG#9W+?}d~NG7ULfF=SGBncN0}CWYuplVvQ6JdSpWE~)uh zG)X)Cm9s|Jni*4cB9*?_=il42QsKnPw~_MuY{J*Yk_PecV);`Q&{2`BSvR z&0Z~gZkDfhJ@&Tst6vYg_}<<+6&hZ~t>^Wsrc>zX(c}pycJ`QYtZTls)XOzG_cScA zH1a4m*aa>1O6^?s{6nAn_AXUcZV`{g>=m^NKmM%`*th4x{qvq(BAPOR;?Dgp{?5x; z7H2%_H?DbA%CPORY}}rTj~THlwU)o_c2}8dhO7vK^@6CZdtTX>{B=_t)e`3|TSiOmEywWXt3xw7EPFV}9pL`x)^YWW{$qC;~ zy?ye0vXeR|FFUcT^fhbhr)jgtJuE&bK? z_3C%^UMuDAv5edIuWIg<5LLVKcXvMhd>+4JUAKttBfTk2tlTH=?X6CJw=U#m(7N1p zF_uqnY@FeH{=}(sbFDlkAL|YJTKnZiAZU2o`{m{2(`&{5UkuR|`hRij>7_x-zgd1d zUvICZ?my3`S(5*})>Q4~tCQu9sCrNJ3BTQAvTI*`JY#d>c{}~iIj29=I_9j~^QlO) zZq|~29}e>yrM}wz>FTZZhiBhH41*}LTyVbN9w6sG*Gg5|EN{~FIQiGV8eC$k&t7F( z=+Gx`uP3h)sl_d}?|B{j|CnWe6MA}7-uunhlwTX8y8Uidcks00XJ-^I-P;KlPBc@ZCQ%E%MGK|KE3gnbPEJ zyMHdAQStVvSzUd%H@laAJeK=q+D~q=lbiUb?=JgTkacZKXUUrjdM*~_@9wCs-~Vyh zo4ec3KeYOBOghHivgpYa@%XC9efM`7x3aw6TITe1hl^_T#K~p3(Yw2Ies=Xg`Fm8} z<7d&+C8w8)pMF}kx#r{3@ON#@4ZgF@xXn55bB;5ueZhm`k4!Re zZK!L0z1n%VSg`J%0d*&7$-uPO%s!=^U?fgvj`8A(bDyx0@ zTYT(|?$M_9$h4)mpT@Sf-#-z!-fB~^@BGzI51o_kos%LK0h#Y>{9nit;3K^%@cUu@ z@Dq!^i+ewI;|;l(qn|!M_O4{^?bUHih7a%Vt2`aH)2H{q+UTtzO`Fc_?Qi;!_~Ptr z^U^PDwO7}Nhui&rQ@nEJDZ8*g|7||4c)hsv^|Zb@COHA|dOLqOrJv#XR=(`{`@O&U z(gpgy&3m`5<>a@xWd$#0G==)KO7^y$-z z@Fzz)g*7LtT>iA{`>(ZGCiP`Mx;LetpBHjF{h9k^wO(1n$gNeq%9F%nZ$uuLasR~u zM@R8@OIdmf9W}naw^yjG`aY>!?~RJQ_QHoZk{+_`-1TG8xlMm>=YN%&sj_s+lZ~~j z!*peS=2RYJ{q*;{_0y%(-B0pf|2}z|?z!on!OKogy{k8Q&o15HJ|UY#Ei-Q}YL&66 z2v~N0#?!vy|Nlhz*%uTCg{}X2Lzg`xLVDBV-|wG(Sa|qdQ}f&WTXTLb+37a(PjfWy zdZu+YzwU3(-!C-bPv6u>D-UvS@LVe*{<=4Ekqc+)i=A_(r@h+pGV@zc^~T)WWO`=rdd1KedV@tBuxBpeC{&i*oLhO=GmF-Fbsd~diu{B%i`4Q>+Y_z z6t9?TwN-XwvaOuP9?^*`;rH!QPrmjOUh9$j$4IX2Z0Y^gB{dUm0yi40j#;@V zQU|^~PIQaUjoLnG^Es`@AL2h=^lG0g&wW`;e2)Is?CV9>4=wsGcKTZWKF)P~XXCGZ z4f^?Rw``E-UgJ9x*7wUkKeTPp*Z9Y8Hb4FF(0Nnt?Qa3{b!?k1M(CP7pA+nTJ%4h) zU6kl9y;|vIHdi(z>HJIZ%!r9_h}yd6z)t_>uz3&4%<^GX;1TBrgL!9XZ$F_^`~7oW z{GWqCnd=qLpPg-5`eez3wwDQC{=O-3VSn#o!=-Wi-s^|o8yk&Y{8b3ykWF9f*0!Kp z@Kf=hABNlKGqdw~)YbNX>T`ZM-@Ja|d$rTc-h7_yZ-2RC_OHGPtmQGwN;1FCW9MIg zeMJK`(Hz+W^sIOQy=Aa7eHr&Ls8J zZoNb88uE45SH|9+IGIW6XLDZPg7pQw0uO_t*LRD@Nxht={!9J#1YwbYLo;nQ{BsDb zvDB>o=EC2}wg14MZ6{|uirW43nd7@7&K*+bSugay*;aijn7J^*x$EBL*>|5Ge_{Ii z+0Kf?Pp3t*S*Olm_xzd_y0kH_LD{X(Z|R?;)aQpZf|qUSp8UkkVJ5G*NY)--0pVN8 zybjZ{Eu^13G2)${b!W%I;`m2(Z`bXVe7`cZwa{HiF5&ovm69S%DLyjWKs z^ZM|0*Vl)u@A}Gf8_RW-PBkdJeDv^|6>Ucoryfw+kowxZ>Gj7u&(2=8`aC20*tyP$ ztn7CteCgf#VrAlatJjV{9&Gqmz*m^NE~E7IoX5u!jvQH>6bxxQHTEbR_^tV)?~p?0yG^OD!>-?LDOYRV zm0Gzu&G%0Jz1WXW_7u;y{+oX8PUSjHmxd43PV+7X+J)wydYbj6d4*2wx5Pc~ChfDk zX4ft5fAZ_y9ht!y`|q!Il-3Md;&CRRx?tCXKc8g;8zv~c2(94r)68`E@7T`VtUk}{ zZDC=r@lW>TLIY6-S&rC65g(`6JV^L1@$&z_^Z!}IPl6_Pgr4%Q-dJ#JLt(O(ENktY z$G@J<&RyqU;UU4=YU%x&}T!ohig8bdpP6ZhHSwDYrS!qVJ?ub!^MJc}VjGL#w*-%tM_G6Cb_$ z6X86u;Pc(m;}=YJTc|FdBU<;N_xkP)dODvTHhBx`pJ!5K|8n#wzi`{!{@TxL1B#tG zImAEz&RU{(%i{@)H>3Gkn#ya3@yDcHl?-u9{*QQrRsd%h0ZJM3XiN1aO5z(F!M_i+e((@ z5i_RRzSeQybMM+7WjUTFc`^12)}QuP`X;(?K4^q>gT~UI>N^r&Ut1~s)v@xy0msT; zGoRnNp*T%ocf{Y_b6fr%>~7oo=$ZHp zdi`FMVM{?cl^r;2;`n$HTvBvmXU)>NV^Xo8?ah>uUB_c;^M7(oiLlPP`G_g~ z!I2;B2{)v_ohh(oO{%oO(g)b&7=vI1do--wI@$nVr%hx^A)SVP1b+@5E2d&%}FlfP0}uT;9Ve!F^*E0?(E)#Gld8##X8TEQ=Iqw|qnsBLaN@0VyM^k^%9u&(`zoF1)qhkyzJ=4gS>kTh zY2T_wb3a!<1+|KndG#Mt%O^j+xBK(A=$FShIm*?L*S>J2|Jm0wQyC|U3A6fOQT)OqI;-B7jnT^v^ zP4DDfoz*{gdt0^uzo1?GmZPM%RWLrY7!joWRnsajr7 zy{4KN+PThOkl!;;^vZ#D#*a;(&t2W{#?(+WTgjRuu|ECMlMv_T31?>6UY{s)@4rNw z_o~Dl#mB#GD!XeX{^-s9jVsO0xY?h$d~g`dwYpTKZw@o&*{(JkR^l?Df^ zr=H2^2+s3g`9Fc9Usb~Ecie6F<7Ea}pK7+eRb2g~T)4LBd&{GcC-urLr#{brs%xqu zn=iBQ?~CQ9O%J85eU;Veap{DolJ!}S=t;Q(kG}5>I-M_)ctfUzajDS5wAn_cs`Hco z|NATG*%^N5F555l>zPYsx=RC2h<-Gz)#2xtSI%LdG@(bD*Z<^O=lqKsHvG{mZ(`?^ zTM$tcaZ~&FrpnLLLLR%kbX5?#*iyqcrAlu_?){wWN7gcVWdz-sb2msRlMXS153j0mm5JP}RoE!dx{F|&^^uM+|dtKV;kS9mNpC3AK`~9x$IiFrmed|%X zJzU$$?P{suq3xOdjl6$bJOo6NzTMqeYklyQJdbhrmGUs1&n}GyrTRao<|wlB|BJYI zdj02MB`t$JzrJscf4AK3e$)5A2VVc){h{oWzJ`^-udCN?=8-vW&t z8dq)R$teY1xagMksBRHk^`SpCEAF2)zq^8!KdR+K=Y7E{)t%R$o|;y=(|UgGx7IJ$ zymp94c=W5E`##yE$f+YtX`TSv0kfA=V^0Mz3c1NoVUzmozeqHWK|dsp@R9=3CkWfU1v&_ zIr!_KL`Oc)dX|GnyMtsjr%g!7@8IP8kndY@{HrTyzr(|YMHSWsXZQU*_vFIiHAga) zm;*mLXc`??1A3{VB3fl~w%uBJD|9-tB#NCAB=4sb6~h+`+F#Bg(aR(TCRdfK`G; zDT_Rp_sPZtG8|fTh_!afbgdUJBwVj}H}0LiCxb(tMK`i(-%D@%%j!p(|LT#aEEdma|z8+*VXmTy+nB9rL4aZ}0D#e2TYQ zysgVSVms&Gj|bdunO)kHdV1P!p{m;Tua!5OZku43_|}cFU!|pHs@Bv=C*;;i2-~%q zT;zT@p~_{4f`Ya?^C|n+C%tD_R`*@Gd_hI%UG3bgYpksk^iTZ?eD;3z*6gR3y!E)& zw(a<^zpJgZ@J);0)wR*b*B);cKDXlF(;pv&i(03(&HnYSWcrI<@ow%pb$ev4NLQ`7 z*HM1Q%%#}B>O^qVzL{Kdj-G01v(54gMHLmCRgNT2bv)7f@epr!bm`Wrs)S!QHXEKS zaldrAeo0-2rEUD(M|W0yex@sO=-C8$rHJGY?;h&DzO%CVu+Vz(sUbh?{3{qau6H8V zU0>>CNIa%GRp7%vwJWpB?OKzbtvc5vV_kLTrJ)_u(zUN5Ij+tX=g1dbr$#A zv`BT{i;rTT7F$P(P0^F$>t~g9R=1o{Asvu%Quwa);)cKt{l>Qaj^cB97AxLAb2y`W z;*Dx{jgHGZlig=74pg}JptrAmqgm9`jn8J??w(lk>&whvZ#PeI@)I!K#{J4`rtP+; z+F@Q---XV(C?Vu9h0Q56K;_Sz*wvh`duGQd*|<(nW%1|c{Jb&cc;EZEdR=m+)#q%M zmoJJg%Cj|0{Ozr`&tOWj^A)~8oqGy%R^-h!DH3{pVA58;>B=W>=!OV>HT*3w`D6ax zxmn^57e=lOd&NI#%}SMq@I%#V9gDlu7&-3S{g@rF$@Tr-_a4Fis=jyD*e%hzaqhhS z)>o*8lf%PfklX@-b+)l)23Q!krV3@JjcUZ1kIBb2PSQ#|wcC^^bcC^X~m$ zD1Xe{&Hi1<;fA_P%l-8$E1ziBZJn(qY%I6s>#M7Ui77w4E!d6)XnmMkdMw!0HZMfO z@q?Hkn>b|fo`2i-+aC)KCoxDKYGSx~*vMJ(c|qdc)$8}Z+n0UlH1nq;-c9Wd@Esx( zBv~rN6AjnLub-!^xJS5ozG6l2Ug_A~F>iX3LR^mqc;DE3{^}Fi?$9~qLRBnYdPnm+ zPH~v0{3_hHSdhu?e`D=Lh5xgEzb}<*pTFgX_k+LZB98Y->tBz(e0*>H&)$PmnJhS5 zx;Qv`cF&vh!12KhW!EJe&T^^p#@81UrZ2@}61oe0@1#hI+OX`< ztq*u6vF7&NjQS_70(@Z+t8)|I{=2%je5X*hvip9ijoLF*;#>IF&wMLwtY@RP@lKaP z&8at=_E$LhaV!k4+11Wd814C0wsDzC^6^XANx9X_u0Qm&s(h6qC95QP+?O>{DWNH@Ek&09 zm(s)J-zH4TF~>cQ-8g*wsE6|t>VUGlP)QJz3=jEZq?~2 z@###Pzi52^SkPe-wqM8g@9%97FT1R}+9R<1!{3{TVLTzGjrW>0womNf{F^du#sznq zy=`XpQ(cU|s67Ua_)o6vdC`6GllvsYZyz?QRq06DKDRtvp0i)rnL%9YzJ=oV=BoRk zg(~IC&-;W1>THxdACrAU&&%xc{Gd(3Y4)mq+u&A#hRpw67dWvkG4_A9`l-I_km?E~ z3?9D!u~a!>-sJdSlG`2|!G~K+T6y2lQnd zed=2#>PO9nkL|6@NH|JoSLdF8gaBsbgGbQSNp%`yubOp`(!Nd?E3I@XYup0(=*kA zi=Lg)oT?SpaVxd;)6-Ok|F2sLk58Uun!RIN&a}a{8{w0rTpi4 z&qckWwnDbO-}T==tO!~Wptv$<=_Z#;M=w0>64w`7-@Rn5`~MYTYgdVAMHt+zEt$$1y?e&FEXU<(2zP+zx=a(eO@7Hc` zs{6}k`TNbQN$#8%#N+Fp?$QfdvSCVS>K2E8OqU3*un zT$y{n+*H^1y3?L&+i~S*A?5^I1RVGs6^B`-nU8p;`SVx%)mfBiT**3dwt2|{t4P8d zzRdjeQGNcd`_tpExxTt$cl_+)$G)>{uAZIW`8ZAI`j?$nqBH+IxqLq5)s~le^C$jG zcwo1_(s<Dm`CTNxxc@=tv*@JcWUnK?Yqr3URxY~-(bP| z?Dl`QMORjDD!#7g-gJ<|);6%yUr_k%N1ko@cULXltr)icXT-e9cdYIzeDZd0ru=`9 zw>{^kmVU2Vb*Jl`{=;RT<@G~QR&C-;Y~o3?zuq#n1}K6Qzd)#0n39Z)>P zkkn*k`{w5J)XUG#f)=x@MyVW;-OfAloUFBnia$%Bh`{9I^D3h5#qV*6%Ch(PTUE+> zY0_1p>@_`0XA5!lG`=v=_$KjB|NSl1M+W-)R%}*QdoeXRuG*PT&PGEbxYGY|Z^Mi| zpI*H-D*CcI==YqcJju9@+wJ!zmE?OpcH@;bjNJT8xH9nTU0!K3tDeP@dFw-!n&%Z> zi%h*F@xd~pkN42w8^^UWbr;EP%)49HW7WC!U(@IFQ|DTjpIqE;uX_LO4aMiL^Ovli zB-;`1rV?H0$I#Htc&vR*Z1n+O#yQgGH(z%?^&m0##CPj*u6e=@PnX?Xb42y#p`-2L z3!#hE_y2FGa{6@o(D!@OA4w&(i8svNTfDrls!{AZw?eyhB50+$vB;h7wb5HAXie2V z{qAmc%GFt>OgFi_c)#u|*b~8`ko9jzbM7A-#emZ6>tV0|8D3wv?w(C?=+EW(E!=Zv zb0}?ayJ?^D>dDE;b)Q2nAA0rl=JxviyE%i_c6?}VCvLI2?rih?iJ_~nop@)fdHi{M z*}a`-?-t5g)ts<3*q-aXk?qIVx~i8)#ZUIV-(PNS%P$RItlo)OtiGm=_toyt{a+gk zf4(-l;*nl3xkzui-fx4Jx0g~+f314?z$Peqd!2Aj-e*{iE9Xj!-BqP4gSVHRcrAG6<{{Vjr$I~87e(CK znkyWm%E~2HFi#w`eti$y9`B0>Z|JR7y%tgV-DsbxcYV$GX~C1Ot;tMYexRCprMz)V z-ux;NyNFF^be+UiAF~k@;=ISXQdao~+VwMmIwiWa5`YuuX`K;N> z0;?nX_o}|!ZMK&wsr~oiu%ha6);WBw@4CfRl_%xe5V?%Kzp2yc)<$RRmC@__UQe6T zBIVV^C90#cbIMeu9A@XMlHaX=q)&)5c|N~QI-P5}5H_JlsuC29SdwZ(_Ndsli-^WM|RYX02swJz`HUVYB(#Z>VQE;$7cHxdAT7^Sy)J-6_t$>?egqc5m4menI(Zb3!6N*D13ei}orFhBZf7pJz%PDU4hk zRKg4?Si{^XIK zCxv-CJcF0L?A>x(a9iE=*vB4=`jhr`;izK6r&Q_{FzoK+UqyO`g z2MhVR+V)PGWq-hW-i4(e^Zq6L_W1l{HBbFR)y#WqR<=!CJL&H#hsn|t1G%e~)T(69 z|+?f!-2 z$=L)v(~o%aMe({pXMNH0bIQLDE4iF_dt!A}lpD)=?qeT7tLJmiRnGZv?nprR#%uW@ z%MY%Wde*k_+urZuyKNG`B-xlsPF*r_vP&l?^SKjdbG}qGJ)Jk3$8qOj+pQHRFY#zr z?Rc%X^T(kJdhXda56kb@ivQ}(=Kc6Cz~e$AGy9h$UzsQ!!Q?pk*E7`P_?!+2B>iu4 zQj$KjFX4~I?}v|TYTs0dr?suVroH3gLcjc#$2r$nmA+9By~5q>be6SMK>Ag2NrC6S zg@1b`es-;Rd9>BXv4`!x#Rb(9?7II1gmv`wv!nBhY*@G7GrIBhr2MUEI+2I|`mv?O zYa8BOrx>MDRr>12xwrl2ny;Skld*i&!FjqobBBDT*Yg!mSM0G%nW1#2qu<{0%-Q_w zh!OXNI~b4mi}RendfcmLzU;rI%`t+Hr84)IFg~lT`d)Nx=F0cyI9^|^gEtW*of&Es zx4SI}Ss!<{_05NW-+ylS^5g0B?74als;i!zowakqx`aj6xtElGe|tN9=VvjC*n}4o zX3t@j{R=vEbdw)9Gwat$U%q_5yu~v+M?L)ct9jukWmg?|9Qt8bkA50U;z#%j_b$uN zEzP>IzYod{$G@Qw<~#tz%f}B^@zQKt776VFQ0x|^W&*9w+mT|U3+ebPXw)a2vNSf zT3H1^i1|%p-m#q8J6O4&%aNdsTu0Td60`MS?Rb{;KEyCDvz4^r?n}ipIEAxUfEc6 zp(~>!dd*kIzbk)>PUqfvy@rpe$myz4&oz;iUPpwdgU8nw%@f$>xjx~mdt~!PcGiAI zady>)y$e=UslA^5Q}d1JLnqFAC8d7PE#6qo)%chE`>&ugWRbhLQqINibCum|-uK(SZ=O>&SG6iGJh<4nl^wdq zeS&nafc>N!%Bq{#xBL44KlrU~UX6m&@n>h_HH_2Fg=L3Sinrg2`rL59pk{whce@0? z+nxLWqCWk-c?H)0Ji);v!J=v6Tw&Z~YrbDo0GX+LzBAAQ!Vsaq92?L1TMyq-$+c6m%&pgcuC-0q6(G2eghL;q?8YOWUDGeLIw=iC|3 zr4C3sEL?tRsrN>WS5=?V1VMx9E0%e`<6ZdK<#dDo!dI9}+!Y&DpiA7}$nQTX$g$tz z(Dv`pCGO#7{nBFkJf4>#ws%^qyipg7T=#edY<>I6F9+DU_HI|)v;HspH|ubPB~67a zN5fNX6bL3&dkn#qT=jw$)eK~H^lb+X^(%k@k6Jf7HxzwQiH zWeqndT;*XRZpq4~&~W7EB;(enTnj(V+5hn_6Ve`&1_nVD-dEz~u7!5sweBKaGxrxP zeEsys$K-V!yo#@HZ>xQ+)wbyoGjF@Q0ar3d-&bDV>jod5LzlL9Hbpy&#_Ig8xt{xe z-x1Ll4%abE*alh(n0lGAm#W^kTYPzHl-Mq>?Nb(co}R)I@P$3ma?%OQ#a+4= zCp_28Rmqmsb3gS#=y<x1Hl|9$-(0+#d)lmfx{l}7{{N0QgepZV41c_jol*B!$M)Vi@M`y%ik5Q|kN7&p zsI*pWQv2&%P;gN(!9K6XD`|h;yB1CJSK!s|JHyYvTlcQ~)U7G|Paf%saMP;(xWNIj z=6#tHXVdnM_B4~kYW=Wqr7^0QRYhdB>bm713M&JI(ooxrtY$~?D6Yrh{qY#vl|GG*P)xLNa^ z1J%ET$n!qAeQfqKjffQ;mydW&*Ido)u~XnC$LpdhmbM?k96mjlHaZ@g^}^zVKtNgV z?6d8js<+tRuHP^WG6u z^@#f<=fU4jXR{{l{Ba{-!x8;;$1{>UP3*p%m8kcd@p0n{nXc?LhKVJyzYkPI!E2htxFzsPqJe4u+e#l22Apv_iod+9v*6)j5qsbbd^wM7cPu*%#|$l5b;1@FzikD-(yP51`jIC4aBRe zRIfF6*d_It-{~y21 za{1*|b~~f(UW!b4MMkyJ@jlSX_YM6$J+|}e#Pe%^Y)t(!=l={%nR|=>`iV2`xbWkc zXXhi;m>rStVsL{z)l7R O1_n=8KbLh*2~7aJB+=;r diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index 6275808900f585..0d557364edae57 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -19,11 +19,12 @@ replication and failover for GitLab. ## Architecture The Omnibus GitLab recommended configuration for a PostgreSQL cluster with -replication and failover requires: +replication, failover requires: -- A minimum of three database nodes. -- A minimum of three `Consul` server nodes. -- A minimum of one `pgbouncer` service node, but it's recommended to have one per database node. An internal load balancer (TCP) is required when there is more than one `pgbouncer` service node. +- A minimum of three PostgreSQL nodes. +- A minimum of three Consul server nodes. +- A PgBouncer cluster of 1 node or more to handle primary database writes and reads. + - An internal load balancer (TCP) is also required to balance requests between the nodes. ```plantuml @startuml database_load_balancing @@ -49,7 +50,7 @@ card "Database" as database { pgbouncer3 -[#4EA7FF]-> postgres_primary postgres_primary .[#4EA7FF]-> postgres_secondary1 postgres_primary .[#4EA7FF]-> postgres_secondary2 - postgres_secondary1 .[hidden]u-> postgres_primary + postgres_secondary2 .[hidden]u-> postgres_primary } gitlab -[#32CD32]-> ilb @@ -61,7 +62,9 @@ ilb -[#9370DB]-> pgbouncer1 ilb -[#9370DB]-> pgbouncer2 ilb -[#9370DB]-> pgbouncer3 -consul .[#e76a9b]-> database +consul .[#e76a9b]-> pgbouncer1 +consul .[#e76a9b]-> pgbouncer2 +consul .[#e76a9b]-> pgbouncer3 @enduml ``` @@ -76,13 +79,14 @@ shipped with Omnibus GitLab, and thus Patroni becomes mandatory for replication ### Database node -Each database node runs three services: +Each database node runs four services: - `PostgreSQL`: The database itself. - `Patroni`: Communicates with other Patroni services in the cluster and handles failover when issues with the leader server occurs. The failover procedure consists of: - Selecting a new leader for the cluster. - Promoting the new node to leader. - Instructing remaining servers to follow the new leader node. +- `PgBouncer`: A local pooler for the node. Used for read queries as part of [Database Load Balancing](database_load_balancing.md). - `Consul` agent: To communicate with Consul cluster which stores the current Patroni state. The agent monitors the status of each node in the database cluster and tracks its health in a service definition on the Consul cluster. ### Consul server node @@ -100,8 +104,22 @@ Each PgBouncer node runs two services: Each service in the package comes with a set of [default ports](../package_information/defaults.md#ports). You may need to make specific firewall rules for the connections listed below: +There are several connection flows in this setup@ + +#### Primary + - Application servers connect to either PgBouncer directly via its [default port](../package_information/defaults.md) or via a configured Internal Load Balancer (TCP) that serves multiple PgBouncers. -- PgBouncer connects to the primary database servers [PostgreSQL default port](../package_information/defaults.md) +- PgBouncer connects to the primary database server's [PostgreSQL default port](../package_information/defaults.md) + +#### Database Load Balancing + +For read queries against data that haven't been recently changed and are update to date on all database nodes: + +- Application servers connect to the local PgBouncer service via its [default port](../package_information/defaults.md) on each database node in round robin fashion +- Local PgBouncer connects to the local database server's [PostgreSQL default port](../package_information/defaults.md) + +#### Replication + - Patroni actively manages the running PostgreSQL processes and configuration. - PostgreSQL secondaries connect to the primary database servers [PostgreSQL default port](../package_information/defaults.md) - Consul servers and agents connect to each others [Consul default ports](../package_information/defaults.md) @@ -242,7 +260,7 @@ Here is an example: ```ruby # Disable all components except Patroni and Consul -roles(['patroni_role']) +roles(['patroni_role', 'pgbouncer_role']) # PostgreSQL configuration postgresql['listen_address'] = '0.0.0.0' @@ -283,6 +301,15 @@ patroni['allowlist'] = %w(XXX.XXX.XXX.XXX/YY 127.0.0.1/32) # Replace XXX.XXX.XXX.XXX/YY with Network Address postgresql['trust_auth_cidr_addresses'] = %w(XXX.XXX.XXX.XXX/YY 127.0.0.1/32) +# Local PgBouncer service for Database Load Balancing +pgbouncer['databases'] = { + gitlabhq_production: { + host: "127.0.0.1", + user: "PGBOUNCER_USERNAME", + password: 'PGBOUNCER_PASSWORD_HASH' + } +} + # Replace placeholders: # # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z @@ -380,7 +407,7 @@ You can use different certificates and keys for both API server and client on di However, the CA certificate (`patroni['tls_ca_file']`), TLS certificate verification (`patroni['tls_verify']`), and client TLS authentication mode (`patroni['tls_client_mode']`), must each have the same value on all nodes. -### Configuring the PgBouncer node +### Configuring the PgBouncer nodes 1. Make sure you collect [`CONSUL_SERVER_NODES`](#consul-information), [`CONSUL_PASSWORD_HASH`](#consul-information), and [`PGBOUNCER_PASSWORD_HASH`](#pgbouncer-information) before executing the next step. @@ -520,6 +547,7 @@ attributes set, but the following need to be set. gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = 'POSTGRESQL_USER_PASSWORD' gitlab_rails['auto_migrate'] = false + gitlab_rails['db_load_balancing'] = { 'hosts' => ['POSTGRESQL_NODE_1', 'POSTGRESQL_NODE_2', 'POSTGRESQL_NODE_3'] } ``` 1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. @@ -636,7 +664,7 @@ On database nodes edit `/etc/gitlab/gitlab.rb`: ```ruby # Disable all components except Patroni and Consul -roles(['patroni_role']) +roles(['patroni_role', 'pgbouncer_role']) # PostgreSQL configuration postgresql['listen_address'] = '0.0.0.0' @@ -656,6 +684,15 @@ patroni['postgresql']['max_wal_senders'] = 7 patroni['allowlist'] = = %w(10.6.0.0/16 127.0.0.1/32) postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/16 127.0.0.1/32) +# Local PgBouncer service for Database Load Balancing +pgbouncer['databases'] = { + gitlabhq_production: { + host: "127.0.0.1", + user: "pgbouncer", + password: '771a8625958a529132abe6f1a4acb19c' + } +} + # Configure the Consul agent consul['services'] = %w(postgresql) consul['configuration'] = { @@ -690,115 +727,6 @@ After deploying the configuration follow these steps: gitlab-rake gitlab:db:configure ``` -### Example minimal setup - -This example uses 3 PostgreSQL servers, and 1 application node (with PgBouncer setup alongside). - -It differs from the [recommended setup](#example-recommended-setup) by moving the Consul servers into the same servers we use for PostgreSQL. -The trade-off is between reducing server counts, against the increased operational complexity of needing to deal with PostgreSQL [failover](#manual-failover-procedure-for-patroni) procedures in addition to [Consul outage recovery](../consul.md#outage-recovery) on the same set of machines. - -In this example, we start with all servers on the same 10.6.0.0/16 private network range; they can connect to each freely other on those addresses. - -Here is a list and description of each machine and the assigned IP: - -- `10.6.0.21`: PostgreSQL 1 -- `10.6.0.22`: PostgreSQL 2 -- `10.6.0.23`: PostgreSQL 3 -- `10.6.0.31`: GitLab application - -All passwords are set to `toomanysecrets`. Please do not use this password or derived hashes. - -The `external_url` for GitLab is `http://gitlab.example.com` - -After the initial configuration, if a failover occurs, the PostgresSQL leader node changes to one of the available secondaries until it is failed back. - -#### Example minimal configuration for database servers - -On database nodes edit `/etc/gitlab/gitlab.rb`: - -```ruby -# Disable all components except Patroni and Consul -roles(['patroni_role']) - -# PostgreSQL configuration -postgresql['listen_address'] = '0.0.0.0' -postgresql['hot_standby'] = 'on' -postgresql['wal_level'] = 'replica' - -# Disable automatic database migrations -gitlab_rails['auto_migrate'] = false - -# Configure the Consul agent -consul['services'] = %w(postgresql) - -postgresql['pgbouncer_user_password'] = '771a8625958a529132abe6f1a4acb19c' -postgresql['sql_user_password'] = '450409b85a0223a214b5fb1484f34d0f' - -# Sets `max_replication_slots` to double the number of database nodes. -# Patroni uses one extra slot per node when initiating the replication. -patroni['postgresql']['max_replication_slots'] = 6 - -patroni['username'] = 'PATRONI_API_USERNAME' -patroni['password'] = 'PATRONI_API_PASSWORD' - -# Set `max_wal_senders` to one more than the number of replication slots in the cluster. -# This is used to prevent replication from using up all of the -# available database connections. -patroni['postgresql']['max_wal_senders'] = 7 - -patroni['allowlist'] = = %w(10.6.0.0/16 127.0.0.1/32) -postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/16 127.0.0.1/32) - -consul['configuration'] = { - server: true, - retry_join: %w(10.6.0.21 10.6.0.22 10.6.0.23) -} -``` - -[Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. - -#### Example minimal configuration for application server - -On the server edit `/etc/gitlab/gitlab.rb`: - -```ruby -external_url 'http://gitlab.example.com' - -gitlab_rails['db_host'] = '127.0.0.1' -gitlab_rails['db_port'] = 6432 -gitlab_rails['db_password'] = 'toomanysecrets' -gitlab_rails['auto_migrate'] = false - -postgresql['enable'] = false -pgbouncer['enable'] = true -consul['enable'] = true - -# Configure PgBouncer -pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul) - -# Configure Consul agent -consul['watchers'] = %w(postgresql) - -pgbouncer['users'] = { - 'gitlab-consul': { - password: '5e0e3263571e3704ad655076301d6ebe' - }, - 'pgbouncer': { - password: '771a8625958a529132abe6f1a4acb19c' - } -} - -consul['configuration'] = { - retry_join: %w(10.6.0.21 10.6.0.22 10.6.0.23) -} -``` - -[Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. - -#### Example minimal setup manual steps - -The manual steps for this configuration are the same as for the [example recommended setup](#example-recommended-setup-manual-steps). - ## Patroni NOTE: diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md index c7fd2f6d669702..4d1f0daf66bcb4 100644 --- a/doc/development/sidekiq_style_guide.md +++ b/doc/development/sidekiq_style_guide.md @@ -574,7 +574,7 @@ of reading a stale record is non-zero due to replicas potentially lagging behind When the number of jobs that rely on the database increases, ensuring immediate data consistency can put unsustainable load on the primary database server. We therefore added the ability to use -[database load balancing for Sidekiq workers](../administration/postgresql/database_load_balancing.md#load-balancing-for-sidekiq). +[database load balancing for Sidekiq workers](../administration/postgresql/database_load_balancing.md). By configuring a worker's `data_consistency` field, we can then allow the scheduler to target read replicas under several strategies outlined below. -- GitLab From 96177db01406e4a25c904cf82f230599df1e2f1b Mon Sep 17 00:00:00 2001 From: Grant Young Date: Wed, 10 Nov 2021 17:10:37 +0000 Subject: [PATCH 06/23] Further DB LB docs --- .../postgresql/database_load_balancing.md | 43 +++++++++- doc/administration/postgresql/pgbouncer.md | 2 +- .../postgresql/replication_and_failover.md | 49 ++++++----- .../reference_architectures/10k_users.md | 83 ++++++++++++------- doc/development/scalability.md | 5 +- 5 files changed, 123 insertions(+), 59 deletions(-) diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md index f49e84aed1c616..7b238283489aa7 100644 --- a/doc/administration/postgresql/database_load_balancing.md +++ b/doc/administration/postgresql/database_load_balancing.md @@ -19,7 +19,7 @@ This functionality is provided natively in GitLab Rails and Sidekiq where it can be configured to balance the load round robin, without any external dependencies. ```plantuml -@startuml database_load_balancing +@startuml card "**Internal Load Balancer**" as ilb #9370DB together { @@ -60,6 +60,47 @@ consul .[#e76a9b]-> pgbouncer3 @enduml ``` +```plantuml +@startuml +card "**Internal Load Balancer**" as ilb #9370DB +skinparam linetype ortho + +together { + collections "**GitLab Rails** x3" as gitlab #32CD32 + collections "**Sidekiq** x4" as sidekiq #ff8dd1 +} + +collections "**Consul** x3" as consul #e76a9b + +card "Database" as database { + collections "**PGBouncer x3**\n//Consul//" as pgbouncer #4EA7FF + + card "**PostgreSQL** //Primary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_primary #4EA7FF + collections "**PostgreSQL** //Secondary// **x2**\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary #4EA7FF + + pgbouncer -[#4EA7FF]-> postgres_primary + postgres_primary .[#4EA7FF]-> postgres_secondary + postgres_secondary .[hidden]u-> postgres_primary +} + +gitlab -[#32CD32]-> ilb +gitlab -[hidden]-> pgbouncer +gitlab .[#32CD32,norank]-> postgres_primary +gitlab .[#32CD32,norank]-> postgres_secondary + +sidekiq -[#ff8dd1]-> ilb +sidekiq -[hidden]-> pgbouncer +sidekiq .[#ff8dd1,norank]-> postgres_primary +sidekiq .[#ff8dd1,norank]-> postgres_secondary + +ilb -[#9370DB]-> pgbouncer + +consul -[#e76a9b]r-> pgbouncer +consul .[#e76a9b,norank]r-> postgres_primary +consul .[#e76a9b,norank]r-> postgres_secondary +@enduml +``` + ### Requirements Note the following requirements for Database Load Balancing: diff --git a/doc/administration/postgresql/pgbouncer.md b/doc/administration/postgresql/pgbouncer.md index e5fef61540a842..6c866fba0ab61e 100644 --- a/doc/administration/postgresql/pgbouncer.md +++ b/doc/administration/postgresql/pgbouncer.md @@ -17,7 +17,7 @@ through `/etc/gitlab/gitlab.rb`. ## PgBouncer as part of a fault-tolerant GitLab installation -This content has been moved to a [new location](replication_and_failover.md#configuring-the-pgbouncer-node). +This content has been moved to a [new location](replication_and_failover.md#configuring-the-pgbouncer-nodes). ## PgBouncer as part of a non-fault-tolerant GitLab installation diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index 0d557364edae57..bafc8e5db69e92 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -27,44 +27,43 @@ replication, failover requires: - An internal load balancer (TCP) is also required to balance requests between the nodes. ```plantuml -@startuml database_load_balancing +@startuml card "**Internal Load Balancer**" as ilb #9370DB +skinparam linetype ortho together { - card "**GitLab Rails / Sidekiq**" as gitlab #32CD32 + collections "**GitLab Rails** x3" as gitlab #32CD32 + collections "**Sidekiq** x4" as sidekiq #ff8dd1 } collections "**Consul** x3" as consul #e76a9b card "Database" as database { - card "**PGBouncer**\n//Consul//" as pgbouncer1 #4EA7FF - card "**PGBouncer**\n//Consul//" as pgbouncer2 #4EA7FF - card "**PGBouncer**\n//Consul//" as pgbouncer3 #4EA7FF + collections "**PGBouncer x3**\n//Consul//" as pgbouncer #4EA7FF card "**PostgreSQL** //Primary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_primary #4EA7FF - card "**PostgreSQL** //Secondary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary1 #4EA7FF - card "**PostgreSQL** //Secondary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary2 #4EA7FF - - pgbouncer1 -[#4EA7FF]-> postgres_primary - pgbouncer2 -[#4EA7FF]-> postgres_primary - pgbouncer3 -[#4EA7FF]-> postgres_primary - postgres_primary .[#4EA7FF]-> postgres_secondary1 - postgres_primary .[#4EA7FF]-> postgres_secondary2 - postgres_secondary2 .[hidden]u-> postgres_primary + collections "**PostgreSQL** //Secondary// **x2**\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary #4EA7FF + + pgbouncer -[#4EA7FF]-> postgres_primary + postgres_primary .[#4EA7FF]-> postgres_secondary + postgres_secondary .[hidden]u-> postgres_primary } gitlab -[#32CD32]-> ilb -gitlab .[#32CD32]-> postgres_primary -gitlab .[#32CD32]-> postgres_secondary1 -gitlab .[#32CD32]-> postgres_secondary2 +gitlab -[hidden]-> pgbouncer +gitlab .[#32CD32,norank]-> postgres_primary +gitlab .[#32CD32,norank]-> postgres_secondary + +sidekiq -[#ff8dd1]-> ilb +sidekiq -[hidden]-> pgbouncer +sidekiq .[#ff8dd1,norank]-> postgres_primary +sidekiq .[#ff8dd1,norank]-> postgres_secondary -ilb -[#9370DB]-> pgbouncer1 -ilb -[#9370DB]-> pgbouncer2 -ilb -[#9370DB]-> pgbouncer3 +ilb -[#9370DB]-> pgbouncer -consul .[#e76a9b]-> pgbouncer1 -consul .[#e76a9b]-> pgbouncer2 -consul .[#e76a9b]-> pgbouncer3 +consul -[#e76a9b]r-> pgbouncer +consul .[#e76a9b,norank]r-> postgres_primary +consul .[#e76a9b,norank]r-> postgres_secondary @enduml ``` @@ -259,7 +258,7 @@ repmgr-specific configuration as well. Especially, make sure that you remove `po Here is an example: ```ruby -# Disable all components except Patroni and Consul +# Disable all components except Patroni, PgBouncer and Consul roles(['patroni_role', 'pgbouncer_role']) # PostgreSQL configuration @@ -663,7 +662,7 @@ An internal load balancer (TCP) is then required to be setup to serve each PgBou On database nodes edit `/etc/gitlab/gitlab.rb`: ```ruby -# Disable all components except Patroni and Consul +# Disable all components except Patroni, PgBouncer and Consul roles(['patroni_role', 'pgbouncer_role']) # PostgreSQL configuration diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index 9c3c33e1fa86e1..4971a5f71a9095 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -49,6 +49,8 @@ For all PaaS solutions that involve configuring instances, it is strongly recomm ```plantuml @startuml 10k +skinparam linetype ortho + card "**External Load Balancer**" as elb #6a9be7 card "**Internal Load Balancer**" as ilb #9370DB @@ -83,31 +85,38 @@ card "Database" as database { card "redis" as redis { collections "**Redis Persistent** x3" as redis_persistent #FF6347 collections "**Redis Cache** x3" as redis_cache #FF6347 + + redis_cache -[hidden]-> redis_persistent } cloud "**Object Storage**" as object_storage #white elb -[#6a9be7]-> gitlab -elb -[#6a9be7]--> monitor +elb -[#6a9be7,norank]--> monitor -gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]-> object_storage -gitlab -[#32CD32]---> redis +gitlab -[#32CD32,norank]--> ilb +gitlab -[#32CD32]r-> object_storage +gitlab -[#32CD32]----> redis +gitlab .[#32CD32]----> database gitlab -[hidden]-> monitor gitlab -[hidden]-> consul -sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]-> object_storage -sidekiq -[#ff8dd1]---> redis +sidekiq -[#ff8dd1,norank]--> ilb +sidekiq -[#ff8dd1]r-> object_storage +sidekiq -[#ff8dd1]----> redis +sidekiq .[#ff8dd1]----> database sidekiq -[hidden]-> monitor sidekiq -[hidden]-> consul -ilb -[#9370DB]-> gitaly_cluster -ilb -[#9370DB]-> database +ilb -[#9370DB]--> gitaly_cluster +ilb -[#9370DB]--> database +ilb -[hidden]--> redis +ilb -[hidden]u-> consul +ilb -[hidden]u-> monitor consul .[#e76a9b]u-> gitlab consul .[#e76a9b]u-> sidekiq -consul .[#e76a9b]> monitor +consul .[#e76a9b]r-> monitor consul .[#e76a9b]-> database consul .[#e76a9b]-> gitaly_cluster consul .[#e76a9b,norank]--> redis @@ -471,7 +480,7 @@ run: node-exporter: (pid 30093) 76833s; run: log: (pid 29663) 76855s ## Configure PostgreSQL -In this section, you'll be guided through configuring an external PostgreSQL database +In this section, you'll be guided through configuring PostgreSQL database to be used with GitLab. ### Provide your own PostgreSQL instance @@ -488,12 +497,19 @@ If you use a cloud-managed service, or provide your own PostgreSQL: needs privileges to create the `gitlabhq_production` database. 1. Configure the GitLab application servers with the appropriate details. This step is covered in [Configuring the GitLab Rails application](#configure-gitlab-rails). +1. For improved performance, configuring [Database Load Balancing](../postgresql/database_load_balancing.md) with multiple read replicas is recommended. See [Configure GitLab using an external PostgreSQL service](../postgresql/external.md) for further configuration steps. ### Standalone PostgreSQL using Omnibus GitLab +The recommended PostgreSQL setup includes the following components: + +- 3 PostgreSQL nodes with PgBouncer running locally for [Database Load Balancing](../postgresql/database_load_balancing.md) +- 3 PgBouncer nodes to handle primary queries + - 1 load balancer: A load balancer is required for PgBouncer. The [internal load balancer](#configure-the-internal-load-balancer) will be used. + The following IPs will be used as an example: - `10.6.0.21`: PostgreSQL primary @@ -548,8 +564,8 @@ in the second step, do not supply the `EXTERNAL_URL` value. 1. On every database node, edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section: ```ruby - # Disable all components except Patroni and Consul - roles(['patroni_role']) + # Disable all components except Patroni, PgBouncer and Consul + roles(['patroni_role', 'pgbouncer_role']) # PostgreSQL configuration postgresql['listen_address'] = '0.0.0.0' @@ -594,6 +610,15 @@ in the second step, do not supply the `EXTERNAL_URL` value. # Replace 10.6.0.0/24 with Network Address postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32) + # Local PgBouncer service for Database Load Balancing + pgbouncer['databases'] = { + gitlabhq_production: { + host: "127.0.0.1", + user: "pgbouncer", + password: '' + } + } + # Set the network addresses that the exporters will listen on for monitoring node_exporter['listen_address'] = '0.0.0.0:9100' postgres_exporter['listen_address'] = '0.0.0.0:9187' @@ -654,7 +679,7 @@ If the 'State' column for any node doesn't say "running", check the -## Configure PgBouncer +### Configure PgBouncer Now that the PostgreSQL servers are all set up, let's configure PgBouncer. The following IPs will be used as an example: @@ -1673,6 +1698,7 @@ To configure the Sidekiq nodes, on each one: gitlab_rails['db_password'] = '' gitlab_rails['db_adapter'] = 'postgresql' gitlab_rails['db_encoding'] = 'unicode' + gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.6.0.21', '10.6.0.22', '10.6.0.23'] } # PostgreSQL IPs ## Prevent database migrations from running on upgrade automatically gitlab_rails['auto_migrate'] = false @@ -1797,6 +1823,7 @@ On each node perform the following: gitlab_rails['db_host'] = '10.6.0.20' # internal load balancer IP gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = '' + gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.6.0.21', '10.6.0.22', '10.6.0.23'] } # PostgreSQL IPs # Prevent database migrations from running on upgrade automatically gitlab_rails['auto_migrate'] = false @@ -2212,6 +2239,7 @@ For all PaaS solutions that involve configuring instances, it is strongly recomm ```plantuml @startuml 10k +skinparam linetype ortho card "Kubernetes via Helm Charts" as kubernetes { card "**External Load Balancer**" as elb #6a9be7 @@ -2221,7 +2249,6 @@ card "Kubernetes via Helm Charts" as kubernetes { collections "**Sidekiq** x4" as sidekiq #ff8dd1 } - card "**Prometheus + Grafana**" as monitor #7FFFD4 card "**Supporting Services**" as support } @@ -2249,37 +2276,35 @@ card "Database" as database { card "redis" as redis { collections "**Redis Persistent** x3" as redis_persistent #FF6347 collections "**Redis Cache** x3" as redis_cache #FF6347 + + redis_cache -[hidden]-> redis_persistent } cloud "**Object Storage**" as object_storage #white elb -[#6a9be7]-> gitlab -elb -[#6a9be7]-> monitor +elb -[hidden]-> sidekiq elb -[hidden]-> support gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]-> object_storage +gitlab -[#32CD32]--> object_storage gitlab -[#32CD32]---> redis +gitlab -[#32CD32,norank]---> database gitlab -[hidden]--> consul sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]-> object_storage +sidekiq -[#ff8dd1]--> object_storage sidekiq -[#ff8dd1]---> redis +sidekiq .[#ff8dd1,norank]---> database sidekiq -[hidden]--> consul -ilb -[#9370DB]-> gitaly_cluster -ilb -[#9370DB]-> database +ilb -[#9370DB]--> gitaly_cluster +ilb -[#9370DB]--> database +ilb -[hidden]--> redis consul .[#e76a9b]-> database -consul .[#e76a9b]-> gitaly_cluster -consul .[#e76a9b,norank]--> redis - -monitor .[#7FFFD4]> consul -monitor .[#7FFFD4]-> database -monitor .[#7FFFD4]-> gitaly_cluster -monitor .[#7FFFD4,norank]--> redis -monitor .[#7FFFD4]> ilb -monitor .[#7FFFD4,norank]u--> elb +consul .[#e76a9b,norank]-> gitaly_cluster +consul .[#e76a9b]-> redis @enduml ``` diff --git a/doc/development/scalability.md b/doc/development/scalability.md index bd999e7a9ba34c..3fc3d160bc8645 100644 --- a/doc/development/scalability.md +++ b/doc/development/scalability.md @@ -123,9 +123,8 @@ the read replicas. [Omnibus ships with Patroni](../administration/postgresql/rep #### Load-balancing -GitLab EE has [application support for load balancing using read -replicas](../administration/postgresql/database_load_balancing.md). This load balancer does -some actions that aren't traditionally available in standard load balancers. For +GitLab EE has [application support for load balancing using read replicas](../administration/postgresql/database_load_balancing.md). +This load balancer does some actions that aren't traditionally available in standard load balancers. For example, the application considers a replica only if its replication lag is low (for example, WAL data behind by less than 100 MB). -- GitLab From bab3aec50e272d2e732425fff5d9524a24ba95d9 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Wed, 10 Nov 2021 17:13:23 +0000 Subject: [PATCH 07/23] Update graph for DB LB docs --- .../postgresql/database_load_balancing.md | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md index 7b238283489aa7..751b785c72b05f 100644 --- a/doc/administration/postgresql/database_load_balancing.md +++ b/doc/administration/postgresql/database_load_balancing.md @@ -21,42 +21,41 @@ it can be configured to balance the load round robin, without any external depen ```plantuml @startuml card "**Internal Load Balancer**" as ilb #9370DB +skinparam linetype ortho together { - card "**GitLab Rails / Sidekiq**" as gitlab #32CD32 + collections "**GitLab Rails** x3" as gitlab #32CD32 + collections "**Sidekiq** x4" as sidekiq #ff8dd1 } collections "**Consul** x3" as consul #e76a9b card "Database" as database { - card "**PGBouncer**\n//Consul//" as pgbouncer1 #4EA7FF - card "**PGBouncer**\n//Consul//" as pgbouncer2 #4EA7FF - card "**PGBouncer**\n//Consul//" as pgbouncer3 #4EA7FF + collections "**PGBouncer x3**\n//Consul//" as pgbouncer #4EA7FF card "**PostgreSQL** //Primary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_primary #4EA7FF - card "**PostgreSQL** //Secondary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary1 #4EA7FF - card "**PostgreSQL** //Secondary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary2 #4EA7FF - - pgbouncer1 -[#4EA7FF]-> postgres_primary - pgbouncer2 -[#4EA7FF]-> postgres_primary - pgbouncer3 -[#4EA7FF]-> postgres_primary - postgres_primary .[#4EA7FF]-> postgres_secondary1 - postgres_primary .[#4EA7FF]-> postgres_secondary2 - postgres_secondary2 .[hidden]u-> postgres_primary + collections "**PostgreSQL** //Secondary// **x2**\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary #4EA7FF + + pgbouncer -[#4EA7FF]-> postgres_primary + postgres_primary .[#4EA7FF]-> postgres_secondary + postgres_secondary .[hidden]u-> postgres_primary } gitlab -[#32CD32]-> ilb -gitlab .[#32CD32]-> postgres_primary -gitlab .[#32CD32]-> postgres_secondary1 -gitlab .[#32CD32]-> postgres_secondary2 +gitlab -[hidden]-> pgbouncer +gitlab .[#32CD32,norank]-> postgres_primary +gitlab .[#32CD32,norank]-> postgres_secondary + +sidekiq -[#ff8dd1]-> ilb +sidekiq -[hidden]-> pgbouncer +sidekiq .[#ff8dd1,norank]-> postgres_primary +sidekiq .[#ff8dd1,norank]-> postgres_secondary -ilb -[#9370DB]-> pgbouncer1 -ilb -[#9370DB]-> pgbouncer2 -ilb -[#9370DB]-> pgbouncer3 +ilb -[#9370DB]-> pgbouncer -consul .[#e76a9b]-> pgbouncer1 -consul .[#e76a9b]-> pgbouncer2 -consul .[#e76a9b]-> pgbouncer3 +consul -[#e76a9b]r-> pgbouncer +consul .[#e76a9b,norank]r-> postgres_primary +consul .[#e76a9b,norank]r-> postgres_secondary @enduml ``` -- GitLab From fb005fee0d5a0c615fba1f6eca20a491c608399f Mon Sep 17 00:00:00 2001 From: Grant Young Date: Wed, 10 Nov 2021 17:13:56 +0000 Subject: [PATCH 08/23] Update graph further for DB LB docs --- .../postgresql/database_load_balancing.md | 41 ------------------- 1 file changed, 41 deletions(-) diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md index 751b785c72b05f..d8d3d5094da6ac 100644 --- a/doc/administration/postgresql/database_load_balancing.md +++ b/doc/administration/postgresql/database_load_balancing.md @@ -59,47 +59,6 @@ consul .[#e76a9b,norank]r-> postgres_secondary @enduml ``` -```plantuml -@startuml -card "**Internal Load Balancer**" as ilb #9370DB -skinparam linetype ortho - -together { - collections "**GitLab Rails** x3" as gitlab #32CD32 - collections "**Sidekiq** x4" as sidekiq #ff8dd1 -} - -collections "**Consul** x3" as consul #e76a9b - -card "Database" as database { - collections "**PGBouncer x3**\n//Consul//" as pgbouncer #4EA7FF - - card "**PostgreSQL** //Primary//\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_primary #4EA7FF - collections "**PostgreSQL** //Secondary// **x2**\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary #4EA7FF - - pgbouncer -[#4EA7FF]-> postgres_primary - postgres_primary .[#4EA7FF]-> postgres_secondary - postgres_secondary .[hidden]u-> postgres_primary -} - -gitlab -[#32CD32]-> ilb -gitlab -[hidden]-> pgbouncer -gitlab .[#32CD32,norank]-> postgres_primary -gitlab .[#32CD32,norank]-> postgres_secondary - -sidekiq -[#ff8dd1]-> ilb -sidekiq -[hidden]-> pgbouncer -sidekiq .[#ff8dd1,norank]-> postgres_primary -sidekiq .[#ff8dd1,norank]-> postgres_secondary - -ilb -[#9370DB]-> pgbouncer - -consul -[#e76a9b]r-> pgbouncer -consul .[#e76a9b,norank]r-> postgres_primary -consul .[#e76a9b,norank]r-> postgres_secondary -@enduml -``` - ### Requirements Note the following requirements for Database Load Balancing: -- GitLab From 12bcbd83c58fa5e956010411fb38110adf95caf0 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Wed, 10 Nov 2021 17:24:24 +0000 Subject: [PATCH 09/23] Fix broken links --- doc/administration/index.md | 2 +- doc/administration/logs.md | 2 +- doc/administration/postgresql/replication_and_failover.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/administration/index.md b/doc/administration/index.md index 53a3c305aab471..e164a93b343efb 100644 --- a/doc/administration/index.md +++ b/doc/administration/index.md @@ -31,7 +31,7 @@ Learn how to install, configure, update, and maintain your GitLab instance. ### Installing GitLab - [Install](../install/index.md): Requirements, directory structures, and installation methods. - - [Database load balancing](database_load_balancing.md): Distribute database queries among multiple database servers. + - [Database load balancing](postgresql/database_load_balancing.md): Distribute database queries among multiple database servers. - [Omnibus support for log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-shipping-gitlab-enterprise-edition-only). - [Reference architectures](reference_architectures/index.md): Add additional resources to support more users. - [Installing GitLab on Amazon Web Services (AWS)](../install/aws/index.md): Set up GitLab on Amazon AWS. diff --git a/doc/administration/logs.md b/doc/administration/logs.md index bf74a96a627d7e..c9b1253f041510 100644 --- a/doc/administration/logs.md +++ b/doc/administration/logs.md @@ -830,7 +830,7 @@ are generated in a location based on your installation method: > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/15442) in GitLab 12.3. -Contains details of GitLab [Database Load Balancing](database_load_balancing.md). +Contains details of GitLab [Database Load Balancing](postgresql/database_load_balancing.md). Depending on your installation method, this file is located at: - Omnibus GitLab: `/var/log/gitlab/gitlab-rails/database_load_balancing.log` diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index bafc8e5db69e92..b8b5730f840050 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -85,7 +85,7 @@ Each database node runs four services: - Selecting a new leader for the cluster. - Promoting the new node to leader. - Instructing remaining servers to follow the new leader node. -- `PgBouncer`: A local pooler for the node. Used for read queries as part of [Database Load Balancing](database_load_balancing.md). +- `PgBouncer`: A local pooler for the node. Used for read queries as part of [Database Load Balancing](postgresql/database_load_balancing.md). - `Consul` agent: To communicate with Consul cluster which stores the current Patroni state. The agent monitors the status of each node in the database cluster and tracks its health in a service definition on the Consul cluster. ### Consul server node -- GitLab From e4cfb39f96ac39ae574b5a4ce9d789d9713a343e Mon Sep 17 00:00:00 2001 From: Grant Young Date: Thu, 11 Nov 2021 10:05:17 +0000 Subject: [PATCH 10/23] Link fixes --- .../monitoring/performance/performance_bar.md | 7 +++---- doc/administration/postgresql/replication_and_failover.md | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md index d798feb71a9f70..6a5ddb3435dd6e 100644 --- a/doc/administration/monitoring/performance/performance_bar.md +++ b/doc/administration/monitoring/performance/performance_bar.md @@ -26,10 +26,9 @@ From left to right, the performance bar displays: details for each query: - **In a transaction**: shows up below the query if it was executed in the context of a transaction - - **Role**: shows up when [database load - balancing](../../database_load_balancing.md) is enabled. It shows - which server role was used for the query. "Primary" means that the query - was sent to the read/write primary server. "Replica" means it was sent + - **Role**: shows up when [database load balancing](../../postgresql/database_load_balancing.md) + is enabled. It shows which server role was used for the query. "Primary" means + that the query was sent to the read/write primary server. "Replica" means it was sent to a read-only replica. - **Config name**: shows up only when the `GITLAB_MULTIPLE_DATABASE_METRICS` environment variable is set. This is diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index b8b5730f840050..bafc8e5db69e92 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -85,7 +85,7 @@ Each database node runs four services: - Selecting a new leader for the cluster. - Promoting the new node to leader. - Instructing remaining servers to follow the new leader node. -- `PgBouncer`: A local pooler for the node. Used for read queries as part of [Database Load Balancing](postgresql/database_load_balancing.md). +- `PgBouncer`: A local pooler for the node. Used for read queries as part of [Database Load Balancing](database_load_balancing.md). - `Consul` agent: To communicate with Consul cluster which stores the current Patroni state. The agent monitors the status of each node in the database cluster and tracks its health in a service definition on the Consul cluster. ### Consul server node -- GitLab From 44610e5eb619ec708dee387b9a4340942ed7de8f Mon Sep 17 00:00:00 2001 From: Grant Young Date: Thu, 11 Nov 2021 10:48:25 +0000 Subject: [PATCH 11/23] Docs polish --- .../postgresql/database_load_balancing.md | 123 +++++++++--------- .../postgresql/replication_and_failover.md | 13 +- .../reference_architectures/10k_users.md | 16 ++- 3 files changed, 79 insertions(+), 73 deletions(-) diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md index d8d3d5094da6ac..d9c57c318434bb 100644 --- a/doc/administration/postgresql/database_load_balancing.md +++ b/doc/administration/postgresql/database_load_balancing.md @@ -18,6 +18,8 @@ all PostgreSQL nodes to increase performance. This functionality is provided natively in GitLab Rails and Sidekiq where it can be configured to balance the load round robin, without any external dependencies. +

+ ```plantuml @startuml card "**Internal Load Balancer**" as ilb #9370DB @@ -37,8 +39,7 @@ card "Database" as database { collections "**PostgreSQL** //Secondary// **x2**\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary #4EA7FF pgbouncer -[#4EA7FF]-> postgres_primary - postgres_primary .[#4EA7FF]-> postgres_secondary - postgres_secondary .[hidden]u-> postgres_primary + postgres_primary .[#4EA7FF]r-> postgres_secondary } gitlab -[#32CD32]-> ilb @@ -59,17 +60,18 @@ consul .[#e76a9b,norank]r-> postgres_secondary @enduml ``` +
+ ### Requirements Note the following requirements for Database Load Balancing: - A HA Postgres setup that has 1 or more secondary nodes replicating the primary. -- Each Postgres node can be connected with the same credentials and on the same port. +- Each Postgres node can be connected to with the same credentials and on the same port. -Additionally the following is recommended but not required: +When using Omnibus GitLab the additional requirements apply when [using HA](replication_and_failover.md): - PgBouncer configured on each PostgreSQL node to pooling all load balanced connections. - - For external PostgreSQL setups, a different database pooler could be used as desired. ## Configuring Database Load Balancing @@ -82,16 +84,18 @@ Refer below to each section for more details. ### Hosts -Configuring with a list of hosts is straight forward -For the environment in which you want to use load balancing, you'll need to add -the following. This balances the load between `host1.example.com` and -`host2.example.com`. +Configuring with a list of hosts is straight forward. For the environment in which you want to use +load balancing, you add the `gitlab_rails['db_load_balancing']` setting into the `gitlab.rb file. + +For example, on an environment that has PostgreSQL running on the hosts `host1.example.com`, +`host2.example.com` and `host3.example.com` and reachable on the same port configured with +`gitlab_rails['db_port']`: 1. Edit `/etc/gitlab/gitlab.rb` and add the following line: - ```ruby - gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.example.com'] } - ``` +```ruby +gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.example.com', `host3.example.com`] } +``` 1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. @@ -99,15 +103,17 @@ the following. This balances the load between `host1.example.com` and > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/5883) in GitLab 11.0. -Service discovery allows GitLab to automatically retrieve a list of secondary -databases to use, instead of having to manually specify these in the -`database.yml` configuration file. Service discovery works by periodically +Service discovery allows GitLab to automatically retrieve a list of PostgreSQL +hosts to use. Service discovery works by periodically checking a DNS A record, using the IPs returned by this record as the addresses for the secondaries. For service discovery to work, all you need is a DNS server and an A record containing the IP addresses of your secondaries. -To use service discovery you need to change your `database.yml` configuration -file so it looks like the following: + +When using Omnibus GitLab the provided [Consul](../consul.md) service works as +a DNS server and will return PostgreSQL addresses via the `postgresql-ha.service.consul` +record. An example of how you would configure this along with details for each +setting after is as follows: 1. Edit `/etc/gitlab/gitlab.rb` and add the following line: @@ -122,13 +128,10 @@ gitlab_rails['db_load_balancing'] = { 'discover' => { } } ``` + 1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. -#### Configuration - -The following options can be set: - | Option | Description | Default | |----------------------|---------------------------------------------------------------------------------------------------|-----------| | `nameserver` | The nameserver to use for looking up the DNS record. | localhost | @@ -155,10 +158,6 @@ When the list of hosts is updated, it might take a while for the old connections to be terminated. The `disconnect_timeout` setting can be used to enforce an upper limit on the time it takes to terminate all old database connections. -Some nameservers (like [Consul](https://www.consul.io/docs/discovery/dns#udp-based-dns-queries)) can return a truncated list of hosts when -queried over UDP. To overcome this issue, you can use TCP for querying by setting -`use_tcp` to `true`. - ### Handling Stale Reads **(PREMIUM SELF)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3526) in GitLab 10.3. @@ -176,28 +175,47 @@ There are three configuration options that influence this behavior: | `max_replication_lag_time` | The maximum number of seconds a secondary is allowed to lag behind before we stop using it. | 60 seconds | | `replica_check_interval` | The minimum number of seconds we have to wait before checking the status of a secondary. | 60 seconds | -The defaults should be sufficient for most users. Should you want to change them -you can specify them in `config/database.yml` like so: - -```yaml -production: - username: gitlab - database: gitlab - encoding: unicode - load_balancing: - hosts: - - host1.example.com - - host2.example.com - max_replication_difference: 16777216 # 16 MB - max_replication_lag_time: 30 - replica_check_interval: 30 +The defaults should be sufficient for most users. + +An example of configuring these options with a hosts list would be: + +```ruby +gitlab_rails['db_load_balancing'] = { + 'hosts' => ['host1.example.com', 'host2.example.com', `host3.example.com`] + 'max_replication_difference' => 16777216 # 16 MB + 'max_replication_lag_time' => 30 + 'replica_check_interval' => 30 +} +``` + +## Logging + +The load balancer logs various events in +[`database_load_balancing.log`](../logs.md#database_load_balancinglog), such as + +- When a host is marked as offline +- When a host comes back online +- When all secondaries are offline +- When a read is retried on a different host due to a query conflict + +The log is structured with each entry a JSON object containing at least: + +- An `event` field useful for filtering. +- A human-readable `message` field. +- Some event-specific metadata. For example, `db_host` +- Contextual information that is always logged. For example, `severity` and `time`. + +For example: + +```json +{"severity":"INFO","time":"2019-09-02T12:12:01.728Z","correlation_id":"abcdefg","event":"host_online","message":"Host came back online","db_host":"111.222.333.444","db_port":null,"tag":"rails.database_load_balancing","environment":"production","hostname":"web-example-1","fqdn":"gitlab.example.com","path":null,"params":null} ``` ## Implementation Details ### Balancing queries -Read-only `SELECT` queries balance among all the secondary hosts. +Read-only `SELECT` queries balance among all the given hosts. Everything else (including transactions) executes on the primary. Queries such as `SELECT ... FOR UPDATE` are also executed on the primary. @@ -225,26 +243,3 @@ operation is retried up to 3 times using an exponential back-off. When using load balancing, you should be able to safely restart a database server without it immediately leading to errors being presented to the users. - -### Logging - -The load balancer logs various events in -[`database_load_balancing.log`](../logs.md#database_load_balancinglog), such as - -- When a host is marked as offline -- When a host comes back online -- When all secondaries are offline -- When a read is retried on a different host due to a query conflict - -The log is structured with each entry a JSON object containing at least: - -- An `event` field useful for filtering. -- A human-readable `message` field. -- Some event-specific metadata. For example, `db_host` -- Contextual information that is always logged. For example, `severity` and `time`. - -For example: - -```json -{"severity":"INFO","time":"2019-09-02T12:12:01.728Z","correlation_id":"abcdefg","event":"host_online","message":"Host came back online","db_host":"111.222.333.444","db_port":null,"tag":"rails.database_load_balancing","environment":"production","hostname":"web-example-1","fqdn":"gitlab.example.com","path":null,"params":null} -``` diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index bafc8e5db69e92..2490e85bdcc3cb 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -23,8 +23,12 @@ replication, failover requires: - A minimum of three PostgreSQL nodes. - A minimum of three Consul server nodes. -- A PgBouncer cluster of 1 node or more to handle primary database writes and reads. +- A minimum of three PgBouncer nodes that track and handle primary database writes and reads. - An internal load balancer (TCP) is also required to balance requests between the nodes. +- [Database Load Balancing](database_load_balancing.md) enabled. + - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. + +
```plantuml @startuml @@ -45,8 +49,7 @@ card "Database" as database { collections "**PostgreSQL** //Secondary// **x2**\n//Patroni//\n//PgBouncer//\n//Consul//" as postgres_secondary #4EA7FF pgbouncer -[#4EA7FF]-> postgres_primary - postgres_primary .[#4EA7FF]-> postgres_secondary - postgres_secondary .[hidden]u-> postgres_primary + postgres_primary .[#4EA7FF]r-> postgres_secondary } gitlab -[#32CD32]-> ilb @@ -67,6 +70,8 @@ consul .[#e76a9b,norank]r-> postgres_secondary @enduml ``` +
+ You also need to take into consideration the underlying network topology, making sure you have redundant connectivity between all Database and GitLab instances to avoid the network becoming a single point of failure. @@ -1014,7 +1019,7 @@ Here are a few key facts that you must consider before upgrading PostgreSQL: configured replication method (`pg_basebackup` is the only available option). It might take some time for replica to catch up with the leader, depending on the size of your database. -- An overview of the upgrade procedure is outlined in [Patoni's documentation](https://patroni.readthedocs.io/en/latest/existing_data.html#major-upgrade-of-postgresql-version). +- An overview of the upgrade procedure is outlined in [Patroni's documentation](https://patroni.readthedocs.io/en/latest/existing_data.html#major-upgrade-of-postgresql-version). You can still use `gitlab-ctl pg-upgrade` which implements this procedure with a few adjustments. Considering these, you should carefully plan your PostgreSQL upgrade: diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index 4971a5f71a9095..51b3e44dade7bf 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -75,8 +75,8 @@ card "Gitaly Cluster" as gitaly_cluster { card "Database" as database { collections "**PGBouncer** x3" as pgbouncer #4EA7FF - card "**PostgreSQL** (Primary)" as postgres_primary #4EA7FF - collections "**PostgreSQL** (Secondary) x2" as postgres_secondary #4EA7FF + card "**PostgreSQL** //Primary//" as postgres_primary #4EA7FF + collections "**PostgreSQL** //Secondary// x2" as postgres_secondary #4EA7FF pgbouncer -[#4EA7FF]-> postgres_primary postgres_primary .[#4EA7FF]> postgres_secondary @@ -506,9 +506,15 @@ further configuration steps. The recommended PostgreSQL setup includes the following components: -- 3 PostgreSQL nodes with PgBouncer running locally for [Database Load Balancing](../postgresql/database_load_balancing.md) -- 3 PgBouncer nodes to handle primary queries - - 1 load balancer: A load balancer is required for PgBouncer. The [internal load balancer](#configure-the-internal-load-balancer) will be used. +The Omnibus GitLab recommended configuration for a PostgreSQL cluster with +replication, failover requires: + +- A minimum of three PostgreSQL nodes. +- A minimum of three Consul server nodes. +- A PgBouncer cluster of 1 node or more to handle primary database writes and reads. + - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) is also required to balance requests between the nodes. +- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled for improved read queries performance. + - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: -- GitLab From 20cb3917b649881ef6b87f340351ac9a9f8439de Mon Sep 17 00:00:00 2001 From: Grant Young Date: Thu, 11 Nov 2021 10:56:58 +0000 Subject: [PATCH 12/23] Correct markdownlint markers --- doc/administration/postgresql/database_load_balancing.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md index d9c57c318434bb..edcf468a522259 100644 --- a/doc/administration/postgresql/database_load_balancing.md +++ b/doc/administration/postgresql/database_load_balancing.md @@ -91,6 +91,7 @@ For example, on an environment that has PostgreSQL running on the hosts `host1.e `host2.example.com` and `host3.example.com` and reachable on the same port configured with `gitlab_rails['db_port']`: + 1. Edit `/etc/gitlab/gitlab.rb` and add the following line: ```ruby @@ -98,6 +99,7 @@ gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.ex ``` 1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. + ### Service Discovery **(PREMIUM SELF)** -- GitLab From 80fa2632c6aa97babf1184555eb6e6caced55050 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Thu, 11 Nov 2021 11:22:11 +0000 Subject: [PATCH 13/23] Remove divs --- doc/administration/postgresql/database_load_balancing.md | 6 +----- doc/administration/postgresql/replication_and_failover.md | 4 ---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md index edcf468a522259..04c89f77dc62a4 100644 --- a/doc/administration/postgresql/database_load_balancing.md +++ b/doc/administration/postgresql/database_load_balancing.md @@ -18,8 +18,6 @@ all PostgreSQL nodes to increase performance. This functionality is provided natively in GitLab Rails and Sidekiq where it can be configured to balance the load round robin, without any external dependencies. -
- ```plantuml @startuml card "**Internal Load Balancer**" as ilb #9370DB @@ -60,8 +58,6 @@ consul .[#e76a9b,norank]r-> postgres_secondary @enduml ``` -
- ### Requirements Note the following requirements for Database Load Balancing: @@ -69,7 +65,7 @@ Note the following requirements for Database Load Balancing: - A HA Postgres setup that has 1 or more secondary nodes replicating the primary. - Each Postgres node can be connected to with the same credentials and on the same port. -When using Omnibus GitLab the additional requirements apply when [using HA](replication_and_failover.md): +When using Omnibus GitLab the additional requirements apply when [configuring a multi-node setup](replication_and_failover.md): - PgBouncer configured on each PostgreSQL node to pooling all load balanced connections. diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index 2490e85bdcc3cb..42b81961032dee 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -28,8 +28,6 @@ replication, failover requires: - [Database Load Balancing](database_load_balancing.md) enabled. - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. -
- ```plantuml @startuml card "**Internal Load Balancer**" as ilb #9370DB @@ -70,8 +68,6 @@ consul .[#e76a9b,norank]r-> postgres_secondary @enduml ``` -
- You also need to take into consideration the underlying network topology, making sure you have redundant connectivity between all Database and GitLab instances to avoid the network becoming a single point of failure. -- GitLab From eb1e242d5a89723c4aa32a3817fb5bf2d1032b82 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Thu, 11 Nov 2021 16:29:26 +0000 Subject: [PATCH 14/23] Update RA docs with DB LB --- .../reference_architectures/10k_users.md | 19 +-- .../reference_architectures/25k_users.md | 104 ++++++++++----- .../reference_architectures/3k_users.md | 126 ++++++++++-------- .../reference_architectures/50k_users.md | 106 ++++++++++----- .../reference_architectures/5k_users.md | 124 +++++++++-------- 5 files changed, 295 insertions(+), 184 deletions(-) diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index 51b3e44dade7bf..950d2b2778f4e4 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -497,7 +497,8 @@ If you use a cloud-managed service, or provide your own PostgreSQL: needs privileges to create the `gitlabhq_production` database. 1. Configure the GitLab application servers with the appropriate details. This step is covered in [Configuring the GitLab Rails application](#configure-gitlab-rails). -1. For improved performance, configuring [Database Load Balancing](../postgresql/database_load_balancing.md) with multiple read replicas is recommended. +1. For improved performance, configuring [Database Load Balancing](../postgresql/database_load_balancing.md) + with multiple read replicas is recommended. See [Configure GitLab using an external PostgreSQL service](../postgresql/external.md) for further configuration steps. @@ -618,10 +619,10 @@ in the second step, do not supply the `EXTERNAL_URL` value. # Local PgBouncer service for Database Load Balancing pgbouncer['databases'] = { - gitlabhq_production: { - host: "127.0.0.1", - user: "pgbouncer", - password: '' + gitlabhq_production: { + host: "127.0.0.1", + user: "pgbouncer", + password: '' } } @@ -687,7 +688,9 @@ If the 'State' column for any node doesn't say "running", check the ### Configure PgBouncer -Now that the PostgreSQL servers are all set up, let's configure PgBouncer. +Now that the PostgreSQL servers are all set up, let's configure PgBouncer +for tracking and handling reads/writes to the primary database. + The following IPs will be used as an example: - `10.6.0.31`: PgBouncer 1 @@ -1702,9 +1705,8 @@ To configure the Sidekiq nodes, on each one: gitlab_rails['db_host'] = '10.6.0.40' # internal load balancer IP gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = '' - gitlab_rails['db_adapter'] = 'postgresql' - gitlab_rails['db_encoding'] = 'unicode' gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.6.0.21', '10.6.0.22', '10.6.0.23'] } # PostgreSQL IPs + ## Prevent database migrations from running on upgrade automatically gitlab_rails['auto_migrate'] = false @@ -1830,6 +1832,7 @@ On each node perform the following: gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = '' gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.6.0.21', '10.6.0.22', '10.6.0.23'] } # PostgreSQL IPs + # Prevent database migrations from running on upgrade automatically gitlab_rails['auto_migrate'] = false diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md index 25cafbe667b9bc..e56a5c2ff214a5 100644 --- a/doc/administration/reference_architectures/25k_users.md +++ b/doc/administration/reference_architectures/25k_users.md @@ -49,6 +49,8 @@ For all PaaS solutions that involve configuring instances, it is strongly recomm ```plantuml @startuml 25k +skinparam linetype ortho + card "**External Load Balancer**" as elb #6a9be7 card "**Internal Load Balancer**" as ilb #9370DB @@ -73,8 +75,8 @@ card "Gitaly Cluster" as gitaly_cluster { card "Database" as database { collections "**PGBouncer** x3" as pgbouncer #4EA7FF - card "**PostgreSQL** (Primary)" as postgres_primary #4EA7FF - collections "**PostgreSQL** (Secondary) x2" as postgres_secondary #4EA7FF + card "**PostgreSQL** //Primary//" as postgres_primary #4EA7FF + collections "**PostgreSQL** //Secondary// x2" as postgres_secondary #4EA7FF pgbouncer -[#4EA7FF]-> postgres_primary postgres_primary .[#4EA7FF]> postgres_secondary @@ -83,31 +85,38 @@ card "Database" as database { card "redis" as redis { collections "**Redis Persistent** x3" as redis_persistent #FF6347 collections "**Redis Cache** x3" as redis_cache #FF6347 + + redis_cache -[hidden]-> redis_persistent } cloud "**Object Storage**" as object_storage #white elb -[#6a9be7]-> gitlab -elb -[#6a9be7]--> monitor +elb -[#6a9be7,norank]--> monitor -gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]-> object_storage -gitlab -[#32CD32]---> redis +gitlab -[#32CD32,norank]--> ilb +gitlab -[#32CD32]r-> object_storage +gitlab -[#32CD32]----> redis +gitlab .[#32CD32]----> database gitlab -[hidden]-> monitor gitlab -[hidden]-> consul -sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]-> object_storage -sidekiq -[#ff8dd1]---> redis +sidekiq -[#ff8dd1,norank]--> ilb +sidekiq -[#ff8dd1]r-> object_storage +sidekiq -[#ff8dd1]----> redis +sidekiq .[#ff8dd1]----> database sidekiq -[hidden]-> monitor sidekiq -[hidden]-> consul -ilb -[#9370DB]-> gitaly_cluster -ilb -[#9370DB]-> database +ilb -[#9370DB]--> gitaly_cluster +ilb -[#9370DB]--> database +ilb -[hidden]--> redis +ilb -[hidden]u-> consul +ilb -[hidden]u-> monitor consul .[#e76a9b]u-> gitlab consul .[#e76a9b]u-> sidekiq -consul .[#e76a9b]> monitor +consul .[#e76a9b]r-> monitor consul .[#e76a9b]-> database consul .[#e76a9b]-> gitaly_cluster consul .[#e76a9b,norank]--> redis @@ -474,7 +483,7 @@ run: node-exporter: (pid 30093) 76833s; run: log: (pid 29663) 76855s ## Configure PostgreSQL -In this section, you'll be guided through configuring an external PostgreSQL database +In this section, you'll be guided through configuring PostgreSQL database to be used with GitLab. ### Provide your own PostgreSQL instance @@ -491,12 +500,26 @@ If you use a cloud-managed service, or provide your own PostgreSQL: needs privileges to create the `gitlabhq_production` database. 1. Configure the GitLab application servers with the appropriate details. This step is covered in [Configuring the GitLab Rails application](#configure-gitlab-rails). +1. For improved performance, configuring [Database Load Balancing](../postgresql/database_load_balancing.md) + with multiple read replicas is recommended. See [Configure GitLab using an external PostgreSQL service](../postgresql/external.md) for further configuration steps. ### Standalone PostgreSQL using Omnibus GitLab +The recommended PostgreSQL setup includes the following components: + +The Omnibus GitLab recommended configuration for a PostgreSQL cluster with +replication, failover requires: + +- A minimum of three PostgreSQL nodes. +- A minimum of three Consul server nodes. +- A PgBouncer cluster of 1 node or more to handle primary database writes and reads. + - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) is also required to balance requests between the nodes. +- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled for improved read queries performance. + - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. + The following IPs will be used as an example: - `10.6.0.21`: PostgreSQL primary @@ -551,8 +574,8 @@ in the second step, do not supply the `EXTERNAL_URL` value. 1. On every database node, edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section: ```ruby - # Disable all components except Patroni and Consul - roles(['patroni_role']) + # Disable all components except Patroni, PgBouncer and Consul + roles(['patroni_role', 'pgbouncer_role']) # PostgreSQL configuration postgresql['listen_address'] = '0.0.0.0' @@ -597,6 +620,15 @@ in the second step, do not supply the `EXTERNAL_URL` value. # Replace 10.6.0.0/24 with Network Address postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32) + # Local PgBouncer service for Database Load Balancing + pgbouncer['databases'] = { + gitlabhq_production: { + host: "127.0.0.1", + user: "pgbouncer", + password: '' + } + } + # Set the network addresses that the exporters will listen on for monitoring node_exporter['listen_address'] = '0.0.0.0:9100' postgres_exporter['listen_address'] = '0.0.0.0:9187' @@ -657,9 +689,11 @@ If the 'State' column for any node doesn't say "running", check the -## Configure PgBouncer +### Configure PgBouncer + +Now that the PostgreSQL servers are all set up, let's configure PgBouncer +for tracking and handling reads/writes to the primary database. -Now that the PostgreSQL servers are all set up, let's configure PgBouncer. The following IPs will be used as an example: - `10.6.0.31`: PgBouncer 1 @@ -1677,8 +1711,8 @@ To configure the Sidekiq nodes, on each one: gitlab_rails['db_host'] = '10.6.0.20' # internal load balancer IP gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = '' - gitlab_rails['db_adapter'] = 'postgresql' - gitlab_rails['db_encoding'] = 'unicode' + gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.6.0.21', '10.6.0.22', '10.6.0.23'] } # PostgreSQL IPs + ## Prevent database migrations from running on upgrade automatically gitlab_rails['auto_migrate'] = false @@ -1805,6 +1839,8 @@ On each node perform the following: gitlab_rails['db_host'] = '10.6.0.20' # internal load balancer IP gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = '' + gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.6.0.21', '10.6.0.22', '10.6.0.23'] } # PostgreSQL IPs + # Prevent database migrations from running on upgrade automatically gitlab_rails['auto_migrate'] = false @@ -2212,16 +2248,16 @@ For all PaaS solutions that involve configuring instances, it is strongly recomm ```plantuml @startuml 25k +skinparam linetype ortho card "Kubernetes via Helm Charts" as kubernetes { card "**External Load Balancer**" as elb #6a9be7 together { - collections "**Webservice** x7" as gitlab #32CD32 + collections "**Webservice** x4" as gitlab #32CD32 collections "**Sidekiq** x4" as sidekiq #ff8dd1 } - card "**Prometheus + Grafana**" as monitor #7FFFD4 card "**Supporting Services**" as support } @@ -2249,37 +2285,35 @@ card "Database" as database { card "redis" as redis { collections "**Redis Persistent** x3" as redis_persistent #FF6347 collections "**Redis Cache** x3" as redis_cache #FF6347 + + redis_cache -[hidden]-> redis_persistent } cloud "**Object Storage**" as object_storage #white elb -[#6a9be7]-> gitlab -elb -[#6a9be7]-> monitor +elb -[hidden]-> sidekiq elb -[hidden]-> support gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]-> object_storage +gitlab -[#32CD32]--> object_storage gitlab -[#32CD32]---> redis +gitlab -[#32CD32,norank]---> database gitlab -[hidden]--> consul sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]-> object_storage +sidekiq -[#ff8dd1]--> object_storage sidekiq -[#ff8dd1]---> redis +sidekiq .[#ff8dd1,norank]---> database sidekiq -[hidden]--> consul -ilb -[#9370DB]-> gitaly_cluster -ilb -[#9370DB]-> database +ilb -[#9370DB]--> gitaly_cluster +ilb -[#9370DB]--> database +ilb -[hidden]--> redis consul .[#e76a9b]-> database -consul .[#e76a9b]-> gitaly_cluster -consul .[#e76a9b,norank]--> redis - -monitor .[#7FFFD4]> consul -monitor .[#7FFFD4]-> database -monitor .[#7FFFD4]-> gitaly_cluster -monitor .[#7FFFD4,norank]--> redis -monitor .[#7FFFD4]> ilb -monitor .[#7FFFD4,norank]u--> elb +consul .[#e76a9b,norank]-> gitaly_cluster +consul .[#e76a9b]-> redis @enduml ``` diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md index 9332ae8d27104a..a3ef0b627c1d8a 100644 --- a/doc/administration/reference_architectures/3k_users.md +++ b/doc/administration/reference_architectures/3k_users.md @@ -58,6 +58,8 @@ For all PaaS solutions that involve configuring instances, it is strongly recomm ```plantuml @startuml 3k +skinparam linetype ortho + card "**External Load Balancer**" as elb #6a9be7 card "**Internal Load Balancer**" as ilb #9370DB @@ -66,7 +68,10 @@ together { collections "**Sidekiq** x4" as sidekiq #ff8dd1 } -card "**Prometheus + Grafana**" as monitor #7FFFD4 +together { + card "**Prometheus + Grafana**" as monitor #7FFFD4 + collections "**Consul** x3" as consul #e76a9b +} card "Gitaly Cluster" as gitaly_cluster { collections "**Praefect** x3" as praefect #FF8C00 @@ -79,47 +84,45 @@ card "Gitaly Cluster" as gitaly_cluster { card "Database" as database { collections "**PGBouncer** x3" as pgbouncer #4EA7FF - card "**PostgreSQL** (Primary)" as postgres_primary #4EA7FF - collections "**PostgreSQL** (Secondary) x2" as postgres_secondary #4EA7FF + card "**PostgreSQL** //Primary//" as postgres_primary #4EA7FF + collections "**PostgreSQL** //Secondary// x2" as postgres_secondary #4EA7FF pgbouncer -[#4EA7FF]-> postgres_primary postgres_primary .[#4EA7FF]> postgres_secondary } -card "**Consul + Sentinel**" as consul_sentinel { - collections "**Consul** x3" as consul #e76a9b - collections "**Redis Sentinel** x3" as sentinel #e6e727 -} - card "Redis" as redis { collections "**Redis** x3" as redis_nodes #FF6347 - - redis_nodes <.[#FF6347]- sentinel } cloud "**Object Storage**" as object_storage #white elb -[#6a9be7]-> gitlab -elb -[#6a9be7]--> monitor +elb -[#6a9be7,norank]--> monitor -gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]-> object_storage -gitlab -[#32CD32]---> redis +gitlab -[#32CD32,norank]--> ilb +gitlab -[#32CD32]r-> object_storage +gitlab -[#32CD32]----> redis +gitlab .[#32CD32]----> database gitlab -[hidden]-> monitor gitlab -[hidden]-> consul -sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]-> object_storage -sidekiq -[#ff8dd1]---> redis +sidekiq -[#ff8dd1,norank]--> ilb +sidekiq -[#ff8dd1]r-> object_storage +sidekiq -[#ff8dd1]----> redis +sidekiq .[#ff8dd1]----> database sidekiq -[hidden]-> monitor sidekiq -[hidden]-> consul -ilb -[#9370DB]-> gitaly_cluster -ilb -[#9370DB]-> database +ilb -[#9370DB]--> gitaly_cluster +ilb -[#9370DB]--> database +ilb -[hidden]--> redis +ilb -[hidden]u-> consul +ilb -[hidden]u-> monitor consul .[#e76a9b]u-> gitlab consul .[#e76a9b]u-> sidekiq -consul .[#e76a9b]> monitor +consul .[#e76a9b]r-> monitor consul .[#e76a9b]-> database consul .[#e76a9b]-> gitaly_cluster consul .[#e76a9b,norank]--> redis @@ -769,7 +772,7 @@ run: sentinel: (pid 30098) 76832s; run: log: (pid 29704) 76850s ## Configure PostgreSQL -In this section, you'll be guided through configuring an external PostgreSQL database +In this section, you'll be guided through configuring PostgreSQL database to be used with GitLab. ### Provide your own PostgreSQL instance @@ -786,12 +789,26 @@ If you use a cloud-managed service, or provide your own PostgreSQL: needs privileges to create the `gitlabhq_production` database. 1. Configure the GitLab application servers with the appropriate details. This step is covered in [Configuring the GitLab Rails application](#configure-gitlab-rails). +1. For improved performance, configuring [Database Load Balancing](../postgresql/database_load_balancing.md) + with multiple read replicas is recommended. See [Configure GitLab using an external PostgreSQL service](../postgresql/external.md) for further configuration steps. ### Standalone PostgreSQL using Omnibus GitLab +The recommended PostgreSQL setup includes the following components: + +The Omnibus GitLab recommended configuration for a PostgreSQL cluster with +replication, failover requires: + +- A minimum of three PostgreSQL nodes. +- A minimum of three Consul server nodes. +- A PgBouncer cluster of 1 node or more to handle primary database writes and reads. + - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) is also required to balance requests between the nodes. +- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled for improved read queries performance. + - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. + The following IPs will be used as an example: - `10.6.0.31`: PostgreSQL primary @@ -846,8 +863,8 @@ in the second step, do not supply the `EXTERNAL_URL` value. 1. On every database node, edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section: ```ruby - # Disable all components except Patroni and Consul - roles(['patroni_role']) + # Disable all components except Patroni, PgBouncer and Consul + roles(['patroni_role', 'pgbouncer_role']) # PostgreSQL configuration postgresql['listen_address'] = '0.0.0.0' @@ -892,6 +909,15 @@ in the second step, do not supply the `EXTERNAL_URL` value. # Replace 10.6.0.0/24 with Network Address postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32) + # Local PgBouncer service for Database Load Balancing + pgbouncer['databases'] = { + gitlabhq_production: { + host: "127.0.0.1", + user: "pgbouncer", + password: '' + } + } + # Set the network addresses that the exporters will listen on for monitoring node_exporter['listen_address'] = '0.0.0.0:9100' postgres_exporter['listen_address'] = '0.0.0.0:9187' @@ -952,9 +978,11 @@ If the 'State' column for any node doesn't say "running", check the -## Configure PgBouncer +### Configure PgBouncer + +Now that the PostgreSQL servers are all set up, let's configure PgBouncer +for tracking and handling reads/writes to the primary database. -Now that the PostgreSQL servers are all set up, let's configure PgBouncer. The following IPs will be used as an example: - `10.6.0.21`: PgBouncer 1 @@ -1613,8 +1641,8 @@ To configure the Sidekiq nodes, one each one: gitlab_rails['db_host'] = '10.6.0.40' # internal load balancer IP gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = '' - gitlab_rails['db_adapter'] = 'postgresql' - gitlab_rails['db_encoding'] = 'unicode' + gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.6.0.31', '10.6.0.32', '10.6.0.33'] } # PostgreSQL IPs + ## Prevent database migrations from running on upgrade automatically gitlab_rails['auto_migrate'] = false @@ -1773,6 +1801,8 @@ On each node perform the following: gitlab_rails['db_host'] = '10.6.0.20' # internal load balancer IP gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = '' + gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.6.0.31', '10.6.0.32', '10.6.0.33'] } # PostgreSQL IPs + # Prevent database migrations from running on upgrade automatically gitlab_rails['auto_migrate'] = false @@ -2182,26 +2212,22 @@ NOTE: For all PaaS solutions that involve configuring instances, it is strongly recommended to implement a minimum of three nodes in three different availability zones to align with resilient cloud architecture practices. ```plantuml -@startuml 3k +@startuml 5k +skinparam linetype ortho card "Kubernetes via Helm Charts" as kubernetes { card "**External Load Balancer**" as elb #6a9be7 together { - collections "**Webservice** x2" as gitlab #32CD32 - collections "**Sidekiq** x3" as sidekiq #ff8dd1 + collections "**Webservice** x4" as gitlab #32CD32 + collections "**Sidekiq** x4" as sidekiq #ff8dd1 } - card "**Prometheus + Grafana**" as monitor #7FFFD4 card "**Supporting Services**" as support } card "**Internal Load Balancer**" as ilb #9370DB - -card "**Consul + Sentinel**" as consul_sentinel { - collections "**Consul** x3" as consul #e76a9b - collections "**Redis Sentinel** x3" as sentinel #e6e727 -} +collections "**Consul** x3" as consul #e76a9b card "Gitaly Cluster" as gitaly_cluster { collections "**Praefect** x3" as praefect #FF8C00 @@ -2221,41 +2247,35 @@ card "Database" as database { postgres_primary .[#4EA7FF]> postgres_secondary } -card "Redis" as redis { +card "redis" as redis { collections "**Redis** x3" as redis_nodes #FF6347 - - redis_nodes <.[#FF6347]- sentinel } cloud "**Object Storage**" as object_storage #white elb -[#6a9be7]-> gitlab -elb -[#6a9be7]-> monitor +elb -[hidden]-> sidekiq elb -[hidden]-> support gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]-> object_storage +gitlab -[#32CD32]--> object_storage gitlab -[#32CD32]---> redis +gitlab -[#32CD32,norank]---> database gitlab -[hidden]--> consul sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]-> object_storage +sidekiq -[#ff8dd1]--> object_storage sidekiq -[#ff8dd1]---> redis +sidekiq .[#ff8dd1,norank]---> database sidekiq -[hidden]--> consul -ilb -[#9370DB]-> gitaly_cluster -ilb -[#9370DB]-> database +ilb -[#9370DB]--> gitaly_cluster +ilb -[#9370DB]--> database +ilb -[hidden]--> redis consul .[#e76a9b]-> database -consul .[#e76a9b]-> gitaly_cluster -consul .[#e76a9b,norank]--> redis - -monitor .[#7FFFD4]> consul -monitor .[#7FFFD4]-> database -monitor .[#7FFFD4]-> gitaly_cluster -monitor .[#7FFFD4,norank]--> redis -monitor .[#7FFFD4]> ilb -monitor .[#7FFFD4,norank]u--> elb +consul .[#e76a9b,norank]-> gitaly_cluster +consul .[#e76a9b]-> redis @enduml ``` diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md index bbdf798d9ad58e..d1ec3e12c5ce23 100644 --- a/doc/administration/reference_architectures/50k_users.md +++ b/doc/administration/reference_architectures/50k_users.md @@ -49,6 +49,8 @@ For all PaaS solutions that involve configuring instances, it is strongly recomm ```plantuml @startuml 50k +skinparam linetype ortho + card "**External Load Balancer**" as elb #6a9be7 card "**Internal Load Balancer**" as ilb #9370DB @@ -73,8 +75,8 @@ card "Gitaly Cluster" as gitaly_cluster { card "Database" as database { collections "**PGBouncer** x3" as pgbouncer #4EA7FF - card "**PostgreSQL** (Primary)" as postgres_primary #4EA7FF - collections "**PostgreSQL** (Secondary) x2" as postgres_secondary #4EA7FF + card "**PostgreSQL** //Primary//" as postgres_primary #4EA7FF + collections "**PostgreSQL** //Secondary// x2" as postgres_secondary #4EA7FF pgbouncer -[#4EA7FF]-> postgres_primary postgres_primary .[#4EA7FF]> postgres_secondary @@ -83,31 +85,38 @@ card "Database" as database { card "redis" as redis { collections "**Redis Persistent** x3" as redis_persistent #FF6347 collections "**Redis Cache** x3" as redis_cache #FF6347 + + redis_cache -[hidden]-> redis_persistent } cloud "**Object Storage**" as object_storage #white elb -[#6a9be7]-> gitlab -elb -[#6a9be7]--> monitor +elb -[#6a9be7,norank]--> monitor -gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]-> object_storage -gitlab -[#32CD32]---> redis +gitlab -[#32CD32,norank]--> ilb +gitlab -[#32CD32]r-> object_storage +gitlab -[#32CD32]----> redis +gitlab .[#32CD32]----> database gitlab -[hidden]-> monitor gitlab -[hidden]-> consul -sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]-> object_storage -sidekiq -[#ff8dd1]---> redis +sidekiq -[#ff8dd1,norank]--> ilb +sidekiq -[#ff8dd1]r-> object_storage +sidekiq -[#ff8dd1]----> redis +sidekiq .[#ff8dd1]----> database sidekiq -[hidden]-> monitor sidekiq -[hidden]-> consul -ilb -[#9370DB]-> gitaly_cluster -ilb -[#9370DB]-> database +ilb -[#9370DB]--> gitaly_cluster +ilb -[#9370DB]--> database +ilb -[hidden]--> redis +ilb -[hidden]u-> consul +ilb -[hidden]u-> monitor consul .[#e76a9b]u-> gitlab consul .[#e76a9b]u-> sidekiq -consul .[#e76a9b]> monitor +consul .[#e76a9b]r-> monitor consul .[#e76a9b]-> database consul .[#e76a9b]-> gitaly_cluster consul .[#e76a9b,norank]--> redis @@ -480,7 +489,7 @@ run: node-exporter: (pid 30093) 76833s; run: log: (pid 29663) 76855s ## Configure PostgreSQL -In this section, you'll be guided through configuring an external PostgreSQL database +In this section, you'll be guided through configuring PostgreSQL database to be used with GitLab. ### Provide your own PostgreSQL instance @@ -497,12 +506,26 @@ If you use a cloud-managed service, or provide your own PostgreSQL: needs privileges to create the `gitlabhq_production` database. 1. Configure the GitLab application servers with the appropriate details. This step is covered in [Configuring the GitLab Rails application](#configure-gitlab-rails). +1. For improved performance, configuring [Database Load Balancing](../postgresql/database_load_balancing.md) + with multiple read replicas is recommended. See [Configure GitLab using an external PostgreSQL service](../postgresql/external.md) for further configuration steps. ### Standalone PostgreSQL using Omnibus GitLab +The recommended PostgreSQL setup includes the following components: + +The Omnibus GitLab recommended configuration for a PostgreSQL cluster with +replication, failover requires: + +- A minimum of three PostgreSQL nodes. +- A minimum of three Consul server nodes. +- A PgBouncer cluster of 1 node or more to handle primary database writes and reads. + - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) is also required to balance requests between the nodes. +- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled for improved read queries performance. + - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. + The following IPs will be used as an example: - `10.6.0.21`: PostgreSQL primary @@ -557,8 +580,8 @@ in the second step, do not supply the `EXTERNAL_URL` value. 1. On every database node, edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section: ```ruby - # Disable all components except Patroni and Consul - roles(['patroni_role']) + # Disable all components except Patroni, PgBouncer and Consul + roles(['patroni_role', 'pgbouncer_role']) # PostgreSQL configuration postgresql['listen_address'] = '0.0.0.0' @@ -604,6 +627,15 @@ in the second step, do not supply the `EXTERNAL_URL` value. # Replace 10.6.0.0/24 with Network Address postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32) + # Local PgBouncer service for Database Load Balancing + pgbouncer['databases'] = { + gitlabhq_production: { + host: "127.0.0.1", + user: "pgbouncer", + password: '' + } + } + # Set the network addresses that the exporters will listen on for monitoring node_exporter['listen_address'] = '0.0.0.0:9100' postgres_exporter['listen_address'] = '0.0.0.0:9187' @@ -664,9 +696,11 @@ If the 'State' column for any node doesn't say "running", check the -## Configure PgBouncer +### Configure PgBouncer + +Now that the PostgreSQL servers are all set up, let's configure PgBouncer +for tracking and handling reads/writes to the primary database. -Now that the PostgreSQL servers are all set up, let's configure PgBouncer. The following IPs will be used as an example: - `10.6.0.31`: PgBouncer 1 @@ -891,7 +925,7 @@ a node and change its status from primary to replica (and vice versa). package of your choice. Be sure to both follow _only_ installation steps 1 and 2 on the page, and to select the correct Omnibus GitLab package, with the same version and type (Community or Enterprise editions) as your current install. -1. Edit `/etc/gitlab/gitlab.rb` and add the same contents as the priimary node in the previous section by replacing `redis_master_node` with `redis_replica_node`: +1. Edit `/etc/gitlab/gitlab.rb` and add the same contents as the primary node in the previous section by replacing `redis_master_node` with `redis_replica_node`: ```ruby # Specify server role as 'redis_replica_role' with Sentinel and enable Consul agent @@ -1684,8 +1718,8 @@ To configure the Sidekiq nodes, on each one: gitlab_rails['db_host'] = '10.6.0.20' # internal load balancer IP gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = '' - gitlab_rails['db_adapter'] = 'postgresql' - gitlab_rails['db_encoding'] = 'unicode' + gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.6.0.21', '10.6.0.22', '10.6.0.23'] } # PostgreSQL IPs + ## Prevent database migrations from running on upgrade automatically gitlab_rails['auto_migrate'] = false @@ -1819,6 +1853,8 @@ On each node perform the following: gitlab_rails['db_host'] = '10.6.0.20' # internal load balancer IP gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = '' + gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.6.0.21', '10.6.0.22', '10.6.0.23'] } # PostgreSQL IPs + # Prevent database migrations from running on upgrade automatically gitlab_rails['auto_migrate'] = false @@ -2226,16 +2262,16 @@ For all PaaS solutions that involve configuring instances, it is strongly recomm ```plantuml @startuml 50k +skinparam linetype ortho card "Kubernetes via Helm Charts" as kubernetes { card "**External Load Balancer**" as elb #6a9be7 together { - collections "**Webservice** x16" as gitlab #32CD32 + collections "**Webservice** x4" as gitlab #32CD32 collections "**Sidekiq** x4" as sidekiq #ff8dd1 } - card "**Prometheus + Grafana**" as monitor #7FFFD4 card "**Supporting Services**" as support } @@ -2263,37 +2299,35 @@ card "Database" as database { card "redis" as redis { collections "**Redis Persistent** x3" as redis_persistent #FF6347 collections "**Redis Cache** x3" as redis_cache #FF6347 + + redis_cache -[hidden]-> redis_persistent } cloud "**Object Storage**" as object_storage #white elb -[#6a9be7]-> gitlab -elb -[#6a9be7]-> monitor +elb -[hidden]-> sidekiq elb -[hidden]-> support gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]-> object_storage +gitlab -[#32CD32]--> object_storage gitlab -[#32CD32]---> redis +gitlab -[#32CD32,norank]---> database gitlab -[hidden]--> consul sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]-> object_storage +sidekiq -[#ff8dd1]--> object_storage sidekiq -[#ff8dd1]---> redis +sidekiq .[#ff8dd1,norank]---> database sidekiq -[hidden]--> consul -ilb -[#9370DB]-> gitaly_cluster -ilb -[#9370DB]-> database +ilb -[#9370DB]--> gitaly_cluster +ilb -[#9370DB]--> database +ilb -[hidden]--> redis consul .[#e76a9b]-> database -consul .[#e76a9b]-> gitaly_cluster -consul .[#e76a9b,norank]--> redis - -monitor .[#7FFFD4]> consul -monitor .[#7FFFD4]-> database -monitor .[#7FFFD4]-> gitaly_cluster -monitor .[#7FFFD4,norank]--> redis -monitor .[#7FFFD4]> ilb -monitor .[#7FFFD4,norank]u--> elb +consul .[#e76a9b,norank]-> gitaly_cluster +consul .[#e76a9b]-> redis @enduml ``` diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md index a1921f50e4ea30..b46f50d7ed0874 100644 --- a/doc/administration/reference_architectures/5k_users.md +++ b/doc/administration/reference_architectures/5k_users.md @@ -55,6 +55,8 @@ For all PaaS solutions that involve configuring instances, it is strongly recomm ```plantuml @startuml 5k +skinparam linetype ortho + card "**External Load Balancer**" as elb #6a9be7 card "**Internal Load Balancer**" as ilb #9370DB @@ -63,7 +65,10 @@ together { collections "**Sidekiq** x4" as sidekiq #ff8dd1 } -card "**Prometheus + Grafana**" as monitor #7FFFD4 +together { + card "**Prometheus + Grafana**" as monitor #7FFFD4 + collections "**Consul** x3" as consul #e76a9b +} card "Gitaly Cluster" as gitaly_cluster { collections "**Praefect** x3" as praefect #FF8C00 @@ -76,47 +81,45 @@ card "Gitaly Cluster" as gitaly_cluster { card "Database" as database { collections "**PGBouncer** x3" as pgbouncer #4EA7FF - card "**PostgreSQL** (Primary)" as postgres_primary #4EA7FF - collections "**PostgreSQL** (Secondary) x2" as postgres_secondary #4EA7FF + card "**PostgreSQL** //Primary//" as postgres_primary #4EA7FF + collections "**PostgreSQL** //Secondary// x2" as postgres_secondary #4EA7FF pgbouncer -[#4EA7FF]-> postgres_primary postgres_primary .[#4EA7FF]> postgres_secondary } -card "**Consul + Sentinel**" as consul_sentinel { - collections "**Consul** x3" as consul #e76a9b - collections "**Redis Sentinel** x3" as sentinel #e6e727 -} - card "Redis" as redis { collections "**Redis** x3" as redis_nodes #FF6347 - - redis_nodes <.[#FF6347]- sentinel } cloud "**Object Storage**" as object_storage #white elb -[#6a9be7]-> gitlab -elb -[#6a9be7]--> monitor +elb -[#6a9be7,norank]--> monitor -gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]-> object_storage -gitlab -[#32CD32]---> redis +gitlab -[#32CD32,norank]--> ilb +gitlab -[#32CD32]r-> object_storage +gitlab -[#32CD32]----> redis +gitlab .[#32CD32]----> database gitlab -[hidden]-> monitor gitlab -[hidden]-> consul -sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]-> object_storage -sidekiq -[#ff8dd1]---> redis +sidekiq -[#ff8dd1,norank]--> ilb +sidekiq -[#ff8dd1]r-> object_storage +sidekiq -[#ff8dd1]----> redis +sidekiq .[#ff8dd1]----> database sidekiq -[hidden]-> monitor sidekiq -[hidden]-> consul -ilb -[#9370DB]-> gitaly_cluster -ilb -[#9370DB]-> database +ilb -[#9370DB]--> gitaly_cluster +ilb -[#9370DB]--> database +ilb -[hidden]--> redis +ilb -[hidden]u-> consul +ilb -[hidden]u-> monitor consul .[#e76a9b]u-> gitlab consul .[#e76a9b]u-> sidekiq -consul .[#e76a9b]> monitor +consul .[#e76a9b]r-> monitor consul .[#e76a9b]-> database consul .[#e76a9b]-> gitaly_cluster consul .[#e76a9b,norank]--> redis @@ -760,7 +763,7 @@ run: sentinel: (pid 30098) 76832s; run: log: (pid 29704) 76850s ## Configure PostgreSQL -In this section, you'll be guided through configuring an external PostgreSQL database +In this section, you'll be guided through configuring PostgreSQL database to be used with GitLab. ### Provide your own PostgreSQL instance @@ -777,12 +780,26 @@ If you use a cloud-managed service, or provide your own PostgreSQL: needs privileges to create the `gitlabhq_production` database. 1. Configure the GitLab application servers with the appropriate details. This step is covered in [Configuring the GitLab Rails application](#configure-gitlab-rails). +1. For improved performance, configuring [Database Load Balancing](../postgresql/database_load_balancing.md) + with multiple read replicas is recommended. See [Configure GitLab using an external PostgreSQL service](../postgresql/external.md) for further configuration steps. ### Standalone PostgreSQL using Omnibus GitLab +The recommended PostgreSQL setup includes the following components: + +The Omnibus GitLab recommended configuration for a PostgreSQL cluster with +replication, failover requires: + +- A minimum of three PostgreSQL nodes. +- A minimum of three Consul server nodes. +- A PgBouncer cluster of 1 node or more to handle primary database writes and reads. + - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) is also required to balance requests between the nodes. +- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled for improved read queries performance. + - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. + The following IPs will be used as an example: - `10.6.0.31`: PostgreSQL primary @@ -837,8 +854,8 @@ in the second step, do not supply the `EXTERNAL_URL` value. 1. On every database node, edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section: ```ruby - # Disable all components except Patroni and Consul - roles(['patroni_role']) + # Disable all components except Patroni, PgBouncer and Consul + roles(['patroni_role', 'pgbouncer_role']) # PostgreSQL configuration postgresql['listen_address'] = '0.0.0.0' @@ -883,6 +900,15 @@ in the second step, do not supply the `EXTERNAL_URL` value. # Replace 10.6.0.0/24 with Network Address postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24 127.0.0.1/32) + # Local PgBouncer service for Database Load Balancing + pgbouncer['databases'] = { + gitlabhq_production: { + host: "127.0.0.1", + user: "pgbouncer", + password: '' + } + } + # Set the network addresses that the exporters will listen on for monitoring node_exporter['listen_address'] = '0.0.0.0:9100' postgres_exporter['listen_address'] = '0.0.0.0:9187' @@ -943,9 +969,11 @@ If the 'State' column for any node doesn't say "running", check the -## Configure PgBouncer +### Configure PgBouncer + +Now that the PostgreSQL servers are all set up, let's configure PgBouncer +for tracking and handling reads/writes to the primary database. -Now that the PostgreSQL servers are all set up, let's configure PgBouncer. The following IPs will be used as an example: - `10.6.0.21`: PgBouncer 1 @@ -1604,8 +1632,8 @@ To configure the Sidekiq nodes, one each one: gitlab_rails['db_host'] = '10.6.0.40' # internal load balancer IP gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = '' - gitlab_rails['db_adapter'] = 'postgresql' - gitlab_rails['db_encoding'] = 'unicode' + gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.6.0.31', '10.6.0.32', '10.6.0.33'] } # PostgreSQL IPs + ## Prevent database migrations from running on upgrade automatically gitlab_rails['auto_migrate'] = false @@ -1764,6 +1792,8 @@ On each node perform the following: gitlab_rails['db_host'] = '10.6.0.20' # internal load balancer IP gitlab_rails['db_port'] = 6432 gitlab_rails['db_password'] = '' + gitlab_rails['db_load_balancing'] = { 'hosts' => ['10.6.0.31', '10.6.0.32', '10.6.0.33'] } # PostgreSQL IPs + # Prevent database migrations from running on upgrade automatically gitlab_rails['auto_migrate'] = false @@ -2153,25 +2183,21 @@ For all PaaS solutions that involve configuring instances, it is strongly recomm ```plantuml @startuml 5k +skinparam linetype ortho card "Kubernetes via Helm Charts" as kubernetes { card "**External Load Balancer**" as elb #6a9be7 together { - collections "**Webservice** x5" as gitlab #32CD32 - collections "**Sidekiq** x3" as sidekiq #ff8dd1 + collections "**Webservice** x4" as gitlab #32CD32 + collections "**Sidekiq** x4" as sidekiq #ff8dd1 } - card "**Prometheus + Grafana**" as monitor #7FFFD4 card "**Supporting Services**" as support } card "**Internal Load Balancer**" as ilb #9370DB - -card "**Consul + Sentinel**" as consul_sentinel { - collections "**Consul** x3" as consul #e76a9b - collections "**Redis Sentinel** x3" as sentinel #e6e727 -} +collections "**Consul** x3" as consul #e76a9b card "Gitaly Cluster" as gitaly_cluster { collections "**Praefect** x3" as praefect #FF8C00 @@ -2191,41 +2217,35 @@ card "Database" as database { postgres_primary .[#4EA7FF]> postgres_secondary } -card "Redis" as redis { +card "redis" as redis { collections "**Redis** x3" as redis_nodes #FF6347 - - redis_nodes <.[#FF6347]- sentinel } cloud "**Object Storage**" as object_storage #white elb -[#6a9be7]-> gitlab -elb -[#6a9be7]-> monitor +elb -[hidden]-> sidekiq elb -[hidden]-> support gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]-> object_storage +gitlab -[#32CD32]--> object_storage gitlab -[#32CD32]---> redis +gitlab -[#32CD32,norank]---> database gitlab -[hidden]--> consul sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]-> object_storage +sidekiq -[#ff8dd1]--> object_storage sidekiq -[#ff8dd1]---> redis +sidekiq .[#ff8dd1,norank]---> database sidekiq -[hidden]--> consul -ilb -[#9370DB]-> gitaly_cluster -ilb -[#9370DB]-> database +ilb -[#9370DB]--> gitaly_cluster +ilb -[#9370DB]--> database +ilb -[hidden]--> redis consul .[#e76a9b]-> database -consul .[#e76a9b]-> gitaly_cluster -consul .[#e76a9b,norank]--> redis - -monitor .[#7FFFD4]> consul -monitor .[#7FFFD4]-> database -monitor .[#7FFFD4]-> gitaly_cluster -monitor .[#7FFFD4,norank]--> redis -monitor .[#7FFFD4]> ilb -monitor .[#7FFFD4,norank]u--> elb +consul .[#e76a9b,norank]-> gitaly_cluster +consul .[#e76a9b]-> redis @enduml ``` -- GitLab From b9de4dfcdb0a0e23e4e02c04da71a85e3951e84d Mon Sep 17 00:00:00 2001 From: Grant Young Date: Thu, 11 Nov 2021 16:39:05 +0000 Subject: [PATCH 15/23] Call out sidekiq more clearly in DB LB docs --- doc/administration/postgresql/database_load_balancing.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md index 04c89f77dc62a4..7a80782c3b1840 100644 --- a/doc/administration/postgresql/database_load_balancing.md +++ b/doc/administration/postgresql/database_load_balancing.md @@ -81,14 +81,15 @@ Refer below to each section for more details. ### Hosts Configuring with a list of hosts is straight forward. For the environment in which you want to use -load balancing, you add the `gitlab_rails['db_load_balancing']` setting into the `gitlab.rb file. +load balancing, you add the `gitlab_rails['db_load_balancing']` setting into the `gitlab.rb` file in +the GitLab Rails / Sidekiq nodes. For example, on an environment that has PostgreSQL running on the hosts `host1.example.com`, `host2.example.com` and `host3.example.com` and reachable on the same port configured with `gitlab_rails['db_port']`: -1. Edit `/etc/gitlab/gitlab.rb` and add the following line: +1. On each GitLab Rails / Sidekiq node, edit `/etc/gitlab/gitlab.rb` and add the following line: ```ruby gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.example.com', `host3.example.com`] } @@ -113,7 +114,7 @@ a DNS server and will return PostgreSQL addresses via the `postgresql-ha.service record. An example of how you would configure this along with details for each setting after is as follows: -1. Edit `/etc/gitlab/gitlab.rb` and add the following line: +1. On each GitLab Rails / Sidekiq node, edit `/etc/gitlab/gitlab.rb` and add the following line: ```ruby gitlab_rails['db_load_balancing'] = { 'discover' => { -- GitLab From 167329a8287ce1f0f4b19b7a5c72ef8103b6ca30 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Thu, 11 Nov 2021 16:58:27 +0000 Subject: [PATCH 16/23] Update 2k charts to be the same as others --- .../reference_architectures/2k_users.md | 18 ++++++------------ .../reference_architectures/3k_users.md | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md index e619294704fe8a..be982712c89366 100644 --- a/doc/administration/reference_architectures/2k_users.md +++ b/doc/administration/reference_architectures/2k_users.md @@ -41,6 +41,8 @@ For all PaaS solutions that involve configuring instances, it is strongly recomm ```plantuml @startuml 2k +skinparam linetype ortho + card "**External Load Balancer**" as elb #6a9be7 collections "**GitLab Rails** x3" as gitlab #32CD32 @@ -1038,6 +1040,7 @@ For all PaaS solutions that involve configuring instances, it is strongly recomm ```plantuml @startuml 2k +skinparam linetype ortho card "Kubernetes via Helm Charts" as kubernetes { card "**External Load Balancer**" as elb #6a9be7 @@ -1045,10 +1048,8 @@ card "Kubernetes via Helm Charts" as kubernetes { together { collections "**Webservice** x3" as gitlab #32CD32 collections "**Sidekiq** x2" as sidekiq #ff8dd1 + card "**Supporting Services**" as support } - - card "**Prometheus + Grafana**" as monitor #7FFFD4 - card "**Supporting Services**" as support } card "**Gitaly**" as gitaly #FF8C00 @@ -1057,7 +1058,6 @@ card "**Redis**" as redis #FF6347 cloud "**Object Storage**" as object_storage #white elb -[#6a9be7]-> gitlab -elb -[#6a9be7]--> monitor gitlab -[#32CD32]--> gitaly gitlab -[#32CD32]--> postgres @@ -1066,14 +1066,8 @@ gitlab -[#32CD32]--> redis sidekiq -[#ff8dd1]--> gitaly sidekiq -[#ff8dd1]-> object_storage -sidekiq -[#ff8dd1]---> postgres -sidekiq -[#ff8dd1]---> redis - -monitor .[#7FFFD4]u-> gitlab -monitor .[#7FFFD4]-> gitaly -monitor .[#7FFFD4]-> postgres -monitor .[#7FFFD4,norank]--> redis -monitor .[#7FFFD4,norank]u--> elb +sidekiq -[#ff8dd1]--> postgres +sidekiq -[#ff8dd1]--> redis @enduml ``` diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md index a3ef0b627c1d8a..1c954e8949fb1e 100644 --- a/doc/administration/reference_architectures/3k_users.md +++ b/doc/administration/reference_architectures/3k_users.md @@ -2212,7 +2212,7 @@ NOTE: For all PaaS solutions that involve configuring instances, it is strongly recommended to implement a minimum of three nodes in three different availability zones to align with resilient cloud architecture practices. ```plantuml -@startuml 5k +@startuml 3k skinparam linetype ortho card "Kubernetes via Helm Charts" as kubernetes { -- GitLab From a6b3393ac7fbbc5b30b3040708dbe460fc2ec6b8 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Thu, 11 Nov 2021 16:58:43 +0000 Subject: [PATCH 17/23] Move links on administor page to better location --- doc/administration/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/administration/index.md b/doc/administration/index.md index e164a93b343efb..17938d820c8930 100644 --- a/doc/administration/index.md +++ b/doc/administration/index.md @@ -31,8 +31,6 @@ Learn how to install, configure, update, and maintain your GitLab instance. ### Installing GitLab - [Install](../install/index.md): Requirements, directory structures, and installation methods. - - [Database load balancing](postgresql/database_load_balancing.md): Distribute database queries among multiple database servers. - - [Omnibus support for log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-shipping-gitlab-enterprise-edition-only). - [Reference architectures](reference_architectures/index.md): Add additional resources to support more users. - [Installing GitLab on Amazon Web Services (AWS)](../install/aws/index.md): Set up GitLab on Amazon AWS. - [Geo](geo/index.md): Replicate your GitLab instance to other geographic locations as a read-only fully operational version. @@ -79,6 +77,8 @@ Learn how to install, configure, update, and maintain your GitLab instance. - [Enabling and disabling features flags](feature_flags.md): how to enable and disable GitLab features deployed behind feature flags. - [Application settings cache expiry interval](application_settings_cache.md) +- [Database load balancing](postgresql/database_load_balancing.md): Distribute database queries among multiple database servers. +- [Omnibus support for log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-shipping-gitlab-enterprise-edition-only). #### Customizing GitLab appearance -- GitLab From a99b949024c68344012b15d3bce7bb9a63de24e3 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Thu, 11 Nov 2021 17:11:59 +0000 Subject: [PATCH 18/23] Update CHN charts --- .../reference_architectures/10k_users.md | 22 +++++++++---------- .../reference_architectures/25k_users.md | 22 +++++++++---------- .../reference_architectures/3k_users.md | 22 +++++++++---------- .../reference_architectures/50k_users.md | 22 +++++++++---------- .../reference_architectures/5k_users.md | 22 +++++++++---------- 5 files changed, 50 insertions(+), 60 deletions(-) diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index 950d2b2778f4e4..be9207b8739185 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -2296,24 +2296,22 @@ elb -[hidden]-> sidekiq elb -[hidden]-> support gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]--> object_storage -gitlab -[#32CD32]---> redis -gitlab -[#32CD32,norank]---> database -gitlab -[hidden]--> consul +gitlab -[#32CD32]r--> object_storage +gitlab -[#32CD32,norank]----> redis +gitlab -[#32CD32]----> database sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]--> object_storage -sidekiq -[#ff8dd1]---> redis -sidekiq .[#ff8dd1,norank]---> database -sidekiq -[hidden]--> consul +sidekiq -[#ff8dd1]r--> object_storage +sidekiq -[#ff8dd1,norank]----> redis +sidekiq .[#ff8dd1]----> database ilb -[#9370DB]--> gitaly_cluster ilb -[#9370DB]--> database -ilb -[hidden]--> redis +ilb -[hidden,norank]--> redis -consul .[#e76a9b]-> database -consul .[#e76a9b,norank]-> gitaly_cluster -consul .[#e76a9b]-> redis +consul .[#e76a9b]--> database +consul .[#e76a9b,norank]--> gitaly_cluster +consul .[#e76a9b]--> redis @enduml ``` diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md index e56a5c2ff214a5..2404eb07ed643d 100644 --- a/doc/administration/reference_architectures/25k_users.md +++ b/doc/administration/reference_architectures/25k_users.md @@ -2296,24 +2296,22 @@ elb -[hidden]-> sidekiq elb -[hidden]-> support gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]--> object_storage -gitlab -[#32CD32]---> redis -gitlab -[#32CD32,norank]---> database -gitlab -[hidden]--> consul +gitlab -[#32CD32]r--> object_storage +gitlab -[#32CD32,norank]----> redis +gitlab -[#32CD32]----> database sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]--> object_storage -sidekiq -[#ff8dd1]---> redis -sidekiq .[#ff8dd1,norank]---> database -sidekiq -[hidden]--> consul +sidekiq -[#ff8dd1]r--> object_storage +sidekiq -[#ff8dd1,norank]----> redis +sidekiq .[#ff8dd1]----> database ilb -[#9370DB]--> gitaly_cluster ilb -[#9370DB]--> database -ilb -[hidden]--> redis +ilb -[hidden,norank]--> redis -consul .[#e76a9b]-> database -consul .[#e76a9b,norank]-> gitaly_cluster -consul .[#e76a9b]-> redis +consul .[#e76a9b]--> database +consul .[#e76a9b,norank]--> gitaly_cluster +consul .[#e76a9b]--> redis @enduml ``` diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md index 1c954e8949fb1e..f16dd2742f49ae 100644 --- a/doc/administration/reference_architectures/3k_users.md +++ b/doc/administration/reference_architectures/3k_users.md @@ -2258,24 +2258,22 @@ elb -[hidden]-> sidekiq elb -[hidden]-> support gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]--> object_storage -gitlab -[#32CD32]---> redis -gitlab -[#32CD32,norank]---> database -gitlab -[hidden]--> consul +gitlab -[#32CD32]r--> object_storage +gitlab -[#32CD32,norank]----> redis +gitlab -[#32CD32]----> database sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]--> object_storage -sidekiq -[#ff8dd1]---> redis -sidekiq .[#ff8dd1,norank]---> database -sidekiq -[hidden]--> consul +sidekiq -[#ff8dd1]r--> object_storage +sidekiq -[#ff8dd1,norank]----> redis +sidekiq .[#ff8dd1]----> database ilb -[#9370DB]--> gitaly_cluster ilb -[#9370DB]--> database -ilb -[hidden]--> redis +ilb -[hidden,norank]--> redis -consul .[#e76a9b]-> database -consul .[#e76a9b,norank]-> gitaly_cluster -consul .[#e76a9b]-> redis +consul .[#e76a9b]--> database +consul .[#e76a9b,norank]--> gitaly_cluster +consul .[#e76a9b]--> redis @enduml ``` diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md index d1ec3e12c5ce23..ac34dcaa584d96 100644 --- a/doc/administration/reference_architectures/50k_users.md +++ b/doc/administration/reference_architectures/50k_users.md @@ -2310,24 +2310,22 @@ elb -[hidden]-> sidekiq elb -[hidden]-> support gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]--> object_storage -gitlab -[#32CD32]---> redis -gitlab -[#32CD32,norank]---> database -gitlab -[hidden]--> consul +gitlab -[#32CD32]r--> object_storage +gitlab -[#32CD32,norank]----> redis +gitlab -[#32CD32]----> database sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]--> object_storage -sidekiq -[#ff8dd1]---> redis -sidekiq .[#ff8dd1,norank]---> database -sidekiq -[hidden]--> consul +sidekiq -[#ff8dd1]r--> object_storage +sidekiq -[#ff8dd1,norank]----> redis +sidekiq .[#ff8dd1]----> database ilb -[#9370DB]--> gitaly_cluster ilb -[#9370DB]--> database -ilb -[hidden]--> redis +ilb -[hidden,norank]--> redis -consul .[#e76a9b]-> database -consul .[#e76a9b,norank]-> gitaly_cluster -consul .[#e76a9b]-> redis +consul .[#e76a9b]--> database +consul .[#e76a9b,norank]--> gitaly_cluster +consul .[#e76a9b]--> redis @enduml ``` diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md index b46f50d7ed0874..500b257b4d98f4 100644 --- a/doc/administration/reference_architectures/5k_users.md +++ b/doc/administration/reference_architectures/5k_users.md @@ -2228,24 +2228,22 @@ elb -[hidden]-> sidekiq elb -[hidden]-> support gitlab -[#32CD32]--> ilb -gitlab -[#32CD32]--> object_storage -gitlab -[#32CD32]---> redis -gitlab -[#32CD32,norank]---> database -gitlab -[hidden]--> consul +gitlab -[#32CD32]r--> object_storage +gitlab -[#32CD32,norank]----> redis +gitlab -[#32CD32]----> database sidekiq -[#ff8dd1]--> ilb -sidekiq -[#ff8dd1]--> object_storage -sidekiq -[#ff8dd1]---> redis -sidekiq .[#ff8dd1,norank]---> database -sidekiq -[hidden]--> consul +sidekiq -[#ff8dd1]r--> object_storage +sidekiq -[#ff8dd1,norank]----> redis +sidekiq .[#ff8dd1]----> database ilb -[#9370DB]--> gitaly_cluster ilb -[#9370DB]--> database -ilb -[hidden]--> redis +ilb -[hidden,norank]--> redis -consul .[#e76a9b]-> database -consul .[#e76a9b,norank]-> gitaly_cluster -consul .[#e76a9b]-> redis +consul .[#e76a9b]--> database +consul .[#e76a9b,norank]--> gitaly_cluster +consul .[#e76a9b]--> redis @enduml ``` -- GitLab From 5478d84f12b0c020d882bef7007e6a7506d5d7e8 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Thu, 11 Nov 2021 17:25:28 +0000 Subject: [PATCH 19/23] Change a word to be more reflective --- doc/administration/postgresql/database_load_balancing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md index 7a80782c3b1840..f2c430f0a65d2c 100644 --- a/doc/administration/postgresql/database_load_balancing.md +++ b/doc/administration/postgresql/database_load_balancing.md @@ -13,7 +13,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w ## Overview With Database Load Balancing, read-only queries can be distributed across -all PostgreSQL nodes to increase performance. +multiple PostgreSQL nodes to increase performance. This functionality is provided natively in GitLab Rails and Sidekiq where it can be configured to balance the load round robin, without any external dependencies. -- GitLab From 978a01b8c1cde79d07809cf83ab5c8fb70e5f5a8 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Fri, 12 Nov 2021 10:17:49 +0000 Subject: [PATCH 20/23] First review pass fixes --- .../postgresql/replication_and_failover.md | 6 ++---- .../reference_architectures/10k_users.md | 16 +++++++--------- .../reference_architectures/25k_users.md | 16 +++++++--------- .../reference_architectures/3k_users.md | 16 +++++++--------- .../reference_architectures/50k_users.md | 16 +++++++--------- .../reference_architectures/5k_users.md | 16 +++++++--------- 6 files changed, 37 insertions(+), 49 deletions(-) diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index 42b81961032dee..9cc88486532c44 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -23,8 +23,8 @@ replication, failover requires: - A minimum of three PostgreSQL nodes. - A minimum of three Consul server nodes. -- A minimum of three PgBouncer nodes that track and handle primary database writes and reads. - - An internal load balancer (TCP) is also required to balance requests between the nodes. +- A minimum of three PgBouncer nodes that track and handle primary database reads and writes. + - An internal load balancer (TCP) to balance requests between the PgBouncer nodes. - [Database Load Balancing](database_load_balancing.md) enabled. - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. @@ -532,8 +532,6 @@ Refer to your preferred Load Balancer's documentation for further guidance. ### Configuring the Application nodes -This - Application nodes run the `gitlab-rails` service. You may have other attributes set, but the following need to be set. diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index be9207b8739185..e13a98f9cbf4b7 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -480,8 +480,8 @@ run: node-exporter: (pid 30093) 76833s; run: log: (pid 29663) 76855s ## Configure PostgreSQL -In this section, you'll be guided through configuring PostgreSQL database -to be used with GitLab. +In this section, you'll be guided through configuring a highly available PostgreSQL +cluster to be used with GitLab. ### Provide your own PostgreSQL instance @@ -505,16 +505,14 @@ further configuration steps. ### Standalone PostgreSQL using Omnibus GitLab -The recommended PostgreSQL setup includes the following components: - -The Omnibus GitLab recommended configuration for a PostgreSQL cluster with -replication, failover requires: +The recommended Omnibus GitLab configuration for a PostgreSQL cluster with +replication and failover requires: - A minimum of three PostgreSQL nodes. - A minimum of three Consul server nodes. -- A PgBouncer cluster of 1 node or more to handle primary database writes and reads. - - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) is also required to balance requests between the nodes. -- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled for improved read queries performance. +- A minimum of three PgBouncer nodes that track and handle primary database reads and writes. + - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. +- [Database Load Balancing](database_load_balancing.md) enabled. - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md index 2404eb07ed643d..293865de41c087 100644 --- a/doc/administration/reference_architectures/25k_users.md +++ b/doc/administration/reference_architectures/25k_users.md @@ -483,8 +483,8 @@ run: node-exporter: (pid 30093) 76833s; run: log: (pid 29663) 76855s ## Configure PostgreSQL -In this section, you'll be guided through configuring PostgreSQL database -to be used with GitLab. +In this section, you'll be guided through configuring a highly available PostgreSQL +cluster to be used with GitLab. ### Provide your own PostgreSQL instance @@ -508,16 +508,14 @@ further configuration steps. ### Standalone PostgreSQL using Omnibus GitLab -The recommended PostgreSQL setup includes the following components: - -The Omnibus GitLab recommended configuration for a PostgreSQL cluster with -replication, failover requires: +The recommended Omnibus GitLab configuration for a PostgreSQL cluster with +replication and failover requires: - A minimum of three PostgreSQL nodes. - A minimum of three Consul server nodes. -- A PgBouncer cluster of 1 node or more to handle primary database writes and reads. - - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) is also required to balance requests between the nodes. -- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled for improved read queries performance. +- A minimum of three PgBouncer nodes that track and handle primary database reads and writes. + - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. +- [Database Load Balancing](database_load_balancing.md) enabled. - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md index f16dd2742f49ae..f269633f64bc4a 100644 --- a/doc/administration/reference_architectures/3k_users.md +++ b/doc/administration/reference_architectures/3k_users.md @@ -772,8 +772,8 @@ run: sentinel: (pid 30098) 76832s; run: log: (pid 29704) 76850s ## Configure PostgreSQL -In this section, you'll be guided through configuring PostgreSQL database -to be used with GitLab. +In this section, you'll be guided through configuring a highly available PostgreSQL +cluster to be used with GitLab. ### Provide your own PostgreSQL instance @@ -797,16 +797,14 @@ further configuration steps. ### Standalone PostgreSQL using Omnibus GitLab -The recommended PostgreSQL setup includes the following components: - -The Omnibus GitLab recommended configuration for a PostgreSQL cluster with -replication, failover requires: +The recommended Omnibus GitLab configuration for a PostgreSQL cluster with +replication and failover requires: - A minimum of three PostgreSQL nodes. - A minimum of three Consul server nodes. -- A PgBouncer cluster of 1 node or more to handle primary database writes and reads. - - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) is also required to balance requests between the nodes. -- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled for improved read queries performance. +- A minimum of three PgBouncer nodes that track and handle primary database reads and writes. + - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. +- [Database Load Balancing](database_load_balancing.md) enabled. - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md index ac34dcaa584d96..b60196e95ad882 100644 --- a/doc/administration/reference_architectures/50k_users.md +++ b/doc/administration/reference_architectures/50k_users.md @@ -489,8 +489,8 @@ run: node-exporter: (pid 30093) 76833s; run: log: (pid 29663) 76855s ## Configure PostgreSQL -In this section, you'll be guided through configuring PostgreSQL database -to be used with GitLab. +In this section, you'll be guided through configuring a highly available PostgreSQL +cluster to be used with GitLab. ### Provide your own PostgreSQL instance @@ -514,16 +514,14 @@ further configuration steps. ### Standalone PostgreSQL using Omnibus GitLab -The recommended PostgreSQL setup includes the following components: - -The Omnibus GitLab recommended configuration for a PostgreSQL cluster with -replication, failover requires: +The recommended Omnibus GitLab configuration for a PostgreSQL cluster with +replication and failover requires: - A minimum of three PostgreSQL nodes. - A minimum of three Consul server nodes. -- A PgBouncer cluster of 1 node or more to handle primary database writes and reads. - - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) is also required to balance requests between the nodes. -- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled for improved read queries performance. +- A minimum of three PgBouncer nodes that track and handle primary database reads and writes. + - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. +- [Database Load Balancing](database_load_balancing.md) enabled. - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md index 500b257b4d98f4..6fcc06e309cede 100644 --- a/doc/administration/reference_architectures/5k_users.md +++ b/doc/administration/reference_architectures/5k_users.md @@ -763,8 +763,8 @@ run: sentinel: (pid 30098) 76832s; run: log: (pid 29704) 76850s ## Configure PostgreSQL -In this section, you'll be guided through configuring PostgreSQL database -to be used with GitLab. +In this section, you'll be guided through configuring a highly available PostgreSQL +cluster to be used with GitLab. ### Provide your own PostgreSQL instance @@ -788,16 +788,14 @@ further configuration steps. ### Standalone PostgreSQL using Omnibus GitLab -The recommended PostgreSQL setup includes the following components: - -The Omnibus GitLab recommended configuration for a PostgreSQL cluster with -replication, failover requires: +The recommended Omnibus GitLab configuration for a PostgreSQL cluster with +replication and failover requires: - A minimum of three PostgreSQL nodes. - A minimum of three Consul server nodes. -- A PgBouncer cluster of 1 node or more to handle primary database writes and reads. - - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) is also required to balance requests between the nodes. -- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled for improved read queries performance. +- A minimum of three PgBouncer nodes that track and handle primary database reads and writes. + - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. +- [Database Load Balancing](database_load_balancing.md) enabled. - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: -- GitLab From 1912e407cc8996303c0747a9911bfb9f5f7be2fe Mon Sep 17 00:00:00 2001 From: Grant Young Date: Fri, 12 Nov 2021 10:30:50 +0000 Subject: [PATCH 21/23] Fix broken links and punctuation error --- doc/administration/postgresql/replication_and_failover.md | 2 +- doc/administration/reference_architectures/10k_users.md | 2 +- doc/administration/reference_architectures/25k_users.md | 2 +- doc/administration/reference_architectures/3k_users.md | 2 +- doc/administration/reference_architectures/50k_users.md | 2 +- doc/administration/reference_architectures/5k_users.md | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index 9cc88486532c44..984495b03d2639 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -104,7 +104,7 @@ Each PgBouncer node runs two services: Each service in the package comes with a set of [default ports](../package_information/defaults.md#ports). You may need to make specific firewall rules for the connections listed below: -There are several connection flows in this setup@ +There are several connection flows in this setup: #### Primary diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index e13a98f9cbf4b7..3f30db8006ca91 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -512,7 +512,7 @@ replication and failover requires: - A minimum of three Consul server nodes. - A minimum of three PgBouncer nodes that track and handle primary database reads and writes. - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. -- [Database Load Balancing](database_load_balancing.md) enabled. +- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled. - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md index 293865de41c087..2280beef297ca3 100644 --- a/doc/administration/reference_architectures/25k_users.md +++ b/doc/administration/reference_architectures/25k_users.md @@ -515,7 +515,7 @@ replication and failover requires: - A minimum of three Consul server nodes. - A minimum of three PgBouncer nodes that track and handle primary database reads and writes. - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. -- [Database Load Balancing](database_load_balancing.md) enabled. +- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled. - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md index f269633f64bc4a..df4bc6b471401f 100644 --- a/doc/administration/reference_architectures/3k_users.md +++ b/doc/administration/reference_architectures/3k_users.md @@ -804,7 +804,7 @@ replication and failover requires: - A minimum of three Consul server nodes. - A minimum of three PgBouncer nodes that track and handle primary database reads and writes. - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. -- [Database Load Balancing](database_load_balancing.md) enabled. +- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled. - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md index b60196e95ad882..b1ee5a178171c3 100644 --- a/doc/administration/reference_architectures/50k_users.md +++ b/doc/administration/reference_architectures/50k_users.md @@ -521,7 +521,7 @@ replication and failover requires: - A minimum of three Consul server nodes. - A minimum of three PgBouncer nodes that track and handle primary database reads and writes. - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. -- [Database Load Balancing](database_load_balancing.md) enabled. +- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled. - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md index 6fcc06e309cede..455e25e80f2df3 100644 --- a/doc/administration/reference_architectures/5k_users.md +++ b/doc/administration/reference_architectures/5k_users.md @@ -795,7 +795,7 @@ replication and failover requires: - A minimum of three Consul server nodes. - A minimum of three PgBouncer nodes that track and handle primary database reads and writes. - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. -- [Database Load Balancing](database_load_balancing.md) enabled. +- [Database Load Balancing](../postgresql/database_load_balancing.md) enabled. - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: -- GitLab From 9d43274c05351b39701bfc1de89e5df2fce39897 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Fri, 12 Nov 2021 10:40:07 +0000 Subject: [PATCH 22/23] Add indent suggestion --- doc/administration/reference_architectures/10k_users.md | 3 ++- doc/administration/reference_architectures/25k_users.md | 3 ++- doc/administration/reference_architectures/3k_users.md | 3 ++- doc/administration/reference_architectures/50k_users.md | 3 ++- doc/administration/reference_architectures/5k_users.md | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md index 3f30db8006ca91..d42e76248d9dec 100644 --- a/doc/administration/reference_architectures/10k_users.md +++ b/doc/administration/reference_architectures/10k_users.md @@ -513,7 +513,8 @@ replication and failover requires: - A minimum of three PgBouncer nodes that track and handle primary database reads and writes. - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. - [Database Load Balancing](../postgresql/database_load_balancing.md) enabled. - - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. + + A local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md index 2280beef297ca3..95892dcd1882f5 100644 --- a/doc/administration/reference_architectures/25k_users.md +++ b/doc/administration/reference_architectures/25k_users.md @@ -516,7 +516,8 @@ replication and failover requires: - A minimum of three PgBouncer nodes that track and handle primary database reads and writes. - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. - [Database Load Balancing](../postgresql/database_load_balancing.md) enabled. - - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. + + A local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md index df4bc6b471401f..89ad619b048cec 100644 --- a/doc/administration/reference_architectures/3k_users.md +++ b/doc/administration/reference_architectures/3k_users.md @@ -805,7 +805,8 @@ replication and failover requires: - A minimum of three PgBouncer nodes that track and handle primary database reads and writes. - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. - [Database Load Balancing](../postgresql/database_load_balancing.md) enabled. - - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. + + A local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md index b1ee5a178171c3..0fc6073f754e95 100644 --- a/doc/administration/reference_architectures/50k_users.md +++ b/doc/administration/reference_architectures/50k_users.md @@ -522,7 +522,8 @@ replication and failover requires: - A minimum of three PgBouncer nodes that track and handle primary database reads and writes. - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. - [Database Load Balancing](../postgresql/database_load_balancing.md) enabled. - - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. + + A local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md index 455e25e80f2df3..4f0b333f2b025d 100644 --- a/doc/administration/reference_architectures/5k_users.md +++ b/doc/administration/reference_architectures/5k_users.md @@ -796,7 +796,8 @@ replication and failover requires: - A minimum of three PgBouncer nodes that track and handle primary database reads and writes. - An [internal load balancer](#configure-the-internal-load-balancer) (TCP) to balance requests between the PgBouncer nodes. - [Database Load Balancing](../postgresql/database_load_balancing.md) enabled. - - This feature requires a local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. + + A local PgBouncer service to be configured on each PostgreSQL node. Note that this is separate from the main PgBouncer cluster that tracks the primary. The following IPs will be used as an example: -- GitLab From 03445c45f57947d037f4642e6183e1c1337940a5 Mon Sep 17 00:00:00 2001 From: Grant Young Date: Mon, 15 Nov 2021 17:03:12 +0000 Subject: [PATCH 23/23] Remove premium tag for DB LB Service Discovery --- doc/administration/postgresql/database_load_balancing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/administration/postgresql/database_load_balancing.md b/doc/administration/postgresql/database_load_balancing.md index f2c430f0a65d2c..d04e4de1cf32f4 100644 --- a/doc/administration/postgresql/database_load_balancing.md +++ b/doc/administration/postgresql/database_load_balancing.md @@ -98,7 +98,7 @@ gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.ex 1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. -### Service Discovery **(PREMIUM SELF)** +### Service Discovery > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/5883) in GitLab 11.0. -- GitLab