《微服务架构实战》读书笔记四----Docker

Docker原理

Docker是一个客户端-服务端(C/S)架构的程序,Docker客户端只需要向Docker服务端或守护进程发出请求,服务器或守护进程将完成所有工作并返回结果。Docker提供了一个命令行工具Docker及一整套RESTful API 可以在同一台宿主机器上运行Docker守护进程和客户端,也可以从本地的Docker客户端连接到运行在另一台宿主机远程的Docker守护进程

Docker依赖的Linux的内核特性包括Namespaces命名空间和Control groups控制组。

Namespace命名空间

  • PID 进程ID隔离
  • NET 管理网络端口
  • IPC 进程间通信
  • 管理跨进程通信的访问
  • MNT 管理挂载点
  • UTS 隔离内核和版本标识
  • Control groups 控制组

Control groups控制组

  • 资源限制
  • 优先级设定
  • 资源度量
  • 资源控制及资源分配

Docker 容器的能力包括

  • 文件系统隔离----每个容器都在自己的root文件系统,可以独立挂载外部文件系统
  • 进程隔离----每个容器都运行在自己的进程环境中,相互之间互不干扰
  • 网络隔离----容器间的虚拟网络接口和IP地址都是分开的
  • 资源隔离和分钟----使用Control groups将CPU和内存等资源独立分配给每个Docker容器

更轻量级的虚拟化

Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案

虚拟机的架构

  • 基础设施:一般是服务器或者云主机
  • 虚拟机管理系统:利用Hypervisor,可以在主操作系统之上运行多个不同的从操作系统,可以构建在基础设施之上,也可以构建在操作系统上
  • 客户端操作系统:假设运行3个相互隔离的应用,则需要用Hypervisor启动3个客户机操作系统,也就是3个虚拟机,这些虚拟机都非常大
  • 各种依赖:每一个客户机操作系统都需要安装许多依赖
  • 应用:安装依赖后,就可以在各个客户机操作系统分别运行应用了,这样各个应用就是相互隔离的

Docker架构

  • 基础设施
  • 主操作系统:所有主流的系统都行对Docker支持
  • Docker守护进程:Docker守护进程代替了Hypervisor,它是运行在操作系统之上的后台进程,负责管理Docker容器
  • 各种依赖:对于Docker,应用的所有依赖都打包在Docker镜像中,Docker容器是基于Docker镜像创建的
  • 应用:应用的源代码与它的依赖度打包在Docker镜像中,不同的应用需要不同的Docker镜像,不同的应用运行在不同的Docker容器中,它们是相互隔离的

虚拟机和Docker对比

  • Docker容器可以秒级启动
  • Docker对系统资源利用高,一台主机可以运行千个Docker容器
  • 容器除了应用,基本不消耗额外系统资源,所使用资源完全是使用宿主机上的资源
  • 传统虚拟机10个应用要10个虚拟机,而Docker只需要10个隔离应用
  • Docker可以跨平台,不需要额外操作系统支持

三个概念理解Docker

Docker有三个概念,镜像、容器和仓库

我们可以从镜像仓库把镜像下载到本地,然后将镜像启动就能创建一个容器

Docker之所以这么吸引人,除了新颖的技术,在官方的Registry的生态圈也是相当吸引人球的地方,在Docker Hub中可以下载大量容器化好的应用镜像

Dockerfile 定制一切

Docker为我们提供一套非常强大的语法,可以利用简单的编写程序构建出任何你想要的环境,同时可以跟业务代码相结合,快速构建和生成所需要的应用

Dockerfile用来创建一个自定义的Image,包含了用户指定的软件依赖等,使用Docker的build命令可以直接构建新的Image,它简化了从头到尾的流程并极大地简化了部署工作

Dockerfile命令

​ FROM

​ FROM命令可能是最重要的Dockerfile命令,此命令定义了使用哪个基础镜像启动构建流程,基础镜像可以为任意镜像,如果基础镜像没有被发现,则Docker将试图从Docker image index来查找该镜像

​ 格式为 FROM image 或者 FROM image tag

第一条指令必须是FROM指令,并且在同一个Dockerfile中创建多个镜像时,可以使用多个FROM命令

​ MAINTAINER

​ 格式为MAINTAINER name,指定维护者信息,其实就是类似javadoc注释中的@author,用户声明作者,一般都放在文件比较靠上的位置

​ RUN

​ 从名称上就可以看出,RUN是执行或运行的意思

​ 格式为RUN command 或 RUN [“executable”,“paraml”,“param2”]

​ 前者将在Shell终端中运行命令,即/bin/sh -c; 后者则使用exec执行。指定使用其他终端可以通过第二种方式实现,例如,RUN ["/bin/bash","-c",“echo hello”]

​ 每条RUN指令将在当前镜像基础上执行指定命令,并提交为新镜像。当命令较长时可以使用“\”来换行

​ EXPOSE

​ 格式EXPOSE port

​ 告诉Docker服务端容器暴露的端口号

​ CMD

​ 支持格式有三种

​ CMD [“executable”,“param1”,“param2”] 使用exec执行

​ CMD command param1 parma2 在/bin/sh,提供给需要交互的应用

​ CMD [“param1”,“param2”] 提供给ENTRYPOINT的默认参数

​ 指定启动容器时执行的命令,每个Dcokerfile只能有一条CMD命令,如果指定了多条命令,则只有最后一条会被执行

​ 如果用户启动容器时指定了运行的命令,则会覆盖CMD指定的命令

​ ENTRYPOINT

​ 两种格式

​ ENTRYPOINT [“executeable”,“param1”,“param2”]

​ ENTRYPOINT command,param1,param2

​ 配置容器启动后执行的命令,并且不可被Docker RUN 提供的参数覆盖

​ 每个Dockerfile中只能有一个ENTRYPOINT,当指定多个时,只有最后一个生效

​ ENV

​ 格式为ENV key value 指定一个环境变量,会被后续RUN指令使用,并在容器运行时保持

​ ADD

​ 格式为 ADD src dest

​ 该命令将复制指定的src到容器中的dest,其中src可以是Dockerfile所在目录的一个相对路径,也可以是一个URL,还可以是一个tar文件

​ COPY

​ 格式为COPY src dest

​ 复制本地主机的 src到容器中的dest

​ 当使用本地目录为源目录时,推荐使用COPY

​ VOLUME

​ 格式为VOLUME["/data"]

​ 创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保存的数据等

​ WORKDIR

​ 格式为WORKDIR /path/to/workdir

​ 为后续的RUN、CMD、ENTRYPOINT指令配置工作目录

​ 可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定路径

​ 例如:

​ WORKDIR /a

​ WORKDIR b

​ WORKDIR c

​ RUN pwd

​ 最终路径为 /a/b/c

​ USER

​ 格式为USER daemon

​ 指定运行容器时的用户名或者UID,后续的RUN也会使用指定用户。

​ 当服务不需要管理员权限的时候,可以通过该命令指定运行用户,并且可以在之前创建所需要的用户,例如,RUN groupadd -r postgres && useradd -r -g postgres postgres 要临时获取管理员权限可以使用gosu,而不推荐sudo

​ ONBUILD

​ 格式为ONBUILD [INSTRUCTION]

​ 配置当前创建镜像作为其他新创建镜像的基础镜像的基础镜像时,所执行的操作指令

Dockerfile构建过程

Dockerfile其实可以看做一个命令集,每行第一个均为一条命令,每行第一个单词就是命令,后面的字符串是该命令所有接收的参数

docker build的流程

  1. 提取Dockerfile(evaluator.go/RUN)
  2. 将Docker按行进行分析(parse/parser.go/Parse),每行第一个单词叫做command,根据command对之后的字符串进行解析
  3. 根据分析的command,在dispatchers.go中选择对应的函数进行处理(dispatchers.go)
  4. 处理完所有的命令,如果需要打标签,则给最后的镜像打上tag,结束

Dockerfile逆向

通过docker history image 可以看到该镜像的历史来源,即使没有Dockerfile 也可以通过history来逆向产生Dockerfile