是否可以使用 Docker.DotNet 在 Azure Pipelines 上以编程方式启动容器?

Is it possible to use Docker.DotNet to start a container programmatically on Azure Pipelines?

我有一个微服务应用程序,我不想启动整个堆栈以进行 运行 集成测试。这个想法是能够 运行 来自 Test Explorer 的测试 Visual Studio。所以我不想创建一个 shell 脚本来完成这个。

思路是在容器中启动所有外部微服务依赖,然后运行被测系统的一个TestServer接收测试请求,以便系统调用其他微服务。

我找到了 this 库并喜欢以编程方式启动容器以实现上述目的的想法。

我不知道这是否可以在 Azure Pipeline 上运行 运行。 Azure 上用于初始化 Docker.DotNet 的正确 docker 引擎地址是什么?

// Default Docker Engine on Windows
using Docker.DotNet;
DockerClient client = new DockerClientConfiguration(
    new Uri("npipe://./pipe/docker_engine"))
     .CreateClient();
// Default Docker Engine on Linux
using Docker.DotNet;
DockerClient client = new DockerClientConfiguration(
    new Uri("unix:///var/run/docker.sock"))
     .CreateClient();

What I don't know, is if this would work running on an Azure Pipeline. What would be the correct docker engine address on Azure to initialize Docker.DotNet?

您可以在托管 windows 代理中运行的管道中添加 Powershell task,内容如下:

[System.IO.Directory]::GetFiles("\.\pipe\") | Select-String "docker"

如果我使用托管 windows2019 代理,结果如下:

所以我认为地址是 \.\pipe\docker_engine(不确定在您的代码中使用时是否需要一些更改,例如斜杠和反斜杠)。

我做了一些研究,发现 Ms 托管代理的 Windows 和 Linux 图像都预装了 Docker 和 Docker-Compose,所以可以使用这些代理启动容器。

我的问题是我没有意识到 Ops 团队配置了 Linux 代理。我试图使用 Windows 命名管道而不是 Unix 套接字来执行。

我相应地更改了 Docker 引擎 API 地址并且它起作用了。

这是我的代码,以防有人有兴趣创建集成测试来启动微服务依赖项的容器:

using Docker.DotNet;
using System;
using System.Runtime.InteropServices;

namespace Relationship.Promotion.Applier.IntegrationTest.Helpers
{
    public class DockerClientFactory
    {
        private static readonly Uri defaultWindowsDockerEngineUri = new Uri("npipe://./pipe/docker_engine");
        private static readonly Uri defaultLinuxDockerEngineUri = new Uri("unix:///var/run/docker.sock");

        public static DockerClient CreateInstance()
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                return new DockerClientConfiguration(defaultWindowsDockerEngineUri).CreateClient();
            else
                return new DockerClientConfiguration(defaultLinuxDockerEngineUri).CreateClient();
        }
    }
}

namespace Relationship.Promotion.Applier.IntegrationTest
{
    public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
    {
        private readonly DockerClient dockerClient;
        private readonly PromotionContainer promotionContainer;
        private readonly StoreContainer storeContainer;
        private readonly ConversionContainer conversionContainer;

        public CustomWebApplicationFactory()
        {
            try
            {
                // Alternative to Docker.DotNet
                // https://github.com/Deffiss/testenvironment-docker
                dockerClient = DockerClientFactory.CreateInstance();

                DockerContainerBase.CleanupOrphanedContainers(dockerClient).Wait(300.Seconds());

                if (!dockerClient.Networks.ListNetworksAsync().Result.Select(n => n.Name).Contains("boti-network"))
                {
                    var result = dockerClient.Networks.CreateNetworkAsync(new Docker.DotNet.Models.NetworksCreateParameters()
                    {
                        Name = "boti-network",
                        Driver = "bridge"
                    }).Result;
                }

                promotionContainer = new PromotionContainer(imageName: "promotion-engine-promotion-api");
                promotionContainer.Start(dockerClient).Wait(300.Seconds());

                conversionContainer = new ConversionContainer(imageName: "promotion-engine-conversion-api");
                conversionContainer.Start(dockerClient).Wait(300.Seconds());

                storeContainer = new StoreContainer("promotion-engine-store-api");
                storeContainer.Start(dockerClient).Wait(300.Seconds());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);

                throw;
            }
        }

        protected override IWebHostBuilder CreateWebHostBuilder()
        {
            return base.CreateWebHostBuilder().UseEnvironment("Docker");
        }

        protected override void Dispose(bool itIsSafeToAlsoFreeManagedObjects)
        {
            if (!itIsSafeToAlsoFreeManagedObjects) return;

            promotionContainer.Remove(dockerClient).Wait(300.Seconds());
            conversionContainer.Remove(dockerClient).Wait(300.Seconds());
            storeContainer.Remove(dockerClient).Wait(300.Seconds());

            dockerClient.Dispose();

            base.Dispose(itIsSafeToAlsoFreeManagedObjects);
        }
    }
}