pei*_*ent 5 docker azure-cosmosdb
我正在尝试学习 Docker,并慢慢地构建一个解决方案。我创建了一个 Azure Function App,它公开了一个 Http 端点。我创建了 Docker 文件来在 Linux 容器中构建和运行解决方案(图像:mcr.microsoft.com/azure-functions/dotnet:3.0Debian 图像)。我在 Windows 机器上。
我在 Windows 计算机上安装了 Azure CosmosDB 模拟器,并希望从 Linux 容器中运行的函数应用连接到它。
我将 Cosmos 的连接字符串作为环境变量传递。
ARG COSMOS_CONNECTION_STRING="AccountEndpoint=https://host.docker.internal:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
Run Code Online (Sandbox Code Playgroud)
我尝试通过以下代码连接到模拟器:
ARG COSMOS_CONNECTION_STRING="AccountEndpoint=https://host.docker.internal:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
Run Code Online (Sandbox Code Playgroud)
当我这样做时,我收到以下错误(我假设前几行是最相关的,但我将其余行包括在内,以防我错了):
---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
at System.Net.Security.SslStream.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
--- End of stack trace from previous location where exception was thrown ---
at System.Net.Security.SslStream.ThrowIfExceptional()
at System.Net.Security.SslStream.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.SslStream.EndProcessAuthentication(IAsyncResult result)
at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
at System.Net.Security.SslStream.<>c.<AuthenticateAsClientAsync>b__65_1(IAsyncResult iar)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.DocumentClient.HttpRequestMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at Microsoft.Azure.Cosmos.GatewayAccountReader.GetDatabaseAccountAsync(Uri serviceEndpoint)
at Microsoft.Azure.Cosmos.Routing.GlobalEndpointManager.GetDatabaseAccountFromAnyLocationsAsync(Uri defaultEndpoint, IList`1 locations, Func`2 getDatabaseAccountFn)
at Microsoft.Azure.Cosmos.GatewayAccountReader.InitializeReaderAsync()
at Microsoft.Azure.Cosmos.CosmosAccountServiceConfiguration.InitializeAsync()
at Microsoft.Azure.Cosmos.DocumentClient.InitializeGatewayConfigurationReaderAsync()
at Microsoft.Azure.Cosmos.DocumentClient.GetInitializationTaskAsync(IStoreClientFactory storeClientFactory)
at Microsoft.Azure.Cosmos.DocumentClient.EnsureValidClientAsync()
at Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler.EnsureValidClientAsync(RequestMessage request)
at Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler.SendAsync(RequestMessage request, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler.SendAsync(Uri resourceUri, ResourceType resourceType, OperationType operationType, RequestOptions requestOptions, ContainerInternal cosmosContainerble`1 partitionKey, Stream streamPayload, Action`1 requestEnricher, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.ContainerCore.ProcessItemStreamAsync(Nullable`1 partitionKey, String itemId, Stream streamPayload, OperationType operationType, ItemRequestOptions requestOptions, CosmosDiagnostiiagnosticsContext, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.ContainerCore.ExtractPartitionKeyAndProcessItemStreamAsync[T](Nullable`1 partitionKey, String itemId, T item, OperationType operationType, ItemRequestOptions requestOptions, CosmcsContext diagnosticsContext, CancellationToken cancellationToken)
at Microsoft.Azure.Cosmos.ContainerCore.UpsertItemAsync[T](T item, Nullable`1 partitionKey, ItemRequestOptions requestOptions, CancellationToken cancellationToken)
Run Code Online (Sandbox Code Playgroud)
当我在浏览器中本地查看模拟器时,它正在使用localhost证书(我假设使用 dotnet cli 创建的证书)。
尝试 1
将本地主机证书导出为.pfx文件,然后通过以下命令让 Linux 容器信任该文件(在我的 Dockerfile 中)
ARG CERTIFICATE_PASSWORD="Test|234"
RUN openssl pkcs12 \
-in "/src/localhost.pfx" \
-clcerts \
-nokeys \
-out "/src/localhost.crt" \
-passin pass:${CERTIFICATE_PASSWORD}
RUN cp "/src/localhost.crt" "/usr/local/share/ca-certificates/"
RUN update-ca-certificates
Run Code Online (Sandbox Code Playgroud)
我认为此尝试不起作用,至少部分是由于 Windows 计算机上的证书是为 创建的localhost,而要从 docker 连接到它,地址需要为host.docker.internal。
尝试 2
将导出的证书添加到运行函数应用程序的 Kestrel 进程,希望它能够通过将以下内容添加到我的 dockerfile 来遵守它
ENV ASPNETCORE_Kestrel__Certificates__Default__Path=/src/localhost.pfx
尝试 3
更新CosmosClient实例化以包含覆盖的选项,HttpClientFactory如下所示:
await new CosmosClient(Environment.GetEnvironmentVariable("AzureCosmosConnectionString", EnvironmentVariableTarget.Process)
.GetContainer("db_name", "container_name")
.UpsertItemAsync<Dto>(dto)
.ConfigureAwait(false);
Run Code Online (Sandbox Code Playgroud)
经过上述每一次尝试,我仍然看到同样的错误。不知道还有什么可以尝试让它发挥作用......
刚刚在 GitHub 上发现了这个问题。看来我并不孤单。
SDK 现在允许以简单的方式覆盖 SSL 验证(参考https://learn.microsoft.com/azure/cosmos-db/local-emulator?tabs=cli%2Cssl-netstd21#disable-ssl-validation):
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
{
HttpClientFactory = () =>
{
HttpMessageHandler httpMessageHandler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
return new HttpClient(httpMessageHandler);
},
ConnectionMode = ConnectionMode.Gateway
};
CosmosClient client = new CosmosClient(endpoint, authKey, cosmosClientOptions);
Run Code Online (Sandbox Code Playgroud)
参考:
遵循本指南,但证书需要是PFX,而不是 CRT。它会要求您设置一些密码。
例如,我将其放在启动 Docker 时可以映射到的文件夹以及我想要运行的代码中:

根据https://learn.microsoft.com/en-us/azure/cosmos-db/local-emulator#running-on-mac-or-linux,我使用ipconfig并获得了我的 Windows IP。
就我而言,我使用的是 NET Core 3.1 SDK,这是来自https://learn.microsoft.com/en-us/dotnet/architecture/microservices/net-core-net-framework-containers/official-的官方 SDK。网络 docker 图像
我使用交互式 shell 启动了容器,并映射localhost到我在步骤 3 中获得的 IP。这让您可以在连接字符串中使用 localhost。
docker run -v /c/DockerSample:/DockerSample --add-host="localhost:192.168.1.15" -it mcr.microsoft.com/dotnet/core/sdk:3.1 /bin/bash
我还安装了保存项目和要导入的证书的文件夹。这不是必需的,但我对 Docker 不熟悉,不知道是否有更好的方法来通过证书。
shell 启动后,我基本上运行模拟器文档中描述的命令并添加证书。

Docker 容器现在应该具有连接到本地主机所需的证书,并且您不需要 HttpClientFactory。
注意:还有一个错误跟踪 HttpClientFactory 未在任何地方使用,这是错误的根源https://github.com/Azure/azure-cosmos-dotnet-v3/pull/1548
首先,感谢Matias Quaranta提供的所有有用文档。在收到他的回答之前,我已经仔细阅读了其中的大部分内容,但我真的很感激编译它所花费的所有时间。
我已经为此奋斗了大约两周,终于得到了一些我认为可行的东西。这项工作主要基于此GitHub 问题中找到的脚本。我发现当您安装 Cosmos DB 模拟器时,您的计算机上会安装一个 PowerShell 模块,因此我尝试尽可能地利用这些功能来完成这项工作。
脚本的入口点是函数Start-CosmosDbEmulatorForDocker,它
.pfx从为模拟器创建的证书生成一个证书。该函数获取的密码是用于生成的文件的密码.pfx。
azureCosmosDbEmulator.ps1
using namespace System.ServiceProcess
Function Start-CosmosDbEmulatorForDocker(
[Parameter()]
[securestring]
$password
) {
$cosmosDbInstallLocation = Get-CosmosDbInstallLocation
If (!$cosmosDbInstallLocation) {
Install-AzureCosmosDBEmulator
}
Write-Host "Importing Microsoft.Azure.CosmosDB.Emulator powershell module."
Import-Module -Name "$($cosmosDbInstallLocation.InstallLocation)\PSModules\Microsoft.Azure.CosmosDB.Emulator"
Install-CosmosDBDockerCertificate -cosmosDbInstallLocation $cosmosDbInstallLocation.InstallLocation -password $password
Start-CosmosDbEmulator -AllowNetworkAccess -Key "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
}
Function Get-CosmosDbInstallLocation() {
Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall | ForEach-Object { Get-ItemProperty $_.PsPath } | Where-Object { $_.DisplayName -eq "Azure Cosmos DB Emulator" } | Select-Object InstallLocation
}
Function Install-AzureCosmosDBEmulator() {
Write-Host "Installing Azure Cosmos Db Emulator."
$installer = "$PSScriptRoot\cosmosEmulatorInstaller.msi"
curl "https://aka.ms/cosmosdb-emulator" -O $installer
Start-Process -Wait -FilePath msiexec -ArgumentList /i, $installer
Remove-Item $installer
}
Function Install-CosmosDBDockerCertificate(
[Parameter()]
[string]
$cosmosDbInstallLocation,
[Parameter()]
[securestring]
$password
) {
If ((Get-CosmosDbEmulatorStatus) -ne [ServiceControllerStatus]::Stopped) {
Write-Host "Stopping Cosmos DB emulator."
Stop-CosmosDbEmulator
}
$dockerCertificatesPath = Join-Path (Split-Path -Path $PSScriptRoot -Parent) "certificates"
$cosmosDbPfxCertificatePath = "$($dockerCertificatesPath)\cosmosdbemulator.pfx"
Uninstall-Certificate -dockerCertificatePath $cosmosDbPfxCertificatePath
Write-Host "Generating new Cosmos DB certificate to work with Docker."
New-CosmosDbEmulatorCertificate "host.docker.internal"
Start-Sleep -s 5
New-DockerCertificate -dockerCertificatePath $cosmosDbPfxCertificatePath -password $password
Set-Location (Split-Path -Path $PSScriptRoot -Parent)
}
Function Uninstall-Certificate(
[Parameter()]
[string]
$dockerCertificatePath
) {
Write-Host "Removing existing DocumentDbEmulatorCertificate certificate."
if (Test-Path $dockerCertificatePath) {
Remove-Item -Path $dockerCertificatePath
}
}
Function New-DockerCertificate(
[Parameter()]
[string]
$dockerCertificatePath,
[Parameter()]
[securestring]
$password
) {
Write-Host "Generating new pfx version of DocumentDbEmulatorCertificate certificate for use in Docker image."
Get-CosmosDbEmulatorCertificate | Export-PfxCertificate -Filepath $dockerCertificatePath -Password $password
}
Run Code Online (Sandbox Code Playgroud)
然后,我在 Docker 映像内运行一个 shell 脚本,将证书安装.pfx到 Docker 容器中。该COSMOS_DB_EMULATOR_PFX_PASSWORD值必须与 PowerShell 脚本使用的值匹配。
trust_cosmos_db_emulator_crt.sh
#!/bin/bash
# Save current working directory
PWD=`pwd`
pushd $PWD
# Find and move to the location of this script
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $DIR
if [ -n "$1" ]; then
COSMOS_DB_EMULATOR_PFX=$1
else
COSMOS_DB_EMULATOR_PFX="/certificates/cosmosdbemulator.pfx"
fi
COSMOS_DB_EMULATOR_PFX_PASSWORD="SUPER_SECRET_PASSWORD"
CERT_TO_TRUST="cosmosdbemulator.crt"
# Generate .crt file if pfx exists
if [ -f "$COSMOS_DB_EMULATOR_PFX" ]; then
openssl pkcs12 -in $COSMOS_DB_EMULATOR_PFX -clcerts -nokeys -out cosmosdbemulator.crt -passin pass:$COSMOS_DB_EMULATOR_PFX_PASSWORD;
fi
# # Trust Cert (will end located in /etc/ssl/certs/ based on *.crt name as a *.pem, e.g. /etc/ssl/certs/cosmosdbemulator.pem for cosmosdbemulator.crt)
if [ -f "$CERT_TO_TRUST" ]; then
cp $CERT_TO_TRUST /usr/local/share/ca-certificates/
update-ca-certificates
rm $CERT_TO_TRUST;
fi
# Restore working directory
popd
Run Code Online (Sandbox Code Playgroud)
我的项目结构如下:
src/
scripts/
azureCosmosDbEmulator.ps1
trust_cosmos_db_emulator_crt.sh
certificates/
DockerFile
Run Code Online (Sandbox Code Playgroud)
Dockerfile 包含以下几行:
COPY ["/scripts/", "/scripts/"]
COPY ["/certificates/", "/certificates/"]
RUN /scripts/trust_cosmos_db_emulator_crt.sh
Run Code Online (Sandbox Code Playgroud)
完成所有这些后,我可以构建 docker 映像,docker build -t temp .然后运行它,docker run -it -p 80:80 temp并且 docker 容器内运行的代码将与本地计算机安装的 Azure Cosmos DB 模拟器版本进行通信。
由于这是一个巨大的痛苦,如果您也遇到这种痛苦,请在此处投票支持 Microsoft 对此提供更好的支持。
| 归档时间: |
|
| 查看次数: |
6002 次 |
| 最近记录: |