Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
197 views
in Technique[技术] by (71.8m points)

python - Docker Traefik fails to route websocket

I have several micro-services running as docker containers. All web services work fine and route correctly.

The only issue is the websocket service. The websocket service itself is using python websockets and has it's own TLS certificates.

Trying to access the websocket with wss://websocket.localhost fails, in the setup below it doesn't find the page at all. In my previous configurations, it results in the Bad Gateway error.

Apparently traefik comes out of the box working with websockets with no additional configurations.

This doesn't seem to be the case. Any pointers?

The websocket connection works without docker or traefik involved, so I ruled that issue out. Any help on this would be extremely appreciated.

docker-compose.yml

version: "3.7"

networks:
  web:
    external: true
  internal:
    external: false

volumes:
  mysql_data:

services:
  traefik:
    image: traefik:v2.2.1
    container_name: traefik
    restart: always
    ports:
      - "80:80"
      - "443:443"
    expose:
      - 8080
    environment:
      - /var/run/docker.sock:/var/run/docker.sock
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./config/:/config
      - ./traefik.yml:/traefik.yml
    networks:
      - web
      - internal
    labels:
      - traefik.http.routers.traefik.tls=true
      - traefik.http.routers.traefik.entrypoints=secure
      - traefik.http.routers.traefik.rule=Host(`traefik.localhost`)
      - traefik.http.routers.traefik.service=api@internal

  dozzle:
    image: amir20/dozzle:latest
    container_name: dozzle
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    expose:
      - 8080
    labels:
      - traefik.http.routers.dozzle.tls=true
      - traefik.http.routers.dozzle.entrypoints=secure
      - traefik.http.routers.dozzle.rule=Host(`dozzle.localhost`) || Host(`logs.localhost`)
    networks:
      - internal

  db:
    image: mysql:latest
    container_name: db
    environment:
      MYSQL_ROOT_PASSWORD: ########
    restart: always
    healthcheck:
      test: "exit 0"
    command: --default-authentication-plugin=mysql_native_password
    ports:
      - '3306:3306'
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - internal

  websocket:
    image: local-websocket-image
    container_name: websocket-stage
    restart: on-failure
    command: python server.py
    depends_on:
      db:
        condition: service_healthy
    expose:
      - 8080
    networks:
      - web
      - internal
    environment:
      - PATH_TO_CONFIG=/src/setup.cfg
    volumes:
      - ${PWD}/docker-config:/src
      - ${PWD}/config/certs/socket:/var
    labels:
      - traefik.http.routers.core-socket-stage-router.tls=true
      - traefik.http.routers.core-socket-stage-router.entrypoints=secure
      - traefik.http.routers.core-socket-stage-router.rule=Host(`websocket.localhost`)

traefik.yml

entryPoints:
  insecure:
    address: :80
    http:
      redirections:
        entryPoint:
          to: secure
          scheme: https
  secure:
    address: :443

log:
  level: INFO
 
accessLog:
  filePath: "traefik-access.log"
  bufferingSize: 100

api:
  dashboard: true
  insecure: true

ping: {}

providers:
  file:
    filename: /config/dynamic.yml # traefik dynamic configuration
    watch: true                   # everytime it changes, it will be reloaded 
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: true

config

tls:
  stores:
    default:
      defaultCertificate:
        certFile: cert.crt
        keyFile: key.key

  certificates:
    - certFile: crt.crt
      keyFile: key.key
      stores:
        - default
 
  domains:
    - main: "localhost"
question from:https://stackoverflow.com/questions/66049995/docker-traefik-fails-to-route-websocket

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

while looking at your configuration, the following doesn't fit:

  • The docker-compose projectname will be part of the domain names. The default is to use the parent folder name of your docker-compose.yaml. You didn't specify it here, therefore I assume it to by traefik. You can set this explicitly in the docker-compose call with docker-compose -p traefik up or by setting the env variable PROJECT_NAME.

  • you are using the domain name '.localhost', but you don't define the domainname explicitly. That means the default name is used which is derived from the service name, the project name (the folder where is docker-compose file is stored), and the docker-network name that you attach to with this pattern: servicename.projectname_networkname. Use the attributes hostname and domainname to explicitly define a name (only works for networks with internal=false). When having two network connections and additionally a domainname definition you get the following domain names:

    1. db.traefik_internal (only intern, db.localhost will not work)
    2. dozzle.traefik_internal (only intern, dozzle.localhost will not work)
    3. traefik.localhost
    4. traefik.traefik_web
    5. traefik.traefik_internal
    6. websocket.localhost
    7. websocket.traefik_web
    8. websocket.traefik_internal
  • external=true just means that the network is created externally by docker network create or by another docker-compose project. The main effect is, that it is not delected when doing docker-compose down. It has nothing to do with the connection to the outside world. To get an isolated internal network you have to use the option internal: true

  • the option condition: service_healthy is no longer supported for version: "3.7", so either remove that option (it nevertheless doesn't work like you expect) or change the version to 2.4

Here my current version of the docker-compose.yaml:

version: "2.4"

networks:
  web:
  internal:
    internal: true

volumes:
  mysql_data:

services:
  traefik:
    image: traefik:v2.2.1
    container_name: traefik
    hostname: traefik
    domainname: localhost
    restart: always
    ports:
      - "80:80"
      - "443:443"
    expose:
      - 8080
    environment:
      - /var/run/docker.sock:/var/run/docker.sock
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./config/:/config
      - ./traefik.yml:/traefik.yml
    networks:
      - web
      - internal
    labels:
      - traefik.http.routers.traefik.tls=true
      - traefik.http.routers.traefik.entrypoints=secure
      - traefik.http.routers.traefik.rule=Host(`traefik.localhost`)
      - traefik.http.routers.traefik.service=api@internal

  dozzle:
    image: amir20/dozzle:latest
    container_name: dozzle
    hostname: dozzle
    domainname: localhost
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    expose:
      - 8080
    labels:
      - traefik.http.routers.dozzle.tls=true
      - traefik.http.routers.dozzle.entrypoints=secure
      - traefik.http.routers.dozzle.rule=Host(`dozzle.traefik_internal`) || Host(`logs.localhost`)
    networks:
      - internal

  db:
    image: mysql:latest
    container_name: db
    hostname: db
    domainname: localhost
    environment:
      MYSQL_ROOT_PASSWORD: ########
    restart: always
    healthcheck:
      test: "exit 0"
    command: --default-authentication-plugin=mysql_native_password
    ports:
      - '3306:3306'
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - internal

  websocket:
    image: local-websocket-image
    container_name: websocket-stage
    hostname: websocket
    domainname: localhost
    restart: on-failure
    command: python server.py
    depends_on:
      db:
        condition: service_healthy
    expose:
      - 8080
    networks:
      - web
      - internal
    environment:
      - PATH_TO_CONFIG=/src/setup.cfg
    volumes:
      - ${PWD}/docker-config:/src
      - ${PWD}/config/certs/socket:/var
    labels:
      - traefik.http.routers.core-socket-stage-router.tls=true
      - traefik.http.routers.core-socket-stage-router.entrypoints=secure
      - traefik.http.routers.core-socket-stage-router.rule=Host(`websocket.localhost`)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...