Docker入门 第二课 镜像构建,Dockerfile解读

 今天聊一聊Dockerfile,docker build,希望通过这篇文章,能让你对docker镜像构建过程有一个认识。

事情还得从一次Docker镜像创建失败说起。

问题背景:

环境VS2017,dotnet 2.1 版本 ,使用默认生成的Dockerfile文件,当我使用docker build命令创建镜像的时候,竟然报错了

Step 6/16 : ......
COPY failed: stat /mnt/sda1/var/lib/docker/tmp/docker-builder802544848/EasySlideVerificationDemoServer/EasySlideVerificationDemoServer.csproj: no such file or directory 

错误信息是,第6步,找不到指定的文件或文件夹,由于我执行docker build命令的路径与 项目文件(xxx.csproj)在同一目录下,所以,这个文件找不到也很正常,那到底是哪里出问题了呢?我们来捋一捋。  

执行docker build命令的路径与项目文件(xxx.csproj)在同一路径。

docker build 命令如下:

docker build -t slide-verify:1.0 .

Dockerfile内容如下:

 1 FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
 2 WORKDIR /app
 3 EXPOSE 80
 4 
 5 FROM microsoft/dotnet:2.1-sdk AS build
 6 WORKDIR /src
 7 COPY ["EasySlideVerificationDemoServer/EasySlideVerificationDemoServer.csproj", "EasySlideVerificationDemoServer/"]
 8 RUN dotnet restore "EasySlideVerificationDemoServer/EasySlideVerificationDemoServer.csproj"
 9 COPY . .
10 WORKDIR "/src/EasySlideVerificationDemoServer"
11 RUN dotnet build "EasySlideVerificationDemoServer.csproj" -c Release -o /app
12 
13 FROM build AS publish
14 RUN dotnet publish "EasySlideVerificationDemoServer.csproj" -c Release -o /app
15 
16 FROM base AS final
17 WORKDIR /app
18 COPY --from=publish /app .
19 ENTRYPOINT ["dotnet", "EasySlideVerificationDemoServer.dll"]

Docker镜像生成原理:

Docker镜像与操作系统镜像本质上是一样的,首先需要一个基础镜像,然后在基础镜像上搭建所需环境,安装软件,最后生成一个新的镜像,新的镜像就可以在Docker中进行部署,生成容器实例,从而开始运行服务。

Dockerfile中的命令:

从上面Dockerfile中,我们可以看到有这几个命令:FROM,WORKDIR,EXPOSE,COPY,RUN,ENTRYPOINT,大概了解一下这几个命令的意思。

FROM:    指定一个基础镜像

WORKDIR:  在镜像内指定一个目录,作为当前工作目录

EXPOSE:     指定端口号

COPY:    从一个目录(这个目录可以是本地目录或中间镜像的目录)中复制文件或者目录到容器里指定路径

RUN:       在镜像内执行相应命令

ENTRYPOINT: 入口点,与RUN类似,同样是执行程序命令,表示容器启动时需要执行的命令。

Dockerfile文件解读: 

我们来解读一下这个Dockerfile

 1 将镜像 microsoft/dotnet:2.1-aspnetcore-runtime 作为基础镜像 (base)
 2 指定镜像内工作目录为:/app
 3 指定端口号为:80
 4 
 5 将镜像 microsoft/dotnet:2.1-sdk 作为系统构建中间镜像(build)
 6 指定镜像内工作目录为: /src
 7 复制项目文件 "EasySlideVerificationDemoServer/EasySlideVerificationDemoServer.csproj" 到镜像目录 "EasySlideVerificationDemoServer/"
 8 执行dotnet restore命令: dotnet restore "EasySlideVerificationDemoServer/EasySlideVerificationDemoServer.csproj"
 9 复制当前目录下所有文件到镜像的工作目录(/src)下:COPY . .
10 指定新的工作目录: "/src/EasySlideVerificationDemoServer"
11 执行dotnet build命令: dotnet build "EasySlideVerificationDemoServer.csproj" -c Release -o /app
12 
13 将构建好的中间镜像(build) 作为发布中间镜像(publish)
14 执行dotnet publish命令: dotnet publish "EasySlideVerificationDemoServer.csproj" -c Release -o /app
15 
16 将基础镜像(base)作为最终镜像(final)
17 指定工作目录: /app
18 复制发布中间镜像(publish)中的/app目录到工作目录:COPY --from=publish /app .
19 指定入口点命令:ENTRYPOINT ["dotnet", "EasySlideVerificationDemoServer.dll"] 

