配置 GitLab 包中的内置 Puma 实例
- Tier: Free, Premium, Ultimate
- Offering: GitLab Self-Managed
Puma 是一个快速、多线程、高并发的 HTTP 1.1 服务器,用于 Ruby 应用程序。它运行提供 GitLab 用户界面功能的核心 Rails 应用程序。
减少内存使用
为了减少内存使用,Puma 会创建工作进程。每次创建工作进程时,它会与主进程共享内存。只有当工作进程修改或添加其内存页面时,才会使用额外的内存。随着工作进程处理更多的 Web 请求,这可能导致 Puma 工作进程随时间推移使用更多的物理内存。随时间推移使用的内存量取决于 GitLab 的使用情况。GitLab 用户使用的功能越多,预期的内存使用量就越高。
为了防止内存无限制增长,GitLab Rails 应用程序运行一个监督线程,如果工作进程在给定时间内超过特定的常驻内存集大小(RSS)阈值,它会自动重启这些工作进程。
GitLab 将内存限制的默认值设置为 1200Mb。要覆盖默认值,请将 per_worker_max_memory_mb 设置为新的 RSS 限制(以 MB 为单位):
-
编辑
/etc/gitlab/gitlab.rb:puma['per_worker_max_memory_mb'] = 1024 # 1 GB -
重新配置 GitLab:
sudo gitlab-ctl reconfigure
当工作进程重启时,短期内运行 GitLab 的能力会降低。如果工作进程被替换得太频繁,请将 per_worker_max_memory_mb 设置为更高的值。
工作进程数量基于 CPU 核心数计算。一个只有 4-8 个工作进程的小型 GitLab 部署,如果工作进程重启过于频繁(每分钟一次或更多),可能会遇到性能问题。
如果服务器有可用内存,将值设置为 1200 或更高可能是有益的。
监控工作进程重启
如果工作进程因内存使用过高而重启,GitLab 会发出日志事件。
以下是 /var/log/gitlab/gitlab-rails/application_json.log 中这些日志事件之一的示例:
{
"severity": "WARN",
"time": "2023-01-04T09:45:16.173Z",
"correlation_id": null,
"pid": 2725,
"worker_id": "puma_0",
"memwd_handler_class": "Gitlab::Memory::Watchdog::PumaHandler",
"memwd_sleep_time_s": 5,
"memwd_rss_bytes": 1077682176,
"memwd_max_rss_bytes": 629145600,
"memwd_max_strikes": 5,
"memwd_cur_strikes": 6,
"message": "rss memory limit exceeded"
}memwd_rss_bytes 是实际消耗的内存量,memwd_max_rss_bytes 是通过 per_worker_max_memory_mb 设置的 RSS 限制。
更改工作进程超时
默认的 Puma 超时时间是 60 秒。
puma['worker_timeout'] 设置不设置最大请求持续时间。
要将工作进程超时更改为 600 秒:
-
编辑
/etc/gitlab/gitlab.rb:gitlab_rails['env'] = { 'GITLAB_RAILS_RACK_TIMEOUT' => 600 } -
重新配置 GitLab:
sudo gitlab-ctl reconfigure
在内存受限环境中禁用 Puma 集群模式
在可用内存少于 4GB 的内存受限环境中,考虑禁用 Puma 集群模式。
将 workers 数量设置为 0 以减少数百 MB 的内存使用:
-
编辑
/etc/gitlab/gitlab.rb:puma['worker_processes'] = 0 -
重新配置 GitLab:
sudo gitlab-ctl reconfigure
与默认设置的集群模式不同,只有一个 Puma 进程将提供服务。有关 Puma 工作进程和线程设置的详细信息,请参阅 Puma 要求。
以这种配置运行 Puma 的缺点是吞吐量降低,在内存受限环境中,这可以被视为一个公平的权衡。
请确保有足够的交换空间可用,以避免内存不足(OOM)情况。有关详细信息,请查看 内存要求。
Puma 单模式已知问题
当以单模式运行 Puma 时,某些功能不受支持:
有关更多信息,请参阅 epic 5303。
配置 Puma 通过 SSL 监听
当通过 Linux 包安装部署时,Puma 默认通过 Unix 套接字监听。要配置 Puma 通过 HTTPS 端口监听,请按照以下步骤操作:
-
为 Puma 将要监听的地址生成 SSL 证书密钥对。对于下面的示例,这是
127.0.0.1。如果使用来自自定义证书颁发机构(CA)的自签名证书,请按照 文档 使其他 GitLab 组件信任它们。
-
编辑
/etc/gitlab/gitlab.rb:puma['ssl_listen'] = '127.0.0.1' puma['ssl_port'] = 9111 puma['ssl_certificate'] = '<path_to_certificate>' puma['ssl_certificate_key'] = '<path_to_key>' # 禁用 UNIX 套接字 puma['socket'] = "" -
重新配置 GitLab:
sudo gitlab-ctl reconfigure
除了 Unix 套接字外,Puma 还在端口 8080 上通过 HTTP 监听,以便为 Prometheus 提供抓取指标。目前无法让 Prometheus 通过 HTTPS 抓取这些指标,并且正在 此问题 中讨论支持。因此,在不丢失 Prometheus 指标的情况下,技术上无法关闭此 HTTP 监听器。
使用加密的 SSL 密钥
Puma 支持使用加密的私有 SSL 密钥,该密钥可以在运行时解密。以下说明说明了如何配置此功能:
-
如果密钥尚未加密,请使用密码加密密钥:
openssl rsa -aes256 -in /path/to/ssl-key.pem -out /path/to/encrypted-ssl-key.pem输入密码两次以写入加密文件。在此示例中,我们使用
some-password-here。 -
创建一个打印密码的脚本或可执行文件。例如,在
/var/opt/gitlab/gitlab-rails/etc/puma-ssl-key-password中创建一个基本脚本,该脚本回显密码:#!/bin/sh echo some-password-here避免在磁盘上存储密码,并使用安全机制(如 Vault)来检索密码。例如,脚本可能如下所示:
#!/bin/sh export VAULT_ADDR=http://vault-password-distribution-point:8200 export VAULT_TOKEN=<some token> echo "$(vault kv get -mount=secret puma-ssl-password)" -
确保 Puma 进程有足够的权限来执行脚本和读取加密密钥:
chown git:git /var/opt/gitlab/gitlab-rails/etc/puma-ssl-key-password chmod 770 /var/opt/gitlab/gitlab-rails/etc/puma-ssl-key-password chmod 660 /path/to/encrypted-ssl-key.pem -
编辑
/etc/gitlab/gitlab.rb,将puma['ssl_certificate_key']替换为加密密钥,并指定puma['ssl_key_password_command]:puma['ssl_certificate_key'] = '/path/to/encrypted-ssl-key.pem' puma['ssl_key_password_command'] = '/var/opt/gitlab/gitlab-rails/etc/puma-ssl-key-password' -
重新配置 GitLab:
sudo gitlab-ctl reconfigure -
如果 GitLab 成功启动,你应该能够删除存储在 GitLab 实例上的未加密 SSL 密钥。
从 Unicorn 切换到 Puma
对于基于 Helm 的部署,请参阅 webservice 图表文档。
Puma 是默认的 Web 服务器,不再支持 Unicorn。
Puma 具有多线程架构,使用的内存比多进程应用程序服务器(如 Unicorn)更少。在 GitLab.com 上,我们看到了 40% 的内存消耗减少。大多数 Rails 应用程序请求通常包含一定比例的 I/O 等待时间。
在 I/O 等待期间,MRI Ruby 将 GVL 释放给其他线程。因此,多线程 Puma 仍然可以比单个进程处理更多的请求。
切换到 Puma 时,任何 Unicorn 服务器配置都不会自动保留,因为这两个应用程序服务器之间存在差异。
要从 Unicorn 切换到 Puma:
-
确定合适的 Puma 工作进程和线程设置。
-
将任何自定义 Unicorn 设置转换为 Puma,在
/etc/gitlab/gitlab.rb中。下表总结了在使用 Linux 包时,哪些 Unicorn 配置键对应于 Puma 中的配置键,以及哪些没有对应的配置项。
Unicorn Puma unicorn['enable']puma['enable']unicorn['worker_timeout']puma['worker_timeout']unicorn['worker_processes']puma['worker_processes']不适用 puma['ha']不适用 puma['min_threads']不适用 puma['max_threads']unicorn['listen']puma['listen']unicorn['port']puma['port']unicorn['socket']puma['socket']unicorn['pidfile']puma['pidfile']unicorn['tcp_nopush']不适用 unicorn['backlog_socket']不适用 unicorn['somaxconn']puma['somaxconn']不适用 puma['state_path']unicorn['log_directory']puma['log_directory']unicorn['worker_memory_limit_min']不适用 unicorn['worker_memory_limit_max']puma['per_worker_max_memory_mb']unicorn['exporter_enabled']puma['exporter_enabled']unicorn['exporter_address']puma['exporter_address']unicorn['exporter_port']puma['exporter_port'] -
重新配置 GitLab:
sudo gitlab-ctl reconfigure -
可选。对于多节点部署,配置负载均衡器使用 就绪检查。
Puma 故障排除
Puma CPU 达到 100% 后出现 502 网关超时
当 Web 服务器在未收到 Puma 工作进程响应后超时(默认:60 秒)时,会发生此错误。如果在此过程中 CPU 旋转到 100%,可能有些事情花费的时间比应有的更长。
要解决此问题,我们首先必须弄清楚发生了什么。以下建议仅在您不介意用户受到停机影响的情况下推荐。否则,请跳到下一节。
-
加载有问题的 URL
-
运行
sudo gdb -p <PID>附加到 Puma 进程。 -
在 GDB 窗口中,键入:
call (void) rb_backtrace() -
这会强制进程生成 Ruby 回溯。检查
/var/log/gitlab/puma/puma_stderr.log中的回溯。例如,您可能会看到:from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `block in start' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `loop' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:36:in `block (2 levels) in start' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:44:in `sample' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `sample_objects' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each_with_object' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `block in sample_objects' from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `name' -
要查看当前线程,运行:
thread apply all bt -
完成使用
gdb调试后,务必从进程中分离并退出:detach exit
如果 Puma 进程在您运行这些命令之前终止,GDB 会报告错误。为了获得更多时间,您总是可以提高 Puma 工作进程超时。对于 Linux 包安装用户,您可以编辑 /etc/gitlab/gitlab.rb 并将其从 60 秒增加到 600:
gitlab_rails['env'] = {
'GITLAB_RAILS_RACK_TIMEOUT' => 600
}对于自编译安装,设置环境变量。请参阅 Puma Worker 超时。
重新配置 GitLab 以使更改生效。
不影响其他用户的故障排除
上一节附加到正在运行的 Puma 进程,这可能会在此期间尝试访问 GitLab 的用户产生不良影响。如果您担心在生产系统中影响他人,可以运行单独的 Rails 进程来调试问题:
-
登录您的 GitLab 账户。
-
复制导致问题的 URL(例如,
https://gitlab.com/ABC)。 -
为您的用户创建个人访问令牌(用户设置 -> 访问令牌)。
-
在 Rails 控制台中,运行:
app.get '<URL FROM STEP 2>/?private_token=<TOKEN FROM STEP 3>'例如:
app.get 'https://gitlab.com/gitlab-org/gitlab-foss/-/issues/1?private_token=123456' -
在新窗口中运行
top。它应该显示此 Ruby 进程使用 100% CPU。记下 PID。 -
遵循上一节中的步骤 2,使用 GDB。
GitLab:API 不可访问
这通常发生在 GitLab Shell 尝试通过内部 API(例如,http://localhost:8080/api/v4/internal/allowed)请求授权,并且检查中的某些内容失败时。此问题可能由以下原因引起:
- 连接到数据库(例如 PostgreSQL 或 Redis)超时
- Git hooks 或 push rules 中的错误
- 访问存储库时出错(例如,过期的 NFS 句柄)
要诊断此问题,尝试重现问题,然后查看是否有通过 top 旋转的 Puma 工作进程。尝试使用之前记录的 gdb 技术。此外,使用 strace 可能有助于隔离问题:
strace -ttTfyyy -s 1024 -p <PID of puma worker> -o /tmp/puma.txt如果您无法隔离哪个 Puma 工作进程是问题所在,尝试在所有 Puma 工作进程上运行 strace,看看 /internal/allowed 端点在哪里卡住:
ps auwx | grep puma | awk '{ print " -p " $2}' | xargs strace -ttTfyyy -s 1024 -o /tmp/puma.txt/tmp/puma.txt 中的输出可能有助于诊断根本原因。