0

I'm trying to decide whether it's better to run nginx as its own container, not containing the client app on it. Then run another container that is the client app. And a third will be a container serving a backend API

I found that trying to combine nginx with a client app's code in a Dockerfile or on the same nginx container instance is not very clean. You end up having to use multi-builds in Docker or some other way to first dockerize your client app before you run nginx over it. So thinking about splitting this out into 2 separate containers to keep it clean.

So I'll end up with 3 Dockerfiles and then use Docker compose to run them:

nginx.Dockerfile (Will use the nginx image from dockerhub.  Is it possible to tell nginx.conf to point to index.html of my client container via localhost resolution somehow and not look in `/usr/share/nginx/html`?)
client.Dockerfile (ReactJS front-end, dist code from webpack)
backend.Dockerfile (server - RESTful or GraphQL Data API)

Goal:

I'd prefer not to mix/have my client app code in the nginx container. I'd like to decouple that from the nginx container...which is why I'm saying I want 3 containers here to keep things decoupled from one another. I realize my nginx.conf will need to find where index.html is of my client app but I want to do so not in the same container.

Put another way, I'm wondering if there's a way to tell nginx.config via localhost to find index.html over say port 80 which is the port my separate client container will be exposing that file from. So instead of having nginx find index.html in /usr/share/nginx/html (same container with client app code) can I tell it to look through localhost:80 which is from my client app container? I want my nginx container to run independently of the client code. Or is that completely not possible and I must copy my client app code into /usr/share/nginx/html and that's the only way, they must live together in the same image and my dreams of decoupling don't make sense?

My client app will still have an expressJS server but just for serving static assets. My backend will be another express API for serving data back up to the client.

PositiveGuy
  • 17,621
  • 26
  • 79
  • 138
  • 1
    Yes, you can keep the nginx Dockerfile separate and then build the client App using it as base Image, I don't know about your exact need to have the nginx file, but you can use official docker hub image of nginx, so in that case you won't have to have a Dockerfile for nginx, and you can base your client App directly on that official image, in client.Dockerile, you can simply overwrite the nginx config. https://hub.docker.com/_/nginx – Atif Saddique Sep 14 '20 at 05:29
  • 3
    This is the exact purpose of docker compose and will also allow you to rebuild separately – sam Sep 14 '20 at 05:38
  • I've been able to build separately using github actions but will still move to docker compose – PositiveGuy Sep 14 '20 at 05:53
  • @AtifSaddique I don't want to base my client app directly off the nginx container because it doesn't have yarn, npm, etc. on it and I end up with a messy Dockerfile because I have to install that with apt. Is there therefore a way to tell `nginx.conf` living in one container to use index.html of another running container from `nginx.conf` by referencing that index.html living in my client container via localhost? – PositiveGuy Sep 14 '20 at 05:59

1 Answers1

2

This setup is fine, assuming running the extra Web server doesn't bother you. I'd probably also base the "client" image off of Nginx, but the only thing it would do is to serve static content from the dist directory that Webpack builds (so it would be a multi-stage build where the final stage doesn't include Node or any of the build tools).

In the "nginx" container, you'd use the proxy_pass directive to forward requests to one container or the other based on URL matching.

location = / {
  return /ui/                  # 302 redirect
}
location /ui/ {
  proxy_pass http://client/    # host name matches a Compose service
}
location /api/ {
  proxy_pass http://backend/   # host name matches a Compose service
}

The further corollary to this is that you don't actually need to host the "client" code in Docker. Webpack generates a bunch of static files, and can be configured to give them unique names. If you have some sort of non-Docker file-serving service (Amazon S3 will work here) you can just upload the results of the Webpack build to that service, and either proxy_pass or redirect to the UI in the same way as here.

(If you were running this application in Kubernetes, with an Nginx-based ingress controller, it would have exactly the layout you describe, and it would be totally routine.)

David Maze
  • 130,717
  • 29
  • 175
  • 215
  • thanks, yea mixing client with nginx means I have to combine nginx with webpack builds, etc and I'd need NodeJS tools like yarn, etc that the nginx image does not have from docker hub. So it's not very clean. Plus if I wanted to really scale any of these, it's better to have them separate anyway. – PositiveGuy Sep 14 '20 at 14:23
  • I'd rather not have webpack build my service and keep webpack to just building client code. – PositiveGuy Sep 14 '20 at 14:24
  • but does having a route in nginx.conf with `/ui/` dictate that you must hit it at `localhost/ui` for `/`? that would be weird. – PositiveGuy Sep 14 '20 at 14:25
  • 1
    The first `location = /` block redirects `http://host-name/` to `http://host-name/ui/`. There are other ways to set this up that have more complex (proxy) Nginx configurations but cleaner URLs. – David Maze Sep 14 '20 at 14:38
  • what do you mean by "compose service" in terms of what else needs to be in this config? is that defining an upstream entry? – PositiveGuy Sep 15 '20 at 05:41
  • 1
    In the `docker-compose.yml` file, there is a top-level block named `services:`, and the keys within that block (the Compose names of the individual services you're running) are usable as host names. [Networking in Compose](https://docs.docker.com/compose/networking/) describes this in more detail. – David Maze Sep 15 '20 at 11:07
  • Note that the multi stage build avoids having build time dependencies like yarn in nginx. – erik258 Sep 15 '20 at 22:10