docker深入

docker run 背后的操作

1.检查本地是否存在指定的镜像,不存在从仓库下载;
2.利用镜像创建一个容器,并启动;
3.分配一个文件系统给容器,并在只读的镜像外层挂载一个可读写层;
4.从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去;
5.从网桥的地址池配置一个IP给容器;
6.执行用户指定的应用程序;
7.执行完毕后容器被自动终止;

build

docker build [选项] <上下文路径/URL/->
首先我们要理解 docker build 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。
Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,
而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。
因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。
也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。
而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。
那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?
这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,
然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
如果在 Dockerfile 中这么写:COPY ./package.json /app/
这并不是要复制执行 docker build 命令所在的目录下的 package.json,也不是复制 Dockerfile 所在目录下的 package.json,
而是复制 上下文(context) 目录下的 package.json。
因此,COPY 这类指令中的源文件的路径都是相对路径。
这也是初学者经常会问的为什么 COPY ../package.json /app 或者 COPY /opt/xxxx /app 无法工作的原因,
因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。
现在就可以理解命令 docker build -t nginx:v3 . 中的这个 .,实际上是在指定上下文的目录,
docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。

copy

COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。
目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。
特别是构建相关文件都在使用 Git 进行管理的时候。

add

ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。
比如 <源路径> 可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去。
下载后的文件权限自动设置为 600,如果这并不是想要的权限,那么还需要增加额外的一层 RUN 进行权限调整,
另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层 RUN 指令进行解压缩。
所以不如直接使用 RUN 指令,然后使用 wget 或者 curl 工具下载,处理权限、解压缩、然后清理无用文件更合理。
因此,这个功能其实并不实用,而且不推荐使用。
如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,
ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。

cmd

CMD 指令的格式和 RUN 相似,也是两种格式:
• shell 格式:CMD <命令>
• exec 格式:CMD ["可执行文件", "参数1", "参数2"...]
• 参数列表格式:CMD ["参数1", "参数2"...]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。
CMD 指令就是用于指定默认的容器主进程的启动命令的。
 
在指令格式上,一般推荐使用 exec 格式,这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号 ",而不要使用单引号。
如果使用 shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行执行。比如:
CMD echo $HOME
在实际执行中,会将其变更为:
CMD [ "sh", "-c", "echo $HOME" ]

Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用 upstart/systemd 去启动后台服务,
容器内没有后台服务的概念。然后发现容器执行后就立即退出了。甚至在容器内去使用 systemctl 命令结果却发现根本执行不了。
这就是因为没有搞明白前台、后台的概念,没有区分容器和虚拟机的差异,依旧在以传统虚拟机的角度去理解容器。
对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,
其它辅助进程不是它需要关心的东西。而使用 service nginx start 命令,则是希望 upstart 来以后台守护进程形式启动 nginx 服务。
而刚才说了 CMD service nginx start 会被理解为 CMD [ "sh", "-c", "service nginx start"],因此主进程实际上是 sh。
那么当 service nginx start 命令结束后,sh 也就结束了,sh 作为主进程退出了,自然就会令容器退出。

ENTRYPOINT 入口点

ENTRYPOINT 的格式和 RUN 指令格式一样,分为 exec 格式和 shell 格式。
ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。ENTRYPOINT 在运行时也可以替代,
不过比 CMD 要略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。
当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,
换句话说实际执行时,将变为:<ENTRYPOINT> "<CMD>"

参考

docker load vs docker import

docker load导入镜像存储到本地镜像库 docker import导入一个容器快照到本地镜像库。 区别在于:容器快照文件会丢弃所有的历史记录和元数据信息,仅保存容器当时的快照状态,而镜像文件则保存完整记录,体积更大; 另外从容器导入快照文件可以重新指定标签等元数据信息; 配置指令 操作指令 网络原理 网络原理 网络原理 网络原理 网络原理 网络原理