【问题标题】:How do I run Elixir Phoenix Docker in production mode using example from Phoenix Guides?如何使用 Phoenix Guides 中的示例在生产模式下运行 Elixir Phoenix Docker?
【发布时间】:2019-10-22 15:34:06
【问题描述】:

Elixir Phoenix 指南中给出的示例 Dockerfile 似乎已过时且损坏。示例可在此处找到:https://hexdocs.pm/phoenix/releases.html#containers

我创建了一个像这样的普通应用程序:mix phx.new hello_world

损坏的 Dockerfile:

# FROM elixir:1.9.0-alpine as build

# install build dependencies
RUN apk add --update git build-base nodejs yarn python

# prepare build dir
RUN mkdir /app
WORKDIR /app

# install hex + rebar
RUN mix local.hex --force && \
    mix local.rebar --force

# set build ENV
ENV MIX_ENV=prod

# install mix dependencies
COPY mix.exs mix.lock ./
COPY config config
RUN mix deps.get
RUN mix deps.compile

# build assets
COPY assets assets
RUN cd assets && npm install && npm run deploy
RUN mix phx.digest

# build project
COPY priv priv
COPY lib lib
RUN mix compile

# build release
COPY rel rel
RUN mix release

# prepare release image
FROM alpine:3.9 AS app
RUN apk add --update bash openssl

RUN mkdir /app
WORKDIR /app

COPY --from=build /app/_build/prod/rel/my_app ./
RUN chown -R nobody: /app
USER nobody

ENV HOME=/app

我正在尝试从 vanilla Phoenix 应用安装运行 Dockerfile,但遇到了许多问题,包括:

# FROM elixir:1.9.0-alpine as build
# Needs to be uncommented

RUN cd assets && npm install && npm run deploy
# npm install failed, had to add nodejs-npm

COPY rel rel
# errors here, there is no rel, should I remove?

More errors later because DATABASE_URL and SECRET_KEY_BASE are not declared

我不是专家,到目前为止 Dockerfile 现在看起来像这样:

FROM elixir:1.9.1-alpine as build

# install build dependencies
# modified: is this correct?
RUN apk add --update git build-base nodejs nodejs-npm yarn python

# prepare build dir
RUN mkdir /app
WORKDIR /app

# install hex + rebar
RUN mix local.hex --force && \
    mix local.rebar --force

# set build ENV
# modified: is this correct?
ENV DATABASE_URL=${DATABASE_URL} \
  SECRET_KEY_BASE=${SECRET_KEY_BASE} \
  MIX_ENV=prod

# install mix dependencies
COPY mix.exs mix.lock ./
COPY config config
RUN mix deps.get
RUN mix deps.compile

# build assets
COPY assets assets
RUN cd assets && npm install && npm run deploy

# build project
COPY priv priv
COPY lib lib
RUN mix compile

# build release
# removed next line, is that correct?
# COPY rel rel
RUN mix release

# prepare release image
FROM alpine:3.9 AS app
RUN apk add --update bash openssl

RUN mkdir /app
WORKDIR /app

COPY --from=build /app/_build/prod/rel/hello_world ./
RUN chown -R nobody: /app
USER nobody

ENV HOME=/app

ENTRYPOINT ["./bin/hello_world", "start"]

我尝试使用以下命令运行容器:

docker run -it -e DATABASE_URL='ecto://postgres:123456@localhost/hello_world_dev' -e SECRET_KEY_BASE='blargblargblarg' hello_world:latest

但我收到此错误:

