ASP.NET 当 Docker Alpine 映像上的 运行 时,带有 DB2 的核心在第一次请求时以代码 139 退出

ASP.NET Core with DB2 exited with code 139 on first request when running on Docker Alpine image

我正在构建一个 ASP.NET Core 3.1 应用程序,它使用 IBM.Data.DB2.Core-lnx 访问遗留应用程序的数据库。它在使用普通的 Debian 映像时有效。但我想使用 Alpine 来代替较小的图像。我有一些关于丢失文件的错误:

Error loading shared library libpam.so.0: No such file or directory (needed by /app/clidriver/lib/liblibdb2.so)

使用 content search,我找到了包含它们的 Alpine 包。但是该应用程序显示了一个奇怪的行为:它启动并且它是 运行 直到我在浏览器中打开它的网站。打开后,容器退出,错误代码为 139,没有消息:

db2_test_1  | info: Microsoft.Hosting.Lifetime[0]
db2_test_1  |       Now listening on: http://0.0.0.0:5000
db2_test_1  | info: Microsoft.Hosting.Lifetime[0]
db2_test_1  |       Application started. Press Ctrl+C to shut down.
db2_test_1  | info: Microsoft.Hosting.Lifetime[0]
db2_test_1  |       Hosting environment: Development
db2_test_1  | info: Microsoft.Hosting.Lifetime[0]
db2_test_1  |       Content root path: /app
db2test_db2_test_1 exited with code 139

我尝试用虚拟睡眠命令替换我的入口点,并通过使用 docker exec:

在容器中打开 shell 来手动启动应用程序
/app # dotnet DB2Test.dll
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
      Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
      No XML encryptor configured. Key {53574556-0fee-4926-b1f3-6c57cdfd395a} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://0.0.0.0:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /app
Segmentation fault (core dumped)

我不知道为什么会这样。我的 Dockerfile:

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-alpine AS sdk-image
WORKDIR /app
COPY DB2Test.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Debug -o /publish

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine AS runtime-image
WORKDIR /app
COPY --from=sdk-image /publish .
#RUN apk add libxml2-dev openssl-dev zlib zlib-dev libressl libressl-dev libc6-compat linux-pam
RUN apk add libxml2-dev libc6-compat linux-pam

ARG ASPNETCORE_ENVIRONMENT=Development
ENV ASPNETCORE_URLS=http://0.0.0.0:5000
ENV ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT}
ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/app/clidriver/lib"

ENTRYPOINT ["dotnet", "DB2Test.dll"]

以及完整 Debian 构建的 Dockerfile:

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS sdk-image
WORKDIR /app
COPY DB2Test.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Debug -o /publish

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS runtime-image
WORKDIR /app
COPY --from=sdk-image /publish .
RUN apt-get update \
    && apt-get install -y libxml2-dev

ARG ASPNETCORE_ENVIRONMENT=Development
ENV ASPNETCORE_URLS=http://0.0.0.0:5000
ENV ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT}
# https://gist.github.com/StevenLiekens/dacbd8cdef93d20bf7fcfc2bdafbce43
ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/app/clidriver/lib"

ENTRYPOINT ["dotnet", "DB2Test.dll"]

如你所见,基本上是一样的。我只是为 Alpine 安装缺少的 apk 包。

ASP.NET 核心应用程序本身没有什么特别之处:带有两个包的 VS Razor Pages 模板

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Dapper" Version="2.0.78" />
    <PackageReference Include="IBM.Data.DB2.Core" Version="3.1.0.400" Condition="'$(OS)' == 'WINDOWS_NT'"/>
    <PackageReference Include="IBM.Data.DB2.Core-lnx" Version="3.1.0.400" Condition="'$(OS)' == 'UNIX'"/>
  </ItemGroup>

</Project>

IndexModel 打开一个 DB2 连接并从数据库中获取一些数据(它是一个测试数据库,获取了 2 行)

public class IndexModel : PageModel {
    private readonly ILogger<IndexModel> _logger;
    public List<Community> Communities { get; private set; }

    public IndexModel(ILogger<IndexModel> logger) {
        _logger = logger;
    }

    public void OnGet() {
        String MyDb2ConnectionString = "Server=cnx-db2:50000;database=SNCOMM;uid=lcuser;pwd=xx;";
        DB2Connection db = new DB2Connection(MyDb2ConnectionString);
        db.Open();
        Communities = db.Query<Community>(@"SELECT COMMUNITY_UUID as Uid, NAME, LOWER_NAME AS LowerName, DESCRIPTION FROM SNCOMM.COMMUNITY")
            .ToList();
    }
}

我认为崩溃与 IBM 的 DB2 库有关,因为它是在第一次请求期间执行的(在这个测试应用程序中还没有 DI 或其他东西,正如你在上面看到的,它是在 GET 请求时第一次初始化的已执行)。

我对 IBM DB2 并不完全熟悉,但它可能与 Alpine Linux 不兼容,至少在您 运行.[=22= 的特定版本中是这样] 由于您能够确认 DB2 本身存在段错误(退出代码 139 表示 SIGSEGV 发生),因此很可能是这种情况。

Alpine Linux 基于 musl libc library, as opposed to glibc, the GNU C library. A libc library provides the C standard library implementation and the POSIX API and as such it powers most Linux programs, and is a fundamental part of the Linux system. While musl aims to maintain compatibility with glibc, in practice there are many compatibility issues, which will prevent programs compiled on a glibc based Linux distro (such as Ubuntu, Debian, RHEL) running on musl libc based distros (such as Alpine and Void Linux), in the case they are dynamically linked. For example: the default thread stack size is 128KB on musl libc, vs. 4MB on glibc - this is a very likely cause for segfaults in complex software. There are many other subtle differences, covered here.

通过谷歌搜索,我发现了以下 Github issue in the node-ibm_db 项目,其中一位 IBM 项目成员发表了以下评论(正好是 1 年前,20/2/20):

node-ibm_db works on Linux-x86-64 images of amazonlinux, ubuntu and debian. It can not work on alpine as IBM Db2 Client Installers are not compiled on alpine linux using musl libc. So, none of the IBM Driver for Db2 will work on alpine linux.

这似乎证实了 IBM DB2 目前确实不支持 Alpine Linux。