Docker 深入浅出
什么是 Docker
在官网上下载安装成功后,使用 docker -v 可以查看 docker 版本
1 | docker -v |
Docker 技术与传统虚拟化方式的区别
传统的虚拟机技术是一套完整的操作系统和应用进程。
Docker 容器内的应用进程则是直接运行于宿主内核,容器没有自己的内核和硬件虚拟。
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。

使用 Docker 优势
1.提供一致的运行环境。
2.轻松的项目迁移。
3.快速部署与回滚。
镜像与容器
Docker 镜像(Image) 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含 任何动态数据,其内容在构建之后也不会被改变。
分层存储
Docker 镜像充分利用 Union FS 的技术,将其设计为分层存储的架构。
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
容器(Container) 是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。
容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。
使用 Docker 镜像
Docker HUB上有很多可以使用的镜像。
1 | docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签] |
上述命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub (docker.io)获取镜像。即 docker.io/library/ubuntu:18.04。
运行
有了镜像以后,我们就能够以这个镜像为基础启动并运行一个容器。
1 | docker run -it --rm [镜像名] bash |
it: 是两个参数,-i:交互操作,-t 终端。rm: 声明容器推出后随之将其删除。bash: 放在镜像名后的是命令。bash 则为 shell 交互式。
第二条命令则是以交互式终端的方式进入容器,修改内容,并以 exit 退出容器内部。
列出镜像
1 | docker images ls |
删除镜像
1 | docker image rm [选项] <镜像1> |
其中 <镜像> 可以是 ID,镜像名,镜像摘要。
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
FROM 指令
使用 FROM 指令指定基础镜像,必备指令,且是第一条指令。
在 Docker Hub 上有很多高质量的官方镜像,例如可以直接拿来使用的服务类镜像,如 nginx、redis、mongo、mysql、tomcat等
RUN 指令
RUN 指令是用来执行命令的,常用的方式有两种
1.shell 格式 RUN <命令>
2.exec 格式 RUN ['可执行文件', '参数1', '参数2'...]
Dockerfile 中每一个指令都会建立一层,RUN 也不例外, 每一个 RUN 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,commit 这一层的修改,构成新的镜像
具体的 commit 原理 详解。
Union FS 是有最大层数限制的。多个 RUN 指令可以使用 && 将指令串联起来。这样就可以减少 docker 镜像层数。
其他指令
COPY 复制文件ADD 更高级的文件复制EXPOSE 暴露端口WORKDIR 指定工作目录
docker 构建镜像
在 Dockerfile 文件所在的目录执行
1 | docker build [选项] <上下文路径/URL/-> |
镜像构建上下文(Context)
docker build 命令最后需要指定上下文路径。
在理解上下文路径的时候,我们需要理解 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 中这么写的
1 | COPY ./package.json /app/ |
这并不是要复制执行 docker build 命令所在的目录下的 package.json,也不是复制 Dockerfile 所在目录下的 package.json,而是复制上下文(context)目录下的 package.json。
一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个 .dockerignore,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。
导出 docker 容器
在 docker build 生成镜像后,可以导出容器
1 | // run 镜像 |
参考
Docker API 文档
Docker 从入门到实践
Docker 实战总结
使用 Docker 构建前端应用
docker-dockerfile