05:20:34.841 [error] GenServer #PID<0.1380.0> terminating
** (RuntimeError) connect raised KeyError exception: key :database not found. The exception details are hidden, as they may contain sensitive data such as database credentials. You may set :show_sensitive_data_on_connection_error to true when starting your connection if you wish to see all of the details
    (elixir) lib/keyword.ex:393: Keyword.fetch!/2
    (postgrex) lib/postgrex/protocol.ex:92: Postgrex.Protocol.connect/1
    (db_connection) lib/db_connection/connection.ex:69: DBConnection.Connection.connect/2
    (connection) lib/connection.ex:622: Connection.enter_connect/5
    (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: nil

..... repeat a lot

05:48:41.296 [info] Application hello_world exited: shutdown

这对我来说都是全新的。我已经轻松部署到 Heroku,但希望使用 Docker 部署到 AWS、GCP 等的幸福路径...

更新 1: 这是config/prod.secret.exs 文件。我没有修改它。请注意它正在加载环境变量:

# In this file, we load production configuration and secrets
# from environment variables. You can also hardcode secrets,
# although such is generally not recommended and you have to
# remember to add this file to your .gitignore.
use Mix.Config

database_url =
  System.get_env("DATABASE_URL") ||
    raise """
    environment variable DATABASE_URL is missing.
    For example: ecto://USER:PASS@HOST/DATABASE
    """

config :hello_world, HelloWorld.Repo,
  # ssl: true,
  url: database_url,
  pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")

secret_key_base =
  System.get_env("SECRET_KEY_BASE") ||
    raise """
    environment variable SECRET_KEY_BASE is missing.
    You can generate one by calling: mix phx.gen.secret
    """

config :hello_world, HelloWorldWeb.Endpoint,
  http: [:inet6, port: String.to_integer(System.get_env("PORT") || "4000")],
  secret_key_base: secret_key_base

# ## Using releases (Elixir v1.9+)
#
# If you are doing OTP releases, you need to instruct Phoenix
# to start each relevant endpoint:
#
#     config :hello_world, HelloWorldWeb.Endpoint, server: true
#
# Then you can assemble a release by calling `mix release`.
# See `mix help release` for more information.

更新 2:

我从config/prod.exs 中删除了import_config "config/prod.secret.exs" 我将 config/prod.secret.exs 重命名为 config/releases.exs 并在文件中将 use Mix.Config 更改为 import Config 并取消注释 config :hello_world, HelloWorldWeb.Endpoint, server: true

现在,当我尝试使用端口映射和 --network="host" 运行容器时

docker run -it -e DATABASE_URL='ecto://postgres:123456@localhost/hello_world_dev' -e SECRET_KEY_BASE='blargblargblarg' -p 5432:5432 --network="host" hello_world:latest

我明白了

23:21:48.635 [error] Postgrex.Protocol (#PID<0.2615.0>) failed to connect: ** (DBConnection.ConnectionError) tcp connect (localhost:5432): connection refused - :econnrefused
23:21:48.635 [error] Postgrex.Protocol (#PID<0.2619.0>) failed to connect: ** (DBConnection.ConnectionError) tcp connect (localhost:5432): connection refused - :econnrefused
23:21:48.639 [info] Running HelloWorldWeb.Endpoint with cowboy 2.6.3 at :::4000 (http)
23:21:48.640 [info] Access HelloWorldWeb.Endpoint at http://example.com
23:21:49.938 [error] Postgrex.Protocol (#PID<0.2623.0>) failed to connect: ** (DBConnection.ConnectionError) tcp connect (localhost:5432): connection refused - :econnrefused

更新 3: 我尝试更改 DATABASE_URL,但没有成功。

# adding port
DATABASE_URL='ecto://postgres:123456@localhost:5432/hello_world_dev'

# changing hostname to 127.0.0.1
DATABASE_URL='ecto://postgres:123456@127.0.0.1/hello_world_dev'

# changing hostname to 127.0.0.1 and adding port
DATABASE_URL='ecto://postgres:123456@127.0.0.1:5432/hello_world_dev'

# changing hostname to 0.0.0.0
DATABASE_URL='ecto://postgres:123456@0.0.0.0/hello_world_dev'

# changing hostname to 0.0.0.0 and adding port
DATABASE_URL='ecto://postgres:123456@0.0.0.0:5432/hello_world_dev'

# changing ecto to postgresql
DATABASE_URL='postgresql://postgres:123456@localhost/hello_world_dev'

# changing ecto to postgresql and localhost to 127.0.0.1
DATABASE_URL='postgresql://postgres:123456@127.0.0.1/hello_world_dev'

# changing ecto to postgresql and localhost to 127.0.0.1 with port
DATABASE_URL='postgresql://postgres:123456@127.0.0.1:5432/hello_world_dev'

# changing ecto to postgresql and localhost to 0.0.0.0
DATABASE_URL='postgresql://postgres:123456@0.0.0.0/hello_world_dev'

# changing ecto to postgresql and localhost to 0.0.0.0 with port
DATABASE_URL='postgresql://postgres:123456@0.0.0.0:5432/hello_world_dev'

¯\_(ツ)_/¯

【问题讨论】:

    标签: docker elixir dockerfile phoenix-framework


    【解决方案1】:

    有一个很好的sample repo 配置用于生产就绪的构建和部署周期。它包含一个 ansible 设置,将维护 docker 映像,在 docker 映像中构建 phoenix 应用程序,并在您的生产服务器上进行自动版本化发布。

    我建议你阅读the blog post guide

    【讨论】:

    • 谢谢。我会试一试,但我想使用最少的工具,因为我仍在努力学习。该示例介绍了 Ansible,我不知道,需要花更多时间学习。此外 Dockerfile 使用 Ubuntu,这意味着最终图像可能非常大(虽然没有检查,但可能是错误的)。我非常感谢您的回复!作为初学者,虽然我认为应该有一种更简单的部署方式(尤其是使用 Docker,而不是 Heroku)
    • @Loading... 通常,对于 elixir 1.9,我们只使用 docker 来构建发布,因为 elixir 1.9 发布是自包含的,这意味着一旦发布发布,就可以打包并部署到一个目标,因此,您无需担心 Docker 最终映像的大小。在文章中,您可以找到elixir 1.9 发布和部署的主要原理。
    【解决方案2】:

    从 ENV 变量定义数据库 url

    config :hello_world, HelloWorld.Repo,
      url: "${DATABASE_URL}"
    

    然后将数据库 url 与 pool_size 一起传递给容器

    DATABASE_URL=ecto://postgres@db/hello_world?pool_size=10
    

    ENV REPLACE_OS_VARS=true 添加到您的Dockerfile(可能仅适用于 Distillery,否则用正确的形式替换 "${DATABASE_URL}" 以获取 ENV 变量在运行时,而不是在编译阶段)。

    以下是如何使用 Distillery 创建简单构建的示例:

    FROM elixir:1.9.0-alpine
    
    RUN mix local.hex --force && \
        mix local.rebar --force && \
        mix archive.install --force hex phx_new 1.4.8
    
    RUN apk add --update nodejs nodejs-npm
    
    ENV MIX_ENV=prod
    
    WORKDIR /srv/app
    
    COPY ./platform/ /srv/app/
    
    # install dependencies (production only)
    RUN mix local.rebar --force
    RUN mix deps.get --only prod
    RUN mix compile
    
    # RUN npm install --global webpack
    RUN cd assets && npm install && ./node_modules/webpack/bin/webpack.js --mode production
    RUN mix phx.digest
    
    RUN mix distillery.release
    
    # alpine version should be the same as build
    FROM alpine:3.9
    RUN apk add --update bash
    ENV REPLACE_OS_VARS=true
    WORKDIR /srv/app
    COPY --from=0  /srv/app/_build/prod/ .
    CMD rel/platform/bin/platform migrate && rel/platform/bin/platform foreground
    

    另外,请记住,由于您在本地运行 postgres(与容器不在同一网络上),因此您需要将其记入账户并使用 docker.for.mac.localhost 作为 postgres--net=host 的主机名docker runlocalhost 作为 postgres 的主机。

    【讨论】:

      【解决方案3】:

      看起来问题出现是因为您没有正确设置配置。

      异常:key :database not found

      为了使用您使用环境变量注入的数据库,您需要显式获取它。根据您组织配置的方式,您应该为您的 Repo 配置一个配置。默认情况下,repo 的配置是:

      config :yourserver_api, YourServer.Repo,
        username: "postgres",
        password: "postgres",
        database: "yourserver_api_prod",
        hostname: "localhost",
        pool_size: 10
      

      如果您确定会注入例如数据库,那么您需要将其更改为:

      database: System.get_env("DATABASE_URL") 
      

      【讨论】:

      • 我需要在config/prod.secret.exs 中指定这个database: System.get_env("DATABASE_URL") 吗?如果是这样,默认情况下它就在那里。为了清楚起见,我会将该文件添加到我的问题描述中。
      • 从错误代码看来,配置甚至没有被调用,你应该得到:缺少环境变量 DATABASE_URL。例如:ecto://USER:PASS@HOST/DATABASE
      • 要跳过这个错误并进入下一个错误,我必须进行额外的更改,我将添加到描述中
      猜你喜欢
      • 1970-01-01
      • 2020-04-26
      • 2017-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-16
      • 1970-01-01
      相关资源
      最近更新 更多