3

I am very new to Elixir. I have built an app which runs locally, which works fine. But now i need to build a container for it with Docker.

However, every attempt to do a release seems to try and connection to RabbitMQ (which is running locally, as a Docker Container).

I don't want, cant have it try and connect to Rabbit each time this container is built, as it will need to be built by a CI / CD pipeline and will never have access to any Rabbit. I have set it up with an ENV, but this needs to be set within my YAML when deploying to my k8s cluster.

So this is the Dockerfile:

# Container Base Image
FROM elixir:1.13.1-alpine as release_build

# Set Working Folder
WORKDIR app

#ENV RABBIT_CONNECTION=""

# Copy Source Code Into Container
COPY ./src/app ./

# Install Hex Package Manager
RUN mix local.hex --force

# Install Rebar
RUN mix local.rebar --force

# Get All Deps
RUN mix deps.get

RUN mix deps.compile

# Compile Application
RUN mix compile

So has you can see i have a ENV set with the Rabbit Connection URL (commented out currently). In this state i get,

The following arguments were given to AMQP.Connection.open/1:

If i set it, but only to an empty string, i get,

no match of right hand side value: {:error, {{:unable_to_parse_uri, :no_scheme}, []}}

I also tried with the valid URL of my locally running container, but has it was using localhost and on a different Docker network, that just returned an econnrefused error.

This is how I am connecting to Rabbit in my app,

rabbit_url = Application.fetch_env!(:rabbit, :url)

# Open Connection To Rabbit
{:ok, connection} = AMQP.Connection.open(rabbit_url)
{:ok, channel} = AMQP.Channel.open(connection)

This is the rabbit config file section,

config :rabbit, url: System.get_env("RABBIT_CONNECTION")

I made a little local Bash script to boot this,

RABBIT_CONNECTION='amqp://admin:password@localhost:5672' iex -S mix

This script works fine with the application to boot and connection to my locally running container for RabbitMQ

So I know there must be a way of getting the code to do a release without an connections, either to Rabbit or a Database or something similar like that.

Any help is most welcome

Thanks,

1
  • OK, i understand making the ENV read at runtime, any ideas on bests ways to do that? Commented Jan 14, 2022 at 16:51

2 Answers 2

3

I have tried to create a project such as yours.

mix new broker_client

In the mix.exs application function is configured to run the start function of my module.

def application do
  [
    extra_applications: [:logger],
    mod: {BrokerClient, []}
  ]
end

Additionally, in config/runtime.exs, I am configuring amqp with the connection and one or more channels as documented here.

import Config

config :amqp,
  connections: [
    myconn: [url: System.get_env("BROKER_URL")]
  ],
  channels: [
    mychan: [connection: :myconn]
  ]

In lib/broker_client.ex I have the start function implemented which creates a simple Task as showing in this answer.

defmodule BrokerClient do
  def sample() do
    {:ok, chan} = AMQP.Application.get_channel(:mychan)
    :ok = AMQP.Basic.publish(chan, "", "", "Hello")
    Process.sleep(1000 * 10)
  end

  def start(_type, _args) do
    IO.puts("starting...")
    Task.start(fn -> sample() end)
  end
end

I can build this fine without having rabbitmq running locally or setting the variable broker.

FROM elixir as builder
WORKDIR /app
RUN mix local.hex --force && mix local.rebar --force
COPY mix.exs mix.lock ./
RUN mix deps.get --only prod
COPY  ./ .
RUN MIX_ENV=prod mix release

FROM debian:stable-slim
ENV LANG="C.UTF-8" LC_AL="C.UTF-8" PATH="/app/bin:$PATH"
COPY --from=builder /app/_build/prod/rel/broker_client /app
CMD [ "broker_client", "start"]

Now I can run this with docker-compose as example.

version: '3.9'

services:
  client:
    build: ./
    environment:
      BROKER_URL: 'amqp://guest:guest@rabbitmq'
    # sleep 10 seconds to give the broker time to start
    command: [ "sh", "-c", "sleep 10 && broker_client start" ]
    depends_on:
      - rabbitmq
  rabbitmq:
    image: rabbitmq

Its probably also useful to look at the offical docs for Application.

Sign up to request clarification or add additional context in comments.

5 Comments

So this project was setup with (please forgive me if I am wrong, still learning) a supervisor? I thought it was best not to, unless needed for something?
also this helps a lot, the code for connection to Rabbit was outside of my module, so it would just connection to rabbit when lunched, i have now moved into into its own function - is it common please to name these functions as start?
I think its not really using a supervisor. Although using one may be better. I dont really know elixir tbh. Maybe it would make to create the project with mix new --sup. If you want to run the code like this you have to implement the start method of the Application, I have most of the things here from the documentation, including the start method hexdocs.pm/elixir/1.13.2/…
This does work, at lest for me for local - i see if its stable or needs a supervisor (which i am still trying to get my head around). The app is very simple, it just makes html files from data sent into Rabbit.
I have used a Task now and also improved the amqp config.
1

You need to use the runtime configuration, otherwise the configuration must be passed at compile-time, to do this all you have to do is add runtime.exs and to get the environment variable there:

config :rabbit, url: System.get_env("RABBIT_CONNECTION")

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.