GitLab系列2 GitLab Workhorse

4,796 阅读4分钟

GitLab Workhorse

上一回介绍了 GitLab 的基础功能和架构,但还没具体讲解用户的请求是怎么被处理的,只是将各个组件的功能职责介绍了一遍,本节将简单介绍 gitlab-workhorse 的功能


首先回顾一下:GitLab 利用 Nginx 将前端的 http/https 请求代理至 gitlab-workhorse,gitlab-workhorse 再将请求转发至 Unicorn Web 服务器。默认情况下 gitlab-workhorse 与前端之间的通信是使用 unix domain socket 进行的,但也支持 TCP 转发请求;GitLab 使用 Unicorn Web 服务器提供动态网页和 API 接口

1. Nginx 入口

从架构图可以看出,HTTP/HTTPS 请求进入 GitLab 的第一站是 nginx

下载 GitLab-ce 官方源码后,进入 ${gitlab-ce根目录}/lib/support/nginx,打开 gitlab-ssl 可以看到 nginx 的配置


GitLab 默认将 http 请求重定向至 https 请求

## Redirects all HTTP traffic to the HTTPS host
server {
  listen 0.0.0.0:80;
  listen [::]:80 ipv6only=on default_server;
  server_name YOUR_SERVER_FQDN;
  server_tokens off;
  return 301 https://$http_host$request_uri;
  access_log  /var/log/nginx/gitlab_access.log gitlab_ssl_access;
  error_log   /var/log/nginx/gitlab_error.log;
}

https 的设置相对比较复杂,这里仅提一个亮点:location: / 下面的 proxy_pass http://gitlab-workhorse; ,说明了 除了某些静态页面,nginx 几乎将所有的 http/https 请求传递给了 gitlab-workhorse 组件处理(使用 unix socket 通信)

Unix Socket 是一种 socket 方式实现的进程间通信功能,它不需要进行复杂的数据打包拆包、校验和计算验证,不需要走网络协议栈,安全可靠。Unix Socket 其实是 socket 的其中一种 AF_UNIX 或 AF_LOCAL 的类型,成为 unix domain socket,是为了进行本地通信,也就是为了实现 IPC,所以构造函数不需要IP和端口,取而代之的是文件路径

upstream gitlab-workhorse {
  # GitLab socket file,
  # for Omnibus this would be: unix:/var/opt/gitlab/gitlab-workhorse/socket
  server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
}
...
## HTTPS host
server {
  listen 0.0.0.0:443 ssl;
  listen [::]:443 ipv6only=on ssl default_server;
  server_name YOUR_SERVER_FQDN;
  server_tokens off;

  ssl on;
  ssl_certificate /etc/nginx/ssl/gitlab.crt;
  ssl_certificate_key /etc/nginx/ssl/gitlab.key;

  ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 5m;
  real_ip_recursive off;    ## If you enable 'on'
  access_log  /var/log/nginx/gitlab_access.log gitlab_ssl_access;
  error_log   /var/log/nginx/gitlab_error.log;

  location / {
    client_max_body_size 0;
    gzip off;
    proxy_read_timeout      300;
    proxy_connect_timeout   300;
    proxy_redirect          off;

    proxy_http_version 1.1;

    proxy_set_header    Host                $http_host;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Forwarded-Ssl     on;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header    X-Forwarded-Proto   $scheme;
    proxy_set_header    Upgrade             $http_upgrade;
    proxy_set_header    Connection          $connection_upgrade_gitlab_ssl;

    proxy_pass http://gitlab-workhorse;
  }

  error_page 404 /404.html;
  error_page 422 /422.html;
  error_page 500 /500.html;
  error_page 502 /502.html;
  error_page 503 /503.html;
  location ~ ^/(404|422|500|502|503)\.html$ {
    root /home/git/gitlab/public;
    internal;
  }
}

2. GitLab-workhorse

那么 GitLab-workhorse 是什么呢?官方解释称它是 GitLab 的智能反向代理服务器,用于处理负载量较大的 HTTP 请求,诸如文件上传/下载、Git push/pull 以及 Git 归档下载等。而实际上可能比较复杂

+-------+  +------------------+  +---------+
|       |  |                  |  |         |
| NGINX +->| gitlab-workhorse +->| Unicorn |
|       |  |                  |  |         |
+-------+  +------------------+  +---------+

