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