概述

什么是容器

容器技术由来已久,却直到几十年后因dotCloud公司(后更名为Docker)于Docker项目中发明的“容器镜像”技术创造性地解决了应用打包的难题才焕发出新的生命力并以“应用容器”的面目风靡于世,Docker的名字更是响彻寰宇,它催生出或改变了一大批诸如容器编排、服务网格和云原生等技术,深刻影响了云计算领域的技术方向。

容器是一种轻量级、可移植、自包含的软件打包技术,使应用程序可以在几乎任何地方以相同的方式运行。

容器的优势

对于开发人员: Build OnceRun Anywhere

容器意味着环境隔离和可重复性。开发人员只需为应用创建一次运行环境,然后打包成容器便可在其他机器上运行。另外,容器环境与所在的Host环境是隔离的,就像虚拟机一样,但更快更简单。

对于运维人员: Configure OnceRun Anything

只需要配置好标准的runtime环境,服务器就可以运行任何容器。这使得运维人员的工作变得更高效、一致和可重复。

Tips:runtime简单来讲,就是让容器跑起来的组件。

容器消除了开发、测试、生产环境的不一致性。

Docker使用Google公司推出的Go语言进行开发实现,基于Linux内核的cgroupnamespace,以及AUFS类的Union FS 等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于LXC,从0.7版本以后开始去除LXC,转而使用自行开发的libcontainer,从1.11开始,则进一步演进为使用runCcontainerd

Docker在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得Docker技术比虚拟机技术更为轻便、快捷。

运行时与编排引擎

参考:Docker简介以及Docker历史

多数技术人员在谈到Docker时,主要是指Docker 引擎。

Docker引擎是用于运行和编排容器的基础设施工具。有 VMware 管理经验的读者可以将其类比为ESXi

ESXi是运行虚拟机的核心管理程序,而Docker 引擎是运行容器的核心容器运行时。

其他Docker公司或第三方的产品都是围绕Docker 引擎进行开发和集成的。

Docker引擎主要有两个版本:企业版(Enterprise Edition,EE)和社区版(Community Edition,CE)。

Docker EE包含Docker CE中的全部功能,还包括了商业支持以及与其他 Docker产品的集成,比如Docker可信镜像库和通用控制面板。

每个季度,企业版和社区版都会发布一个稳定版本。社区版本会提供4个月的支持,而企业版本会提供12个月的支持。

从2017年第一季度开始,Docker版本号遵循YY.MM-xx格式,类似于Ubuntu等项目。例如2018年6月第一次发布的社区版本为18.06.0-ce

Tips:2017年第一季度以前,Docker版本号遵循大版本号.小版本号的格式。采用新格式前的最后一个版本是Docker 1.13

架构

概括起来,Docker容器技术有3个核心概念:容器、镜像和镜像仓库(DockerRegistry)。如果把容器类比为动态的、有生命周期的进程,则镜像就像是静态的可执行程序及其运行环境的打包文件,而镜像仓库则可想象成应用程序分发仓库,事先存储了制作好的各类镜像文件。

image

Docker采用的是Client/Server架构。客户端(client)向服务器(Docker Deamon)发送请求,服务器负责构建、运行和分发容器。客户端和服务器可以运行在同一个Host上,客户端也可以通过socketREST API与远程的服务器通信。

Docker客户端

最常用的Docker客户端是docker命令,除了docker命令还可以通过REST API与服务器通信。

Docker服务器

默认配置下,Docker deamon只能响应本地Host的客户端请求。如果要允许远程客户端请求,需要在配置文件中打开TCP监听。

Docker镜像

可以将Docker镜像看成只读模板,通过它可以创建Docker容器。

镜像由多种生成方法:

  • 从无到有开始创建镜像;
  • 下载并使用别人创建好的现成镜像;
  • 在现有镜像上创建新的镜像;

通常将提供一个基本的操作系统环境,用户可以根据需要,安装和配置软件的镜像叫做base镜像

镜像采用分层结构,即从base镜像一层一层叠加生成,每安装一个软件,就在现有镜像的基础上增加一层。

采用这种分层结构的好处是共享资源。例如有多个镜像都从相同的base镜像构建而来,那么docker host只需在磁盘上保存一份base镜像即可。同时内存中也只需加载一份base镜像,就可以为所有容器服务了,而且镜像的每一层都可以被共享,我们将在后面更深入地讨论这个特性。

Tips:如果多个容器共享一份基础镜像,当某个容器修改了基础镜像的内容,这时其他容器是否会被修改?答案是不会,因为任何修改都只会发生在容器层,所有镜像层都是只读的。

Docker容器

Docker容器就是Docker镜像的运行实例。

用户可以通过CLI(docker命令)或是API启动、停止、移动或删除容器。可以这么认为,对于应用软件,镜像是软件生命周期的构建和打包阶段,而容器则是启动和运行阶段。

Docker仓库

Docker仓库(Registry)是存放Docker镜像的仓库,Registry分私有和共有两种。

Docker Hub是默认的镜像仓库,由Docker公司维护,上面有数以万计的镜像供用户自由下载和使用。

出于对速度或安全的考虑,用户也可以创建自己的私有镜像仓库。

Dockerfile

Dockerfile是一个文本文件,记录了镜像构建的所有步骤。

Dockerfile常用指令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 指定base镜像
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>

# 维护者信息
MAINTAINER <name>

# 构建镜像时执行的命令
RUN <command>
RUN ["executable", "param1", "param2"]

# 将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget
ADD <src>... <dest>
ADD ["<src>",... "<dest>"] # 用于支持包含空格的路径

# 将文件从build context复制文件到镜像
COPY <src>... <dest>
COPY ["<src>",... "<dest>"]

# 构建容器后调用,也就是在容器启动时才进行调用
CMD ["executable","param1","param2"] # 执行可执行文件,优先
CMD ["param1","param2"] # 设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数
CMD command param1 param2 # 执行shell内部命令

# 配置容器,使其可执行化。配合CMD可省去"application",只使用参数
ENTRYPOINT ["executable", "param1", "param2"] # 可执行文件, 优先
ENTRYPOINT command param1 param2 # shell内部命令

# 为镜像添加元数据
LABEL <key>=<value> <key>=<value> <key>=<value> ...

# 设置环境变量
ENV <key> <value> # <key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量
ENV <key>=<value> ... # 可以设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行

# 指定持久化目录
VOLUME ["/path/to/dir"]

# 切换工作目录
WORKDIR /path/to/workdir