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

Need config.ru to Start up a Sinatra App from within a Docker Container?

为什么简单命令 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 创建了一个 Gemfile 和以下 Dockerfile.

FROM ruby:2.7.0

WORKDIR /code
COPY . /code
RUN bundle install

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

起立容器,好像成功了(Docker桌面是绿色的,没有终端报错),但是点击提示的link 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 文件并将我的 Docker 文件的最后一行更改为 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 两种情况下的命令(我清除了 运行 之间的 images/containers):

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

注意:

以下答案与问题的先前版本有关。新问题有不同的答案(使用 -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 公开了一些安全选项(最大文件上传大小、最大总长度 header、网络套接字消息限制等)。

使用服务器的 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 并使用像样的服务器(我喜欢 iodine,但 Puma 更受欢迎,除非你需要 real-time pub/sub、websockets 或一些特定的security/performance 特性,流行往往更安全)。


编辑(根据评论):

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

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

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

#!/usr/bin/env ruby

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

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

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

CMD ["my_script.rb"]

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