Search code examples
dockerelixirelixir-mix

Elixir release inside Docker container without Rabbit MQ Connection


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,


Solution

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