.Net TestContainers集成测试在Azure Pipeline中连接失败求助
Azure Pipeline中TestContainers集成测试连接SQL Server和RabbitMQ失败排查
本地使用Docker运行TestContainers集成测试一切正常,但在Azure Pipeline执行Run Tests任务时,出现SQL Server和RabbitMQ连接失败的问题。此前使用Docker Compose方式运行集成测试可正常通过。
Azure-pipelines.yml配置
trigger: - master pool: vmImage: ubuntu-latest variables: buildConfiguration: 'Release' BuildParameters.RestoreBuildProjects: '**/*.csproj' BuildParameters.TestProjects: | $(BuildParameters.RestoreBuildProjects) !**/*IntegrationTests*.csproj !**/*FunctionalTests*.csproj jobs: - job: BuildAndTest displayName: Build and Test steps: - task: DockerCompose@0 displayName: "Run a Docker Compose command" inputs: dockerComposeFile: "docker-compose-integration-tests.yml" dockerComposeCommand: "up --abort-on-container-exit" env: CONTAINER_NAME: 'bookywooks_orderapi_integrationtests' - task: DotNetCoreCLI@2 displayName: Restore Nuget Packages inputs: command: restore projects: $(BuildParameters.RestoreBuildProjects) configuration: $(buildConfiguration) - task: DotNetCoreCLI@2 displayName: 'dotnet build $(buildConfiguration)' inputs: command: 'build' projects: '**/BookyWooks.sln' arguments: '--configuration $(buildConfiguration)' - script: docker --version displayName: 'Print Docker Version' - task: DotNetCoreCLI@2 displayName: Run Tests inputs: command: test projects: $(BuildParameters.TestProjects) arguments: --configuration $(buildConfiguration) --no-build
docker-compose-integration-tests.yml配置
version: '3.4' services: # Service for running integration tests bookywooks_orderapi_integrationtests: image: ${DOCKER_REGISTRY-}bookywooksorderapiintegrationtests build: context: . dockerfile: BookWooks.OrderApi/src/BookWooks.OrderApi.Web/Dockerfile target: integrationtest # Specify the build target depends_on: - integrationtestsdatabase # Depends on the integration tests database service - rabbitmq # Depends on the RabbitMQ service environment: - ASPNETCORE_ENVIRONMENT=Development - "ConnectionStrings__DefaultConnection=Server=integrationtestsdatabase;Database=BookyWooks.OrderDbIntegrationTests;User=sa;Password=Your_password123; MultipleActiveResultSets=true; TrustServerCertificate=true;" # Database connection string - RabbitMQConfiguration__Config__HostName=${BOOKYWOOKS_RABBITMQ_HOSTNAME:-rabbitmq} # RabbitMQ host name - RabbitMQConfiguration__Config__UserName=${BOOKYWOOKS_RABBITMQ_USERNAME:-guest} # RabbitMQ user name - RabbitMQConfiguration__Config__Password=${BOOKYWOOKS_RABBITMQ_PASSWORD:-guest} # RabbitMQ password volumes: - ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests # Mount volume for test results entrypoint: - dotnet # Entry point to execute dotnet commands - test # Command to run tests - --logger # Specify logger options - trx;LogFileName=/tests/booky-wooks-integration-test-results.xml # Use trx logger format and specify log file path # Service for integration tests database (SQL Server) integrationtestsdatabase: image: "mcr.microsoft.com/mssql/server:2022-latest" # Use SQL Server 2022 image environment: - "SA_PASSWORD=Your_password123" # Set SA password - "ACCEPT_EULA=Y" # Accept End-User License Agreement ports: - "5433:1433" # Map container port 1433 to host port 5433 # Service for RabbitMQ rabbitmq: ports: - "15672:15672" # Map RabbitMQ management port - "5672:5672" # Map RabbitMQ port for message communication image: rabbitmq:3-management-alpine # Use RabbitMQ 3 with management plugin
错误信息
RabbitMQ连接错误
2024-03-12T19:14:08.6922155Z Error creating RabbitMQ connection: None of the specified endpoints were reachable
SQL Server连接错误
2024-03-12T19:14:08.9058528Z Microsoft.Data.SqlClient.SqlException : A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 35 - An internal exception was caught)
Application Test Factory配置
public class OrderApiApplicationFactory<TEntryPoint> : WebApplicationFactory<Program>, IAsyncLifetime where TEntryPoint : Program { private const string Database = "master"; private const string Username = "sa"; private const string Password = "yourStrong(!)Password"; private string _connectionString; private const ushort MsSqlPort = 1433; private const ushort RabbitMqPort = 5672; private readonly MsSqlContainer _mssqlContainer; private readonly IContainer _rabbitMqContainer; public HttpClient HttpClient { get; private set; } = default!; private const string RabbitMqUsername = "guest"; private const string RabbitMqPassword = "guest"; public OrderApiApplicationFactory() { _mssqlContainer = new MsSqlBuilder() .WithImage("mcr.microsoft.com/mssql/server:2022-latest") // Remove static port binding and let Docker assign random ports .WithPortBinding(0, MsSqlPort) .WithEnvironment("ACCEPT_EULA", "Y") .WithEnvironment("SA_PASSWORD", Password) .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(MsSqlPort)) .Build(); _rabbitMqContainer = new RabbitMqBuilder() .WithImage("rabbitmq:3-management-alpine") .WithPortBinding(5672, RabbitMqPort) .WithEnvironment("RABBITMQ_DEFAULT_USER", RabbitMqUsername) .WithEnvironment("RABBITMQ_DEFAULT_PASS", RabbitMqPassword) .WithWaitStrategy(Wait.ForUnixContainer() .UntilPortIsAvailable(RabbitMqPort) .UntilPortIsAvailable(15672)) .Build(); } public async Task InitializeAsync() { await _mssqlContainer.StartAsync(); await _rabbitMqContainer.StartAsync(); GetDatabaseConnectionString(); HttpClient = CreateClient(); } protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureAppConfiguration(configurationBuilder => { var configuration = new ConfigurationBuilder().AddJsonFile("testcontainersappsettings.json") .AddInMemoryCollection(new Dictionary<string, string> { ["ConnectionStrings:DefaultConnection"] = _connectionString, ["RabbitMQConfiguration:Config:HostName"] = _rabbitMqContainer.Hostname }) .AddEnvironmentVariables() .Build(); configurationBuilder.AddConfiguration(configuration); }); builder.ConfigureServices(services => { RemoveDbContextOptions<BookyWooksOrderDbContext>(services); RemoveDbContextOptions<IntegrationEventLogDbContext>(services); AddDbContext<BookyWooksOrderDbContext>(services); AddDbContext<IntegrationEventLogDbContext>(services); }); } private void RemoveDbContextOptions<T>(IServiceCollection services) where T : DbContext { var descriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbContextOptions<T>)); if (descriptor != null) services.Remove(descriptor); } private void AddDbContext<T>(IServiceCollection services) where T : DbContext { services.AddDbContext<T>(options => { options.UseSqlServer(_connectionString, builder => builder.MigrationsAssembly(typeof(T).Assembly.FullName)); }); } private string GetDatabaseConnectionString() { _connectionString = _mssqlContainer.GetConnectionString(); return _connectionString; } public new async Task DisposeAsync() { await _mssqlContainer.DisposeAsync(); await _rabbitMqContainer.DisposeAsync(); } }
排查建议
- 端口冲突检查:Azure Pipeline的Ubuntu代理可能已有服务占用5672或映射的端口,TestContainers容器无法正常绑定。确认TestContainers使用随机端口(代码中
WithPortBinding(0, MsSqlPort)已配置,需验证是否生效),避免固定端口冲突。 - 服务启动等待策略优化:Azure代理资源有限,SQL Server和RabbitMQ启动速度可能慢于本地。给SQL Server添加等待数据库就绪的策略,比如
UntilCommandIsCompleted("SELECT 1");给RabbitMQ添加等待管理API返回成功的检查,确保服务完全启动后再执行测试。 - 网络模式验证:TestContainers在Azure Pipeline中可能使用了非默认网络,导致测试项目无法访问容器。可以显式指定TestContainers使用Docker桥接网络,或者检查容器的网络配置。
- 环境变量一致性:TestContainers中的SA密码(
yourStrong(!)Password)与Docker Compose中的密码(Your_password123)不一致,虽然TestContainers独立创建容器,但需确认是否有环境变量覆盖了TestContainers的配置。 - Docker权限验证:确保Azure Pipeline中的执行用户有足够的Docker权限,能够创建和管理TestContainers容器。
- 版本兼容性检查:对比TestContainers版本与Azure Pipeline中Docker版本的兼容性,参考官方文档确认支持的Docker版本范围。
内容的提问来源于stack exchange,提问作者Ports
相关产品推荐
相关产品推荐

