ETJava Beta | Java    注册   登录
  • 搜索:
  • .Net 6.0 Web API 项目生成镜像并上传到私有仓库 Harbor

    发表于      阅读(1)     博客类别:Crawler     转自:https://www.cnblogs.com/hnzhengfy/p/18334471/Docker_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 即可。