解读完之后,我们了解了这个Dockerfile究竟做了哪些事情。

镜像构建命令:

然后看一下Docker镜像构建命令:docker build。

命令行输入:docker build --help,可查看完整使用方式,此处省略。

Usage: docker build [OPTIONS] PATH | URL | -

下面是我使用的构建命令:

docker build -t slide-verify:1.0 .

-t name:tag   表示给镜像命名,并指定标签(相当于版本号)

-f Dockerfile  指定Dockerfile文件,默认为'PATH/Dockerfile',即当前目录下的Dockerfile

PATH     表示本地工作目录(也就是命令最后的 . ,我这里是项目文件夹)

发现问题:

到这里不难发现,由于镜像构建命令的执行目录与项目文件所在目录一致,并且命令中PATH指定的是当前目录(.),也就是项目文件所在目录,而Dockerfile中文件复制命令明显是在项目文件的上一级,即解决方案文件夹目录,

这样问题似乎就容易解决了,将构建命令改成下面的格式,果不其然,构建成功。

docker build -t slide-verify:1.3 -f Dockerfile ..

然后执行docker run命令,运行构建好的镜像,启动成功,浏览器访问:http://192.168.99.100:5008/  ,项目成功运行。

docker run -it -p 5008:80 --name slide-verify13 slide-verify:1.3

  

  

使用进阶:

我们回头再看一下Dockerfile文件,文件中的5到14行命令,包含了源码复制,项目还原(restore),构建(build),发布(publish)几个步骤。

 5 FROM microsoft/dotnet:2.1-sdk AS build
 6 WORKDIR /src
 7 COPY ["EasySlideVerificationDemoServer/EasySlideVerificationDemoServer.csproj", "EasySlideVerificationDemoServer/"]
 8 RUN dotnet restore "EasySlideVerificationDemoServer/EasySlideVerificationDemoServer.csproj"
 9 COPY . .
10 WORKDIR "/src/EasySlideVerificationDemoServer"
11 RUN dotnet build "EasySlideVerificationDemoServer.csproj" -c Release -o /app
12 
13 FROM build AS publish
14 RUN dotnet publish "EasySlideVerificationDemoServer.csproj" -c Release -o /app

我们知道,.net 中publish本身包含了restore和build,这几个命令理论上是可以合并的,来试一下,将Dockerfile修改为:

 1 FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
 2 WORKDIR /app
 3 EXPOSE 80
 4 
 5 FROM microsoft/dotnet:2.1-sdk AS publish
 6 WORKDIR /src
 7 COPY . .
 8 WORKDIR "/src/EasySlideVerificationDemoServer"
 9 RUN dotnet publish "EasySlideVerificationDemoServer.csproj" -c Release -o /app
10 
11 FROM base AS final
12 WORKDIR /app
13 COPY --from=publish /app .
14 ENTRYPOINT ["dotnet", "EasySlideVerificationDemoServer.dll"]

再次构建镜像,果然构建成功

docker build -t slide-verify:1.4 -f Dockerfile ..

运行,容器成功启动。

docker run -it -p 5009:80 --name slide-verify14 slide-verify:1.4

至此,估计你我对镜像构建,Dockerfile,docker build 有了一个比较清晰的认识。

更进一步:

我们看,Dockerfile中主要做了两件事:

第一,将源码复制到一个中间容器,在容器中编译,打包发布。

第二,将中间容器中的发布文件复制到目的容器,启动运行。

仔细想一想,有必要在容器中进行编译,发布吗?在本地编译发布岂不是更方便。(这里不涉及通过gitlib拉取代码,编译发布,那是另一会儿事。)

于是,我将项目发布,目标运行时为:linux-64,发布到  /bin/Release/netcoreapp2.1/linux 文件夹下,修改Dockerfile如下:

1 FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
2 WORKDIR /app
3 EXPOSE 80
4 
5 COPY ./bin/Release/netcoreapp2.1/linux .
6 
7 ENTRYPOINT ["dotnet", "EasySlideVerificationDemoServer.dll"]
docker build -t slide-verify:1.5 .

  再次构建,因为减少了编译过程,这次构建更快更顺利。现在Dockerfile只剩下了5行代码,所谓熟能生巧,当是如此。

总结:

docker build [OPTIONS] PATH | URL | -

docker build 命令中,PATH参数很重要,要搞明白。

读懂了Dockerfile,自己尝试修改,并加以验证,更能加深理解。

原文地址:https://www.cnblogs.com/flame7/p/13655249.html