使用 imgproxy 处理 ActiveStorage 图片变换

最近将 Geeknote 的图片处理换到了 imgproxy,记录一下过程。

为什么使用 imgproxy

imgproxy 是一个开源的图片处理引擎,可以用于替代 ActiveStorage 的原生图片处理或云服务的图片处理。

相比其他方案,自部署 imgproxy 的优势为:

  • 将图片处理负载移出 Rails 进程。
  • 更方便设置 CDN。
  • 更容易控制成本。(相比云服务)

工作原理

默认情况下,ActiveStorage 在提供图片变换的时候会返回一个重定向地址:

<%= image_tag user.avatar %>

// <img src="/rails/active_storage/representations/redirect/xxx...">

Rails 进程将会接收这个请求,如果这时图片还没处理,Rails 会即时生成图片变换,然后上传到云储存,然后重定向到云储存地址。

当使用 imgproxy 的时候,返回的是 imgproxy 的代理地址:

<%= image_tag user.avatar %>

// <img src="https://imgproxy.yourdomain.com/signed/rs:fill:300:400/g:sm/aHR0cH...">

这时候处理图片的是 imgproxy 进程,Rails 就不用管图片变换了。

操作

以下展示如何在生产环境配置 imgproxy。

imgproxy 需要访问后端储存的文件,用开发环境本地储存有些麻烦,建议用 S3 兼容的服务测试。以下省略 ActiveStorage 的 S3 配置。

安装和设置 imgproxy-rails

在 Gemfile 中添加并安装:

gem "imgproxy-rails"

config/application.rb 中添加配置:

if ENV["IMGPROXY_ENDPOINT"].present?
  config.active_storage.resolve_model_to_route = :imgproxy_active_storage
end

这行配置允许通过环境变量开启 imgproxy 功能。

需要开启时,在 Rails 环境变量中设置:

# 必须
# imgproxy 的终端地址
IMGPROXY_ENDPOINT=https://imgproxy.yourdomain.com

# 推荐
# 与 imgproxy 加签的密钥
IMGPROXY_KEY=yourimgproxykey
IMGPROXY_SALT=yourimgproxysalt

# 可选
# 使用 S3 短地址,避免地址太长
IMGPROXY_USE_S3_URLS=true

更多配置可以参考 https://github.com/imgproxy/imgproxy.rb?tab=readme-ov-file#configuration-options

设置 IMGPROXY_ENDPOINT 后,Rails 会自动把 variant 地址改为 imgproxy 的地址,接下来需要部署 imgproxy 服务。

部署 imgproxy

这里用 fly.io 部署为例,你也可以用其他方式部署。其他部署方式参考 https://docs.imgproxy.net/installation

用 fly 命令行创建应用:

$ fly launch --image darthsim/imgproxy:v3

在弹出网页中选择部署地点和主机配置,确认后配置如下:

app = 'your-imgproxy-app'
primary_region = 'hkg'

[build]
  image = 'darthsim/imgproxy:v3'

[http_service]
  internal_port = 8080
  force_https = true
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 0
  processes = ['app']

执行部署:

$ fly deploy

部署成功后,访问 https://your-imgproxy-app.fly.dev 应可看到 imgproxy 信息。

现在继续添加环境变量:

# 推荐
# 和 Rails 配置配对的加签密钥
IMGPROXY_KEY=yourimgproxykey
IMGPROXY_SALT=yourimgproxysalt

# 使用 S3 后端
IMGPROXY_USE_S3=true
IMGPROXY_S3_ENDPOINT=xxx
IMGPROXY_S3_REGION=auto
AWS_ACCESS_KEY_ID=xxx
AWS_SECRET_ACCESS_KEY=xxx

重启应用后,现在应该可以看到 Rails 应用输出 imgproxy 地址,而 imgproxy 服务正常返回图片。

配置 CDN

imgproxy 本身只处理图片,不处理缓存,要避免每个请求都重新生成图片,需要在 imgproxy 前面加 CDN。

以 Cloudflare 为例,为域名 imgproxy.yourdomain.com 打开代理模式,在缓存规则中设置该域名开启缓存,即可用 CDN 缓存生成的图片。

总结

以上就是在生产环境用 imgproxy 替代 ActiveStorage 图片变换的全过程。

1