查看: 92|回复: 1

Docker 多阶段构建 ASP.NET Core 应用镜像

[复制链接]

5

主题

14

帖子

27

积分

新手上路

Rank: 1

积分
27
发表于 2022-12-9 18:54:01 | 显示全部楼层 |阅读模式
前言

我们一起来写 Dockerfile 构建一个 http://ASP.NET Core 应用镜像,同时还会将镜像发布到 Docker Hub 仓库。

一、创建示例 Web 应用程序

为了演示,我们先创建一个 http://ASP.NET Core 应用程序:
PS D:\Samples> dotnet new web -o AspNetDemo  
已成功创建模板“ASP.NET Core Empty”。  
  
正在处理创建后操作...  
在 D:\Samples\AspNetDemo\AspNetDemo.csproj 上运行 “dotnet restore”...  
  正在确定要还原的项目…  
  已还原 D:\Samples\AspNetDemo\AspNetDemo.csproj (用时 77 ms)。  
已成功还原。
项目创建好了,检查一下看看是否能正常运行:
PS D:\Samples> cd .\AspNetDemo\  
PS D:\Samples\AspNetDemo> dotnet run  
正在生成...  
info: Microsoft.Hosting.Lifetime[14]  
      Now listening on: https://localhost:7000  
info: Microsoft.Hosting.Lifetime[14]  
      Now listening on: http://localhost:5276  
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: D:\Samples\AspNetDemo\
打开输出提示的地址 https://localhost:7000,浏览器正常显示 “Hellow World”,说明应用程序正常。然后我们在 AspNetDemo 目录中添加一个 Dockerfile 文件,为下文作准备。
二、依赖本地环境构建镜像
我们可以在 DockerHub 找到我们需要的 http://ASP.NET Core 运行时基础镜像:


现在,要把我们的 AspNetDemo 应用通过镜像的方式发布,利用我们之前学过的 Docker 知识,我们很自然会想到这样的思路:通过 dotnet publish 命令打包发布文件,然后把发布文件复制到 ASP.NET Core 运行时基础镜像中。
于是我们先在本地(bin/Publish 目录)生成好发布文件:
PS D:\Samples\AspNetDemo> dotnet publish -c release -o bin/Publish  
  正在确定要还原的项目…  
  所有项目均是最新的,无法还原。  
  AspNetDemo -> D:\Samples\AspNetDemo\bin\release\net6.0\AspNetDemo.dll  
  AspNetDemo -> D:\Samples\AspNetDemo\bin\Publish\
然后 Dockerfile 文件可以这样写:
FROM mcr.microsoft.com/dotnet/aspnet:6.0  
WORKDIR /app  
COPY bin/Publish .  
ENTRYPOINT ["dotnet", "AspNetDemo.dll"]
开始构建镜像:
PS D:\Samples\AspNetDemo> docker build -t aspnetdemo .  
[+] Building 0.8s (8/8) FINISHED  
...  
  
PS D:\Samples\AspNetDemo> docker image ls  
REPOSITORY           TAG           IMAGE ID       CREATED          SIZE  
aspnetdemo           latest        62c8c40cbc70   33 seconds ago   208MB
试试在本地使用该镜像运行容器:
PS D:\Samples\AspNetDemo> docker run -d -p 80:80 aspnetdemo  
a4d67637585c67384a6c7a3a9e8a39acc345253730ce22f39b7afdedec353397
打开浏览器访问 localhost 效果如下:


看起来还不错。
三、多阶段构建镜像
相信很多童鞋已经想到了上面依赖本地的开发环境构建镜像存在的问题了。我们前面构建的 aspnetdemo 镜像,是先在本地生成好了发布的文件再复制到镜像里的。这样存在的一个明显问题是,其他人如果环境和我们的不一致,构建的镜像就可能是一个有问题的镜像,甚至直接构建失败。
这种例子很常见,比如同一套代码,在你的机器上可以正常运行,因为环境不同(比如未安装指定的软件、未配置环境变量等),在同事机器上可能就运行不起来。
要避免这种情况,生成发布文件甚至是开发测试的过程,就不能依赖本地的开发环境来做了。即,我们要把生成发布文件的过程也放到 Dockerfile 中去做。
但由于 http://ASP.NET Core 运行时镜像不具有编译的能力,所以我们需要把基础镜像换成 .NET SDK 镜像。这样就可以了吗?这样也不是不可以,但是 .NET SDK 镜像会比 http://ASP.NET Core 运行时镜像大很多,我们可以比较一下:
PS D:\Samples\AspNetDemo> docker image ls  
REPOSITORY                         TAG                IMAGE ID       CREATED             SIZE  
mcr.microsoft.com/dotnet/sdk       6.0                d3863aa157b5   6 days ago          736MB  
mcr.microsoft.com/dotnet/aspnet    6.0                683c56113596   8 weeks ago         208MB
可以看到 .NET SDK 镜像比 http://ASP.NET Core 运行时镜像大了 500 多 MB,这显然会大大降低镜像发布的速度。这时候我们就需要用到多阶段构建了,思路是把镜像的构建分成多个阶段,不同的阶段使用不同的基础镜像,前面的所有阶段都只是为最后一个阶段做准备,最终发布的也是最后一个阶段。
下面使用多阶段构建来改写 Dockerfile,参考如下:
# 阶段一:build  
# 选择 SDK 镜像用于编译源码和生成发布文件  
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build  
WORKDIR /source  
# 复制源代码  
COPY *.csproj *.cs .  
# 生成发布文件  
RUN dotnet publish -c release -o /app  
  
