使用Docker Compose配合Traefik暴露前后端域名时出现404、无路由问题排查
Traefik + Docker Compose 404/无路由问题排查修复
核心配置错误点
- 资源命名全局冲突,导致配置互相覆盖
Traefik的Docker Provider会全局扫描所有接入容器的标签配置,路由、中间件、负载均衡服务的标识是全局唯一的,不会自动按所属容器做命名空间隔离。
当前api和frontend两个服务使用了完全相同的路由名称web、websecure,也定义了同名的HTTPS跳转中间件。容器按启动顺序加载标签时,后启动的frontend的配置会直接覆盖api定义的同名路由、中间件,最终造成Routers面板没有对应路由记录的现象,这也是注释掉frontend配置后api可正常访问的直接原因。 - 未显式指定后端服务的实际监听端口
虽然当前配置开启了--providers.docker.exposedbydefault=true,但Traefik只会自动识别容器暴露的80/443端口。如果你的api、frontend镜像实际监听3000、8080这类非标准端口,Traefik无法自动探知正确转发目标,就会出现服务面板有负载均衡条目,但转发后返回404的问题。 - 网络互通规则缺失,导致自定义网络下网关超时
配置自定义网络后出现网关超时,本质是Traefik和后端服务没有加入同一个可互通的网络。Traefik扫描到容器标签后,需要通过自身所在的网络访问后端容器,如果Traefik不在后端服务所在的自定义网络段,就算路由正常生成,也无法建立转发连接,直接返回网关超时。 - 全局默认暴露开关带来的冗余配置干扰
开启--providers.docker.exposedbydefault=true后,Traefik会把所有启动的容器(包括db数据库服务)都纳入可路由扫描范围,生成无意义的负载均衡条目,进一步增加配置冲突的概率。
修复后的配置参考
version: "3.3" # 显式声明统一的自定义网络,所有服务强制接入同一网络保证互通 networks: app_net: driver: bridge services: traefik: image: "traefik:latest" restart: always container_name: "traefik" command: - "--log.level=DEBUG" - "--api.insecure=true" - "--providers.docker=true" # 关闭默认全量暴露,仅通过标签显式指定需要暴露的服务 - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true" - "--certificatesresolvers.letsencrypt.acme.email=myemail" - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" ports: - "80:80" - "443:443" - "8080:8080" volumes: - "./letsencrypt:/letsencrypt" - "/var/run/docker.sock:/var/run/docker.sock:ro" networks: - app_net db: image: mysql:latest volumes: - db_data:/var/lib/mysql restart: always container_name: "db" environment: MYSQL_ROOT_PASSWORD: mypassword MYSQL_DATABASE: mydb MYSQL_USER: myuser MYSQL_PASSWORD: mypassword ports: - "32769:3306" networks: - app_net api: image: "myregistry/myimage:latest" restart: always container_name: "api" labels: - "traefik.enable=true" # 给api的中间件、路由加独立前缀,避免和其他服务重名 - "traefik.http.middlewares.api-https-redirect.redirectscheme.scheme=https" - "traefik.http.routers.api-web.rule=Host(`mydomainapi`)" - "traefik.http.routers.api-web.entrypoints=web" - "traefik.http.routers.api-web.middlewares=api-https-redirect" - "traefik.http.routers.api-websecure.rule=Host(`mydomainapi`)" - "traefik.http.routers.api-websecure.entrypoints=websecure" - "traefik.http.routers.api-websecure.tls.certresolver=letsencrypt" # 显式指定api实际监听的端口,替换为你的镜像真实端口值 - "traefik.http.services.api.loadbalancer.server.port=8080" depends_on: - db networks: - app_net frontend: image: "myregistry/myimagefrontend:latest" restart: always container_name: "frontend" labels: - "traefik.enable=true" # 给frontend的中间件、路由加独立前缀,避免和其他服务重名 - "traefik.http.middlewares.frontend-https-redirect.redirectscheme.scheme=https" - "traefik.http.routers.frontend-web.rule=Host(`mydomainfrontend`)" - "traefik.http.routers.frontend-web.entrypoints=web" - "traefik.http.routers.frontend-web.middlewares=frontend-https-redirect" - "traefik.http.routers.frontend-websecure.rule=Host(`mydomainfrontend`)" - "traefik.http.routers.frontend-websecure.entrypoints=websecure" - "traefik.http.routers.frontend-websecure.tls.certresolver=letsencrypt" # 显式指定frontend实际监听的端口,替换为你的镜像真实端口值 - "traefik.http.services.frontend.loadbalancer.server.port=80" depends_on: - api networks: - app_net volumes: db_data: driver: local
内容的提问来源于stack exchange,提问作者Pizzoni Alessio
相关产品推荐
相关产品推荐

