TargetFrameworksを用いると複数のフレームワークに対応したライブラリを作成できます。今回は、このTargetFrameworksを指定しつつDockerビルドする方法を紹介します。 なお、コンテナで複数ランタイムを持つ意義はほぼないので、この記事はただの興味本位です。
TargetFrameworksでDockerfileは追加できない
TargetFrameworkだと、Visual StudioでAdd > Docker Support...
メニューを選べます。
しかし、TargetFrameworksを指定しているとVisual StudioでAdd > Docker Support...
メニューがなくなります。
とはいえDockerfileを手動で作成したり、TargetFrameworkの時にDockerfileを追加しておけばDockerfileを追加できます。 今回はTargetFrameworkの時に追加していたDockerfileを使います。
Dockerfileに任意のSDKを追加する
Visual Studioで作成したDockerfileは2レイヤーの構成です。それぞれでSDKとRuntimeをインストールしています。
- ビルドフェーズ: プロジェクトをDockerコンテナでビルド (.NET SDK)
- ファイナルフェーズ: ビルド成果物を実行するための最小限の環境を提供 (.NET Runtime)
この時点で察しがつく通り、SDKをビルドフェーズに追加すればいいのですが、コンテナにおいて異なるランタイムバージョンを同時に実行できる必然性はありません。このため、本記事自体はさしたる意味を持ちませんが知りたいですよね?1見てみましょう。
TargetFrameworkを指定しているプロジェクト
例えばSerialization.Benchmark.csprojがありnet9.0向けとしましょう。プロジェクト参照などをしていると、よくある感じのDockerfileになります。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net9.0</TargetFramework> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> <DockerfileContext>..\..\..</DockerfileContext> </PropertyGroup> <ItemGroup> <PackageReference Include="BenchmarkDotNet" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\Serialization.Core\Serialization.Core.csproj" /> </ItemGroup> </Project>
# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. # This stage is used when running from VS in fast mode (Default for Debug configuration) FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base USER app WORKDIR /app # This stage is used to build the service project FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build ARG BUILD_CONFIGURATION=Release WORKDIR /src COPY ["Directory.Build.props", "src/Directory.Build.props"] COPY ["src/Serialization/Serialization.Benchmark/Serialization.Benchmark.csproj", "src/Serialization/Serialization.Benchmark/"] COPY ["src/Serialization/Serialization.Core/Serialization.Core.csproj", "src/Serialization/Serialization.Core/"] RUN dotnet restore "./src/Serialization/Serialization.Benchmark/Serialization.Benchmark.csproj" COPY . . WORKDIR "/src/src/Serialization/Serialization.Benchmark" RUN dotnet build "./Serialization.Benchmark.csproj" -c $BUILD_CONFIGURATION -o /app/build -f net9.0 # This stage is used to publish the service project to be copied to the final stage FROM build AS publish ARG BUILD_CONFIGURATION=Release RUN dotnet publish "./Serialization.Benchmark.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false -f net9.0 # This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration) FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "Serialization.Benchmark.dll"]
TargetFrameworksに切り替え
csprojのTargetFramework
をTargetFrameworks
に書き換えて、net8.0を追加します。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFrameworks>net8.0;net9.0</TargetFrameworks> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> <DockerfileContext>..\..\..</DockerfileContext> </PropertyGroup> <ItemGroup> <PackageReference Include="BenchmarkDotNet" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\Serialization.Core\Serialization.Core.csproj" /> </ItemGroup> </Project>
ポイントはDockerfileに追加したCOPY --from=mcr.microsoft.com/dotnet/sdk:8.0 /usr/share/dotnet/sdk /usr/share/dotnet/sdk
です。これを指定すると、リモート先のdotnet/sdk8.0イメージから/usr/share/dotnet/sdkをコピーします。
これまでDockerfileでツールを追加する方法はいろいろ変遷を辿ってきました。が、現在の主流は本記事の「既存の公式イメージから必要なファイルだけ持ってくる」方法となっており、curlやaptを使って持ってきたりするより使いやすくオススメです。
# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. # This stage is used when running from VS in fast mode (Default for Debug configuration) FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base USER app WORKDIR /app # This stage is used to build the service project FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build ARG BUILD_CONFIGURATION=Release WORKDIR /src COPY --from=mcr.microsoft.com/dotnet/sdk:8.0 /usr/share/dotnet/sdk /usr/share/dotnet/sdk COPY ["Directory.Build.props", "src/Directory.Build.props"] COPY ["src/Serialization/Serialization.Benchmark/Serialization.Benchmark.csproj", "src/Serialization/Serialization.Benchmark/"] COPY ["src/Serialization/Serialization.Core/Serialization.Core.csproj", "src/Serialization/Serialization.Core/"] RUN dotnet restore "./src/Serialization/Serialization.Benchmark/Serialization.Benchmark.csproj" COPY . . WORKDIR "/src/src/Serialization/Serialization.Benchmark" RUN dotnet build "./Serialization.Benchmark.csproj" -c $BUILD_CONFIGURATION -o /app/build -f net9.0 # This stage is used to publish the service project to be copied to the final stage FROM build AS publish ARG BUILD_CONFIGURATION=Release RUN dotnet publish "./Serialization.Benchmark.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false -f net9.0 # This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration) FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "Serialization.Benchmark.dll"]
これでTargetFrameworksを指定しつつDockerビルドできるようになりました。
$ docker build -t foo -f .\src\Serialization\Serialization.Benchmark\Dockerfile .
まとめ
これ、何度も言いますがあまり意味はないんですよね。Dockerコンテナはシンプルなランタイムサイズにするという原則からも、普段使う必要がないSDKを追加しているのはあまりよくないです。Visual Studioがサポートしていないのも理解できますし、今後もサポートするはずないと考えています。
ただ、Dockerfileで任意のイメージから必要なファイルをコピーする手法は.NET SDKじゃなくとも汎用的に利用できる方法なので覚えておくといいですね。
- ランタイムにも追加してnet8.0とnet9.0の実行を切り替えられるように、などは考えられますが、コンテナ運用的にイメージ分けるほうがいいです。↩