下述的 Rails 组件是运行在 Unicorn Web 服务器的:

  1. workhorse 能处理一些无需调用 Rails 组件的请求,例如静态的 js/css 资源文件


  2. workhorse 能修改 Rails 组件发来的响应。例如:假设你的 Rails 组件使用 send_file ,那么 gitlab-workhorse 将会打开磁盘中的文件然后把文件内容作为响应体返回给客户端
  3. workhorse 能接管向 Rails 组件询问操作权限后的请求,例如 处理 git clone 之前得确认当前客户的权限,在向 Rails 组件询问确认后 workhorse 将继续接管 git clone 的请求


  4. workhorse 能修改发送给 Rails 组件之前的请求信息。例如:当处理 Git LFS 上传时,workhorse 首先向 Rails 组件询问当前用户是否有执行权限,然后它将请求体储存在一个临时文件里,接着它将修改过后的包含此临时文件路径的请求体发送给 Rails 组件
  5. workhorse 能管理与 Rails 组件通信的长时间存活的 websocket 连接


  6. workhorse 无法直接连接数据库,只能与 Rails 组件和 Redis 组件(可选)通信
  7. 所有到达 workhorse 的请求都是由上游代理服务器(nginx)转发而来
  8. workhorse 不接受 https 连接
  9. workhorse 不会清除空闲客户端连接
  10. 所有对 Rails 组件的请求都得经过 workhorse

比如 Unicorn 处理静态资源文件效率相对较低,那就交给 workhorse 处理。由于文章篇幅的原因,这里仅挑一个例子进行讲解:gzip 资源文件

在 ${gitlab-workhorse根目录}/internal/staticpages/servefile.go 的函数 func (s *Static) ServeExisting 内,定义了 workhorse 对静态资源文件的处理方式

假设我们得请求相对 URL 为 /assets/locale/zh_CN/app-3396bd500e53f89d971d8c31ba7275f1c9ae2899062d4a7aeef14339084f44bd.js ,因为带有 assets 前缀,所以 workhorse 将采用处理静态资源文件的方式处理请求,如代码所示

// ${gitlab-workhorse根目录}/internal/upstream/routes.go
// Serve assets
route(
    "", `^/assets/`,
    static.ServeExisting(
        u.URLPrefix,
        staticpages.CacheExpireMax,
        NotFoundUnless(u.DevelopmentMode, proxy),
    ),
    withoutTracing(), // Tracing on assets is very noisy
),

下图对 js 静态资源文件的请求 /assets/locale/zh_CN/app-3396bd500e53f89d971d8c31ba7275f1c9ae2899062d4a7aeef14339084f44bd.js,就是使用 gzip 的方式


如果用户请求头带 Accept-Encoding: gzip 的话,workhorse 将读取相应请求静态资源的 gzip 文件(服务器预压缩的静态资源文件),并向浏览器传送(并非直接传送,还得经过 nginx 服务器)压缩过的内容,浏览器接收到服务器响应之后判断内容是否被压缩,如果被压缩则进行解压缩;当然如果用户请求头不指示使用 gzip ,那 workhorse 就会读取源文件


// ${gitlab-workhorse根目录}/internal/staticpages/servefile.go
// ...省略部分代码
file := filepath.Join(s.DocumentRoot, prefix.Strip(r.URL.Path))
// ...省略部分代码
// Serve pre-gzipped assets
if acceptEncoding := r.Header.Get("Accept-Encoding"); strings.Contains(acceptEncoding, "gzip") {
    content, fi, err = helper.OpenFile(file + ".gz")
    if err == nil {
        w.Header().Set("Content-Encoding", "gzip")
    }
}

从下图感受一下,压缩后的 gz 文件比源文件的 1/3 还小,可以说是大大节省服务器的网络带宽了


同时我们也该注意到,当访问静态资源文件的时候,请求并没有转发到 Unicorn Web 服务器,而是 workhorse 亲自处理了,这就是 workhorse 组件存在的最大意义,就是弥补 Unicorn Web 服务器存在的缺陷。这就得联想一个名言了:

Any problem in computer science can be solved by another layer of indirection

计算机科学领域的任何问题都可以以增加一个中间层来搞定,原来都是满满的套路啊

最后做个总结吧:workhorse 组件最初是为了解决 git-over-http/https 超时的问题,或者换句话说,workhorse 组件解决的是 unicorn 服务器不擅长处理的请求,而诸如动态页面渲染等请求将由 workhorse 帮忙代理至 unicorn 服务器处理,因为 unicorn 服务器擅长处理这类请求。至于最前端的 nginx 服务器则主要用于 https 配置等目的

附录

参考链接

GitLab Workhorse 官方仓库