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