.Net 6.0 Web API 项目生成镜像并上传到私有仓库 Harbor

〇、前言

本文首先简单介绍了 Dockerfile 内容和常用命令;

然后是在 Windows 环境 Docker desktop 的安装和配置;

最后创建了 Web API 示例项目,并简单说明了从构建到推送至 Harbor 镜像仓库的步骤。

一、关于 Dockerfile

1.1 Dockerfile 文件示例

#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.

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app

# EXPOSE 811
# EXPOSE 443
# EXPOSE 可以注释掉,以环境变量 ASPNETCORE_URLS 配置为准
ENV ASPNETCORE_URLS=http://+:811

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["Test.WebApplication1.ImgTest6.0.csproj", "."]
RUN dotnet restore "./Test.WebApplication1.ImgTest6.0.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "Test.WebApplication1.ImgTest6.0.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "Test.WebApplication1.ImgTest6.0.csproj" -c Release -o /app/publish /p:UseAppHost=false

# 设置时间为中国上海
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 设置环境为开发环境
ENV ASPNETCORE_ENVIRONMENT=Development

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

1.2 常见命令

1.2.1 FROM 指定基础镜像或创建新的镜像阶段

// 格式
FROM <image>  # 没有 tag 或 digest 时,默认使用 latest 版本
FROM <image>:<tag>  # tag 标签
FROM <image>:<digest>  # digest 摘要(通常是一个SHA256哈希值)
// 例如
FROM nginx
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
FROM ubuntu:18.04@sha256:3c7f9d4a8b1f3e4e8b1f3e4e8b1f3e4e8b1f3e4e8b1f3e4e8b1f3e4e8b1f3e4

Dockerfile 文件首行命令一定是 FROM 指令。当同时需要多个进出镜像时,可重复使用。

FROM 还有另外一个用途,为镜像创建一个副本,标识一个镜像阶段。

例如下面示例中的第二行命令:

# 【构建阶段】
# 使用 dotnet build 命令编译项目,此阶段的目的是编译源代码,准备用于发布
RUN dotnet build "Test.WebApplication1.ImgTest6.0.csproj" -c Release -o /app/build
# 【准备发布】
# “publish”阶段是通过本命令创建的,这一阶段从“build”阶段继承了编译好的代码,然后准备发布
FROM build AS publish
# 【发布阶段】
# 使用 dotnet publish 命令将应用程序发布到 /app/publish 目录
# 发布操作包括优化应用程序以减少其大小、生成运行时依赖项等
RUN dotnet publish "Test.WebApplication1.ImgTest6.0.csproj" -c Release -o /app/publish /p:UseAppHost=false

1.2.2 WORKDIR 设定工作目录

类似命令行中的 cd 命令,设定之后后续的命令相当于在工作目录中运行

# 格式
WORKDIR /usr/workdir
# 示例
WORKDIR /a  # (这时工作目录为:/a)
WORKDIR b   # (这时工作目录为:/a/b)
WORKDIR /c  # (这时工作目录为:/c)

1.2.3 EXPOSE 指定与外界交互的端口

# 格式
EXPOSE 80
EXPOSE 443

此命令只是声明容器打算使用什么端口,它并不会实际改变容器的网络设置。

也可以不配置 EXPOSE 参数来指定暴露的端口,可以通过配置环境变量的方式指定容器端口,例如:ENV ASPNETCORE_URLS=http://+:81

1.2.4 ENV 设置环境变量

这个命令非常简单,就是用于设置环境变量而已,无论是接下来的指令,还是在容器中运行的程序,都可以使用这里定义的环境变量。

# 格式
ENV <key>=<value>
# 示例,这个环境变量通常用于指定应用程序监听的 URL
ENV ASPNETCORE_URLS=http://+:81
# 表示应用程序将接受来自任何 IP 地址(由 + 表示)的连接请求,并使用端口 81 进行通信

1.2.5 COPY 拷贝文件

这个命令可以拷贝当前宿主机的文件,也可以拷贝当前文件夹中编译后的文件到当前镜像。

# 第一个参数指的是源文件路径,【.】表示当前文件夹
# 第二个参数指的是目标路径,【.】表示将宿主机当前文件夹中的全部文件进行拷贝
COPY . .
# 使用【--from=publish】表示,从 publish 的镜像中拷贝文件,这里的 publish 是引入镜像时指定的别名
# 【/app/publish】是 publish 镜像的文件路径
# 最后这个【.】是指要拷贝到当前镜像来的目录,.表示当前路径,一般也是配合WORKDIR使用
COPY --from=publish /app/publish .

1.2.6 RUN 构建镜像

构建镜像的时候需要安装其他软件或者编译文件的命令都可以通过 RUN 命令执行。

# 格式
RUN <command>  # shell 执行
RUN ["executable", "param1", "param2"]  # exec 执行

多条命令可以用 && 来连接,以发布 ASP.NET Core 项目为例,将代码拷贝到镜像之后,需要进行 restore、build、publish,就可以在这里使用,例如:

