2

I have created the build using command MIX_ENV=prod mix release --env=prod --verbose. It successfully creates the build and I can able to run the console and ping commands and give me the pid. Even when I run the start command it successfully started but when I go to htttp://localhost:4000, the server does not run. When I run _build/prod/rel/project/bin/project foreground command it just hangs in there with no output.

I am using MacOS version: 10.13.2, elixir: 1.6.5 (compiled with OTP 19), otp: Erlang/OTP 20. Here is the log

$ 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

I have already included all the application that shows as warning except jsx because it shows an error of undefined application.

I have also gone through the distillery issue https://github.com/bitwalker/distillery/issues/276 and as this issue suggest I checked my app name and server is true set in the config file so it did not help. I have also logged issue here https://github.com/bitwalker/distillery/issues/433 in more detail.

Here is my release file config

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

When I tried to create a new phoenix application and do the same it runs properly and listen to port 4000 and output foreground command but with the same configuration in my application, it does not listen 4000 port and hangs on foreground command. When I see the netstat for both it seems that 4000 port is not running for my app see enter image description here

I am not sure how else I should debug this problem I tried all things I could. Please let me know if anyone needs any more information. I would appreciate any help/suggestion in this regard.

EDIT: Here are my config and prod files.I just pasted the endpoint detail let me know if anything else needed.

