Fullstaq Ruby:第一印象以及如何迁移 Docker/Kubernetes Ruby 应用程序
什么是 Fullstaq Ruby?
Fullstaq Ruby是标准 MRI Ruby 解释器的定制版本,它替换了内存分配器,应用了安全补丁,并且还有更多好东西。
如果您是老用户,或许还记得 REE(Ruby 企业版),它诞生于 Ruby 1.8.7 和 Ruby on Rails 2.2 的鼎盛时期(差不多十年前了!)。啊,美好的旧时光!您可以通过 RVM 或 Rbenv 安装它,一些遗留应用程序仍在其上运行,或者最近刚刚迁移。REE 在 Ruby 1.8.7 的基础上提供了十几个不同的补丁,用于提升性能、降低内存消耗、调整过时的安全设置等等。
在 MRI 1.9.x 中,大多数问题都得到了解决,随着 REE 的普及,它逐渐被淘汰。但即使是现代的“原生” MRI 仍然有一些相对容易修复的怪癖。其中最烦人的是由于内存碎片导致的内存膨胀。
因此,REE 的创建者Hongli Lai发布 Fullstaq Ruby也就不足为奇了。
REE 已死,Fullstaq Ruby 万岁!
我们为什么需要它?
在Evil Martians的一个项目中,我们遇到了严重的内存膨胀问题。我们的应用程序执行大量 IO 操作,并且拥有许多并发设置较高的 Sidekiq 进程(每个进程 20 个线程)。从性能角度来看,这种设置是最佳的,因为工作进程主要向不同的远程 API、我们自己的数据库和缓存发出请求。但是,如此高的并发性也会导致内存碎片化。每个 Sidekiq 进程都会占用数 GB的内存。
阅读 Nate Berkopec 撰写的《Sidekiq 实践第 1 部分》中有关选择 Sidekiq 并发设置的更多信息。
我们决定将 MRI 2.6.3 替换为带有jemalloc的 Fullstaq Ruby 2.6.3 ,以查看其性能如何。
这就是区别!
我们在生产中运行的商业应用程序上试用了 Fullstaq Ruby,该应用程序全天候处理付费客户的请求。
首先:一切正常。零停机时间!
现在,看看这些监控图。长时间运行进程的内存膨胀几乎已经消失了!
-
Web 应用程序进程的内存消耗已变得非常稳定(减少了 4 倍!)。内存膨胀现象偶尔仍会出现,但读数显示峰值期间的内存消耗减少了约 50%。
-
后台任务执行器(我们正在使用 Sidekiq)也减轻了三分之二的体积。迁移到 Fullstaq Ruby 后,体积从之前的 1.5-2 GB 缩减到了 500-700 MB。
-
短进程(例如 cron 作业)的内存消耗没有明显差异
-
我们没有注意到响应时间或 CPU 利用率有任何变化。
上面的图表证明内存碎片是造成内存消耗高的原因。
就是这样——将一个 ruby 二进制文件交换为另一个 ruby 二进制文件,这是一个很大的改进,不是吗?
还有其他选择吗?
如果您无法使用 jemalloc,或者您无法承担用其他程序替换 MRI 的费用,您可以尝试使用MALLOC_ARENA_MAX=2
spell来调整 MRI 的标准 glibc malloc 行为。结果将与 Fullstaq Ruby 非常接近,几乎可以等同对待。
在我们的例子中,具有有限数量 malloc 区域的 Ruby(右侧)比具有 jemalloc 的 Ruby(左侧)消耗的内存多约 50-100 MB。
阅读更多内容并查看此文章中的基准
MALLOC_ARENA_MAX=2
:
我们决定继续使用 Fullstaq Ruby。
如何安装?
目前,安装 Fullstaq Ruby 的唯一方法是使用 deb 或 rpm 包(直接安装或通过仓库安装)。但我们将应用部署到 Kubernetes 集群,因此需要一个 Docker 镜像。由于官方网站上目前还没有“容器版本”,所以我们不妨自己构建镜像——其实,这并不难!
让我们使用 Debian 9,因为这是官方 Ruby Docker 映像使用的 Linux 发行版,并定义 Ruby 版本:
FROM debian:stretch-slim
ARG RUBY_VERSION=2.6.3-jemalloc
然后安装先决条件,添加 Fullstaq Ruby APT 存储库,安装 Ruby 本身并清理 apt 缓存 - 所有这些都在单个命令中完成,以减少 Docker 层的大小:
RUN apt-get update -q \
&& apt-get dist-upgrade --assume-yes \
&& apt-get install --assume-yes -q --no-install-recommends curl gnupg apt-transport-https ca-certificates \
&& curl -SLf https://raw.githubusercontent.com/fullstaq-labs/fullstaq-ruby-server-edition/master/fullstaq-ruby.asc | apt-key add - \
&& echo "deb https://apt.fullstaqruby.org debian-9 main" > /etc/apt/sources.list.d/fullstaq-ruby.list \
&& apt-get update -q \
&& apt-get install --assume-yes -q --no-install-recommends fullstaq-ruby-${RUBY_VERSION} \
&& apt-get autoremove --assume-yes \
&& rm -fr /var/cache/apt
Fullstaq Ruby 也安装了 Rbenv 作为依赖项,但我们在 Docker 中不需要它,因此让我们以与 Ruby 官方 Docker 镜像相同的方式$PATH
将ruby 和 gems 二进制文件添加到系统中:
ENV GEM_HOME /usr/local/bundle
ENV BUNDLE_PATH="$GEM_HOME" \
BUNDLE_SILENCE_ROOT_WARNING=1 \
BUNDLE_APP_CONFIG="$GEM_HOME" \
RUBY_VERSION=$RUBY_VERSION \
LANG=C.UTF-8 LC_ALL=C.UTF-8
# path recommendation: https://github.com/bundler/bundler/pull/6469#issuecomment-383235438
ENV PATH $GEM_HOME/bin:$BUNDLE_PATH/gems/bin:/usr/lib/fullstaq-ruby/versions/${RUBY_VERSION}/bin:$PATH
CMD [ "irb" ]
就是这样!
我们已经构建并发布了此镜像。您可以从我们的quay.io 仓库中获取它:
docker pull quay.io/evl.ms/fullstaq-ruby:2.6.3-jemalloc-stretch-slim
Dockerfiles 可在 GitHub 上获取:https://github.com/evilmartians/fullstaq-ruby-docker
现在我们可以在应用程序 Dockerfile 中替换基础镜像:
-ARG RUBY_VERSION=2.6.3
+ARG RUBY_VERSION=2.6.3-jemalloc
-FROM ruby:${RUBY_VERSION}-stretch-slim
+FROM quay.io/evl.ms/fullstaq-ruby:${RUBY_VERSION}-stretch-slim
并将其部署到暂存区,然后部署到生产区。
请随意做同样的事情!
回顾
- 迁移过程非常顺利。只需重新安装 Ruby 和 gems,一切就都能正常工作了。
- 应用服务器和后台作业工作进程应该大幅减少内存消耗。
- 对于短进程(如 cron 作业或脚本),内存消耗没有明显差异。
- 性能可能会略有提高,但这取决于您的工作负载情况。