.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 即可。

相关推荐
苓诣17 小时前
Submariner 部署全过程
云计算·k8s
橙子家20 小时前
k8s 中的 Ingress 简介【k8s 系列之三】
k8s
KubeSphere 云原生1 天前
云原生周刊:Prometheus 3.0 Beta 发布|2024.09.16
云计算·k8s·容器平台·kubesphere
耐心坚持努力�2 天前
k8s重要知识点
linux·运维·k8s·k8s重要知识点
行走的山峰4 天前
k8s证书过期处理
k8s
G皮T6 天前
【Kubernetes】K8s 的鉴权管理(二):基于属性 / 节点 / Webhook 的访问控制
kubernetes·k8s·鉴权·rbac·webhook·访问控制·abac
橙子家7 天前
k8s 中的 Service 简介【k8s 系列之二】
k8s
KubeSphere 云原生7 天前
云原生周刊:OpenTofu Registry 获得用户界面和 API|2024.9.9
云计算·k8s·容器平台·kubesphere
阿珂来了10 天前
怎么安装Grafana-Loki
linux·k8s·grafana
solinger12 天前
Tekton简介,安装和构建最简单ci/cd
k8s·tekton·operator