Docker部署ASP.NET Core API时无法自动初始化SQL Server数据库种子数据
问题:Docker Compose启动ASP.NET Core与SQL Server时数据库初始化失败
尝试通过Docker Compose同时启动ASP.NET Core Web API和SQL Server实例,要求启动时自动完成数据库Schema创建、EF迁移执行及种子数据插入(种子数据通过重写EF Core的OnModelCreating方法配置)。但当前配置运行时后端容器无法连接到SQL Server,只能先单独启动数据库容器,再手动执行迁移脚本才能正常运行,需要实现全自动化初始化。
当前配置文件
.NET后端Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 7132 EXPOSE 5194 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["Services/MyApp/MyApp.API/MyApp.API.csproj", "Services/MyApp/MyApp.API/"] RUN dotnet restore "Services/MyApp/MyApp.API/MyApp.API.csproj" COPY . . WORKDIR "/src/Services/MyApp/" RUN dotnet tool install --global dotnet-ef --version 6.0.25 RUN /root/.dotnet/tools/dotnet-ef migrations add InitialCreate --verbose --startup-project "MyApp.API" --project "MyApp.Infrastructure" RUN /root/.dotnet/tools/dotnet-ef database update --verbose --startup-project "MyApp.API" --project "MyApp.Infrastructure" RUN dotnet build "MyApp.API/MyApp.API.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "MyApp.API/MyApp.API.csproj" -c Release -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "MyApp.API.dll"]
Docker Compose配置
version: '3.8' services: myapp-db: image: mcr.microsoft.com/mssql/server:2019-latest container_name: myapp-db environment: ACCEPT_EULA: Y SA_PASSWORD: mypassword ports: - "14330:1433" restart: always networks: - myapp-network volumes: - myapp-db-volume:/var/opt/mssql myapp-backend: build: context: ../myapp-backend/src dockerfile: Dockerfile container_name: myapp-backend environment: ASPNETCORE_HTTP_PORT: "https://+:7132" ASPNETCORE_URLS: "http://+:7132" ports: - "7132:7132" - "5194:5194" restart: always depends_on: - myapp-db networks: - myapp-network networks: myapp-network: volumes: myapp-db-volume:
数据库连接字符串
Server=myapp-db;Database=myappdb;User Id=SA;Password=mypassword;trusted_connection=false;encrypt=false
问题根源与解决步骤
核心问题
- 镜像构建阶段执行迁移错误:当前Dockerfile在
build阶段就执行了dotnet ef database update,此时构建环境中不存在SQL Server容器,必然连接失败。迁移操作应放在容器启动阶段执行,而非镜像构建阶段。 depends_on仅保证启动顺序:即使后端容器在数据库容器之后启动,SQL Server仍需要一段时间完成初始化,此时后端尝试连接会失败,需要等待数据库完全就绪后再执行后续操作。
修正方案
1. 修改Dockerfile,移除构建阶段的迁移命令
将迁移相关操作移到容器启动时执行,同时安装dotnet-ef工具并添加启动脚本:
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 7132 EXPOSE 5194 # 安装sqlcmd工具用于检测SQL Server状态 RUN apt-get update && apt-get install -y mssql-tools unixodbc-dev ENV PATH="$PATH:/opt/mssql-tools/bin" FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["Services/MyApp/MyApp.API/MyApp.API.csproj", "Services/MyApp/MyApp.API/"] RUN dotnet restore "Services/MyApp/MyApp.API/MyApp.API.csproj" COPY . . WORKDIR "/src/Services/MyApp/MyApp.API" RUN dotnet build "MyApp.API.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "MyApp.API.csproj" -c Release -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app COPY --from=publish /app/publish . # 安装dotnet-ef工具 RUN dotnet tool install --global dotnet-ef --version 6.0.25 ENV PATH="$PATH:/root/.dotnet/tools" # 复制启动脚本 COPY start.sh . RUN chmod +x start.sh # 使用自定义启动脚本 ENTRYPOINT ["./start.sh"]
2. 创建启动脚本start.sh
在API项目根目录创建该脚本,实现等待SQL Server就绪、执行迁移、启动API的流程:
#!/bin/bash # 等待SQL Server可连接 until sqlcmd -S myapp-db -U SA -P mypassword -Q "SELECT 1" > /dev/null 2>&1; do echo "等待SQL Server启动中..." sleep 5 done echo "SQL Server已就绪,执行数据库迁移..." dotnet ef database update --startup-project MyApp.API --project ../MyApp.Infrastructure/MyApp.Infrastructure.csproj echo "迁移完成,启动API服务..." dotnet MyApp.API.dll
3. 优化Docker Compose配置,添加健康检查
为SQL Server服务添加健康检查,让后端容器等待数据库完全就绪后再启动:
version: '3.8' services: myapp-db: image: mcr.microsoft.com/mssql/server:2019-latest container_name: myapp-db environment: ACCEPT_EULA: Y SA_PASSWORD: mypassword ports: - "14330:1433" restart: always networks: - myapp-network volumes: - myapp-db-volume:/var/opt/mssql # 健康检查:验证SQL Server是否可正常连接 healthcheck: test: ["CMD-SHELL", "sqlcmd -S localhost -U SA -P mypassword -Q 'SELECT 1' || exit 1"] interval: 10s timeout: 5s retries: 5 myapp-backend: build: context: ../myapp-backend/src dockerfile: Dockerfile container_name: myapp-backend environment: ASPNETCORE_HTTP_PORT: "https://+:7132" ASPNETCORE_URLS: "http://+:7132" # 环境变量传入连接字符串(也可通过配置文件挂载) ConnectionStrings__DefaultConnection: "Server=myapp-db;Database=myappdb;User Id=SA;Password=mypassword;trusted_connection=false;encrypt=false" ports: - "7132:7132" - "5194:5194" restart: always depends_on: myapp-db: condition: service_healthy # 等待数据库健康检查通过 networks: - myapp-network networks: myapp-network: volumes: myapp-db-volume:
4. 验证种子数据
通过modelBuilder.Entity<T>().HasData(...)配置的种子数据,会在dotnet ef database update执行时自动插入到数据库中,无需额外操作。
内容的提问来源于stack exchange,提问作者ServletException
相关产品推荐
相关产品推荐

