You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker网络中已暴露端口但API仍无法访问的排查求助

问题:.NET API无法通过Traefik或直接端口访问,健康检查失败

问题描述

部署VDS时,API既无法本地访问也无法通过网络访问,尝试Traefik反向代理和端口暴露均无效。API本身运行正常且已连接数据库,但外部无法访问,添加健康检查后直接失败。数据库、Traefik、前端应用配置均正常,仅后端API存在问题。

相关配置文件

Dockerfile

ARG DOTNET_SDK=mcr.microsoft.com/dotnet/sdk:8.0

FROM ${DOTNET_RUNTIME} AS base
WORKDIR /app
EXPOSE 5000

FROM ${DOTNET_SDK} AS build
WORKDIR /src

COPY ["CassiniConnect.API/CassiniConnect.API.csproj", "CassiniConnect.API/"]
COPY ["CassiniConnect.Application/CassiniConnect.Application.csproj", "CassiniConnect.Application/"]
COPY ["CassiniConnect.Core/CassiniConnect.Core.csproj", "CassiniConnect.Core/"]

RUN dotnet restore "CassiniConnect.API/CassiniConnect.API.csproj"

COPY . .

ENV ASPNETCORE_URLS="http://+:5000"

WORKDIR /src/CassiniConnect.API
RUN dotnet publish "CassiniConnect.API.csproj" -c Release --no-restore -o /app/publish

FROM base AS final
WORKDIR /app

ENV ASPNETCORE_ENVIRONMENT=Production

COPY --from=build /app/publish .

ENTRYPOINT [ "dotnet", "CassiniConnect.API.dll" ]

docker-compose.yaml

services:
  proxy:
    image: traefik:latest
    container_name: traefik
    command: 
      - "--providers.docker"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=traefik"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.myresolver.acme.email=ujfalusiabel@cassini-org.info"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
      - "--api.dashboard=true"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - letsencrypt:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - traefik

  db:
    image: postgres:latest
    container_name: cassini-db
    restart: always
    env_file:
      - .env  
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - traefik
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  api:
    image: cassini-api:latest
    container_name: cassini-api
    restart: always
    depends_on:
      db:
        condition: service_healthy
    env_file:
      - .env  
    environment:
      DefaultConnection: ${CONNECTION_STRING}
      JwtSettings__SecretKey: ${JwtSettings__SecretKey}
      JwtSettings__Issuer: ${JwtSettings__Issuer}
      JwtSettings__Audience: ${JwtSettings__Audience}
      ASPNETCORE_ENVIRONMENT: ${ASPNETCORE_ENVIRONMENT}
    volumes:
      - storage_data:/app/storage
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(`api.cassini-org.info`)"
      - "traefik.http.routers.api.entrypoints=websecure"
      - "traefik.http.routers.api.tls.certresolver=myresolver"
      - "traefik.http.services.api.loadbalancer.server.port=5000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 10s
      timeout: 5s
      retries: 5

  webapp:
    image: cassini-app:latest
    container_name: cassini-app
    restart: always
    depends_on:
      api:
        condition: service_healthy
    environment:
      - NEXT_APP_API_URL=https://api.cassini-org.info/api
    volumes:
      - webapp_data:/app/.next
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webapp.rule=Host(`cassini-org.info`)"
      - "traefik.http.routers.webapp.entrypoints=websecure"
      - "traefik.http.routers.webapp.tls.certresolver=myresolver"
      - "traefik.http.services.webapp.loadbalancer.server.port=3000"

volumes:
  postgres_data:
  storage_data:
  webapp_data:
  letsencrypt:

networks:
  traefik:
    name: traefik
    driver: bridge

排查与解决方案

1. 修复Dockerfile环境变量传递问题

ASPNETCORE_URLS变量在build阶段设置,但未传递到final运行阶段,导致.NET应用默认仅监听localhost,容器外部无法访问。修改Dockerfile,将该变量移至final阶段:

FROM base AS final
WORKDIR /app

ENV ASPNETCORE_ENVIRONMENT=Production
ENV ASPNETCORE_URLS="http://+:5000"  # 移至运行阶段

COPY --from=build /app/publish .

ENTRYPOINT [ "dotnet", "CassiniConnect.API.dll" ]

2. 修复健康检查依赖缺失问题

默认的.NET ASP.NET镜像不带curl工具,导致健康检查命令执行失败。可选择以下三种方案:

  • 方案1:改用wget(部分镜像内置):
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:5000/health"]
      interval: 10s
      timeout: 5s
      retries: 5
    
  • 方案2:检查端口而非HTTP接口:
    healthcheck:
      test: ["CMD", "nc", "-z", "localhost", "5000"]
      interval: 10s
      timeout: 5s
      retries: 5
    
    需在Dockerfile的final阶段安装netcat:
    RUN apt-get update && apt-get install -y netcat-traditional && rm -rf /var/lib/apt/lists/*
    
  • 方案3:在镜像中安装curl:
    在Dockerfile的final阶段添加:
    RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
    

3. 验证Traefik路由配置

  • 确认api.cassini-org.info的DNS记录已正确指向VDS公网IP
  • 查看Traefik日志,确认API服务是否被正确注册:
    docker logs traefik
    
  • 验证API容器与Traefik在同一网络:
    docker inspect cassini-api | grep "traefik"
    

4. 确认API端口监听状态

进入API容器,检查应用是否在监听0.0.0.0:5000:

docker exec -it cassini-api netstat -tulpn

若输出中无0.0.0.0:5000,则回到第一步确认ASPNETCORE_URLS配置是否生效。

内容的提问来源于stack exchange,提问作者Újfalusi Ábel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 13:14:52