Docker.DotNet 库的使用(一)
缘起:平台开发过程中有个需求,需从一个容器(运行时管理器)中动态创建和管理其他容器(运行实例),并且这些容器处于同个网桥,以便他们能够通过IP或容器名称进行通信。
一、创建运行时管理器
创建运行时管理器 webpai项目,引用 Docker.DotNet 包

初始化客户端:我是windows系统连接本机 docker daemon 使用以下方式,更多的连接方式可以参考dotnet/Docker.DotNet: :whale: .NET (C#) Client Library for Docker API (github.com)
builder.Services.AddSingleton<IDockerClient>(provider =>
{
return new DockerClientConfiguration().CreateClient();
});
先写一个镜像查询api测试一下
using Docker.DotNet;
using Docker.DotNet.Models;
using Microsoft.AspNetCore.Mvc;
namespace RuntimeManager.Controllers;
[ApiController]
[Route("[controller]")]
public class DockerController : ControllerBase
{
private readonly IDockerClient _client;
public DockerController(IDockerClient client)
{
_client = client;
}
[HttpGet(Name = "GetImages")]
public async Task<IEnumerable<ImagesListResponse>> GetImages()
{
IList<ImagesListResponse>? images = await _client.Images.ListImagesAsync(
new ImagesListParameters())
.ConfigureAwait(false);
return images;
}
}
我本地 docker 镜像
swagger 测试结果 可以看到 成功到连接本地 docker 并且可以查询到镜像信息

二、创建运行时实例项目
创建运行时实例项目,这个项目将发布成镜像,运行时管理器,根据这个镜像动态创建容器并与之通信。

创建一个http api 用作内部通信测试,api内容如下:
using Microsoft.AspNetCore.Mvc;
namespace RuntimeManager.RuntimeInstance.Controllers;
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
public IActionResult Get(string testValue)
{
return this.Content($" hello {testValue} ");
}
}
appsettings.json 固定端口:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://+:42286",
"Protocols": "Http1AndHttp2"
}
}
}
}
创建Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
EXPOSE 42286
COPY . .
ENTRYPOINT ["dotnet", "RuntimeManager.RuntimeInstance.dll"]
程序发布制作docker镜像,镜像名称 runtime-instance,供后续运行时管理器使用,docker 命令:
docker build -t runtime-instance .

制作好镜像,我们再回到运行时管理器开发中。
三、docker 运行时功能编写
首先我们创建DockerRuntime.cs 他继承了BackgroundService, 它主要功能为 创建docker容器,返回运行时实例对象,初始化运行时管理网络。
以下代码作用是启动容器后,创建自定义网络,将运行时管理容器加入网络中。
protected async override Task ExecuteAsync(CancellationToken stoppingToken)
{
var containerShortId = Environment.MachineName;
var containerId = (await _dockerClient.Containers.InspectContainerAsync(containerShortId, stoppingToken)).ID;
var response = null as NetworkResponse;
try
{
response = await _dockerClient.Networks.InspectNetworkAsync(_options.Network, stoppingToken);
}
catch (DockerNetworkNotFoundException)
{
await _dockerClient.Networks.CreateNetworkAsync(new() { Name = _options.Network }, stoppingToken);
}
finally
{
if (response == null || !response!.Containers.ContainsKey(containerId))
await _dockerClient.Networks.ConnectNetworkAsync(_options.Network, new NetworkConnectParameters() { Container = containerId }, stoppingToken);
}
}
我们再看一下创建容器代码
public async override Task<IRuntimeInstace> CreateProcessAsync(RuntimeInstance instance, CancellationToken cancellationToken = default)
{
if (instance == null)
throw new ArgumentNullException(nameof(instance));
var containerConfig = new Config();
containerConfig.Env.Add($"RUNTIME_INSTANCEID={instance.Id}");
var createContainerParameters = new CreateContainerParameters(containerConfig)
{
Image = instance.Image,
Name = instance.Name
};
var createContainerResult = await _dockerClient.Containers.CreateContainerAsync(createContainerParameters, cancellationToken);
await _dockerClient.Networks.ConnectNetworkAsync(_options.Network, new NetworkConnectParameters() { Container = createContainerResult.ID }, cancellationToken);
foreach (var warning in createContainerResult.Warnings)
{
this.Logger.LogWarning(warning);
}
return new DockerInstance(createContainerResult.ID, _dockerClient);
}
传入运行时实例,根据运行时实例创建容器,创建成功后将容器加入网络中,返回docker实例对象,以便对这个容器进行操作。
其中还将运行时实例id写入环境变量,对应容器根据这个环境变量启动以区分不同的运行时实例。

浙公网安备 33010602011771号