上篇博文说到使用Visual Studio Tools for Docker帮助咱们生成Dockerfile,如今咱们讨论下生成的Dockerfile的优劣。html
(1)发布API项目docker
新建Web API项目,项目名称为APIshell
在项目所在目录输入指令:dotnet publish --runtime ubuntu.16.04-x64ubuntu
(2)建立镜像api
在发布目录publish文件下新建Dockerfile文件,黏贴如下代码app
# 声明使用的基础镜像 FROM microsoft/dotnet:2.1-sdk # 设置工做目录 WORKDIR /app # 将本地应用拷贝到 容器/app 目录下 COPY ./ ./ # 设置导出端口 EXPOSE 80 # 指定应用入口点 API.dll表明的是主程序文件 ENTRYPOINT ["dotnet", "API.dll"]
在Dockerfile所在的目录下输入指令生成镜像:docker build -t api .工具
不要忘记后面有一个点 .性能
生成api:latest镜像成功单元测试
参考博客操做步骤:https://www.cnblogs.com/bluesummer/p/8087326.html测试
(3)分析镜像
咱们来查看镜像信息,输入:docker images
发现api:latest镜像的大小为1.83GB,大得有点夸张。
如今咱们来分析这个Dockerfile:
FROM microsoft/dotnet:2.1-sdk
FROM:指定所建立的基础镜像,若是本地不存在,则默认去Docker Hub下载指定镜像。任何Dockerfile中的第一条指令必须为FROM指令。而且,若是在同一个Dockerfile中建立多个镜像,可使用多个FROM指令。
文中Dockerfile基于microsoft/dotnet:2.1-sdk镜像,而图中可看到,microsoft/dotnet:2.1-sdk镜像大小已经达到1.73GB了,因此最后生成的api:latest镜像大小为1.83GB也不足为怪。
那问题来了,咱们应该采用哪一个镜像做为基础镜像,来建立咱们本身的镜像呢?
查阅了微软官网文档说明:
microsoft/dotnet:<version>-sdk包含带有.NET Core 和命令行工具 (CLI) 的.NET Core SDK。此镜像将映射到开发方案,可以使用此镜像进行本地开发、调试和单元测试。
microsoft/dotnet:<version>-runtime包含.NET Core(runtime和库),而且针对在生产环境中运行.NET Core 应用进行了优化。
咱们修改Dockerfile的FROM指令为
FROM microsoft/dotnet:2.1-aspnetcore-runtime
其余指令保持不变,新建一个api:1.0.0的镜像。
能够看到,镜像大小瞬间小了不少。可是咱们还不知足,由于本来microsoft/dotnet:2.1-aspnetcore-runtime镜像才253MB,而咱们的镜像是353MB。
WORKDIR /app
WORKDIR:为后续RUN、CMD和ENTRYPOINT指令设置工做目录。可使用多个WORKDIR,后续命令若是参数是相对路径,则会基于以前命令指定路径。例如:
WORKDIR /app
WORKDIR publish
WORKDIR api
最终路径为:/app/publish/api
这个指令在这里看上去应该优化不了。
COPY ./ ./
COPY:格式为COPY <src> <dest>。复制本地主机的<src>(为Dockerfile所在的目录的相对路径)下的内容到容器中的<dest>下。目标路径不存在,则自动建立。
./ ./ 就是将本地Dockerfile所在的目录的文件和文件夹都复制到镜像中的/app目录下。
注意区分如下两条指令:
COPY test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/
COPY test /absoluteDir/ # adds "test" to /absoluteDir/
想了下,这里应该是不合理的,把所有文件都复制过去,这确定会形成镜像变大。
又想了下,这个已是咱们发布过的文件,那还能怎么办。问题先放这里,等会再解决。
EXPOSE 80
EXPOSE:声明镜像内服务所监听的端口。
ENTRYPOINT ["dotnet", "API.dll"]
ENTRYPOINT:指定镜像的默认入口命令,该入口命令会在启动容器是做为跟命令执行,全部传入值做为该命令的参数。支持两种格式:
ENTRYPOINT [“executable”,”param1”,”param2”] (exec调用执行)
ENTRYPOINT command param1 param2 (shell中执行)
每一个Dockerfile里若出现多个ENTRYPOINT,只有放后面的那个ENTRYPOINT有效。
Dockerfile参考:https://docs.docker.com/engine/reference/builder/#usage 里面有各个指令的详细介绍。
在多阶段构建的过程当中,咱们在Dockerfile使用多个FROM指令,每一个FROM指令使用不一样的基础镜像构成了不一样阶段。你能够选择从上一个阶段的产物(artifacts)复制到下一个阶段,从而确保不会把不须要的东西带到下一阶段。这种方法能够有效减少Docker镜像的大小。
默认状况下,这些阶段没有被命名,能够经过它们的整数引用它们,第一个FROM指令从0开始。然而,咱们也能够以as <NAME>的方式命名每一个阶段。
参考官网:https://docs.docker.com/develop/develop-images/multistage-build/
如下咱们用Visual Studio Tools for Docker生成的Dockerfile进行介绍。
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY ["API/API.csproj", "API/"] RUN dotnet restore "API/API.csproj" COPY . . WORKDIR "/src/API" RUN dotnet build "API.csproj" -c Release -o /app FROM build AS publish RUN dotnet publish "API.csproj" -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "API.dll"]
它分为四个阶段,分别是base、build、publish和final。
base阶段:上面已经分析了这里再也不详述。
build阶段:
FROM microsoft/dotnet:2.1-sdk AS build以microsoft/dotnet:2.1-sdk为基础镜像
WORKDIR /src工做目录为/src
COPY ["API/API.csproj", "API/"]把Dockerfile所在目录的API/API.csproj文件复制到容器的/src/API/中
RUN dotnet restore "API/API.csproj"在当前镜像的基础上执行dotnet restore "API/API.csproj",把API项目的依赖项和工具还原,并输出结果。
dotnet restore这条命令是使用 NuGet 还原依赖项以及在 project 文件中指定项目特殊的工具执行对依赖项和工具的还原。
COPY . .
WORKDIR "/src/API"切换工做目录到/src/API,能够用WORKDIR API替代,但明显第一种方法更直观。
RUN dotnet build "API.csproj" -c Release -o /app执行dotnet build "API.csproj" -c Release -o /app,以Release模式生成API项目及其全部依赖项并把生成的二进制文件输出到/app目录。
publish阶段:
FROM build AS publish以上一阶段build为基础镜像
RUN dotnet publish "API.csproj" -c Release -o /app执行dotnet publish "API.csproj" -c Release -o /app,以Release模式把API应用程序及其依赖项打包到/app目录以部署到托管系统。
final阶段:
FROM base AS final以上阶段base为基础镜像
WORKDIR /app以/app为工做目录
COPY --from=publish /app .把publish阶段生成的/app目录下的文件和文件夹复制到/app目录。
这样作的缘由是,上阶段的产物是不会带到下一阶段。
如今能够解释为何使用Visual Studio Tools for Docker不用发布也能生成可运行的镜像了,它实时 (JIT) 编译,提升启动性能。并且它只获取了程序运行所须要的文件放到镜像中。
咱们生成一个最新的镜像
发现它和microsoft/dotnet:2.1-aspnetcore-runtime镜像同样大,这下知足了,毕竟咱们没写什么代码到项目中。
1.精简镜像用途,尽可能让每一个镜像的用途都比较集中、单一,避免构造大而复杂,功能多的镜像。
2.选用合适的基础镜像。
3.在Dockerfile中写上注释,方便维护和他人使用。
4.正确使用版本号,如1.0.1。
5.使用多阶段构建镜像。