4

I'm studying Docker and how to properly publish my containers coding a very simple web application. It has only two containers, backend and frontend. I've published the source code here: https://github.com/sinayra/hello-private-world

If I run locally each node application, its works "fine" (everything is public, but this is not what I want in the end of the day). Then, I created Dockerfile for each container, to try to simulate a real application. For the frontend, I exposed the port 3000 to access from the host. However, I don't want to expose the port 3333 for the host, and, because of that, my front can't fetch the data from the back.

Here is my index.html from the frontend.

<html>
  <head>
    <title>Hello Public World</title>
  </head>
  <body>
    <h1>Message from backend:</h1>
    <p id="backend"></p>

    <script>
      const back = document.getElementById("backend");
      const url = "http://localhost:3333/"; //I tried backend:3333, but without success

      fetch(url)
        .then((resp) => resp.json()) // Transform the data into json
        .then(function (data) {
          back.innerHTML = data.message;
        });
    </script>
  </body>
</html>

Its Dockerfile:

# pull official base image
FROM node:lts

# set working directory
WORKDIR /usr/src/app

# install app dependencies
COPY package.json ./
RUN npm install

# add app
COPY . ./

EXPOSE 3000

# start app
CMD ["npm", "run", "start"]

My index.js from backend:

const express = require("express");
const app = express();
const cors = require("cors");
const port = 3333;

const allowlist = ["http://localhost:3000"];
const corsOptions = {
  origin: allowlist,
  methods: "GET",
  preflightContinue: false,
  optionsSuccessStatus: 204,
};
app.use(cors(corsOptions));

app.use(express.json());
app.get("/", (req, res) => {
  console.log("GET /");
  res.json({ message: "Hello Private World!" });
});

app.listen(port, () =>
  console.log(`Example app listening at http://localhost:${port}`)
);

Its Dockerfile:

# pull official base image
FROM node:lts

# set working directory
WORKDIR /usr/src/app

# install app dependencies
COPY package.json ./
RUN npm install

# add app
COPY . ./

#EXPOSE 3333

# start app
CMD ["npm", "run", "start"]

And the Docker compose file

version: "3.8"
services:

  backend:
    image: helloworld-private
    build:
      context: ./backend
      dockerfile: Dockerfile
    working_dir: /usr/src/app
    container_name: "express_container"
    volumes:
      - ./backend/:/usr/src/app
      - /usr/src/app/node_modules
    networks: 
      - webappnetwork
    expose:
      - "3333"

  web:
    image: helloworld-public
    build:
      context: ./web
      dockerfile: Dockerfile
    working_dir: /usr/src/app
    container_name: "liveserver_container"
    links:
      - backend
    volumes:
      - ./web/:/usr/src/app
      - /usr/src/app/node_modules
    environment:
      - PORT=3000
    networks: 
      - webappnetwork
    ports: 
      - "3000:3000"

networks:
  webappnetwork:
    driver: bridge

Is there any abstraction layer that I'm missing?

  • Side Note: I see what you did there with that title phrasing. ;) – J. Scott Elblein Sep 3 at 17:34
  • omg... I swear it was unintentional – Sinayra Sep 4 at 18:07
2

The layer that you're missing is nginx proxy. See my toy Mafia Game project here how to do it:

The missing piece in your case is nginx proxy that you use in your UI.

So what you need to do is to define some location for your api - i.e. /api. Then your proxy it via nginx - see my nginx configuration for the project above here - https://github.com/taleodor/mafia-vue/tree/master/nginx. At the same time it's highly recommended for performance to deploy static resources on the same nginx.

Use Dockerfile to define environment variables and defaults, i.e. in my project - https://github.com/taleodor/mafia-vue/blob/master/Dockerfile

Finally, you can update configuration via environment variables in your docker-compose yaml or k8s definitions.

| improve this answer | |
  • I updated my repository with this solution and everything seems nice now. But, just one more question, what are these configurations that can be updated with environment variables in docker-compose that you suggested? Is there any guide that I could follow? – Sinayra Jul 2 at 18:50
  • You don't have to parametrize anything right away. But as you go you may notice certain things that need parametrizing - so for those things you may create env variables. Personally, I usually always parametrize host names and ports for upstreams - because name conventions may be sometimes different between local docker, docker-compose and k8s. Sometimes you will find other things later to parametrize - but no hard rules here. + Rarely, there may be more complex cases, where you can include more sophisticated templating engine if needed. – taleodor Jul 2 at 21:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.