RUN dotnet restore "./Test.WebApplication1.ImgTest6.0.csproj" && dotnet build "./Test.WebApplication1.ImgTest6.0.csproj" -c Release -o /app/build && dotnet publish "./Test.WebApplication1.ImgTest6.0.csproj" -c Release -o /app/publish /p:UseAppHost=false

1.2.7 ENTRYPOINT 容器启动命令

指定容器启动的要运行的命令,可以追加命令

# 格式
ENTRYPOINT ["executable", "param1", "param2"] 
ENTRYPOINT command param1 param2  # shell 内部命令
# 示例
ENTRYPOINT ["dotnet", "Test.WebApplication1.ImgTest6.0.dll"]
# 相当于在命令行执行 dotnet Test.WebApplication1.ImgTest6.0.dll 这个命令

注意:只允许有一个 ENTRYPOINT 命令,多指定时会覆盖前面的设置,而只执行最后的 ENTRYPOINT 指令

1.2.8 VOLUME 指定持久化目录

用于创建一个数据卷(volume),它允许将容器内的目录或文件与宿主机进行共享,卷会一直存在,直到没有任何容器在使用它。

通过使用数据卷,可以在容器之间共享数据,或者在容器重启时保留数据。对卷的修改会立即生效,但不会对镜像产生影响。

# 格式
VOLUME ["/path/to/dir"]
# 示例
VOLUME ["/data"]  # 创建了一个名为/data的数据卷
VOLUME ["/var/www", "/var/log/apache2"]  # 同时创建两个数据卷

注意:VOLUME 指令只是声明了数据卷的位置,并不会实际创建它们。数据卷的创建是在容器运行时进行的,通常是通过 docker run 命令或 Docker Compose 等工具来完成。 

参考: https://blog.csdn.net/WuLex/article/details/113730475    https://blog.csdn.net/guojiaqi_/article/details/135909376

二、构建镜像并推送到 Harbor

2.1 Docker 安装

2.1.1 环境准备

  • 启用相关 Windows 功能

打开“控制面板”,进入“程序和功能”,勾选“Hyper-V”和“适用于 Linux 的 Windows 子系统”:

安装完成后,按照提示需要重启。

  • 安装 wsl

WSL(Windows Subsystem for Linux)是 Windows 10 的一个子系统,它允许用户在 Windows 上运行 Linux 发行版。

通过 WSL,用户可以在 Windows 上安装和运行 Linux 命令行工具、应用程序和环境,而无需使用传统的虚拟机或双启动设置。

wsl --install

检查是否安装成功

如下图运行wsl --list --verbose或简化为wsl -l -v,列出所有已安装的WSL分发版及其版本号,以及如何进入 wsl 命令模式:

另外,如何实现 WSL 和 Windows 文件系统互相访问?

在 WSL 中,你可以通过/mnt/目录访问 Windows 文件系统。例如,要在 WSL 中访问 C 盘的某个文件,可以使用以下路径:

/mnt/c/path/to/your/file.txt

在Windows中,你可以通过\\wsl$\路径访问WSL文件系统。例如,要在Windows中访问WSL的某个文件,可以使用以下路径:

\\wsl$\your_linux_distro\home\your_username\path\to\your\file.txt

2.1.2 环境准备中遇到的问题

1/3)安装 wsl 过程中出现如下报错,输出全是 ????? 报错提示不明确

C:\WINDOWS\system32>wsl --install
正在安装: Ubuntu
已安装 Ubuntu。
正在启动 Ubuntu...
Installing, this may take a few minutes...
WslRegisterDistribution failed with error: 0x80370114
Error: 0x80370114 ??????????????????
Press any key to continue...
分发“Ubuntu”的安装过程失败,退出代码: 1。
Error code: Wsl/InstallDistro/WSL_E_INSTALL_PROCESS_FAILED

解决步骤如下:

在 win10 搜索框搜索“应用和浏览器控制”,进入该界面
点击“Exploit Protection设置”
切换到"程序设置"
下拉找到“C:\WINDOWS\System32\vmcompute.exe”,点击编辑
找到“控制流保护(CFG)”,取消下面的“替代系统设置”。
打开 windows 命令行 ,输入“net start vmcompute”

参考:https://blog.csdn.net/gyjjj12/article/details/115531298

2/3)上边最后一部重启 vmcompute 时提示系统错误 1058

C:\WINDOWS\system32>net start vmcompute 
发生系统错误 1058。
无法启动服务,原因可能是已被禁用或与其相关联的设备没有启动。

解决方法:

  服务【Hyper-V 主机计算服务】被禁用了或没有运行,开启即可。

3/3)检查下 Hyper-V 相关服务开启情况

2.1.3 下载和安装

官网下载 Docker Desktop Installer.exe:https://www.docker.com/products/docker-desktop/

双击下载安装包(Docker Desktop Installer.exe)进行安装,如下图,第一项建议勾选:

然后点击 OK 按钮,完成安装。

另外,关于 WSL 2 和 Hyper-V:

