Reputation: 2259
I'm trying to follow along with this tutorial to get my (functioning on localhost) elixir/phoenix app running in a docker container and I'm running into difficulties.
https://pspdfkit.com/blog/2018/how-to-run-your-phoenix-application-with-docker/
Here is my error:
[info] JOIN "room:lobby" to AlbatrossWeb.RoomChannel
phoenix_1 | Transport: Phoenix.Transports.WebSocket (2.0.0)
phoenix_1 | Serializer: Phoenix.Transports.V2.WebSocketSerializer
phoenix_1 | Parameters: %{}
phoenix_1 | inside room:lobby channel handler
phoenix_1 | [info] Replied room:lobby :ok
phoenix_1 | [error] Ranch protocol #PID<0.403.0> of listener AlbatrossWeb.Endpoint.HTTP (cowboy_protocol) terminated
phoenix_1 | ** (exit) exited in: Phoenix.Endpoint.CowboyWebSocket.resume()
phoenix_1 | ** (EXIT) an exception was raised:
phoenix_1 | ** (Protocol.UndefinedError) got FunctionClauseError with message "no function clause matching in Poison.Encoder.__protocol__/1" while retrieving Exception.message/1 for %Protocol.UndefinedError{description: "", protocol: Poison.Encoder, value: ["127", "127", "room:lobby", "phx_reply", %{response: %{}, status: :ok}]}
phoenix_1 | (poison) lib/poison/encoder.ex:66: Poison.Encoder.impl_for!/1
phoenix_1 | (poison) lib/poison/encoder.ex:69: Poison.Encoder.encode/2
phoenix_1 | (poison) lib/poison.ex:41: Poison.encode!/2
phoenix_1 | (phoenix) lib/phoenix/transports/v2/websocket_serializer.ex:22: Phoenix.Transports.V2.WebSocketSerializer.encode!/1
phoenix_1 | (phoenix) lib/phoenix/transports/websocket.ex:197: Phoenix.Transports.WebSocket.encode_reply/2
phoenix_1 | (phoenix) lib/phoenix/endpoint/cowboy_websocket.ex:77: Phoenix.Endpoint.CowboyWebSocket.websocket_handle/3
phoenix_1 | (cowboy) /app/deps/cowboy/src/cowboy_websocket.erl:588: :cowboy_websocket.handler_call/7
phoenix_1 | (phoenix) lib/phoenix/endpoint/cowboy_websocket.ex:49: Phoenix.Endpoint.CowboyWebSocket.resume/3
phoenix_1 | (cowboy) /app/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4
phoenix_1 | [info] JOIN "room:lobby" to AlbatrossWeb.RoomChannel
<....repeat forever....>
I'm not sure what is going on.
My room lobby is simply a socket channel defined room_channel.ex as:
###room_channel.ex###
defmodule AlbatrossWeb.RoomChannel do
use Phoenix.Channel
def join("room:lobby", _message, socket) do
IO.puts "inside room:lobby channel handler"
{:ok, socket}
end
def join("room:" <> _private_room_id, _params, _socket) do
{:error, %{reason: "unauthorized"}}
end
def handle_in("updated_comments", %{"payload"=>payload}, socket) do
IO.puts("inside updated_comments handle_in")
broadcast! socket, "updated_comments", payload
# ArticleController.retrieve(socket)
{:noreply, socket}
end
end
###room_channel.ex###
It runs fine when I run this without my docker files - what I added is the following:
###run.sh###
docker-compose up --build
###run.sh###
###Dockerfile###
FROM elixir:latest
RUN apt-get update && \
apt-get install -y postgresql-client
# Create app directory and copy the Elixir projects into it
RUN mkdir /app
COPY . /app
WORKDIR /app
# Install hex package manager
RUN mix local.hex --force
# Compile the project
RUN mix do compile
CMD ["/app/entrypoint.sh"]
###Dockerfile###
###docker-compose###
# Version of docker-compose
version: '3'
# Containers we are going to run
services:
# Our Phoenix container
phoenix:
# The build parameters for this container.
build:
# Here we define that it should build from the current directory
context: .
environment:
# Variables to connect to our Postgres server
PGUSER: postgres
PGPASSWORD: postgres
PGDATABASE: db
PGPORT: 5432
# Hostname of our Postgres container
PGHOST: db
ports:
# Mapping the port to make the Phoenix app accessible outside of the container
- "4000:4000"
depends_on:
# The db container needs to be started before we start this container
- db
db:
# We use the predefined Postgres image
image: postgres:9.6
environment:
# Set user/password for Postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
# Set a path where Postgres should store the data
PGDATA: /var/lib/postgresql/data/pgdata
restart: always
volumes:
- pgdata:/var/lib/postgresql/data
# Define the volumes
volumes:
pgdata:
###docker-compose###
###entrypoint.sh###
#!/bin/bash
while ! pg_isready -q -h $PGHOST -p $PGPORT -U $PGUSER
do
echo "$(date) - waiting for database to start"
sleep 2
done
# Create, migrate, and seed database if it doesn't exist.
if [[ -z `psql -Atqc "\\list $PGDATABASE"` ]]; then
echo "Database $PGDATABASE does not exist. Creating..."
createdb -E UTF8 $PGDATABASE -l en_US.UTF-8 -T template0
echo "1"
mix do ecto.drop, ecto.create
echo "2"
mix phx.gen.schema Binarys binary postnum:integer leftchild:integer rightchild:integer downvotes:integer message:string parent:string upvotes:integer
echo "3"
mix phx.gen.schema Comments comment postnum:integer children:map downvotes:integer message:string parent:string upvotes:integer identifier:uuid
echo "4"
mix ecto.migrate
echo "5"
mix run priv/repo/seeds.exs
echo "Database $PGDATABASE created."
fi
exec mix phx.server
###entrypoint.sh###
I also changed the config in my dev.exs file like this:
###dev.exs###
config :albatross, Albatross.Repo,
adapter: Ecto.Adapters.Postgres,
username: "postgres",
password: "postgres",
hostname: "db",
database: "db",
# port: 5432,
pool_size: 10
###dev.exs###
Interestingly all of these errors seem to spawn when my frontend is up, but not making requests (other than connecting to the socket). If I try and make an http request I get this:
phoenix_1 | [info] POST /addComment
phoenix_1 | inside addComment
phoenix_1 | [debug] Processing with AlbatrossWeb.PageController.addComment/2
phoenix_1 | Parameters: %{"payload" => %{"message" => "sf", "parent" => "no_parent", "postnum" => 6, "requestType" => "post", "urlKEY" => "addComment"}}
phoenix_1 | Pipelines: [:browser]
phoenix_1 | [error] Failure while translating Erlang's logger event
phoenix_1 | ** (Protocol.UndefinedError) got FunctionClauseError with message "no function clause matching in Plug.Exception.__protocol__/1" while retrieving Exception.message/1 for %Protocol.UndefinedError{description: "", protocol: Plug.Exception, value: %Protocol.UndefinedError{description: "", protocol: Plug.Exception, value: %Protocol.UndefinedError{description: "", protocol: Plug.Exception, value: %Protocol.UndefinedError{description: "", protocol: String.Chars, value: %Postgrex.Query{columns: nil, name: "", param_formats: nil, param_oids: nil, param_types: nil, ref: nil, result_formats: nil, result_oids: nil, result_types: nil, statement: ["INSERT INTO ", [34, "comment", 34], [], [32, 40, [[[[[[[[[[], [34, "children", 34], 44], [34, "downvotes", 34], 44], [34, "identifier", 34], 44], [34, "message", 34], 44], [34, "parent", 34], 44], [34, "postnum", 34], 44], [34, "upvotes", 34], 44], [34, "inserted_at", 34], 44], 34, "updated_at", 34], ") VALUES ", [], 40, [[[[[[[[[[], [36 | "1"], 44], [36 | "2"], 44], [36 | "3"], 44], [36 | "4"], 44], [36 | "5"], 44], [36 | "6"], 44], [36 | "7"], 44], [36 | "8"], 44], 36 | "9"], 41], [], " RETURNING ", [], 34, "id", 34], types: nil}}}}}
phoenix_1 | (plug) lib/plug/exceptions.ex:4: Plug.Exception.impl_for!/1
phoenix_1 | (plug) lib/plug/exceptions.ex:19: Plug.Exception.status/1
phoenix_1 | (plug) lib/plug/adapters/translator.ex:79: Plug.Adapters.Translator.non_500_exception?/1
phoenix_1 | (plug) lib/plug/adapters/translator.ex:49: Plug.Adapters.Translator.translate_ranch/5
phoenix_1 | (logger) lib/logger/erlang_handler.ex:104: Logger.ErlangHandler.translate/6
phoenix_1 | (logger) lib/logger/erlang_handler.ex:97: Logger.ErlangHandler.translate/5
phoenix_1 | (logger) lib/logger/erlang_handler.ex:30: anonymous fn/3 in Logger.ErlangHandler.log/2
phoenix_1 | (logger) lib/logger.ex:861: Logger.normalize_message/2
phoenix_1 | (logger) lib/logger.ex:684: Logger.__do_log__/3
phoenix_1 | (kernel) logger_backend.erl:51: :logger_backend.call_handlers/3
phoenix_1 | (kernel) logger_backend.erl:38: :logger_backend.log_allowed/2
phoenix_1 | (ranch) /app/deps/ranch/src/ranch_conns_sup.erl:167: :ranch_conns_sup.loop/4
phoenix_1 | (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
phoenix_1 |
So you can see that it sees the request and it appears to be manipulating it. It just can't return it. I have my ports exposed in both my Docker and docker-compose files, I really can't see what else could be going wrong as I have this app working when I run it outside the docker containers.
What is going wrong?
Upvotes: 0
Views: 1006
Reputation: 62
Sorry to add this as an answer but I am facing some problems while trying to create a docker container for an Elixir/Phoenix app, I am only creating a REST API (no html) typoe of project and it works perfectly locally but when I create the docker image and container and I run the container, the app runs perfectly but when I try from postman I always get an error saying socket hang up
which is not so clear, please check below the errors from postman.
picture socket hang up error 1 picture socket hang up error 2
This is my Dockerfile:
FROM elixir:alpine
RUN mkdir /app
COPY . /app
WORKDIR /app
RUN apk update && apk add inotify-tools
RUN mix local.hex --force && mix local.rebar --force
RUN mix do deps.get, deps.compile
EXPOSE 4000
CMD ["mix", "phx.server"]
You have to know that I am trying to be the simplest as I can since I am learning elixir and phoenix yet and I want to keep things clear for me so I don't have any networking or security with docker-compose or any other integration. I just want to run my REST API made in Elixir/Phoenix inside a docker container.
Upvotes: 0
Reputation: 24
Not enough reputation to reply to other answer, but I wanted to inform potential readers that the EXPOSE instruction is nothing but documentation. It is not necessary to expose a port before publishing it.
From the official docker documentation:
The EXPOSE instruction does not actually publish the port. It functions as a type of documentation between the person who builds the image and the person who runs the container, about which ports are intended to be published. To actually publish the port when running the container, use the -p flag on docker run to publish and map one or more ports, or the -P flag to publish all exposed ports and map them to high-order ports.
Upvotes: 0
Reputation: 3748
I think the problem lies in your Dockerfile.
You didn't expose any port.
To be able to publish port, you need to first expose the post.
Try adding EXPOSE 4000
in your Dockerfile.
Upvotes: 1