# 阶段二:final  
# 使用 ASP.NET Core 运行时镜像  
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS final  
WORKDIR /app  
# 从 build 阶段复制生成好的发布文件  
COPY --from=build /app .  
ENTRYPOINT ["dotnet", "AspNetDemo.dll"]
这个 Dockerfile 还可以继续优化,我将在下一节课讲镜像的优化时再改写它。
为了观察效果,我们稍微修改一下 Program.cs 中 Http 响应的内容:
var builder = WebApplication.CreateBuilder(args);  
var app = builder.Build();  
  
app.MapGet("/", () => "v2: Hello Docker!");  
  
app.Run();
再次构建并运行容器:
PS D:\Samples\AspNetDemo> docker image build -t aspnetdemo .  
[+] Building 5.8s (13/13) FINISHED  
...  
  
PS D:\Samples\AspNetDemo> docker image ls  
REPOSITORY                    TAG             IMAGE ID       CREATED          SIZE  
aspnetdemo                    latest          d6a596b1514d   55 seconds ago   208MB  
  
PS D:\Samples\AspNetDemo> docker run -d -p 80:80 aspnetdemo  
e9b9045299f5a3b7614cb3cee91b00ebe67a066b1f65eff46369fa1844b1d824
打开浏览器访问 localhost 验证一下效果:


可以看到我们虽然把生成发布文件的过程放到了 Dockerfile 中,但通过多阶段构建,最后构建出来的镜像也是 208M,和前面一样。
所以,我们可以把编译运行所需要的环境配置都写到 Dockerfile 中,这样可以保证任何一台机器都可以顺利构建镜像,且不管谁来构建,相同的源代码构建出来的镜像都是一样的。
四、发布镜像
最后我们可以把构建好的镜像发布到自己的 Docker 仓库,这里以 Docker Hub 为例(实际生产环境请发布到自己的私有仓库)。先在 Docker Hub 创建一个 Repositry:


推送镜像前,需要在本地登录一下:
PS D:\Samples\AspNetDemo> docker login  
Authenticating with existing credentials...  
Login Succeeded
然后给我们的镜像打上一个标签(默认是latest):
PS D:\Samples\AspNetDemo> docker tag aspnetdemo liamwang/aspnetdemo  
# 也可以指定标签:docker tag aspnetdemo:latest liamwang/aspnetdemo:latest
然后推送到远程仓库:
PS D:\Samples\AspNetDemo> docker push liamwang/aspnetdemo  
The push refers to repository [docker.io/liamwang/aspnetdemo]  
e68e6a7d93c2: Pushed  
ace5cec48f84: Pushed  
17aff088b762: Pushed  
9a515fdf7f03: Pushed  
c4d9ca739af5: Pushed  
3f94255da7c2: Pushed  
608f3a074261: Pushed  
latest: digest: sha256:dc479f2e52d48b3a81c0a83b5c740a085b299d046f268d21bb61c5bcfa5ae608 size: 1787  
PS D:\Samples\AspNetDemo>
这步完成后,可以在 Docker Hub 上看到已发布的镜像:


然后我们可以到任意一台服务器 pull 该镜像运行容器了。
五、小结
本节我们以 http://ASP.NET Core 应用为例,先是用依赖本地环境的方式构建了镜像,分析了这种方式存在的问题,然后讲了如何使用多阶段构建来解决这个问题,最后演示了如何把已经构建好的镜像发布到自己的 Docker 镜像仓库。
下节我们来解析和理解镜像的分层,理解镜像的分层可以帮助我们优化镜像的构建过程,也有助于制作更优质的镜像。

其他推荐:
玩转Github:ASP.NET Core入门学习资源汇总
学习C#有没有什么比较系统的资源?
.net core高频面试题有哪些?
玩转Github:强烈推荐这份.NET程序员面试手册,4万字干货!
有哪些不错的windows form开源项目推荐?
回复

使用道具 举报

1

主题

6

帖子

11

积分

新手上路

Rank: 1

积分
11
发表于 6 天前 | 显示全部楼层
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表