需要 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
为什么简单命令 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