在 dotnet runtime 的容器中安装 dotnet global tool
Intro
.NET Core 从 2.1 开始支持 Global tool, 借助 global tool 我们可以通过命令行来实现很多功能,微软提供的一系列的 dotnet 诊断工具也都提供了 global tool,我们可以通过 global tool 比较方便的进行使用,
但是 global tool 默认是只能装了 SDK 之后才能装,在实际的生产环境我们一般都是使用只包含 runtime 的 docker 镜像,没有 SDK 就不能直接安装 global tool,那我们要怎么做才能在只有 runtime 的 docker 镜像中使用 global tool 呢?且看下面的介绍
Global tool
dotnet global tool 是框架依赖发布的,所以是需要依赖运行时的,如果 dotnet tool 依赖的运行时找不到的时候,会尝试使用高版本的 runtime,遵循前滚(roll-forward)规则
应用程序前滚至指定的主要版本和次要版本的最高修补程序版本。
如果主要版本号和次要版本号没有匹配的运行时,则使用下一个较高的次要版本。
前滚不会发生在 runtime 的预览版本,也不会发生在预览版和正式版之间。因此,使用预览版创建的 .NET global tool 必须由作者重新生成和重新发布,再重新安装。
在下面两种常见的场景下默认不会发生 roll-forward :
只有低版本运行时可用时,roll-forward 只会选择之后的版本,低版本不会被选择
只有更高的主要版本运行时可用时,roll-forward 默认不会跨越主要版本的边界,主要版本发生变化有些 API 可能会有不兼容的变更
如果找不到一个合适的 runtime 版本,会运行失败并抛出错误信息。
我们可以通过 dotnet --info
或者 dotnet --list-runtimes
来查看已有的 runtime 信息
Install dotnet global tool
通过 dotnet tool install --global dotnet-dump
我们就可以安装 dotnet-dump 这个 global tool 了,但是就像前面提到的,我们必须要安装 SDK 才能安装 global tool,但是其实 global tool 运行的时候只依赖于 runtime,这就有点“悖论”了,明明我只需要 Runtime 就可以运行的,但是要安装 SDK 才能安装,在 Github 上提了一个 issue,有需要的可以关注一下:https://github.com/dotnet/sdk/issues/18168
在网上 Google 之后就会发现有一些解决方案,大体上分为两类,一种是直接安装 SDK 或使用 SDK 的环境,第二种则是在 SDK 的环境下安装 global tool,装好之后把 global tool 拷贝到只有 runtime 的环境中
Docker practice
在 docker 环境中,我们可以结合默认的多阶段构建的方案,在 build 镜像中安装 dotnet global tool,在最后拷贝发布内容的时候同时也拷贝 dotnet global tool
Dockerfile
示例如下:
FROM mcr.microsoft.com/dotnet/sdk:5.0-alpine AS build-env
WORKDIR /app# install dotnet tool
RUN dotnet tool install --global dotnet-dumpCOPY SparkTodo.Shared/SparkTodo.Shared.csproj SparkTodo.Shared/
COPY SparkTodo.API/SparkTodo.API.csproj SparkTodo.API/
RUN dotnet restore SparkTodo.API/SparkTodo.API.csproj# copy everything and build
COPY . .WORKDIR /app/SparkTodo.API
RUN dotnet publish -c Release -o out# build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:5.0-alpine
LABEL Maintainer="WeihanLi"
WORKDIR /app
COPY --from=build-env /app/SparkTodo.API/out .
COPY --from=build-env /root/.dotnet/tools /root/.dotnet/tools
ENV PATH="/root/.dotnet/tools:${PATH}"
EXPOSE 80
ENTRYPOINT ["dotnet", "SparkTodo.API.dll"]
通过多阶段构建的方式,我们可以避免直接使用特别大的 SDK 镜像,通过这种方式安装 dotnet tool 镜像只会增加几十M的大小(我装了一个 dotnet-dump,具体还是要看 dotnet tool 的大小)
通过 docker run --rm --name sparktodo sparktodo-api
运行一个容器,然后通过 docker exec -it sparktodo sh
进入到容器内部,然后就可以测试我们安装的 dotnet global tool 了
可以看到我们安装的 dotnet global tool 已经可以正常使用了
More
我们在 Dockerfile
里安装了 dotnet global tool 并使用了默认的 dotnet tool 的路径,并配置了环境变量以便于可以直接使用 dotnet global tool,如果需要也可以配置 dotnet tool 的安装路径,通过 dotnet tool install --global dotnet-dump --tool-path /usr/bin
来指定自定义的路径
References
https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools
https://docs.microsoft.com/en-us/dotnet/core/tools/troubleshoot-usage-issues
https://docs.microsoft.com/en-us/dotnet/core/deploying/#publish-framework-dependent
https://andrewlock.net/running-net-core-global-tools-in-non-sdk-docker-images/
https://github.com/dotnet/sdk/issues/18168
https://github.com/WeihanLi/SparkTodo/blob/master/Dockerfile