需要 config.ru 从 Docker 容器内启动 Sinatra 应用程序?

问题描述

为什么简单的命令 ruby my app.rb 不能从 Docker 容器内启动我的 Sinatra 应用程序?

我有一个非常简单的 Sinatra 应用:

# myapp.rb

require 'sinatra'

get '/' do
  'Hello World!'
end

我使用 ruby myapp.rb 在本地运行它,得到以下输出

== Sinatra (v2.1.0) has taken the stage on 4567 for development with backup from Puma
Puma starting in single mode...
* Puma version: 5.1.1 (ruby 2.7.0-p0) ("At Your Service")
*  Min threads: 0
*  Max threads: 5
*  Environment: development
*          PID: 49242
* Listening on http://127.0.0.1:4567
* Listening on http://[::1]:4567
Use Ctrl-C to stop

http://127.0.0.1:4567 上打开没有问题。在移动到 Dockerize 应用程序时,我使用 Sinatra 和以下 Dockerfile 创建了一个 Gemfile。

FROM ruby:2.7.0

workdir /code
copY . /code
RUN bundle install

CMD ["ruby","myapp.rb"]

立起容器,看起来成功了(Docker 桌面是绿色的,没有终端错误),但是点击建议的链接 http://localhost:4567/ 没有加载(悲伤的 Chrome 脸)。容器内的日志看起来像这样

[2020-12-27 18:04:52] INFO WEBrick 1.6.0
[2020-12-27 18:04:52] INFO ruby 2.7.0 (2019-12-25) [x86_64-linux]
== Sinatra (v2.1.0) has taken the stage on 4567 for development with backup from WEBrick
[2020-12-27 18:04:52] INFO WEBrick::HTTPServer#start: pid=1 port=4567

但是,当我添加下面的 config.ru 文件并将 Dockerfile 的最后一行更改为 CMD ["bundle","exec","rackup","--host","0.0.0.0","-p","4567"] 时,http://localhost:4567/ 打开没有问题。

# config.ru

require './myapp'
    
run Sinatra::Application

为什么需要进行这些调整才能使应用正常运行?来自容器的日志看起来几乎相同。

[2020-12-27 18:01:49] INFO WEBrick 1.6.0
[2020-12-27 18:01:49] INFO ruby 2.7.0 (2019-12-25) [x86_64-linux]
[2020-12-27 18:01:49] INFO WEBrick::HTTPServer#start: pid=1 port=4567
172.17.0.1 - - [27/Dec/2020:18:02:44 +0000] "GET / HTTP/1.1" 200 12 0.0420

我不一定想知道这里的“最佳实践”(这是一个副项目)。我更多地只是想了解我在 Dockerizing 应用程序的工作原理方面可能遗漏了什么。

两种情况下的 Docker 命令(我在两次运行之间清除图像/容器):

docker build --tag sinatra-img .
docker run --name sinatra-app -dp 4567:4567 sinatra-img

解决方法

当您在 Docker 容器中使用 ruby myapp.rb 启动您的应用程序时,您的应用程序正在侦听 localhost,因为它在开发模式下运行。如果您的 Docker 服务器在 VM 中运行,您将无法访问您的应用程序。要解决此问题,当您在 Docker 容器中运行您的应用时,请确保它正在侦听 0.0.0.0:ruby myapp.rb -o 0.0.0.0

,

注意

以下答案与问题的先前版本有关。新问题有不同的答案(使用 -o 0.0.0.0 CLI 参数修复绑定地址)。


Sinatra 框架基于 Rack,需要一个与 Rack 兼容的服务器......或者它也可以回退到 Ruby 语言包中包含的 WEBrick 服务器。

WEBrick 是一款不错的服务器,但它并不是为较重的负载或在生产中运行的实际 Web 应用程序的需求而设计的。

因此,您应该使用与机架兼容的服务器。

然而,这并不意味着您必须使用 rackup CLI 助手。

某些服务器,例如 Puma、iodine 和passenger,包含自己的CLI,因此您可以使用以下方式运行您的应用程序:

CMD ["bundle","exec","puma","-p","4567"]

键入 puma -h(或 iodine -h)以获得更多命令行选项。服务器的特定 CLI 可能会提供一些您无法通过 backup 获得的特定于服务器的功能。例如,Iodine 通过其 CLI 公开了一些安全选项(最大文件上传大小、最大总标头长度、Web 套接字消息限制等)。

使用服务器的 CLI 界面应该被认为是更好的选择

此外,虽然我不推荐它,但有些服务器还提供了 Ruby API,允许您从 Ruby 脚本(而不是 config.ru 文件)启动服务器。即,碘(我有偏见):

ENV['PORT'] ||= "4567"
require 'iodine' # will test the `ENV['PORT']` value

require 'sinatra'

get '/' do
  'Hello world!'
end

Iodine.listen service: :http,public: './public',handler: Sinatra::Application
# Iodine.threads = 16 # or whatever.
# Iodine.workers = -2 # half the core count (negative value).
Iodine.start

我不会使用这种方法。它往往更脆弱,并且还会对应用程序中的环境和服务器设置进行硬编码。

我只想添加 config.ru 并使用一个不错的服务器(我喜欢碘,但 Puma 更受欢迎,除非您需要实时发布/订阅、websockets 或某些特定的安全/性能功能,流行通常更安全)。


编辑(根据评论):

如果您真正想要的是将命令 bundle exec 嵌入到 Ruby 脚本中(使用 gemfile 进行版本控制),您可以使用以下行启动脚本

#!/usr/bin/env ruby
require 'bundler'
Bundler.require

或者,如果您根本不想使用 gemfile(或不需要版本控制),您可以在第一行开始:

#!/usr/bin/env ruby

然后你就可以直接启动你的服务器了:

CMD ["puma","4567"]

或者,不使用服务器的 CLI,使用上面的示例脚本,运行:

CMD ["my_script.rb"]