问题描述
在使用Docker时,遇到了一个问题。他通常在Linux上运行所有的服务作为Docker容器。最近,他收到了一个来自一家大公司的订单,要求将所有应用程序日志显示在主机机器的EventLog中。他们的管理员对Docker不太了解,并且不希望在此部署中使用集中式日志记录解决方案,如Kibana。用户想知道在Windows上是否有可行的方法来实现这一点。
解决方案
请注意以下操作注意版本差异及修改前做好备份。
方案1
在Windows上(Dotnet Core),可以使用PowerShell远程调用来实现。以下是具体步骤:
1. 在容器中,需要通过以下命令设置一个受信任的主机:
RUN @powershell Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*' -Force
最好将*
替换为主机的实际IP地址。请注意,它在系统重新启动时可能会更改。
2. 在主机上,需要创建一个名为MyLogger
的事件源。
3. 在应用程序中,可以创建一个自定义日志记录器,并将其添加到Microsoft Logging管道中。以下是示例代码:
using System.Management.Automation;
using System.Net;
using Microsoft.Extensions.Logging;
namespace LogWriter
{
public class RemoteEventLogProvider : ILoggerProvider
{
private readonly string _computer;
private readonly PowerShell _powershell;
private readonly NetworkCredential _credential;
public RemoteEventLogProvider(string computer, NetworkCredential credential)
{
_computer = computer;
_credential = credential;
_powershell = PowerShell.Create();
}
public ILogger CreateLogger(string category)
{
return new RemoteEventLogger(_computer, _credential, _powershell, category);
}
public void Dispose()
{
_powershell.Dispose();
}
}
public class RemoteEventLogger : ILogger
{
private readonly string _computer;
private readonly string _category;
private readonly PowerShell _powershell;
private readonly NetworkCredential _credential;
public RemoteEventLogger(string computer, NetworkCredential credential, PowerShell powershell, string category)
{
_computer = computer;
_category = category;
_credential = credential;
_powershell = powershell;
}
public IDisposable BeginScope<TState>(TState state)
{
throw new NotImplementedException();
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
var script = ScriptBlock.Create($"$password = ConvertTo-SecureString '{_credential.Password}' -AsPlainText -Force;" +
$"$credential = New-Object System.Management.Automation.PSCredential ('{_credential.UserName}', $password);" +
$"Invoke-Command -ScriptBlock {{ Write-EventLog -EntryType {logLevel} -EventId 0 -Source MyLogger -LogName Application -Message \"{formatter(state, exception)}\" }} -ComputerName {_computer} -Credential $credential;");
var results = _powershell.AddScript(script.ToString()).Invoke();
_powershell.Commands.Clear();
}
}
}
- 在应用程序的入口点中,使用上述日志记录器。以下是示例代码:
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace LogWriter
{
class Program
{
static async Task Main(string[] args)
{
var config = new ConfigurationBuilder()
.AddCommandLine(args)
.AddEnvironmentVariables()
.Build();
var computer = config["ConnectionStrings:Host.Address"];
var provider = new RemoteEventLogProvider(computer, new NetworkCredential($"{computer}\\username", "password"));
using (var factory = new LoggerFactory().AddConsole())
{
factory.AddProvider(provider);
var logger = factory.CreateLogger("MyLogger");
logger.LogInformation($"Remote log at {computer}");
while (true)
{
try
{
logger.LogInformation($"The time is {DateTime.UtcNow}");
if (DateTime.UtcNow.Second % 2 == 0)
{
throw new Exception("random shit");
}
else
{
logger.LogWarning("Example warning");
}
}
catch (Exception ex)
{
logger.LogError(ex, ex.Message);
}
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
}
}
}
- 创建一个Dockerfile,用于构建和运行应用程序。以下是示例代码:
# Build
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env
WORKDIR /app
COPY . /app
RUN dotnet publish -c Release -o /app/out
# Runtime
FROM mcr.microsoft.com/windows/servercore:ltsc2019
RUN curl -o c:\dotnet.exe https://download.visualstudio.microsoft.com/download/pr/a9bb6d52-5f3f-4f95-90c2-084c499e4e33/eba3019b555bb9327079a0b1142cc5b2/dotnet-hosting-2.2.6-win.exe
RUN c:\dotnet.exe /quiet /install
# Host
RUN @powershell Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*' -Force
# Application
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT dotnet LogWriter.dll
请注意,上述代码中的ConnectionStrings:Host.Address
应替换为实际的主机地址。
方案2
使用脚本或工具来管理容器的启动顺序可能会增加复杂性,并且需要确保容器A和容器B之间的依赖关系正确设置。
另一种方法是编写脚本或使用工具来控制容器的运行顺序。你可以使用docker run
命令来手动控制容器的启动顺序,或者使用一些第三方工具来管理容器的依赖关系。
示例:
以下是一个简单的bash脚本示例,可以在容器A启动后启动容器B:
#!/bin/bash
# 启动容器A
docker run -d --name container_a your_image_a
# 等待容器A完全启动
while ! docker exec container_a echo "Container A is ready"; do
sleep 1
done
# 启动容器B
docker run -d --name container_b your_image_b
在这个示例中,我们首先使用docker run
命令启动容器A,并将其命名为container_a
。然后,使用一个循环来等待容器A完全启动(这里是通过在容器内运行echo
命令来测试)。一旦容器A就绪,我们再使用docker run
命令启动容器B,并将其命名为container_b
。