本文以 WOL 的.NET 项目为例介绍了 Dockerfile基础知识编写要点,旨在帮助读者更好地理解和掌握如何为 .NET 应用创建优化 Dockerfile

1. 背景

前面我们已经勾选了 Docker 容器支持项目已经生成一个默认的 Dockerfile。但在实际项目中,我们需要根据项目的实际需求环境定制化 Dockerfile以便更好地利用 Docker 的优势。本文将以 WOL (Wake On LAN) 项目为例,详细介绍如何编写一个针对 .NET 应用的 Dockerfile 文件,并分享一些实用的编写技巧

2. 从默认模板入手

2.1 默认模板

以下是 .NET8 中 Web API 项目的默认模板,在这个默认模板中,我们可以看到一个典型的多阶段构建过程

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base  
USER app  
WORKDIR /app  
EXPOSE 8080  
EXPOSE 8081  

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build  
ARG BUILD_CONFIGURATION=Release  
WORKDIR /src  
COPY ["WebApplication3/WebApplication3.csproj", "WebApplication3/"]  
RUN dotnet restore "./WebApplication3/./WebApplication3.csproj"  
COPY . .  
WORKDIR "/src/WebApplication3"  
RUN dotnet build "./WebApplication3.csproj" -c $BUILD_CONFIGURATION -o /app/build  

FROM build AS publish  
ARG BUILD_CONFIGURATION=Release  
RUN dotnet publish "./WebApplication3.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false  

FROM base AS final  
WORKDIR /app  
COPY --from=publish /app/publish .  
ENTRYPOINT ["dotnet", "WebApplication3.dll"]

2.2 模板介绍

根据默认模板,我们先来复习一下 Dockerfile基本结构

  1. FROM指定基础镜像,并可以使用 AS 为这个阶段设置别名,如 FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
  2. USER设置运行时的用户,如 USER app
  3. WORKDIR设置工作目录,如 WORKDIR /app
  4. EXPOSE暴露容器需要监听端口,如 EXPOSE 8080EXPOSE 8081
  5. COPY复制文件或目录,如 COPY ["WebApplication3/WebApplication3.csproj", "WebApplication3/"]
  6. RUN执行命令,如 RUN dotnet restore "./WebApplication3/./WebApplication3.csproj"
  7. ARG定义构建参数,如 ARG BUILD_CONFIGURATION=Release
  8. ENTRYPOINT指定容器启动执行命令,如 ENTRYPOINT ["dotnet", "WebApplication3.dll"]

在默认模板中,我们还可以看到一个典型的多阶段构建过程。多阶段构建可以减小最终生成镜像大小提高构建速度。在上述示例中,我们使用了四个阶段,分别命名basebuildpublishfinal用于不同的构建和运行阶段。

3. WOL 项目的 Dockerfile

一般来说使用默认的 Dockerfile 就可以了,但是有时候我们需要根据项目的特定需求和环境对 Dockerfile 进行调整和优化

3.1 额外依赖

项目可能需要一些额外系统依赖或者工具。可以通过 RUN 指令和包管理器(如 aptgetyum 等)来安装这些依赖。有时我们可以在编程中就能发现并意识到,但是更多的时候需要我们在容器化后对项目做好测试工作,确保所有功能都能正常运行。

比如在 WOL 项目的工具类中有写这样一个检查设备是否在线函数

internal static bool Ping(string iP)
{
    // 检查IP是否在线
    Ping ping = new Ping();
    PingReply pingReply = ping.Send(iP,100);
    return pingReply.Status == IPStatus.Success;
}

写起来倒是很简单,也很省事。但是如果不知道其运行原理的话,就会在容器化后遇到问题。这个函数使用System.Net.NetworkInformation.Ping 类来检查目标 IP 地址是否在线,但是其依然是使用系统Ping 工具处理。Docker 的 Base 镜像都是最简版本,为了轻便,都是不包含 Ping 工具的。

为了解决这个问题,我们就需要缺失的依赖安装恢复,Debian 的 Ping 工具包Iputils-ping,在 final 阶段通过 apt 安装即可

3.2 更改安装

在上一节中,我们讨论了安装额外依赖的必要性。为了加速镜像构建过程中的软件包安装,我们可以考虑修改安装源。通常,我们会在物理机上更改镜像源以加速软件包的安装。同样的方法也适用于容器镜像的构建过程。如果不更改安装源,一个镜像的构建可能会花费数小时,这对于开发者来说是难以忍受的。

在使用 mcr.microsoft.com/dotnet/aspnet:8.0 镜像时,底层基于 Debian 系统。为了加速软件包的安装,我们可以更改安装源。在这个例子中,我们将安装源更改为清华大学的镜像源。以下是如何在 Dockerfile 中进行更改的示例

sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list.d/debian.sources && 
apt-get update

这段代码使用 sed 命令修改/etc/apt/sources.list.d/debian.sources 文件中的安装源,将其从 deb.debian.org 更改为了 mirrors.tuna.tsinghua.edu.cn接下来,使用 apt-get update 命令更新软件列表

通过这样的设置,我们可以加速后续软件包的下载和安装过程,从而提高整体镜像构建速度。

3.3 清理垃圾

执行 apt update 和安装软件包之后,我们需要注意清理系统中产生的缓存临时文件。这些文件会增加镜像的大小,而在大多数情况下,它们在运行时并不需要。因此,我们可以在 Dockerfile 中添加相应的指令清理这些垃圾文件,从而减小最终生成的镜像大小

在使用 apt-get 安装软件包后,可以使用以下命令清理缓存

apt-get clean
rm -rf /var/lib/apt/lists/*

这段代码首先使用 apt-get clean 命令清理软件缓存然后使用 rm -rf /var/lib/apt/lists/* 删除下载软件列表。这样一来,我们就可以在构建过程中有效地减小镜像大小

3.4 权限问题

前面已节我们已经按照了系统ping 工具,这样我们就可以正常的使用 System.Net.NetworkInformation.Ping 类来检查目标 IP 地址是否在线。但是在 Linux 系统中,Ping 的实现依赖于 ICMP 协议,而在容器中执行 ICMP 请求通常需要特殊权限,直接容器化运行项目会发现下面的问题:

ping: socktype: SOCK_RAW                                                         
ping: socket: Operation not permitted                                            
ping: => missing cap_net_raw+p capability or setuid?

这是因为为了提高安全性,我们已经将应用的运行用户更改为了 app。为了解决这个问题,我们可以在 Dockerfile 中为更改 ping 命令的权限。

chmod u+s /bin/ping

3.5 编码问题

处理这里,Dockerfile 的文件就基本解决完毕。但是在实际测试中我们会发现,API 接口返回中文会出现乱码的情况:

请添加图片描述

我们可以看到,由配置文件返回中文信息没有出现乱码,而通过代码直接返回中文出现了乱码的情况。这里是因为默认的代码文件编码是 GB18030 ,我们只需要将其改为 UTF-8 ,然后重新制备镜像即可解决这种硬编码导致的乱码问题。

请添加图片描述

4. 要点总结

通过以上的问题重现和处理,我们最终的项目 Dockerfile 如下

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["WakeOnLan/WakeOnLan.csproj", "WakeOnLan/"]
RUN dotnet restore "WakeOnLan/WakeOnLan.csproj"
COPY . .
WORKDIR "/src/WakeOnLan"
RUN dotnet build "WakeOnLan.csproj" -c $BUILD_CONFIGURATION -o /app/build

FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "WakeOnLan.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=true

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list.d/debian.sources 
    && apt-get update 
    && apt-get install -y --no-install-recommends iputils-ping 
    && apt-get clean 
    && rm -rf /var/lib/apt/lists/* 
    && chmod u+s /bin/ping
WORKDIR /app
USER app
EXPOSE 8080
COPY --from=publish /app/publish .
ENTRYPOINT ["./WakeOnLan"]

最后总结在编写 Dockerfile 时需要注意的一些要点

  1. 使用多阶段构建:多阶段构建可以减小最终生成的镜像大小,提高构建速度。在上述示例中,我们使用了三个阶段,分别用于编译发布和运行应用程序

  2. 使用官方镜像:尽量使用官方提供的基础镜像,以确保镜像的安全性可靠性

  3. 优化镜像层:尽量将不会经常变动的文件放在前面,以充分利用镜像层缓存提高构建速度。

  4. 安装额外依赖:根据项目实际需求,安装额外的依赖和工具,确保所有功能正常运行。

  5. 更改安装源:更改镜像源以加速软件包的下载和安装过程,减少构建时间

  6. 清理缓存临时文件清理缓存临时文件,减小生成的镜像大小。

  7. 解决权限问题:确保容器化应用程序安全性,遵循最小权限原则,避免使用 root 用户运行容器。设置运行时用户并调整文件和目录的权限。

  8. 充分测试:对容器化后的项目进行充分的测试工作,以确保所有功能都能正常运行。

通过关注这些要点,我们可以针对实际项目编写和调整 Dockerfile,以便更好地利用 Docker 的优势。同时,对容器化后的项目进行充分的测试工作也是至关重要的。

原文地址:https://blog.csdn.net/marin1993/article/details/134638885

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_23222.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注