首页 > 解决方案 > 创建发布版本后无法在 Elixir 中运行发布应用程序

问题描述

我已经使用命令创建了构建MIX_ENV=prod mix release --env=prod --verbose。它成功创建了构建,我可以运行consoleandping命令并给我pid. 即使我运行 start 命令,它也成功启动,但是当我转到 时htttp://localhost:4000,服务器没有运行。当我运行_build/prod/rel/project/bin/project foreground命令时,它只是挂在那里没有输出。

我正在使用 MacOS 版本:10.13.2,elixir:1.6.5(使用 OTP 19 编译),otp:Erlang/OTP 20。这是日志

$ MIX_ENV=prod mix release --env=prod --verbose
Generated project app
==> Loading configuration..
==> Assembling release..
==> Building release project:0.0.1 using environment prod
==> One or more direct or transitive dependencies are missing from
    :applications or :included_applications, they will not be included
    in the release:

    :jsx

    This can cause your application to fail at runtime. If you are sure that this is not an issue, you may ignore this warning.
==> Release successfully built!
You can run it in one of the following ways:
  Interactive: _build/prod/rel/project/bin/project console
  Foreground: _build/prod/rel/project/bin/project foreground
  Daemon: _build/prod/rel/project/bin/project start

我已经包含了所有显示为警告的应用程序,除了jsx因为它显示未定义应用程序的错误。

我还经历了酿酒厂问题https://github.com/bitwalker/distillery/issues/276并且因为这个问题表明我检查了我的应用程序名称并且服务器在配置文件中设置为 true,所以它没有帮助。我还在这里更详细地记录了问题https://github.com/bitwalker/distillery/issues/433

这是我的发布文件配置

environment :prod do
  set include_erts: true
  set include_src: false
  set cookie: :"lfHBC,7lDxe6kbZJ%M.x4=r!>[F*DhL)ly`?d$>%iE=9y)V4_Oulis?4Rvm)~!55"
end

# You may define one or more releases in this file.
# If you have not set a default release, or selected one
# when running `mix release`, the first release in the file
# will be used by default

release :project do
  set version: current_version(:project)
  set applications: [
    :runtime_tools
  ]
end

当我尝试创建一个新的 phoenix 应用程序并执行相同操作时,它正常运行并侦听端口 4000 并输出前台命令,但在我的应用程序中使用相同的配置,它不侦听 4000 端口并挂起前台命令。当我看到netstat两者时,我的应用程序似乎没有运行 4000 端口,请参阅 在此处输入图像描述

我不知道我应该如何调试这个问题我尝试了所有我能做的事情。如果有人需要更多信息,请告诉我。我将不胜感激这方面的任何帮助/建议。

编辑: 这是我的配置和产品文件。我刚刚粘贴了端点详细信息,让我知道是否还有其他需要。

# config.exs
config :project, Project.Endpoint,
  url: [host: "localhost"],
  secret_key_base: some_secret_key,
  render_errors: [view: Project.ErrorView, accepts: ~w(html json)],
  check_origin: false,
  pubsub: [name: Project.PubSub, adapter: Phoenix.PubSub.PG2]

# prod.exs
config :project, Project.Endpoint,
  http: [port: 4000],
  url: [scheme: "https", host: "server.example.com", port: 443],
  server: true,
  code_reloader: false

标签: elixirphoenix-frameworkreleasedistillery

解决方案


好吧,根据评论中的调试步骤,我认为我们的答案在于使用开发凤凰服务器时的配置与发布本身之间的差异。

配置工作不同的关键原因是开发服务器通常旨在在您的机器上本地运行 - 因此它很自然地运行开发机器上存在的环境变量,并在本地运行时提供配置。

但是,发行版旨在在远程机器上运行,通常在物理上或实际上不同于构建发行版的机器上的机器上运行。

这意味着严重依赖运行时配置的功能必须在版本中以不同方式处理。(Phoenix.Endpoint 的几个关键值,如 :port、:host 等都属于此范围)

有几种不同的最佳实践或模式可用于弥合这种配置差异,因为发行版的使用在 Elixir 中得到了发展,这在很大程度上要归功于 Distillery。我认为今天的社区是一个非常健康的地方。

当一个版本被编译时,它无法访问它最终运行的机器的运行时。因此,如果一个模块需要运行时配置,将配置委托给启动模块的 Supervisor 是一个很好的方法。在主管初始化模块时,它已在远程机器上启动,现在可以访问该远程环境。

当主管启动时,我几乎总是配置两个模块:Ecto'sRepo和 Phoenix's Endpoint

实际上,您可以Endpoint通过将load_from_system_env值设置为true. 您需要确保PORT HOST在环境中设置值,通过export PORT=4000export HOST=localhost/或获取.env文件,这将导致在运行版本和最终运行版本的远程计算机上在本地计算机上产生相同的行为(假设两台机器具有相同的环境值):

# config/prod.exs
config :trackbees, TrackbeesWeb.Endpoint,
  load_from_system_env: true,
  ...

Phoenix.Endpoint的角色之一是充当作为监督树的一部分启动和停止端点的包装器。它提供init/2回调作为此包装策略的一部分。您会看到,当init/2触发时,它将在以下位置进行模式匹配:load_from_system_env

# trackbees/lib/trackbees_web/endpoint.ex
def init(_key, config) do
  if config[:load_from_system_env] do
    Keyword.put ...

我认为这是 Trackbees 目前遇到的问题的具体解决方案。我建议添加load_from_system_env配置值,并可能插入一个调试语句,如语句IO.inspect 上方if验证该值是否通过。

仅供参考,如果您也使用 Ecto,有一个匹配的init包装器,如果您愿意,也可以以类似的方式使用:

# lib/trackbees/repo.ex
def init(_, opts) do
  case Application.get_env(:trackbees, :env) do
    :test ->
      {:ok, opts}

    _ ->
      opts =
        opts
        |> Keyword.put(:url, System.get_env("DATABASE_URL"))
        |> Keyword.put(:username, System.get_env("DB_USERNAME"))
        |> Keyword.put(:password, System.get_env("DB_PASSWORD"))
        |> Keyword.put(:database, System.get_env("DB_NAME"))
        |> Keyword.put(:hostname, System.get_env("DB_HOSTNAME"))

      {:ok, opts}
  end
end

推荐阅读