Search code examples
dockernginxtraefik

Docker Swarm Nginx Service behind Traefik giving a 502 Error


I have a docker swarm configured to use Traefik as reverse proxy. One of the containers in my swarm is running an Nginx server but I am getting a 502 Bad Gateway error when I navigate to that particular endpoint. Traefik is setup as follows:

version: '3.5'
services:
  traefik:
    image: traefik:alpine
    command: |-
      --entryPoints="Name:http Address::80 Redirect.EntryPoint:https"
      --entryPoints="Name:https Address::443 TLS"
      --defaultentrypoints="http,https"
      --accesslogsfile="/var/log/access.log"
      --acme 
      --acme.acmelogging="true"
      --acme.domains="${SERVER},${SANS1}"
      --acme.email="${ACME_EMAIL}"
      --acme.entrypoint="https"
      --acme.httpchallenge
      --acme.httpchallenge.entrypoint="http"
      --acme.storage="/opt/traefik/acme.json"
      --acme.onhostrule="true"
      --docker
      --docker.swarmmode
      --docker.domain="${SERVER}"
      --docker.network="frontend"
      --docker.watch
      --api
    networks:
      - frontend
    ports: 
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host
      - target: 8080
        published: 8080
        mode: host
    volumes:
      - traefik_acme:/opt/traefik
      - traefik_logs:/var/log/access.log
      - /var/run/docker.sock:/var/run/docker.sock:ro
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]
networks:
  frontend:
    name: "frontend"
    driver: overlay
volumes:
  traefik_acme:
  traefik_logs:

This compose file provides the overlay network and the Traefik service. The rest of my swarm is defined in the following compose file:

version: "3.5"
services:
  test:
    image: emilevauge/whoami
    deploy:
      labels:
        traefik.enable: "true"
        traefik.frontend.rule: "Host:${SERVER};PathPrefixStrip:/test"
        traefik.port: 80
    networks:
      - frontend
  octeditor:
    image: ${DOCKER_OCTEDITOR_IMAGE_TAG}
    deploy:
      replicas: 1
      labels:
        traefik.enable: "true"
        traefik.frontend.rule: "Host:${SERVER}"
        traefik.port: 3000
    networks:
      - frontend
    ports:
      - "3000:80"
  octserver:
    image: ${DOCKER_OCTSERVER_IMAGE_TAG}    
    deploy:
      replicas: 1
      labels:
        traefik.enable: "true"
        traefik.frontend.rule: "Host:${SERVER};PathPrefixStrip:/api"
        traefik.port: 4000
    networks:
      - frontend
    ports:
      - "4000:4000" 
  visualizer:
    image: dockersamples/visualizer:stable
    deploy:
      placement:
        constraints:
          - 'node.role == manager'
      labels:
        traefik.enable: "true"
        traefik.frontend.rule: "Host:${SERVER};PathPrefixStrip:/visualizer"
        traefik.port: 8001
    networks:
      - frontend
    ports: 
      - "8001:8080"
    stop_grace_period: 1m30s
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
  frontend:
    external: true

The relevant configuration is for the octeditor service:

  octeditor:
    image: ${DOCKER_OCTEDITOR_IMAGE_TAG}
    deploy:
      replicas: 1
      labels:
        traefik.enable: "true"
        traefik.frontend.rule: "Host:${SERVER}"
        traefik.port: 3000
    networks:
      - frontend
    ports:
      - "3000:80"

I'm mapping port 80 (which Nginx listens to by default) to port 3000 where Traefik is configured to locate this service. This is the Dockerfile for the service running Nginx:

FROM node:latest as builder

WORKDIR /usr/src/app

COPY package.json . 

RUN npm install

COPY . .

RUN npm run build

FROM nginx

COPY --from=builder /usr/src/app/build /usr/share/nginx/html

I simply build a react app and copy the build folder to the /usr/share/nginx/html folder. I've tried building and running this Dockerfile as a standalone container and it works, also I've checked the contents of the html folder and everything looks correct. The other services, apart from the visualizer service, are running correctly. Only this octedtior service and the visualizer service are giving me 502 errors. Can anyone suggest a solution or even how to check the traffic being sent to the nginx container? I've tried docker ps servicename but I can't see any errors coming from the service.

Edit:

If I change the configuration of the octeditor to this:

  octeditor:
    image: ${DOCKER_OCTEDITOR_IMAGE_TAG}
    deploy:
      replicas: 1
      labels:
        traefik.enable: "true"
        traefik.frontend.rule: "Host:${SERVER}"
        traefik.port: 80
    networks:
      - frontend
    ports:
      - "80:80"

And remove the test service that was previously listening on port 80 it seems to work. I don't understand what was wrong with the previous configuration however? I thought I was mapping traffic from port 3000 to port 80 of the container before, whereas now I'm mapping from port 80 to 80, but nothing should have changed from the perspective of the container, right?


Solution

  • The relevant configuration is for the octeditor service:

    octeditor:
        image: ${DOCKER_OCTEDITOR_IMAGE_TAG}
        deploy:
          replicas: 1
          labels:
            traefik.enable: "true"
            traefik.frontend.rule: "Host:${SERVER}"
            traefik.port: 3000
        networks:
          - frontend
        ports:
          - "3000:80"
    

    I'm mapping port 80 (which Nginx listens to by default) to port 3000 where Traefik is configured to locate this service.

    The traefik port needs to be 80, not 3000. The port mapping will create a forward from the host on 3000 to the container on 80. However traefik talks directly to the container over a shared network (frontend) and you need to provide it the container port.

    There is no need to publish a host port for services accessed through traefik or any other reverse proxy unless you need to access them directly without the proxy (which brings into question whether you need a reverse proxy in those scenarios). In other words, this could be written without the ports:

      octeditor:
        image: ${DOCKER_OCTEDITOR_IMAGE_TAG}
        deploy:
          replicas: 1
          labels:
            traefik.enable: "true"
            traefik.frontend.rule: "Host:${SERVER}"
            traefik.port: 80
        networks:
          - frontend