简体   繁体   English

如何使用 Phoenix Guides 中的示例在生产模式下运行 Elixir Phoenix Docker?

[英]How do I run Elixir Phoenix Docker in production mode using example from Phoenix Guides?

The example Dockerfile given on the Elixir Phoenix Guides appears to be outdated and broken. Elixir Phoenix Guides 上给出的示例 Dockerfile 似乎已经过时和损坏。 The example is found here: https://hexdocs.pm/phoenix/releases.html#containers该示例可在此处找到: https://hexdocs.pm/phoenix/releases.html#containers

I created a vanilla application like like so: mix phx.new hello_world我创建了一个像这样的香草应用程序: mix phx.new hello_world

The broken Dockerfile:坏掉的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

I am trying to run the Dockerfile from a vanilla Phoenix app install and have run into many issues including:我正在尝试从 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

I am no expert and so far the Dockerfile now look like this:我不是专家,到目前为止 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"]

I try to run the container with this command:我尝试使用以下命令运行容器:

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

But I get this error:但我得到这个错误:

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

This is all very new to me.这对我来说都是很新鲜的。 I have deployed to Heroku easily but would like the happy path using Docker to deploy to AWS, GCP, etc...我已经轻松部署到 Heroku 但希望使用 Docker 部署到 AWS、GCP 等的快乐路径...

Update 1: Here is the config/prod.secret.exs file.更新 1:这是config/prod.secret.exs文件。 I have not modified it.我没有修改它。 Please note it is loading environment variables:请注意它正在加载环境变量:

# 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.

Update 2:更新 2:

I removed import_config "config/prod.secret.exs" from config/prod.exs I renamed config/prod.secret.exs to config/releases.exs and in the file I changed use Mix.Config to import Config and uncommented config:hello_world, HelloWorldWeb.Endpoint, server: true我从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

Now when I try to run the container like so with port mapping and --network="host"现在,当我尝试使用端口映射和 --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

I get我明白了

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

Update 3: I have tried changing the DATABASE_URL but none worked.更新 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'

¯\_(ツ)_/¯

There is a good sample repo is configured for production-ready build and deploy cycle.为生产就绪的构建和部署周期配置了一个很好的示例存储库 It contains an ansible setup that will maintain docker image, build phoenix app in docker image, and do automated versioned releases on your production server.它包含一个 ansible 设置,它将维护 docker 映像,在 docker 映像中构建 phoenix 应用程序,并在您的生产服务器上执行自动版本化发布。

And I recommend you to read the blog post guide我建议您阅读博客文章指南

Define database url from ENV variable从 ENV 变量定义数据库 url

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

Then use the database url with pool_size as you pass it to container然后将数据库pool_size与 pool_size 一起使用,并将其传递给容器

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

Add ENV REPLACE_OS_VARS=true to your Dockerfile (may only work with Distillery, otherwise substitute "${DATABASE_URL}" with correct form to get ENV variable at runtime , not on compiling stage).ENV REPLACE_OS_VARS=true添加到您的Dockerfile (可能仅适用于 Distillery,否则用正确的形式替换"${DATABASE_URL}"在运行时获取 ENV 变量,而不是在编译阶段)。

Here is example how to create simple build with Distillery:以下是如何使用 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

Also, keep in mind since you have postgres running locally (not on the same network as container), you need to take it into accout and use docker.for.mac.localhost as the host name for postgres or --net=host for docker run and localhost as the host for postgres .另外,请记住,由于您在本地运行postgres (与容器不在同一网络上),您需要将其纳入帐户并使用docker.for.mac.localhost作为postgres的主机名或--net=host docker runlocalhost作为postgres的主机。

Looks like the problem appears because you did not set the config correctly.看起来问题出现是因为您没有正确设置配置。

exception: key:database not found异常:键:找不到数据库

In order to use the database that you injected using environment variables you need to explicitly get it.为了使用您使用环境变量注入的数据库,您需要显式获取它。 Depending on how you organized your config, you should have a config for your Repo.根据您组织配置的方式,您应该为您的 Repo 配置一个配置。 By default the config for repo is:默认情况下,repo 的配置是:

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

If you know for sure that for example database will be injected, then you need to change it to:如果您确定将注入例如数据库,那么您需要将其更改为:

database: System.get_env("DATABASE_URL") 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM