Docker 深入浅出

Docker 深入浅出

什么是 Docker

Docker 官网

在官网上下载安装成功后,使用 docker -v 可以查看 docker 版本

1
2
3
docker -v

Docker version 20.10.7, build f0df350

Docker 技术与传统虚拟化方式的区别
传统的虚拟机技术是一套完整的操作系统和应用进程。
Docker 容器内的应用进程则是直接运行于宿主内核,容器没有自己的内核和硬件虚拟。
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。

使用 Docker 优势

1.提供一致的运行环境。
2.轻松的项目迁移。
3.快速部署与回滚。

镜像与容器

Docker 镜像(Image) 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含 任何动态数据,其内容在构建之后也不会被改变。

分层存储

Docker 镜像充分利用 Union FS 的技术,将其设计为分层存储的架构。
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。

容器(Container) 是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。
容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。

使用 Docker 镜像

Docker HUB上有很多可以使用的镜像。

1
2
3
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]

docker pull ubuntu:18.04

上述命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub (docker.io)获取镜像。即 docker.io/library/ubuntu:18.04。

运行

有了镜像以后,我们就能够以这个镜像为基础启动并运行一个容器。

1
2
3
docker run -it --rm [镜像名] bash

docker exec -it [镜像名] bash

it: 是两个参数,-i:交互操作,-t 终端。
rm: 声明容器推出后随之将其删除。
bash: 放在镜像名后的是命令。bash 则为 shell 交互式。

第二条命令则是以交互式终端的方式进入容器,修改内容,并以 exit 退出容器内部。

列出镜像

1
2
docker images ls
docker images |grep [镜像名]

删除镜像

1
docker image rm [选项] <镜像1>

其中 <镜像> 可以是 ID镜像名镜像摘要

Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

FROM 指令

使用 FROM 指令指定基础镜像,必备指令,且是第一条指令。
在 Docker Hub 上有很多高质量的官方镜像,例如可以直接拿来使用的服务类镜像,如 nginxredismongomysqltomcat

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/-> 

docker build 选项参数

镜像构建上下文(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// run 镜像
docker run <镜像名>

// -d run container in background and print container ID
// -p 本地的 80 端口映射到容器的 80 端口
docker run -d -p 80:80 <镜像名>

// 查看容器
docker ps -a

// 停止容器
docker stop <容器id>

// 删除容器
docker rm <容器id>

// 导出容器
docker export <容器名> > <保存路径>/[容器名].tar

参考

Docker API 文档
Docker 从入门到实践
Docker 实战总结
使用 Docker 构建前端应用
docker-dockerfile