在Docker容器中运行.NET Core单元测试遇SQL Server预登录握手异常
问题描述
已在Dockerfile中配置SQL Server,手动构建启动容器后进入Test目录执行dotnet test可成功运行单元测试,但通过启动容器的命令直接执行测试时,始终报错:
Microsoft.Data.SqlClient.SqlException: 已成功与服务器建立连接,但在预登录握手期间发生错误。(provider: TCP Provider, error: 0 - Success)
现有配置
连接字符串
SqlConnection sqldr = new Microsoft.Data.SqlClient.SqlConnection("Server=host.docker.internal;User ID=sa;Password=abcDEF123#;Encrypt=false;MultiSubnetFailover=true;TrustServerCertificate=true");
Dockerfile
FROM mcr.microsoft.com/mssql/server:2019-latest as sqlserver USER root ENV ACCEPT_EULA=Y ENV SA_PASSWORD=abcDEF123# ENV MSSQL_PID=Developer ENV MSSQL_TCP_PORT=1433 #copy all the sql files to the docker container #COPY /Database/Scripts /database/ RUN ((/opt/mssql/bin/sqlservr --accept-eula & ) | grep -q "Service Broker manager has started") COPY . . # install the dotnet environment RUN apt-get update && \ apt-get install -y dotnet-sdk-6.0
相关命令
- 后台启动容器:
docker run -d sqlserver_sqltoolsservice - 触发报错的测试命令:
docker run -p 1433:1433 sqlserver_sqltoolsservice /bin/bash -c "cd Data.Test && dotnet test"
已尝试但无效的配置
在Dockerfile中添加以下SSL/TLS配置后问题依旧:
RUN sed -i 's/DEFAULT@SECLEVEL=2/ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256/g' /etc/ssl/openssl.cnf RUN sed -i 's/DEFAULT@SECLEVEL=2/ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256/g' /usr/lib/ssl/openssl.cnf RUN sed -i 's/MinProtocol = TLSv1.2/MinProtocol = TLSv1/g' /etc/ssl/openssl.cnf RUN sed -i 's/CipherString = DEFAULT@SECLEVEL=2/CipherString = DEFAULT@SECLEVEL=1/g' /etc/ssl/openssl.cnf
问题原因及解决方案
1. SQL Server未启动就执行测试
Dockerfile中的RUN命令是在镜像构建阶段执行的,容器启动时并不会自动启动SQL Server。直接用docker run ... dotnet test命令时,容器优先执行测试命令,根本没启动SQL Server,导致连接失败。
临时修复命令
在启动命令中先启动SQL Server,等待足够时间后再执行测试:
docker run -p 1433:1433 sqlserver_sqltoolsservice /bin/bash -c "/opt/mssql/bin/sqlservr --accept-eula & sleep 30 && cd Data.Test && dotnet test"
注:
sleep 30可根据实际启动时间调整,也可以用sqlcmd做更可靠的就绪检查。
2. 连接字符串指向错误
手动进入容器时能连上,是因为你手动启动了容器内的SQL Server,此时应该连接localhost而非host.docker.internal(该地址指向宿主机)。修改连接字符串:
SqlConnection sqldr = new Microsoft.Data.SqlClient.SqlConnection("Server=localhost,1433;User ID=sa;Password=abcDEF123#;Encrypt=false;TrustServerCertificate=true");
移除
MultiSubnetFailover=true,单实例场景无需该参数。
3. 优化Dockerfile实现自动化流程
创建启动脚本entrypoint.sh,实现SQL Server启动、就绪检查、自动执行测试的完整流程:
#!/bin/bash # 启动SQL Server后台进程 /opt/mssql/bin/sqlservr --accept-eula & # 等待SQL Server就绪 until /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P abcDEF123# -Q "SELECT 1" > /dev/null 2>&1; do echo "等待SQL Server启动..." sleep 5 done # 执行单元测试 cd Data.Test && dotnet test # 保持容器运行(可选,测试后需保留容器时启用) wait
修改Dockerfile,安装mssql-tools用于连接检查,并设置脚本为入口:
FROM mcr.microsoft.com/mssql/server:2019-latest as sqlserver USER root ENV ACCEPT_EULA=Y ENV SA_PASSWORD=abcDEF123# ENV MSSQL_PID=Developer ENV MSSQL_TCP_PORT=1433 # 安装dotnet-sdk和mssql-tools RUN apt-get update && \ apt-get install -y dotnet-sdk-6.0 curl && \ curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - && \ curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list > /etc/apt/sources.list.d/msprod.list && \ apt-get update && \ apt-get install -y mssql-tools unixodbc-dev && \ echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc && \ source ~/.bashrc # 复制项目文件和启动脚本 COPY . . COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh # 设置入口脚本 ENTRYPOINT ["/entrypoint.sh"]
构建镜像后,直接运行容器即可自动完成所有流程:
docker run -p 1433:1433 sqlserver_sqltoolsservice
内容的提问来源于stack exchange,提问作者Odol