# 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
punitcse
  • 717
  • 7
  • 27
  • Can you post your ```config/config.exs``` and your ```config/prod.exs``` files? It's possbile something there like the host, port, etc isn't being grabbed. – The Brofessor Jul 19 '18 at 01:38
  • @TheBrofessor I have added the config and prod.exs files. I will check with changing the hostname to localhost and port. Do you suggest anything else? – punitcse Jul 19 '18 at 07:13
  • @TheBrofessor I tried with removing `url` option in `prod.exs` but still nothing happened it does not listen to port 4000. I have tried my config and prod same as the new app (which is working on 4000) but nothing works. Any suggestion? – punitcse Jul 19 '18 at 07:38
  • If you comment out the ```prod.exs``` ```url``` line, and rebuild the release, does ```foreground``` still just hang with no output? also, is your ```secret_key_base``` a string literal in the config or is it pulled from an environment variable in some way? This looks pretttyyy close so its gotta be something small – The Brofessor Jul 19 '18 at 15:00
  • @TheBrofessor Yes I tried to comment url in prod.exs and run then still `foreground` just hang with no output. The secret_key_base is string I just write it here like this for security reason but its something like `"hDq+GvvUL88RGa4oiTHODlb0oAQcGu041l9tF2DxEYzjDS0TyHQt7FxVI1RuDBIQ"` – punitcse Jul 19 '18 at 15:07
  • 2
    If you have the ability to put up a repo that can be used to replicate I'd be happy to try and debug it. If not no worries. At the risk of just more speculation, if ```foreground``` has no output at all, its likely the Endpoint isn't initializing (Or else you'd see ```Trackbees.Endpoint with Cowboy``` in the output. You'd also see something logged if the VM crashed etc. I'd try and see if the configuration of the endpoint is correct or broken, by inserting an ```IO.inspect``` in the init function of ```lib/trackbees_web/endpoint.ex``` and logging what ```config``` outputs. – The Brofessor Jul 20 '18 at 00:22
  • Sorry I won't be able to put the repo. I will check the endpoint and let you know the result. thanks for helping out. – punitcse Jul 21 '18 at 07:55
  • @TheBrofessor I tried to create an `init` method in endpoint and print the config like ```def init(:supervisor, config) do IO.inspect("===============endpoint========") IO.inspect(config) {:ok, config} end``` but it did not call the method. How can I print the config in endpoint. Can you please elaborate I am not very proficient in phoenix. – punitcse Jul 21 '18 at 10:59
  • @TheBrofessor I found one intresting thing that when I run the console(`_build/prod/rel/trackbees/bin/trackbees console`) and in that I run `Trackbees.Endpoint.start_link` the server starts and listen 4000 port. Before running the `start_link` if I run `Trackbees.Endpoint.config(:url)` it gives me an error `** (ArgumentError) argument error (stdlib) :ets.lookup(Trackbees.Endpoint, :url) (trackbees) lib/phoenix/endpoint.ex:481: Trackbees.Endpoint.config/2` after running `start_link` it return succesfully `Endpoint.config(:url) [path: "/", host: "localhost"]` – punitcse Jul 21 '18 at 11:54
  • 1
    Oh awesome. Pretty sure you’re last debug step gives us the answer. I’ll submit one so it has a thorough explanation and we can go from there. – The Brofessor Jul 21 '18 at 18:05
  • Sure @TheBrofessor, I am looking forward to it. I really appreciate your help throughout this. Many thanks – punitcse Jul 21 '18 at 18:15

1 Answers1

1

Alrighty so based on the debugging steps in the comments I think our answer lies in the difference between configuration when using the development phoenix server, and the release itself.

The key reason configuration works differently is that the development server is most often intended to run locally on your machine - so it's natural for it to run off of environment variables present on the dev machine, and to have configuration available in the local runtime.

However, releases are intended to run on remote machines, often on machines physically or virtually distinct from the machine the release was built on.

This means that features that depend heavily on runtime configuration have to be handled differently in the release. (Several crucial values of Phoenix.Endpoint like :port, :host, etc fall under this)

There have been several different best-practices or patterns to use to bridge this difference in configuration as the use of releases has evolved in Elixir thanks largely to Distillery. I think where the community is at today is a really healthy place.

When a release is compiled, it doesn't have access to the runtime of the machine it will eventually run on. Therefore, if a module needs runtime configuration, its a good approach to delegate that config to the Supervisor that boots the module. At the time the Supervisor initializes the module, its been booted on the remote machine and now has access to that remote environment.

There are two modules I almost always configure when the supervisor starts: Ecto's Repo and Phoenix's Endpoint.

You can actually tell Endpoint to configure itself from the remote machine's runtime by setting the load_from_system_env value to true. You'll need to make sure values for PORT HOST are set in the environment, via export PORT=4000 and export HOST=localhost or sourcing a .env file, and this will result in the same behavior on your local machine when running the release and the remote machine you eventually run the release on (assuming both machines have the same environment values):

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

One of the roles of Phoenix.Endpoint is to act as a wrapper for starting and stopping the endpoint as part of the Supervision tree. It provides an init/2 callback as part of this wrapping strategy. You'll see that when init/2 fires, it will pattern match on :load_from_system_env:

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

I think this is the specific solution to the issue that Trackbees is having right now. I'd recommend adding the load_from_system_env config value, and probably inserting a debug statement like IO.inspect above the if statement to verify the value is getting through.

FYI, in case you're using Ecto as well, there's a matching init wrapper that you can use in a similar way if you want too:

# 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
The Brofessor
  • 2,371
  • 17
  • 13
  • I have tried to set `load_from_system_env: true`. but it still has not listening port 4000. I also added the `init` method and `IO.inspect` like ` def init(_key, config) do IO.inspect("...................configs..................................") IO.inspect(config) if config[:load_from_system_env] do end end` But it does not show any output both when I run `mix phoenix.server` or build release or run start or console anything. I think it somehow not starting the Endpoint – punitcse Jul 22 '18 at 06:50
  • Oops I forgot, to mention you'll need to make sure ```PORT``` is exported to your environment. I edited the answer. You also shouldn't need to add an init method, it will be at the bottom of the ```endpoint.ex``` file in your Phoenix project already, and you'll just want to insert the ```IO.inspect``` function inside the already existing init. – The Brofessor Jul 22 '18 at 18:31
  • I tried as you said it above comment but it still is not listening port 4000. I don't think the problem is with not taking the port or host because I already tried by giving the static value 4000 and localhost directly on the `prod.ex` file. What I understood the problem is it is not calling the Endpoint.start_link while in the release build. Let me know if we can discuss on slack, I think it should be very minor.I pinged you on https://elixir-lang.slack.com/ – punitcse Jul 23 '18 at 06:58
  • Also In my endpoint file there's no `init` method https://gist.github.com/punitcse/fbd472b0bdedc974f6d5d16a26cdd827 – punitcse Jul 23 '18 at 07:04
  • Eek, it must be an older version of Phoenix. You'll need to check that versions docs/source to see where to inspect or configure the endpoint – The Brofessor Jul 26 '18 at 00:08