WSL 2 相较于 Hyper-V 有更低的资源占用;
WSL 2 允许用户在不启动完整虚拟机的情况下运行容器,这简化了操作并减少了系统的复杂性;
对于仅需要运行 Docker 容器的用例,WSL 2 提供了足够且优化过的支持;
如果需要使用其他虚拟机或需要精细控制 Docker 资源分配,则可能需要依赖 Hyper-V;
当使用 WSL 2 作为后端时,Docker 镜像和容器无法在不同的 Windows 用户账户之间共享。

验证是否安装成功

直接打开或以管理员身份运行 Docker Desktop。

启动后 Docker 是 running 状态,如下图:

如下简单操作测试 Docker 是否安装成功:

# 查看版本
C:\WINDOWS\system32>docker -v
Docker version 27.1.1, build 6312585
# 尝试拉取测试镜像 hello-world
C:\WINDOWS\system32>docker pull hello-world
Using default tag: latest
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:1408fec50309afee38f3535383f5b09419e6dc0925bc69891e79d84cc4cdcec6
Status: Downloaded newer image for hello-world:latest
docker.io/library/hello-world:latest
...
# 运行测试镜像 hello-world,成功输出:Hello from Docker!
C:\WINDOWS\system32>docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.
# 查看当前全部镜像
C:\WINDOWS\system32>docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
hello-world   latest    d2c94e258dcb   15 months ago   13.3kB

2.1.4 Docker 必要的配置

  • 国内镜像仓库配置

在 Docker-->Setting-->Docker Engine 模块的配置中,新增配置项"registry-mirrors":(推荐使用阿里云的镜像地址,后边有详解如何获取)

{
	"builder": {
		"gc": {
			"defaultKeepStorage": "20GB",
			"enabled": true
		}
	},
	"experimental": false,
	"registry-mirrors": [
		"http://hub-mirror.c.163.com",
		"https://docker.mirrors.ustc.edu.cn"
	]
}

其中,"http://hub-mirror.c.163.com"为网易云镜像地址;"https://docker.mirrors.ustc.edu.cn"为中国科学大学镜像地址。

最后点击“apply & restart”会自动重启 Docker,使新配置项生效。也可以通过命令docker info来查看当前的全部配置项。

推荐使用个人阿里云镜像地址,需要单独申请,方法:登录地址【https://cr.console.aliyun.com】,然后如下图操作,没有申请过的话需要申请下:

  

具体的配置,可以参考对应系统的操作文档,来更新配置。

2.2 创建 Web API 项目

在编辑器里创建新的项目,如下图勾选配置:

项目创建以后,会自动生成一个 Dockerfile 文件,需要对此文件做相应修改,详见本文 1.1 Dockerfile 文件示例

如下示例文件目录:

2.3 镜像编译并推送到 Harbor 镜像库

首先在项目文件夹,打开命令行工具,Shift+鼠标右键,选择“在此处打开 Powershell 窗口”,打开后,输入 cmd,进入命令行模式。

第一步:镜像构建

通过 build 语句,构建项目:

docker build -t test-dotnet/testwebapplication1imgtest60:stage_v1.0 .

其中,test-dotnet 表示项目名称,test-dotnet/testwebapplication1imgtest60 表示镜像名称,冒号后边的 stage_v1.0 是给镜像打的标签。

第二步:打标签

将本地的镜像对应到私有 Harbor 仓库中的镜像:(harbor.xxxx.com 为仓库的域名,需要更换成自己的)

docker tag test-dotnet/testwebapplication1imgtest60:stage_v1.0 harbor.xxxx.com/test-dotnet/testwebapplication1imgtest60:stage_v1.0

随后一步:将本地镜像推送到远程仓库

// 先登录
docker login harbor.xxxx.com
// 登录成功后,再推送
docker push harbor.xxxx.com/test-dotnet/testwebapplication1imgtest60:stage_v1.0

最后再登录 Harbor 进行仓库,查看对应标签的镜像是否已经上传成功。

确认推送成功后,接口通过镜像地址来拉取。例如:

// 域名+项目名称+Tag,拉取指定版本
docker pull harbor.xxxx.com/test-dotnet/testwebapplication1imgtest60:stage_v1.0

2.4 镜像构建和推送过程中遇到的问题

2.4.1 Program does not contain a static 'Main' method suitable for an entry point

解决方法:解决方案文件(.sln)需要和项目文件(.csproj、Dockerfile)在同一目录下。

根据提示内容,确认无法判断问题原因,实际上项目中就不包含 Main 这个方法。

详情可参考:https://stackoverflow.com/questions/52991469/getting-program-does-not-contain-a-static-main-method-suitable-for-an-entry-p

2.4.2 Docker 运行异常导致的 Error 提示

ERROR: request returned Internal Server Error for API route and version http://%2F%2F.%2Fpipe%2FdockerDesktopLinuxEngine/_ping, check if the server supports the requested API version

重启下 Docker Desktop 即可。

posted @ 2024-08-02 17:23  橙子家  阅读(423)  评论(0编辑  收藏  举报