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,自己尝试修改,并加以验证,更能加深理解。