大家好,我是张晋涛。
文章过长,目录如下:
Docker 架构
这里我们先从宏观上对 Dockedocker和虚拟机的区别r
有个大概的认识,它整体上是个 C/S 架构;我们dockerhub平时使用的 docker
命令docker面试题及答案就是它的 CLI 客户端,而它的服务端是docker常用命令 dockerd
在 Linux 系统中,通常我们是使用 systemd
进行管理,所以我们可以使用 systemctldocker和虚拟机的区别 start docker
来启动服务。(但是请注意,dockerd
是否能运行与 systemd
并无任何关系,你可以像平时执行一个普通的二进制docker和虚拟机的区别程序一样,直接通过 dockerd
来启动docker服务,注意需要 root 权限)
实际上也就是
(图片来源:docker overview)
docker
CLI 与 dockerd
的交互是通docker和虚拟机的区别过 REST API 来完成的,当我们执行 docker version
的时候过滤 API 可以看到如下输出:
➜~dockerversion|grepAPI
APIversion:1.41
APdockerhubIvdocker容器ersion:1.41(minimumvdocker面试题及答案ersion1.12)
上面一行是 docker
CLI 的 API 版本,下面则代表了 dodocker安装部署ckerd
的 API 版本,它的后面还有个括号,是因为 Docker 具备了很良好的兼容性,这里表示它最小可兼容的 API 版本是 1.12docker和虚拟机的区别 。
对于我们进行 C/S 架构的项目开发而言,一般都是 API 先行, 所以我们先来看下 API 的部分。
当然,本文的主体docker是干什么的是构建系统相关的,所以docker常用命令我们就直接来看构建相关的 API 即可。
接下来会说 CLI,docker面试题及答案代码以 v20.10.5 为准。最后说服务端 Dockerd 。
API
Docker 维护团队在每个版本正式发布之后,都会将 API 文档发布docker怎么读出来,可以通过 Docker Engine API 在线浏览docker是干什么的,也可以自行构建 API 文档。
首先 clone Docker 的源docker是干什么的代码仓库, 进入项目仓库内执行 make swagger-docs
即可在启动一个容docker菜鸟教程器同时将端口暴露至本地的 9000
端口, 你可以直接通过 htdocker怎么读tp://127.0.0.1:9000 访问本地的 API 文档。
(MoeLove)➜gitclonehttps:docker容器//github.comdocker是干什么的/docker/docker.gitdocker常用命令docker
(MoeLove)➜cddocker
(MoeLove)➜dockergit:(mastdocker和虚拟机的区别er)gitcheckout-bv20.10.5v20.10.5
(MoeLovedockerhub)➜dockergit:(v20.10.5)makeswagger-docs
APIdocdocker常用命令sprevidocker安装部署ewwillberunningathttp:docker面试题及答案//dockerhublocalhost:9000
打开 http://127.0.0.1:9000/#operation/ImageBuild 这个地址就可以看到 1.41 版本的构建镜像所需的 API 了。我们对此 API 进dockerhub行下分析。
请求地址和方法
接口地址是 /v1.41/build
方法是 POST
,我们可以使用一个较新docker常用命令版本的 cudocker常用命令rl
工具来验证下此docker常用命令接口(需要使用 --unix-socket
连接docker怎么读 Docker 监听的 UNIX Domain Socket )。dockerdocker和虚拟机的区别d
默认情况下监听在 /var/run/dockerdockerhub.sock
,当然你也可以给 dockerd
传递 --host
参数用于监听 HTTP 端口或者其他路径的 unix socket .
/#curl-XPOST--unix-socket/var/run/docker.socklocalhost/v1.41/build
{"message":"Cadocker是干什么的nnotlocatespecifidocker怎么读edDockerfile:Dockerfile"}
从docker常用命令上面的输出我们可以看到,我们确实访问到了该接口,同时该接口的响应是提示需要 Dockerfile
.
请求体docker容器
请求体是一个 tar
归档文件,可选docker菜鸟教程择无压缩、gzip
、bzip2
、xz
压缩等形式。关于这几种压缩格式就不再展开介绍了,但值得注docker容器意的是 如果使用了压缩,则传输体积会变小,即网络消耗会相应减少。但压缩/解压缩需要耗费 CPU 等docker和虚拟机的区别计算资源 这在我们对大规模镜像构建做优化时是个值得权衡的点。
请求头
因为要发送的是个 tar
归档文件,Content-type
默认是 application/x-tar
。另一个会发送的头是 X-Registry-Config
,这是一dockerhub个由 Base64 编码后的 Docker Registry 的配置信息,内容与 $HOME/.docker/config.json
中的 auths
内的信息一致。
这些配置信息,在你执行 ddocker常用命令ocker login
后会自动写入到 $HOME/.docker/config.jsondocker常用命令
文件内的。这些信息被传输到 dockdocker和虚拟机的区别erd
在构建过程中作为拉取镜像的认证信息使用。
请求参数
最后就是请求参数了,参数有很多,通过 docker build --help
基本都可以docker面试题及答案看到对应含义的,这里不再一一展开了docker面试题及答案,后面会有一些关键参数的介绍。
小结
上面我们介绍了 Docdocker容器ker
构建镜像相关的docker是干什么的 API,我们可以直接访问Docker Engine 的 API 文档。dockerfile或者通过源码仓库,自己来构建一个本地的 API 文档服务,使dockerfile用浏览器进行访问。
通过 API 我们也知道了该接口所需的请求体是一个 tar
归档文件(可选择压缩算法进行压缩),同时它的请求头中会携带用户在镜像docker安装部署仓库中的认证信息。这提醒我们, 如果在使用远程 Dockerd
构建时,请注意安全,尽量使用 tls
进行加密,以免数据泄漏docker是干什么的。
CLI
API 已经介绍完了,我们来看下 docker
CLI,我以前的文章中介绍过现在 Docker 中有两个构建系统,一个是 v1 版本的 builder
另一个是 v2 版本的即 BuildKit
我们来分别深入源码来看看在构建镜像时,他们各自的行为吧。
准备代码
CLI 的代码仓库在 htdocker容器tps://github.com/docker/cli 本文的代码以 v20.10.5
为准。
通过以下步骤使用此版本的代码:
(MoeLove)➜gdockeritclonehttpdockerhubs://github.com/docker/cli.git
(MoeLove)➜cdcli
(MoeLove)➜cligit:(master)gitcheckout-bv20.10.docker是干什么的5v20.10.5
逐步分解
docker
是我们所使用的客户端工具,用于与 dodocker菜鸟教程ckerd
进行交互。关于构建相关的部分, 我们所熟知的便是 ddocker怎么读ocker build
或者是 dockerdocker常用命令 image bdocker怎么读uild
,在 19.03 中新增的是 docker builder build
,但其实他们都是同一个只是做了个 alias 罢了:
//cmd/docker面试题及答案docker/docker.dockerfilego#L237
ifv,ok:=aliadocker容器sMap["builder"];ok{
aliases=append(aliases,
[2][]string{{"build"},{v,"build"}},
[2][]stridocker和虚拟机的区别ng{{"image","build"},{v,"buildocker和虚拟机的区别d"}},
)
}
真正的入口函数其实在 cli/command/image/build.go
;区分如何调用的逻辑如下:
funcrunBuild(dockerClicommadocker和虚拟机的区别nd.Cli,optionsbuildOptiodocker和虚拟机的区别ns)error{
buildocker是干什么的dkitEnabled,err:dockerfile=command.BuildKitEnabled(dockerCli.ServerIdockerhubnfo())
iferr!=nil{
returnerr
}
ifbuilddocker和虚拟机的区别kitEnabled{
returnrunBuildBuildKit(dockerCli,optdockerhubions)
}
//省略掉了对于buildocker怎么读der的实际逻辑
}
这里就是判断下是否支持 buildkit
//cli/command/cli.go#Ldockerfile176
funcBuildKitEnabled(siServerInfo)(bool,error){
buildkitEnabled:=si.BuildkitVersion==types.BuilderBuildKit
ifbuildkitEnv:=osdocker和虚拟机的区别.Getenv("DOCdocker和虚拟机的区别KER_BUILDdocker安装部署KIT");buildkitEnv!=""{
varerrerror
buildkitEnabled,errdockerhub=strconv.ParseBdockerfileool(buildkitEnv)
iferr!=nil{
returnfalse,errors.Wrap(err,"DOCdocker常用命令KER_BUILDKdockerhubITenvironmentvariableexpectsbooleanvalue")
}
}
returnbuildkitEnabled,nil
}
当然,从docker安装部署这里可以得到两个信息:
-
通过
dodocker常用命令ckerd
的docker常用命令配置可开启
buildkit
。在
/etc/dockerdocker/ddocker怎么读aemon.json
中添加如下内容,并重docker菜鸟教程启
dockerd
即可:
{
"features":{
"buildkit":true
}
}
-
在
docker
CLI 上也可开启
buildkit
的支持,并且 CLI 的配置可覆盖服务端配置。通过
export DOCKER_BUILDKIT=1
即可开启
buildkit
的支持,设置为 0 则关闭(0/false/f/F 之类的也都是相同的结果)
从上面的介绍也看到了,对于原本默认的 builder 而言, 入口逻docker是干什么的辑在 runBuild
中, 而对于使用 buildkit 的则是 rudocker常用命令nBuildBuildKit
接下来,我们对两者进行逐步分解。
builder v1
在 runBuild
函数中,大致经dockerfile历了以下阶段:
参数处理
最开始的部分是一些对参数的处docker是干什么的理和校验。
-
stream
和compress
不可同时使用。
因为如果我们指定了 compress
的话,则 CLI 会使用 gzip
将构建上下文进行压缩,这样也docker容器就没法很好的通过 stream
的模式来处理构建的上下文了。
当然你也可能会想,从技docker常用命令术上来讲,压缩和流式没有什么必然的冲突,是可实现的。事实的确如此,如果从技术的角度上来讲两者并非完全不能一起存在,无非就是增加解压缩的动作。但是当开启 stream
模式,对每个文件都进行压缩和解压的操作那将会是很大的资源浪费,同时也增加了其复杂度,所以在 CLI 中便直接进行了限制,不允许同时使用 compress
和 stdocker和虚拟机的区别ream
-
不可同时使用 stdin
读取Dockerfile
和build context
。
在进行构建时,如果我们将 Dockerfildocker常用命令e
的名字传递为 -
时,表示从 stdin
读取其内容。
例如,某个目录下有三个文件 foo
bar
和 Dockerfile
,通过管道将 Dockerfile
的内容通过 stdin
传递给 docker build
(MoeLove)➜xls
barDockerfilefoo
(MoeLove)➜xcatDockerfile|DOCKER_BUILDKdocker容器IT=0dockerbuild-f-.
Sedocker是干什么的ndingbuildcontexttoDockerdaemon15.41kB
Step1/3:FROMsdockerfilecratch
---&dockergt;
Step2/3:docker面试题及答案COPYfoofoo
--dockerhub->a2af45d66bb5
Step3/3:COPYbarbar
--->cc803c675dd2
Successfullybuiltcc803c675dd2
可以看到通过 stdin
传递 Dockerfile
的方式能成功的构建镜像。接下来我们尝试通过 stdin
将 build context
传递进去。
(MoeLove)➜xtar-cvfx.tarfoobarDockerfile
foo
bar
Dodocker菜鸟教程ckerfile
(MoeLove)➜xcatx.tar|DOCKER_BUILDKIT=0dockerbuild-fDockerfdocker面试题及答案ile-
SendingdockerfilebuildcontexttoDockerdaedocker菜鸟教程mon10.24kB
Step1/3:FROMscradocker怎么读tch
--->
Step2/3:COPYdocker面试题及答案foofoodocker
--->09319712e220
Step3/docker是干什么的3:Cdocker容器OPYbarbar
--->ce88644a7395
Successfullybuiltdocker是干什么的ce88644a7395
可以看到通过 stdin
传递 build context
的方式也可以成功构建镜像。
但如果 Dockerfile
的名称dockerfile与构建的上下文都指定为 -
即 docker build -f - -
时,会发生什么呢?
(MoeLove)➜xDOdocker常用命令CKER_BUILDKIT=0dockerbuild-f--
invalidargument:can'docker安装部署tusestdinfordocker安装部署bothbuildcontextanddockerfile
就会报错了。所以, 不能同时使docker是干什么的用 stdin
读取 Dockerfiledocker
和 buidocker常用命令ld context
。
-
build context
支持四种行为。
switch{
casedocker常用命令options.contextFromStdin():
//省略
caseisLocalDir(specifiedContext):
//省略
caseurlutil.IsGitURL(specifiedContextdocker容器):
//省略
caseurlutidocker容器l.IsURL(specifiedContext):
//省略
defadocker面试题及答案ult:
returnerrors.Errorf("unabletopreparecontext:path%qnotfound",specifiedContext)
}
从 stddocker常用命令in
传入,上文已经演示过了,传递给 stdin
的是 tar
归档文件。当然也可以是指定一个具体的 PATH
,我们通常使用的 docker build .
便是这种用法;
或者可以指定一个 git
仓库的地址,CLI 会调用 git
命令将仓库 clone
至一个临时目录,进行使用;
最后一种是,给定一个 URL
地址,该地址可以是 一个具体的 Dockerfile 文件地址 或者是 一个 tar
归档文件的下载地址 。
这几种基本docker容器就是字面上的区别,至于 CLI 的行为差异,主要是最后一种,当 URL
地址是一个具体的 Dockerfile
文件地址,在这种情况下 build context
相当于只有 Dockerfile
自身,所以并不能使用 COPY
之类的指定,至于 ADD
也只能使用可访问的外docker容器部地址。
-
可使用 .dockerignore
忽略不需要的文件
我在之前的文章中有分享过相关的dockerfile内容。这里我们看看它的实现逻辑。
//cli/command/image/build/dockerignore.go#L13
funcReadDockerignore(contextDirstring)([]string,error){
varexcludes[]string
f,err:=os.Opdocker面试题及答案en(filepath.Join(contextDir,".dockerignore"))
switch{
caseos.IsNotExist(erdocker安装部署r):
returnexcludes,nil
caseerr!=nil:
returnnil,err
}
deferf.Close()
redockerfileturndockerignore.ReadAll(f)
}
-
.dockerignore
是一个固定的文件名,并且需要放在build context
的根目录下。类似前面提到的,使用一个Dockerfile
文件的 URL 地址作为build context
传入docker和虚拟机的区别的方式,便无法使用.dockeridocker安装部署gnore
。 -
.dockerigndockerhubore
文件可以不存在,但在读取的时候如果遇到错误,便会抛出错docker和虚拟机的区别误。 -
通过
.dockerignore
将会过docker菜鸟教程滤掉不希望加入到镜像内,或者过滤掉与镜像无关的内容。
最后 CLI 会将 build contexdockerhubt
中的内容经过 .dockerigndocker面试题及答案ore
过滤后,打包成为真正的 build context
即真正的构建上下文。这也是为什么有时候你发现自己明明在 Dockerfile
里面写了 COPY xx xx
但是最后没有发现该文件的情况。很可能就是被 .dockerignore
给忽略掉了。这样有利于优化 CLI 与 dockerd
之间的传输docker压力之类的。
-
docker
CLI 还会去读取
~/.docker/config.json
中的内容。
这与前docker常用命令面 API 部分所描述的docker常用命令内容基本是一致的。将认证信息通过 X-Registry-Config
头传递给 dockerd
用于docker菜鸟教程在需要拉取镜像时进行身份校验。
-
调用 API 进行实际构建任务
当一切所需的校docker安装部署验和信息都docker是干什么的准备就绪之后,则开始调用 dockerCli.Client
封装的 API 接口,将dockerhub请求发送至 dockerd
,进行实际的构建任务。
response,err:=dockerCli.Client().ImageBuild(ctx,body,buildOptions)
iferr!=nil{
ifoptions.quiet{
fmtdocker菜鸟教程.Fprintf(dockerCli.Err(),"%s",progBuff)
}
cancel()
returnerr
}
deferresponse.Body.Closedocker和虚拟机的区别()
到这里其实一次构建的过程中 CLI 所处理的流程就基本结束了docker和虚拟机的区别,之后便是按照传递的参数进行进度的输出或是将镜像 ID 写入到文件之类的。这部分就不进行展开了。
小结
整个过程大致如下图:
从入口函数 runBuild
开始,经过判断是否支持 buildkit
,如果不支持 buildkit
则继续使用 v1 的 buildedocker容器r
。接下来读取各类参数,按照不同的参数执行各类不同的docker和虚拟机的区别处理逻辑。这里需要注意的就是 Dockerfile
及 build context
都可支持从文件或者 stdin
等读入,具体使用时,需要注意docker怎么读。另外 .dockerignodocker是干什么的re
文件可过滤掉 buildockerfiled contdocker是干什么的ext
中的一些文件,在使用时,可通过此方法进行构建效率的优化,当然也需要注意,在通过 URL 获取 Dockerfile
的时候,是不存在 build context
的,所以类似 COPY
这样的命dockerhub令也就无法使用了。当所有的 build context
和参数都准备就绪后,接下来调用封装好的客户端,将这些请求按照本文docker和虚拟机的区别开始之初介绍的 API 发送给 dockerd
,由其进行真正的构建逻辑。
最后当构建结束后,CLI 根据参数决定是否要显示docker和虚拟机的区别构建进度或dockerfile者结果。
buidocker和虚拟机的区别ldkdocker常用命令it
接下来我们来看看 buildkit
如何来执行构建,方法入口与 builder
一致,但是在 buildkitEnabled
处,由于开启了 buildkit
支持,所以跳转到了 runBuildBuildKit
。
funcrunBuild(dockerClicdocker面试题及答案ommand.Cli,optionsbuildOptions)error{
buildkitEnabled,err:=command.BuildKitEnabled(dockerCli.ServerInfo())
iferr!=nil{
returnedocker安装部署rr
}
ifbuidocker是干什么的ldkitEnabled{
returnrudockerfilenBuildBuildKdocker安装部署it(dockerCli,options)
}
//省略掉了对于builder的实际逻辑docker
}
创建会话
但是与 builder
不同的是,这里先执行了一次 trySessiondockerhub
函数docker是干什么的。
//cli/command/image/budockerhubild_buildkit.go#L50
s,err:=trySessiodocker安装部署n(dockerCli,options.context,false)
iferr!=nil{
returnerr
}
ifs==nil{
returnerrors.Errorf("buildkitnotsupportedbydaemon")
}
这个docker容器函数是用来做什么的呢?我们来找docker菜鸟教程到该函数所在的文件 cli/command/image/build_session.go
//cli/cdocker是干什么的ommand/image/build_session.go#L29
functrydocker安装部署Session(dockerClicodocker常用命令mmand.Cli,contextDirstring,forStreambool)(*session.Session,error){
if!isSessionSuppdockerorted(dockerCli,forStream){
returnnil,nil
}
sharedKey:=gdocker安装部署etBuildSharedKey(contextDir)docker怎么读
s,err:=session.NewSession(context.Background(),filepath.Base(contextDir),sharedKey)
iferr!=ndockerhubil{
retdockerhuburnnil,dockerfileerrors.dockerhubWrap(err,"failedtocreatesession")
}
redocker是干什么的turns,nil
}
当然还包括它其中最主要的 isSessdocker和虚拟机的区别ionSupported
函数:
//cli/command/image/build_session.go#L22
funcisSessionSupported(dockerClicommand.Cli,forStreambool)bool{
if!forStream&&versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(),"1.39"){
redocker安装部署turntrue
}docker是干什么的
returndockedocker和虚拟机的区别rCli.ServerInfo()docker容器.HasExperimental&&versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(),"1.31")
}
isSessionSupported
很明显是用于判断是docker菜鸟教程否支持 Session
,这里由于我们会传入 forStream
为 false
,而且当前的 API 版本是 1.41 比 1.39 大,所以此函数会返回 true
。其docker和虚拟机的区别实在 builder
中也执行过相同的逻辑,只不过是在传递了 --stream
参数后,使用 Session
获取一个长连接以达到 stream
的处理能力。
这也就是为什么会有下面 dockerCli.ServerInfo().HasExdockerperimental && versions.GreaterThanOrEqualTo(dockerCli.Client().Clidocker安装部署entVersion(), "1.31")
这个判断存在的原因了。docker怎么读
当确认支持 Sdocker菜鸟教程ession
时,docker面试题及答案则会调用 session.NewSession
创建一个新的会话。
//github.com/mobydocker是干什么的/buildkit/sessiodockerfilen/session.go#L47
funcNewSession(ctxcontexdocker面试题及答案t.Contexdocker容器t,name,sharedKeystring)(*Session,error){
id:=identity.Nedocker常用命令wID()
varunary[]grpc.UnaryServerIntercdockerhubeptor
varstream[]grpc.StreamServedocker安装部署rInterceptor
serverOpts:docker和虚拟机的区别=[]grpc.ServerOption{}
ifspan :=opentracing.SpanFromContext(ctx)dockerhub;span !=nil{
tracer:=span.Tracer()
unary=append(unary,otgrpc.OpenTracingServerInterceptor(tracer,traceFilter()))
stream=append(stream,otgrpc.OpenTracingStreamServerIntercedocker和虚拟机的区别ptor(span.Tracer(),docker容器tracedocker常用命令Filter()))
}
unary=append(unary,grdocker和虚拟机的区别pcerrors.UnaryServerIdocker和虚拟机的区别nterceptor)
stream=appendocker常用命令d(stream,grpcerrors.StreamServerInterceptor)
iflen(unary)==1{
serverOpts=append(serverOpts,grpc.UnaryInterceptor(unary[0]))
}elseiflen(unary)>1{
serverOpts=appenddocker安装部署(serverOpts,grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(unary...)))
}
iflen(stream)==1{
serverOpts=append(serverOpts,grpc.StreamInterceptor(stream[0])docker常用命令)docker面试题及答案
}edocker是干什么的lseiflen(stream)&gdockerhubt;1{
servdocker和虚拟机的区别erOdockerpts=append(serverOpts,grdocker容器pc.StreamIntercedocker容器ptor(grpc_middleware.ChainStreamServer(sdocker菜鸟教程tream...)))
}
s:=&Session{
id:id,
nadocker容器me:name,
sharedKey:sharedKey,
grpcServer:grpc.NewServer(serverOpts...),
}
grpc_health_v1.RegdockerhubisterHealthSdockerfileerver(s.grpcServer,health.NewServerdocker常用命令(docker怎么读))
returns,nil
}
它创建了一个长连接会话,接下来的操作也都会基于这个会话来做。接下来的操作与docker常用命令 builder
大体一致,docker安装部署先判断 context
是以哪种形式提供的;当然它也与 builder
一样,是不允许同时从 stdin
获取 Dockerfile
和 build context
。
switch{
caseoptions.contextdockerfileFromStdin():
//省略处理逻辑
casdocker菜鸟教程eisLocaldockerDir(options.context):
//docker菜鸟教程省略处理逻辑
caseurlutil.IsGitURL(options.contedocker和虚拟机的区别xt):
//省略docker处理逻辑docker安装部署
caseurlutildockerfile.IsURL(options.contexdocker和虚拟机的区别t):
//省略处理逻辑
default:
returnerrors.Edocker是干什么的rrorf("unabletodocker安装部署preparecontext:path%qnotfound",options.context)
}
这里的docker是干什么的处理逻辑与 v1 builder
保持一致的原因,主要在于用户体验上,当前的 CLI 的功能已经基本稳定,用户也已经习惯,所以即使是增加了 BuildKit
也并没有对主体docker安装部署的操作逻辑造成多docker菜鸟教程大改变。
选择输出模式
BuildKidocker怎么读t
支持了三种不同的输出模式 local
tar
和正常模式(即存储在 dockerd
中), 格式为 -o type=localdockerhub,dedockerst=path
如果需要将构建的docker常用命令镜像进行分发,或是需要进行镜像内文件浏览的话,使用这个方式也是很方便的。
outputs,err:=parseOutputs(options.outputs)
iferr!=nil{
returnerrors.Wrapf(errdocker怎么读,"failedtoparseoutputs")
}
for_,out:=rangeoutputs{
switchout.docker安装部署Typdocker和虚拟机的区别e{
case"local":
//省略
case"tar":
//省略
}
}
其实它支持的模式还有第 4 种, 名为 cacheonly
但它并不会像前面提到的三种模式一样,有个很直观的输出,而且用的人可能会很少,所以就没有单独写了。
读取认证信息
dockerAuthProvider:=authprovider.NewDockerAuthProvider(os.Stderr)
s.Allow(dockerAuthProvider)
这里的行为与上面提到的 builder
的行为基本一致,这里主要有两个需要注意的点:
-
Allow() 函数
func(sdocker菜鸟教程*Session)Allow(aAttachable){
a.Register(s.grpcServer)
}
这个 Alldockerfileow
函数就是允许通过上面提到的 grpc 会话访问给定的服务。
-
authprovider
authprovider
是 BuildKitdocker和虚拟机的区别
提供的一组抽象接口集合,通过它们docker是干什么的可以访问到机器上的配置文件,进而拿到认证信息,行为与 buidocker容器lder
基本一致。
高阶特性:secrets
和 ssh
我其他的文章讲过这两种高阶特性的使用了,本篇中就不再多使用进行过多说明了,只来大体看下该部分的原理和逻辑。
secretsprovider
和 sshprovider
都是 buildkit
在提供的,利用这两种特性可以在 Docker 镜像进行构建时更加安全,且更加灵活。
funcparseSecretSpdocker和虚拟机的区别edocker是干什么的cs(sl[]string)(session.Attdockerhubachable,error){
fs:=make([]secdocker菜鸟教程retsprovider.Source,0,len(sl))
for_,v:=rangesl{
s,err:=parseSecret(v)
iferr!=nil{
returnnil,errdocker安装部署
}
fs=append(fs,*s)
}
store,err:=secretsprovider.NewStore(fs)
ifdocker和虚拟机的区别err!=nil{
returnnil,err
}
returnsecretsprovider.NdockerewSecretProvider(store),nil
}
关于 secrets
方面docker和虚拟机的区别,最终的 parseSecret
会完成格式相关的校验之类的;
funcparseSSHSpdocker安装部署ecs(sl[]string)(session.Attachable,error){
confidocker怎么读gs:=make([]sshprovider.AgentConfig,0,len(sl))
for_,v:=rangesl{
c:=parseSSH(v)
configs=appenddocker(configs,*c)
}
retudocker菜鸟教程rnsshprovider.NewSSHAgentPrdocker面试题及答案ovider(configsdocker是干什么的)
}
而关于 ssh
方面,则与上方的 secrets
基本一致,通过 sshprovider
允许进行 ssh 转发之类的,这里不再深入展开了。
调用 API 发送构建请求
这里主要有两种情况。
-
当
build context
是从
stdin
读,并且是一个
tardocker常用命令
文件时
buildID:=stringid.Genedocker常用命令rateRandomID()
ifbody!=nil{
eg.Go(func()error{
buildOptions:=types.ImageBuildOptions{
Version:types.Bdockerhubuildocker容器derBuildKit,
BuildID:uploadRequestRemote+":"+buildID,
}
response,err:=dockerCli.Client().ImageBuild(codocker是干什么的ntext.Background(),bdocker菜鸟教程ody,buildOptions)
iferr!=nil{
returnerr
}
deferresponse.Body.Close()
returnnil
})
}
它会执行上述这部docker容器分逻辑,但同时也要注意,这是使用的是 Golandocker怎么读g 的 goroutine
,到这里也并不是结束,这部分代码之后的代码也同样会被执行。这就说到了另一种情况了(通常情况)。
-
使用
doBuild
完成逻辑
eg.Go(fdocker和虚拟机的区别unc()error{
defdocker面试题及答案erfundocker容器c(){
s.Close()
}()
buildOptions:=imageBuildOptions(dockerCli,options)
buildOpdocker安装部署tions.Version=types.BuilderBuildKit
buildOptidocker是干什么的ons.Dockerfile=ddocker和虚拟机的区别ockerfileName
bdocker容器uildOptions.RemoteContext=remote
buildOptions.docker是干什么的SessionID=s.ID()
buildOptions.BuildID=buildID
buildOptions.Outputs=outputs
returndoBuild(ctdocker常用命令x,eg,dockerCli,stdoutUsed,optiondocker常用命令s,buildOptions)
})
那 doBuild
会做些什么呢?它同样也调用了 API 向 dockerd
发起了构建请求。
funcdoBuild(ctxcondockerfiletext.Context,eg*errgroup.Group,dodockerfileckerdocker容器Clicommadocker怎么读nd.Cli,stdoudocker面试题及答案tUsedbdockerhubool,optionsbuildOptions,buildOptionstypes.ImageBuildOptions,atsession.Attachable)(finalErrerror){
response,err:=dockerCli.Client().ImadockerfilegeBuild(cdockerontext.Backgdocker常用命令round(),nil,buildOptions)
iferr!=nil{
returnerr
}
deferrdocker安装部署esponse.Body.Close()
//省略
}
从以上的介绍我们可以先做个小的总docker安装部署结。docker菜鸟教程当 build context
从 stdindocker常用命令
读,并且是个 tar
归档时,实际会向 dockerd
发起两次 /build
请求 而一般情况下只docker常用命令会发送一次请求。
那这里会有什么差别呢?此处先不展开,我们留到下面讲 dockerd
服务端的时候再docker和虚拟机的区别来解释。
小结
这里我们对开启了 buildkitdockerhub
支持的 CLI 构建镜像的过程进行了分析,大致过程如下:
从入docker怎么读口函数 rudocker容器nBuild
开始,判断是否支持 buildkit
,如果支持 buildkdocker容器it
则调用 runBuildBuildKit
。与 v1 的 bdocker和虚拟机的区别uilder
不同的是,开启了 buildkit
后,会首先创建一个长连接的会话docker和虚拟机的区别,并一直docker菜鸟教程保持。其次,与 builder
相同,判断 build context
的来源,格式之类的,校验参数等。当然,buildkit
支持三种不同的输docker菜鸟教程出格式 tar
, local
或正常的存储于 Dockdockerer 的目录中。另外是在 buildkit
中新增的高docker面试题及答案阶特性,可以配置 secrets
和 ssdocker怎么读h
密钥等功能。最后,再调用 API 与 dockerd
交互完成镜像的构建。
服务端:dockerd
上面分别介绍了 API, CLI 的 v1 builder
和 bdocker安装部署uildkit
,接下来我们看看服务端的具体原理和逻辑。
Client 函数
还记得docker是干什么的上面部分中最后通过 API 与服务端交互的 ImageBuild
函数docker和虚拟机的区别吗?在开始 dockerd
的介绍前,我们来看下这个客户端接口的docker具体内容。
//github.com/docker/dockedocker菜鸟教程r/client/image_build.go#L20
func(cli*Client)ImageBuild(ctxcontext.Context,buildContextio.Reader,optionstypes.ImageBuildOptions)(types.ImageBuilddockerhubResponse,error){
query,err:=cli.imageBuildOptidocker是干什么的onsToQuery(options)
iferr!=nil{
returntypes.ImageBuildResponse{},err
}
headers:=http.Header(make(map[string][]string))
buf,err:=json.Marshal(options.AuthConfigs)
iferr!=nil{
returntypes.ImageBuildResponse{},err
}
headers.Add("X-Rdocker和虚拟机的区别egidockerstry-Configdocker和虚拟机的区别",base64.URLEncoding.EncodeToString(bdocker面试题及答案uf))
headers.Set("Content-Type","application/x-tar")
servedocker常用命令rResp,err:=cli.postRaw(ctx,"docker怎么读/build",query,buildContext,headers)
iferr!=nil{
returntypes.ImageBuildResponse{},err
}
osType:=gdocker是干什么的etDockerOS(serverResp.header.Getdocker是干什么的("Server"))
retudocker面试题及答案rntypes.ImageBuildResponse{
Body:servedocker安装部署rResp.body,
OSType:osType,
},nil
}
没有什么太特别的地方,行为与 API 一致。通过这里我们确认它确实访问docker菜鸟教程的 /build
接口,所以,我们来看看 dockerd
的dockerfile /build
接口,看看它在构建镜像的时候做docker是干什么的了什么。
dockerd
由于本文集中讨论的是构建系统相关的部分docker面试题及答案,所以也就不再过多赘述与构建无关的内容了,我们直接来看,当 CLI 通过 /build
接口发送请求后,会发生什么。
先docker常用命令来看该 API 的入口:
//api/server/router/build/build.go#L32
func(r*buildRouter)initRoutes(){
r.routes=[]router.Route{
router.NewPostRoute("/builddocker常用命令",r.postBuild),
router.NewPostRoute("/build/prune",r.postPrune),
router.NewPostRdocker菜鸟教程oute("/build/cancel",r.postCancel),
}
}
dockerd
提供了一套类 RESTful 的后端接口服务,处理逻辑的入口便是上面的 postBuilddocker面试题及答案
函数。
该函数的内容较多,我们来分解下它的主要步骤。
buildOptions,err:=newImageBuildockerfiledOptions(ctx,r)dockerhub
iferr!=nil{
returnerrf(docker是干什么的edocker容器rr)
}
newImageBuildOptions
函数就是构造构建参数的,将通docker容器过 API 提交过来的dockerfile参数转换为docker怎么读构建动作实际需要的参数形式。
buildOptions.AuthConfigs=getAuthConfigs(r.Header)
getAuthConfigs
函数用于从请求头拿到认证信docker是干什么的息dockerfile
imgID,err:=br.backend.Build(ctx,backend.BuildCondocker是干什么的fig{
Source:body,
Options:buildOptions,
ProgressWriterdocker是干什么的:buildProgressWriter(out,wantAux,createProgressdocker菜鸟教程Reader)docker面试题及答案,
})
ifedockerfilerr!=nildocker常用命令{
returnerrf(err)
}
这里就需要注意了: 真正的构建过程要开始了。使用 backend 的 Build
函数来完成真正的构建过程
//apdocker常用命令i/servdocker容器er/bacdocker面试题及答案kend/build/backend.go#L53
func(b*Backend)Build(ctxcontext.Context,configbackend.BuildConfig)(string,error){
optiondocker是干什么的s:=config.Options
useBuildKit:=options.Version==types.BuilderBuidockerhubldKit
tagger,edocker容器rr:=NewTagger(b.imageComponent,config.ProgressWriter.StdoutFormatter,optdockerfileions.Tags)
ifedockerhubrr!=nil{
retdocker是干什么的urn"",err
}
varbuild*builder.Result
ifdockerhubuseBuildKit{
build,err=b.buidocker常用命令ldkit.Build(ctx,config)
iferr!=nil{
return"",err
}
}else{
build,err=b.builder.Build(ctx,config)
iferr!=nil{
return"docker",err
}
}
ifbuild==nil{
return"",nil
}
varimageID=build.ImageID
ifdocker和虚拟机的区别options.Squash{
ifimageIDdocker容器,err=squashBuild(build,b.imageComponent);err!=nil{
return"",err
}
ifconfig.ProgressWritdocker面试题及答案er.AuxFormatter!=ndocker怎么读il{
iferrdocker面试题及答案=config.ProgressWriter.Auxdocker容器Formatter.docker是干什么的Emit("moby.image.id",types.docker安装部署BuildResult{ID:imageID});err!=nil{
return"",err
}
}
}
if!useBuildKit{
stdout:=config.ProgressWriter.StdoutFormatter
fmt.Fprintf(stdout,"Successfullybudocker是干什么的ilt%s\n",stringid.Trdocker和虚拟机的区别uncateID(imageID))
}
ifimageID!=""{
err=tagdockerhubger.TagImages(image.ID(imageID))
}
returnimageID,err
}
这个函数看着比较长,但主要功能就以下三点:
-
NewTagger
是用于给镜像打标签,也就是我们的-t
参数相关的内容,这里不做展开。 -
通过判断dockerfile是否使用了
buildkidockerhubt
来调用不同的构建后端。
useBuildKit:=options.Version==types.BuilderBuildKit
varbuild*builder.Rdocker是干什么的esult
ifusdocker面试题及答案eBuildKit{
build,docker和虚拟机的区别err=b.bdockeruildkitdocker是干什么的.Build(ctx,condocker和虚拟机的区别fig)
iferr!=nidockerl{
return"",err
}
}else{docker安装部署
build,err=b.bdocker容器uilder.Build(ctx,config)
iferr!=nil{
return"",err
}
}
-
处理构建完成后的动作。
到这个函数之后,就分别是 v1 builder
与 buildkit
对 Dockerfile
的解析,以及对 build context
的操作了。
这里涉及到的内容与我下一篇文章《高效构建 Docker 镜像的最佳实践》的内部关联比较大,此处就不再进行展开了。敬请期待下一篇文章。
总结
本文首先介绍了 Docker 的 C/S 架构,介绍了构建镜像所用的 API , API 文档可以在线查看或dockerhub者本地构建。之后深入到 Dockerdocker容器 CLdocker容器I 的源码中,逐步分解 v1 builder
与 buildkit
在构建镜像时执行的过程的差异。最后,我们深入到 dockerd
的源码中,了解到了对不同构建后端的docker怎么读调用。至此,Docker 构建镜像的原理及主体代码就介绍完毕。
但这还并不是结束,我会在后续文章中分享镜像构建的相关实践,敬请期待!
欢迎订阅我的文章公众号【MoeLove】
本docker菜鸟教程文分享自微信公众号 - MoeLdocker怎么读ove(TheMoeLovdocker安装部署e)。
如有侵docker和虚拟机的区别权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
发表评论