配置 GitLab 包内捆绑的 Puma 实例
Puma 是一个用于 Ruby 应用程序的快速、多线程和高度并发的 HTTP 1.1 服务器。它运行提供极狐GitLab 面向用户的功能的核心 Rails 应用程序。
减少内存使用
为了减少内存使用,Puma 派生 worker 进程。每次创建 worker 时,它都会与主进程共享内存。Worker 仅在更改或添加到其内存页面时才使用额外的内存。随着 worker 处理额外的网络请求,这可能会导致 Puma worker 随着时间的推移使用更多的物理内存。随着时间的推移使用的内存量取决于极狐GitLab 的使用情况。极狐GitLab 用户使用的功能越多,随着时间的推移,预期的内存使用量就越高。
为了阻止不受控制的内存增长,Rails 应用程序运行一个 supervision 线程,如果他们在一定时间内超过给定的驻留集大小 (RSS) 阈值,该线程会自动重启 worker。
极狐GitLab 为内存限制设置了默认值 1200Mb。要覆盖默认值,请将 per_worker_max_memory_mb
设置为新的 RSS 限制(以兆字节为单位):
-
编辑
/etc/gitlab/gitlab.rb
:puma['per_worker_max_memory_mb'] = 1024 # 1GB
-
重新配置极狐GitLab:
sudo gitlab-ctl reconfigure
当 worker 重新启动时,运行极狐GitLab 的能力会在短时间内降低。如果 worker 更换得太频繁,请将 per_worker_max_memory_mb
设置为更高的值。
Worker 计数是根据 CPU 内核计算的。如果 worker 过于频繁地重新启动(每分钟一次或更多次),具有 4-8 个 worker 的小型部署可能会遇到性能问题。
如果服务器有空闲内存,则建议设置 1200
或更高的值。
监控 worker 重启
如果 worker 由于高内存使用而重新启动,极狐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 限制。
更改 worker 超时
默认 Puma 超时为 60 秒。
puma['worker_timeout']
没有设置最大请求持续时间。要将 worker 超时更改为 600 秒:
-
编辑
/etc/gitlab/gitlab.rb
:gitlab_rails['env'] = { 'GITLAB_RAILS_RACK_TIMEOUT' => 600 }
-
重新配置极狐GitLab:
sudo gitlab-ctl reconfigure
在内存受限的环境中禁用 Puma 集群模式
在可用 RAM 少于 4GB 的内存受限环境中,请考虑禁用 Puma 集群模式。
将 workers
的数量设置为 0
以减少数百 MB 的内存使用量:
-
编辑
/etc/gitlab/gitlab.rb
:puma['worker_processes'] = 0
-
重新配置极狐GitLab:
sudo gitlab-ctl reconfigure
与默认设置的集群模式不同,只有一个 Puma 进程将为应用程序提供服务。 有关 Puma worker 和线程设置的详细信息,请参阅 Puma 要求。
在这种配置中运行 Puma 的缺点是吞吐量降低,这在内存受限的环境中可以被认为是公平的权衡。
请记住有足够的可用交换空间以避免内存不足 (OOM) 情况。 查看内存要求,了解详细信息。
Puma 单一模式已知问题
在单一模式下运行 Puma 时,不支持某些功能:
- 分阶段重启
- Memory killers <!– ## 将 Puma 与 Rugged 一起使用时的性能警告
对于使用 NFS 存储 Git 仓库的部署,极狐GitLab 通过 Rugged 使用直接 Git 访问来提高性能。
如果直接 Git 访问可用并且 Puma 正在单线程运行,则会自动启用 Rugged 的使用,除非它被功能标志禁用。
MRI Ruby 使用全局 VM 锁 (GVL)。 GVL 允许 MRI Ruby 是多线程的,但最多只能在单个内核上运行。
Git 包括密集的 I/O 操作。当 Rugged 长时间使用一个线程时,可能正在处理请求的其他线程可能会饿死。在单线程模式下运行的 Puma 没有这个问题,因为最多同时处理一个请求。
目前正在努力移除 Rugged 的使用。尽管目前没有 Rugged 的性能是可以接受的,但在某些情况下,使用它运行可能仍然有益。
鉴于使用多线程 Puma 运行 Rugged 的警告以及 Gitaly 可接受的性能,如果使用 Puma 多线程(当 Puma 配置为使用多个线程运行时),我们将禁用 Rugged 使用。
在某些情况下,此默认行为可能不是最佳配置。如果 Rugged 在您的部署中发挥重要作用,我们建议您进行基准测试以找到最佳配置:
- 最安全的选择是从单线程 Puma 开始。
- 要强制 Rugged 与多线程 Puma 一起使用,您可以使用功能标志。 –>
将 Puma 配置为通过 SSL 监听
Puma 与 Linux 软件包安装一起部署时,默认通过 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>' # Disable UNIX socket puma['socket'] = ""
-
重新配置极狐GitLab:
sudo gitlab-ctl reconfigure
使用加密的 SSL 密钥
- 引入于极狐GitLab 16.1。
Puma 支持使用加密的私有密钥,这在运行时可以被解密。以下说明说明了如何配置此功能:
-
如果未加密,则使用密码加密密钥:
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 实例上的未加密 SSL 密钥。
从 Unicorn 切换到 Puma
webservice
chart 文档。Puma 是默认的 Web 服务器,不再支持 Unicorn。
Puma 具有多线程架构,与 Unicorn 等多进程应用服务器相比,它使用的内存更少。在 JihuLab.com 上,我们看到内存的消耗减少了 40%。大多数 Rails 应用程序请求通常包含一定比例的 I/O 等待时间。
在 I/O 等待期间,MRI Ruby 将 GVL 释放给其它线程。因此,多线程 Puma 仍然可以处理比单个进程更多的请求。
当切换到 Puma 时,由于两个应用程序服务器之间的差异,任何 Unicorn 服务器配置都不会自动继承。
从 Unicorn 切换到 Puma:
- 确定合适的 Puma worker 和 thread 设置。
-
在
/etc/gitlab/gitlab.rb
中,将任何自定义 Unicorn 设置转换为 Puma。下表总结了在使用 Linux 包时,哪些 Unicorn 配置键与 Puma 中的配置键对应,哪些没有对应的配置键。
Unicorn Puma unicorn['enable']
puma['enable']
unicorn['worker_timeout']
puma['worker_timeout']
unicorn['worker_processes']
puma['worker_processes']
n/a puma['ha']
n/a puma['min_threads']
n/a puma['max_threads']
unicorn['listen']
puma['listen']
unicorn['port']
puma['port']
unicorn['socket']
puma['socket']
unicorn['pidfile']
puma['pidfile']
unicorn['tcp_nopush']
n/a unicorn['backlog_socket']
n/a unicorn['somaxconn']
puma['somaxconn']
n/a puma['state_path']
unicorn['log_directory']
puma['log_directory']
unicorn['worker_memory_limit_min']
n/a 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
- (可选)对于多节点部署,将负载均衡器配置为使用 readiness check。
Puma 故障排查
Puma 占用 100% CPU 后出现 502 错误
当无法从 Puma 工作器听到响应后,就会出现 Web 服务器超时,然后就会出现此错误。如果在此进程中,CPU 是用来飙升至 100%,那么可能是有某些操作花费的时间超出了正常时长。
要修复此问题,我们首先需要找出发生了什么。以下提示仅建议在您不介意用户受到影响的情况下使用。否则,请跳至下一节。
- 加载出现问题的 URL。
- 运行
sudo gdb -p <PID>
连接到 Puma 进程。 -
在 GDB 窗口中,输入:
call (void) rb_backtrace()
-
这会强制进程来生成一个 Ruby backtrace。检查
/var/log/gitlab/puma/puma_stderr.log
以获取 backtrace。例如,您可能会看到: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
来将超时时间从 60s 改为 600s:
gitlab_rails['env'] = {
'GITLAB_RAILS_RACK_TIMEOUT' => 600
}
对于自行编码安装的实例,请设置环境变量。可参考Puma 工作器超时。
配置极狐GitLab 以使配置生效。
在不影响其他用户的情况下排查故障
之前的部分是需要连接到运行的 Puma 进程,这可能会对在这期间尝试访问极狐GitLab 的用户产生不可预知的影响。如果您担心在生产系统上影响其他用户,您可以单独运行一个 Rails 进程来调试问题:
- 登录到您的极狐GitLab 账号。
- 拷贝引起问题的 URL(比如,
https://jihulab.com/ABC
)。 - 创建用户个人访问令牌(用户设置 -> 访问令牌)。
- 拉起一个 极狐GitLab Rails 控制台。
-
在 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
。就会展示出 CPU 使用率达到 100% 的 Ruby 进程。记录下此 PID。 - 遵循之前部分中的步骤 2,使用 GDB 调试此进程。
极狐GitLab:API 不可访问
当极狐GitLab Shell 尝试通过内部 API(比如,http://localhost:8080/api/v4/internal/allowed
)来请求认证时就会出现此错误,而且会导致有些检查失败。引起此问题的原因可能有很多:
- 数据库连接超时(比如,PostgreSQL 或 Redis)
- Git 勾子或推送规则错误。
- 访问仓库错误(比如,stale NFS handles)
要诊断此问题,尝试重现此问题,然后通过 top
命令来查看是否有 Puma 进程处于忙碌状态。然后尝试使用上面提到的 gdb
技巧。此外,使用 strace
可能有助于隔离问题:
strace -ttTfyyy -s 1024 -p <PID of puma worker> -o /tmp/puma.txt
如果您无法隔离出现问题的 Puma 工作器,您可以在所有的工作器上运行 strace
来查看 /internal/allowed
端点被卡住的地方:
ps auwx | grep puma | awk '{ print " -p " $2}' | xargs strace -ttTfyyy -s 1024 -o /tmp/puma.txt
/tmp/puma.txt
中的输出可能会帮助对根因进行诊断。