summaryrefslogtreecommitdiff
path: root/doc/administration/gitaly
diff options
context:
space:
mode:
Diffstat (limited to 'doc/administration/gitaly')
-rw-r--r--doc/administration/gitaly/img/gitlab_gitaly_version_mismatch_v12_4.pngbin0 -> 21779 bytes
-rw-r--r--doc/administration/gitaly/index.md114
-rw-r--r--doc/administration/gitaly/praefect.md154
-rw-r--r--doc/administration/gitaly/reference.md16
4 files changed, 205 insertions, 79 deletions
diff --git a/doc/administration/gitaly/img/gitlab_gitaly_version_mismatch_v12_4.png b/doc/administration/gitaly/img/gitlab_gitaly_version_mismatch_v12_4.png
new file mode 100644
index 00000000000..4d2c5cdb00c
--- /dev/null
+++ b/doc/administration/gitaly/img/gitlab_gitaly_version_mismatch_v12_4.png
Binary files differ
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 82283650070..9218ffa4006 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -221,6 +221,8 @@ Git operations in GitLab will result in an API error.
```toml
listen_addr = '0.0.0.0:8075'
+ internal_socket_dir = '/var/opt/gitlab/gitaly'
+
[auth]
token = 'abc123secret'
@@ -332,7 +334,10 @@ When you tail the Gitaly logs on your Gitaly server you should see requests
coming in. One sure way to trigger a Gitaly request is to clone a repository
from your GitLab server over HTTP.
-DANGER: **Danger:** If you have [custom server-side Git hooks](../custom_hooks.md#custom-server-side-git-hooks) configured, either per repository or globally, you must move these to the Gitaly node. If you have multiple Gitaly nodes, copy your custom hook(s) to all nodes.
+DANGER: **Danger:**
+If you have [custom server-side Git hooks](../custom_hooks.md) configured,
+either per repository or globally, you must move these to the Gitaly node.
+If you have multiple Gitaly nodes, copy your custom hook(s) to all nodes.
### Disabling the Gitaly service in a cluster environment
@@ -714,8 +719,115 @@ result as you did in the beginning:
Note that `enforced="true"`, meaning that authentication is being enforced.
+## Direct Git access in GitLab Rails
+
+Also known as "the Rugged patches".
+
+### History
+
+Before Gitaly existed, the things that are now Gitaly clients used to
+access Git repositories directly. Either on a local disk in the case of
+e.g. a single-machine Omnibus GitLab installation, or via NFS in the
+case of a horizontally scaled GitLab installation.
+
+Besides running plain `git` commands, in GitLab Rails we also used to
+use a Ruby gem (library) called
+[Rugged](https://github.com/libgit2/rugged). Rugged is a wrapper around
+[libgit2](https://libgit2.org/), a stand-alone implementation of Git in
+the form of a C library.
+
+Over time it has become clear to use that Rugged, and particularly
+Rugged in combination with the [Unicorn](https://bogomips.org/unicorn/)
+web server, is extremely efficient. Because libgit2 is a *library* and
+not an external process, there was very little overhead between GitLab
+application code that tried to look up data in Git repositories, and the
+Git implementation itself.
+
+Because Rugged+Unicorn was so efficient, GitLab's application code ended
+up with lots of duplicate Git object lookups (like looking up the
+`master` commmit a dozen times in one request). We could write
+inefficient code without being punished for it.
+
+When we migrated these Git lookups to Gitaly calls, we were suddenly
+getting a much higher fixed cost per Git lookup. Even when Gitaly is
+able to re-use an already-running `git` process to look up e.g. a commit
+you still have the cost of a network roundtrip to Gitaly, and within
+Gitaly a write/read roundtrip on the Unix pipes that connect Gitaly to
+the `git` process.
+
+Using GitLab.com performance as our yardstick, we pushed down the number
+of Gitaly calls per request until the loss of Rugged's efficiency was no
+longer felt. It also helped that we run Gitaly itself directly on the
+Git file severs, rather than via NFS mounts: this gave us a speed boost
+that counteracted the negative effect of not using Rugged anymore.
+
+Unfortunately, some *other* deployments of GitLab could not ditch NFS
+like we did on GitLab.com and they got the worst of both worlds: the
+slowness of NFS and the increased inherent overhead of Gitaly.
+
+As a performance band-aid for these stuck-on-NFS deployments, we
+re-introduced some of the old Rugged code that got deleted from
+GitLab Rails during the Gitaly migration project. These pieces of
+re-introduced code are informally referred to as "the Rugged patches".
+
+### Activation of direct Git access in GitLab Rails
+
+The Ruby methods that perform direct Git access are hidden behind [feature
+flags](../../development/gitaly.md#legacy-rugged-code). These feature
+flags are off by default. It is not good if you need to know about
+feature flags to get the best performance so in a second iteration, we
+added an automatic mechanism that will enable direct Git access.
+
+When GitLab Rails calls a function that has a Rugged patch it performs
+two checks. The result of both of these checks is cached.
+
+1. Is the feature flag for this patch set in the database? If so, do
+ what the feature flag says.
+1. If the feature flag is not set (i.e. neither true nor false), try to
+ see if we can access filesystem underneath the Gitaly server
+ directly. If so, use the Rugged patch.
+
+To see if GitLab Rails can access the repo filesystem directly, we use
+the following heuristic:
+
+- Gitaly ensures that the filesystem has a metadata file in its root
+ with a UUID in it.
+- Gitaly reports this UUID to GitLab Rails via the `ServerInfo` RPC.
+- GitLab Rails tries to read the metadata file directly. If it exists,
+ and if the UUID's match, assume we have direct access.
+
+Because of the way the UUID check works, and because Omnibus GitLab will
+fill in the correct repository paths in the GitLab Rails config file
+`config/gitlab.yml`, **direct Git access in GitLab Rails is on by default in
+Omnibus**.
+
+### Plans to remove direct Git access in GitLab Rails
+
+For the sake of removing complexity it is desirable that we get rid of
+direct Git access in GitLab Rails. For as long as some GitLab installations are stuck
+with Git repositories on slow NFS, however, we cannot just remove them.
+
+There are two prongs to our efforts to remove direct Git access in GitLab Rails:
+
+1. Reduce the number of (inefficient) Gitaly queries made by
+ GitLab Rails.
+1. Persuade everybody who runs a Highly Available / horizontally scaled
+ GitLab installation to move off of NFS.
+
+The second prong is the only real solution. For this we need [Gitaly
+HA](https://gitlab.com/groups/gitlab-org/-/epics?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=Gitaly%20HA),
+which is still under development as of December 2019.
+
## Troubleshooting Gitaly
+### Checking versions when using standalone Gitaly nodes
+
+When using standalone Gitaly nodes, you must make sure they are the same version
+as GitLab to ensure full compatibility. Check **Admin Area > Gitaly Servers** on
+your GitLab instance and confirm all Gitaly Servers are `Up to date`.
+
+![Gitaly standalone software versions diagram](img/gitlab_gitaly_version_mismatch_v12_4.png)
+
### `gitaly-debug`
The `gitaly-debug` command provides "production debugging" tools for Gitaly and Git
diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md
index 83c9aa3f013..6193a40ac4f 100644
--- a/doc/administration/gitaly/praefect.md
+++ b/doc/administration/gitaly/praefect.md
@@ -20,21 +20,19 @@ for updates and roadmap.
### Architecture
-For this document, the following network topology is assumed:
+The most common architecture for Praefect is simplified in the diagram below:
```mermaid
graph TB
- GitLab --> Gitaly;
GitLab --> Praefect;
- Praefect --> Praefect-Gitaly-1;
- Praefect --> Praefect-Gitaly-2;
- Praefect --> Praefect-Gitaly-3;
+ Praefect --> Gitaly-1;
+ Praefect --> Gitaly-2;
+ Praefect --> Gitaly-3;
```
Where `GitLab` is the collection of clients that can request Git operations.
-`Gitaly` is a Gitaly server before using Praefect. The Praefect node has three
-storage nodes attached. Praefect itself doesn't store data, but connects to
-three Gitaly nodes, `Praefect-Gitaly-1`, `Praefect-Gitaly-2`, and `Praefect-Gitaly-3`.
+The Praefect node has threestorage nodes attached. Praefect itself doesn't
+store data, but connects to three Gitaly nodes, `Gitaly-1`, `Gitaly-2`, and `Gitaly-3`.
Praefect may be enabled on its own node or can be run on the GitLab server.
In the example below we will use a separate server, but the optimal configuration
@@ -49,41 +47,43 @@ purposes.
In this setup guide we will start by configuring Praefect, then its child
Gitaly nodes, and lastly the GitLab server configuration.
+#### Secrets
+
+We need to manage the following secrets and make them match across hosts:
+
+1. `GITLAB_SHELL_SECRET_TOKEN`: this is used by Git hooks to make
+ callback HTTP API requests to GitLab when accepting a Git push. This
+ secret is shared with GitLab Shell for legacy reasons.
+1. `PRAEFECT_EXTERNAL_TOKEN`: repositories hosted on your Praefect
+ cluster can only be accessed by Gitaly clients that carry this
+ token.
+1. `PRAEFECT_INTERNAL_TOKEN`: this token is used for replication
+ traffic inside your Praefect cluster. This is distinct from
+ `PRAEFECT_EXTERNAL_TOKEN` because Gitaly clients must not be able to
+ access internal nodes of the Praefect cluster directly; that could
+ lead to data loss.
+
#### Praefect
On the Praefect node we disable all other services, including Gitaly. We list each
-Gitaly node that will be connected to Praefect under `praefect['storage_nodes']`.
+Gitaly node that will be connected to Praefect as members of the `praefect` hash in `praefect['virtual_storages']`.
-In the example below, the Gitaly nodes are named `praefect-gitaly-N`. Note that one
+In the example below, the Gitaly nodes are named `gitaly-N`. Note that one
node is designated as primary by setting the primary to `true`.
-`praefect['auth_token']` is the token used to authenticate with the GitLab server,
-just like `gitaly['auth_token']` is used for a standard Gitaly server.
-
-The `token` field under each storage listed in `praefect['storage_nodes']` is used
-to authenticate each child Gitaly node with Praefect.
-
```ruby
-# /etc/gitlab/gitlab.rb
+# /etc/gitlab/gitlab.rb on praefect server
# Avoid running unnecessary services on the Gitaly server
postgresql['enable'] = false
redis['enable'] = false
nginx['enable'] = false
prometheus['enable'] = false
+grafana['enable'] = false
unicorn['enable'] = false
sidekiq['enable'] = false
gitlab_workhorse['enable'] = false
gitaly['enable'] = false
-```
-
-##### Set up Praefect and its Gitaly nodes
-
-In the example below, the Gitaly nodes are named `praefect-git-X`. Note that one node is designated as
-primary, by setting the primary to `true`:
-
-```ruby
-# /etc/gitlab/gitlab.rb
# Prevent database connections during 'gitlab-ctl reconfigure'
gitlab_rails['rake_cache_clear'] = false
@@ -95,25 +95,27 @@ praefect['enable'] = true
# firewalls to restrict access to this address/port.
praefect['listen_addr'] = '0.0.0.0:2305'
-# virtual_storage_name must match the same storage name given to praefect in git_data_dirs
-praefect['virtual_storage_name'] = 'praefect'
-
-# Authentication token to ensure only authorized servers can communicate with
-# Praefect server
-praefect['auth_token'] = 'praefect-token'
-praefect['storage_nodes'] = {
- 'praefect-gitaly-1' => {
- 'address' => 'tcp://praefect-git-1.internal:8075',
- 'token' => 'praefect-gitaly-token',
- 'primary' => true
- },
- 'praefect-gitaly-2' => {
- 'address' => 'tcp://praefect-git-2.internal:8075',
- 'token' => 'praefect-gitaly-token'
- },
- 'praefect-gitaly-3' => {
- 'address' => 'tcp://praefect-git-3.internal:8075',
- 'token' => 'praefect-gitaly-token'
+# Replace PRAEFECT_EXTERNAL_TOKEN with a real secret
+praefect['auth_token'] = 'PRAEFECT_EXTERNAL_TOKEN'
+
+# Replace each instance of PRAEFECT_INTERNAL_TOKEN below with a real
+# secret, distinct from PRAEFECT_EXTERNAL_TOKEN.
+# Name of storage hash must match storage name in git_data_dirs on GitLab server.
+praefect['virtual_storages'] = {
+ 'praefect' => {
+ 'gitaly-1' => {
+ 'address' => 'tcp://gitaly-1.internal:8075',
+ 'token' => 'PRAEFECT_INTERNAL_TOKEN',
+ 'primary' => true
+ },
+ 'gitaly-2' => {
+ 'address' => 'tcp://gitaly-2.internal:8075',
+ 'token' => 'PRAEFECT_INTERNAL_TOKEN'
+ },
+ 'gitaly-3' => {
+ 'address' => 'tcp://gitaly-3.internal:8075',
+ 'token' => 'PRAEFECT_INTERNAL_TOKEN'
+ }
}
}
```
@@ -126,37 +128,40 @@ Next we will configure each Gitaly server assigned to Praefect. Configuration f
is the same as a normal standalone Gitaly server, except that we use storage names and
auth tokens from Praefect instead of GitLab.
-Below is an example configuration for `praefect-gitaly-1`, the only difference for the
+Below is an example configuration for `gitaly-1`, the only difference for the
other Gitaly nodes is the storage name under `git_data_dirs`.
-Note that `gitaly['auth_token']` matches the `token` value listed under `praefect['storage_nodes']`
+Note that `gitaly['auth_token']` matches the `token` value listed under `praefect['virtual_storages']`
on the Praefect node.
```ruby
-# /etc/gitlab/gitlab.rb
+# /etc/gitlab/gitlab.rb on gitaly node inside praefect cluster
# Avoid running unnecessary services on the Gitaly server
postgresql['enable'] = false
redis['enable'] = false
nginx['enable'] = false
prometheus['enable'] = false
+grafana['enable'] = false
unicorn['enable'] = false
sidekiq['enable'] = false
gitlab_workhorse['enable'] = false
+prometheus_monitoring['enable'] = false
# Prevent database connections during 'gitlab-ctl reconfigure'
gitlab_rails['rake_cache_clear'] = false
gitlab_rails['auto_migrate'] = false
+# Replace GITLAB_SHELL_SECRET_TOKEN below with real secret
+gitlab_shell['secret_token'] = 'GITLAB_SHELL_SECRET_TOKEN'
+
# Configure the gitlab-shell API callback URL. Without this, `git push` will
# fail. This can be your 'front door' GitLab URL or an internal load
# balancer.
-# Don't forget to copy `/etc/gitlab/gitlab-secrets.json` from web server to Gitaly server.
gitlab_rails['internal_api_url'] = 'https://gitlab.example.com'
-# Authentication token to ensure only authorized servers can communicate with
-# Gitaly server
-gitaly['auth_token'] = 'praefect-gitaly-token'
+# Replace PRAEFECT_INTERNAL_TOKEN below with a real secret.
+gitaly['auth_token'] = 'PRAEFECT_INTERNAL_TOKEN'
# Make Gitaly accept connections on all network interfaces. You must use
# firewalls to restrict access to this address/port.
@@ -164,16 +169,13 @@ gitaly['auth_token'] = 'praefect-gitaly-token'
gitaly['listen_addr'] = "0.0.0.0:8075"
git_data_dirs({
- "praefect-gitaly-1" => {
+ "gitaly-1" => {
"path" => "/var/opt/gitlab/git-data"
}
})
```
-Note that just as with a standard Gitaly server, `/etc/gitlab/gitlab-secrets.json` must
-be copied from the GitLab server to the Gitaly node for authentication purposes.
-
-For more information on Gitaly server configuration, see our [gitaly documentation](index.md#3-gitaly-server-configuration).
+For more information on Gitaly server configuration, see our [Gitaly documentation](index.md#3-gitaly-server-configuration).
#### GitLab
@@ -182,25 +184,29 @@ is done through setting the `git_data_dirs`. Assuming the default storage
is present, there should be two storages available to GitLab:
```ruby
+# /etc/gitlab/gitlab.rb on gitlab server
+
+# Replace PRAEFECT_EXTERNAL_TOKEN below with real secret.
git_data_dirs({
"default" => {
- "gitaly_address" => "tcp://gitaly.internal"
+ "path" => "/var/opt/gitlab/git-data"
},
"praefect" => {
- "gitaly_address" => "tcp://praefect.internal:2305"
+ "gitaly_address" => "tcp://praefect.internal:2305",
+ "gitaly_token" => 'PRAEFECT_EXTERNAL_TOKEN'
}
})
-gitlab_rails['gitaly_token'] = 'praefect-token'
+# Replace GITLAB_SHELL_SECRET_TOKEN below with real secret
+gitlab_shell['secret_token'] = 'GITLAB_SHELL_SECRET_TOKEN'
```
Note that the storage name used is the same as the `praefect['virtual_storage_name']` set
on the Praefect node.
-Also, the `gitlab_rails['gitaly_token']` matches the value of `praefect['auth_token']`
-on Praefect.
+Save your changes and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
-Restart GitLab using `gitlab-ctl restart` on the GitLab node.
+Run `gitlab-rake gitlab:gitaly:check` to confirm that GitLab can reach Praefect.
### Testing Praefect
@@ -208,10 +214,18 @@ To test Praefect, first set it as the default storage node for new projects
using **Admin Area > Settings > Repository > Repository storage**. Next,
create a new project and check the "Initialize repository with a README" box.
-If you receive a 503 error, check `/var/log/gitlab/gitlab-rails/production.log`.
-A `GRPC::Unavailable (14:failed to connect to all addresses)` error indicates
-that GitLab was unable to connect to Praefect.
-
-If the project is created but the README is not, then ensure that the
-`/etc/gitlab/gitlab-secrets.json` file from the GitLab server has been copied
-to the Gitaly servers.
+If you receive an error, check `/var/log/gitlab/gitlab-rails/production.log`.
+
+Here are common errors and potential causes:
+
+- 500 response code
+ - **ActionView::Template::Error (7:permission denied)**
+ - `praefect['auth_token']` and `gitlab_rails['gitaly_token']` do not match on the GitLab server.
+ - **Unable to save project. Error: 7:permission denied**
+ - Secret token in `praefect['storage_nodes']` on GitLab server does not match the
+ value in `gitaly['auth_token']` on one or more Gitaly servers.
+- 503 response code
+ - **GRPC::Unavailable (14:failed to connect to all addresses)**
+ - GitLab was unable to reach Praefect.
+ - **GRPC::Unavailable (14:all SubCons are in TransientFailure...)**
+ - Praefect cannot reach one or more of its child Gitaly nodes.
diff --git a/doc/administration/gitaly/reference.md b/doc/administration/gitaly/reference.md
index fe88ef13958..2c5e54743c3 100644
--- a/doc/administration/gitaly/reference.md
+++ b/doc/administration/gitaly/reference.md
@@ -134,7 +134,7 @@ A lot of Gitaly RPCs need to look up Git objects from repositories.
Most of the time we use `git cat-file --batch` processes for that. For
better performance, Gitaly can re-use these `git cat-file` processes
across RPC calls. Previously used processes are kept around in a
-["git cat-file cache"](https://about.gitlab.com/blog/2019/07/08/git-performance-on-nfs/#enter-cat-file-cache).
+["Git cat-file cache"](https://about.gitlab.com/blog/2019/07/08/git-performance-on-nfs/#enter-cat-file-cache).
In order to control how much system resources this uses, we have a maximum number
of cat-file processes that can go into the cache.
@@ -165,11 +165,11 @@ Gitaly restarts its `gitaly-ruby` helpers when their memory exceeds the
| Name | Type | Required | Description |
| ---- | ---- | -------- | ----------- |
-| `dir` | string | yes | Path to where gitaly-ruby is installed (needed to boot the process).|
-| `max_rss` | integer | no | Resident set size limit that triggers a gitaly-ruby restart, in bytes. Default is `200000000` (200MB). |
-| `graceful_restart_timeout` | string | no | Grace period before a gitaly-ruby process is forcibly terminated after exceeding `max_rss`. Default is `10m` (10 minutes).|
-| `restart_delay` | string | no |Time that gitaly-ruby memory must remain high before a restart. Default is `5m` (5 minutes).|
-| `num_workers` | integer | no |Number of gitaly-ruby worker processes. Try increasing this number in case of `ResourceExhausted` errors. Default is `2`, minimum is `2`.|
+| `dir` | string | yes | Path to where `gitaly-ruby` is installed (needed to boot the process).|
+| `max_rss` | integer | no | Resident set size limit that triggers a `gitaly-ruby` restart, in bytes. Default is `200000000` (200MB). |
+| `graceful_restart_timeout` | string | no | Grace period before a `gitaly-ruby` process is forcibly terminated after exceeding `max_rss`. Default is `10m` (10 minutes).|
+| `restart_delay` | string | no |Time that `gitaly-ruby` memory must remain high before a restart. Default is `5m` (5 minutes).|
+| `num_workers` | integer | no |Number of `gitaly-ruby` worker processes. Try increasing this number in case of `ResourceExhausted` errors. Default is `2`, minimum is `2`.|
| `linguist_languages_path` | string | no | Override for dynamic `languages.json` discovery. Defaults to an empty string (use of dynamic discovery).|
Example:
@@ -231,11 +231,11 @@ The following values configure logging in Gitaly under the `[logging]` section.
| `level` | string | no | Log level: `debug`, `info`, `warn`, `error`, `fatal`, or `panic`. Default: `info`. |
| `sentry_dsn` | string | no | Sentry DSN for exception monitoring. |
| `sentry_environment` | string | no | [Sentry Environment](https://docs.sentry.io/enriching-error-data/environments/) for exception monitoring. |
-| `ruby_sentry_dsn` | string | no | Sentry DSN for gitaly-ruby exception monitoring. |
+| `ruby_sentry_dsn` | string | no | Sentry DSN for `gitaly-ruby` exception monitoring. |
While the main Gitaly application logs go to stdout, there are some extra log
files that go to a configured directory, like the GitLab Shell logs.
-Gitlab Shell does not support `panic` or `trace` level logs. `panic` will fall
+GitLab Shell does not support `panic` or `trace` level logs. `panic` will fall
back to `error`, while `trace` will fall back to `debug`. Any other invalid log
levels will default to `info`.