Trying to do a multistage docker deployment for my Phoenix api but keep getting a 502 gateway error

I am trying to do a multistage docker deployment for my phoenix api. Somehow I keep getting a 502 gateway error and I don’t know why.

Here’s the thing, the logs on aws elastic beanstalk are so confusing that I’m lost. I don’t know where this crashed. Here is one example of the log:

The database for MyApi.Repo has already been created
01:38:45.895 [info] Already up
01:38:46.303 [info] Running MyApiWeb.Endpoint with cowboy 2.6.1 at http://api
01:38:46.308 [error] Could not find static manifest at "/app/_build/prod/lib/my_api/priv/static/cache_manifest.json". Run "mix phx.digest" after building your static files or remove the configuration from "config/prod.exs".
02:50:54.671 [info] SIGTERM received - shutting down

Currently I’m doing a multi-container deployment using docker and aws. It consists of a container for my react frontend, a container for my phoenix backend and a container for my nginx server that sits in front of my other two containers and routes them accordingly.

Here is the configuration for my nginx server:

upstream client {
  server client:3000;
}

upstream api {
  server api:4000;
}

server {
  listen 80;

  location / {
    proxy_pass http://client;
  }

  location /sockjs-node {
    proxy_pass http://client;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
  }

  location /api {
    rewrite /api/(.*) /$1 break;
    proxy_pass http://api;
  }
}

What this is saying is that whenever my endpoint has a /api at the back, send it to the api with port 4000. I know this works because my non-multi-stage docker build version actually works.

Now i’ve set up a multi-stage build for my phoenix api container

I tried doing this:

*# Dockerfile*

FROM bitwalker/alpine-elixir-phoenix:latest as build

*# prepare build dir*

WORKDIR /app

*# set build ENV*

ENV MIX_ENV=prod

*# install mix dependencies*

COPY mix.exs mix.lock ./

*# COPY config ./*

*# COPY deps ./*

RUN mix do deps.get, deps.compile

*# build release*

COPY . .

RUN mix release --env=prod

*# prepare release image*

FROM alpine:3.6

RUN apk add --update bash openssl

WORKDIR /app

COPY --from=build /app/_build/prod/rel/my_api ./

ENV REPLACE_OS_VARS=true

ENV PORT=4000

EXPOSE $PORT

CMD /app/bin/my_api foreground

Using this I’ve been getting consistent 502 errors. I specified ENV REPLACE_OS_VARS=true as mentioned in this tutorial: https://medium.com/polyscribe/a-complete-guide-to-deploying-elixir-phoenix-applications-on-kubernetes-part-1-setting-up-d88b35b64dcd

I do this because of this reason in the tutorial:

An important caveat is that when Distillery builds our release, the config/* files are evaluated at compile time, not run time. This means we can’t just use System.get_env/1 to get the environment variable value since it’ll pull the value out of the environment on our development machine. Instead, we’re going to use template strings to which will automatically be replaced by environment variables at run-time as long as the environment variable REPLACE_OS_VARS=true is set.

Is this enough detail? If not, let me know what else I can provide here.

2 Likes

You need to specify an IP address in your endpoint configuration, not a name.

2 Likes

do you mean this section of my prod.exs file?

config :maisie_api, MyApiWeb.Endpoint,
  http: [:inet6, port: System.get_env("PORT") || 4000],
  url: [host: "api", port: 80],
  cache_static_manifest: "priv/static/cache_manifest.json",
  code_reloader: false,
  server: true

As I understand, it should not be “api” but System.get_env(“HOST”) where HOST is the endpoint.

Since this is a multi-container deployment. What should the HOST be? the elastic beanstalk url? the upstream endpoint specified in my nginx conf file? Please elaborate which ip address to specify

Here is my nginx conf file

upstream client {
  server client:3000;
}

upstream api {
  server api:4000;
}

server {
  listen 80;

  location / {
    proxy_pass http://client;
  }

  location /sockjs-node {
    proxy_pass http://client;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
  }

  location /api {
    rewrite /api/(.*) /$1 break;
    proxy_pass http://api;
  }
}

This line of you log made me think you had something like http: [ip: "api"] in your config. Perhaps I got confused a bit…

Anyway, a 502 usually means, that something is wrong between your nginx and phoenix.

Please verify first whether or not you are able to access your phoenix from its own host, then try if you can reach it by IP from your nginx’ host, then by name.

1 Like

Got it to work without distillery. turns out the dockerfile just needs to run a command mix compile before running the rest of the commands