Docker 介绍

如果您是程序员或技术人员,您至少可以听说Docker:一个有用的工具,用于在“容器”中打包,运送和运行应用程序。这很难让所有的注意力得到 这些天 - 从开发人员和系统管理员一样。 即使像Google,VMware和亚马逊这样的大公司也在建设服务来支持它。

Docker是一个新的容器化的技术,它轻巧,且易移植,号称“build once, configure once and run anywhere”。

无论您是否对Docker有进行使用过,我仍然认为了解一些关于“容器”的基本概念以及如何与虚拟机(VM)进行比较是非常重要的。 虽然互联网充满了Docker的优秀使用指南,但我找不到许多初学者友好的概念指南,特别是在容器组成的方面。 所以,希望这篇文章会解决这个问题。

我们先来了解什么VM和容器呢?

什么是“容器”和“虚拟机”?

容器和虚拟机的目标是相似的:将应用程序及其依赖项隔离成可以在任何地方运行的独立单元。
此外,容器和虚拟机不再需要物理硬件,从而在能源消耗和成本效益方面更有效地利用计算资源。
容器和虚拟机之间的主要区别在于它们的架构方法。 我们来看看吧。

虚拟机

虚拟机本质上是一个真正的计算机的仿真,它执行像真正的计算机那样的程序。虚拟机使用“虚拟机监控程序”运行在物理机的顶部。管理程序又可以在主机或“裸机”上运行。
我们来解释这个行话:
虚拟机管理程序是虚拟机在其上运行的一个软件,固件或硬件。虚拟机管理程序本身运行在物理计算机上,被称为“主机”。主机为VM提供资源,包括RAM和CPU。这些资源在虚拟机之间划分,可以根据您的需要进行分发。因此,如果一个虚拟机正在运行资源较多的应用程序,则可能会为在同一主机上运行的其他虚拟机分配更多的资源。
在主机上运行的虚拟机(再次使用虚拟机管理程序)通常也称为“客户机”。此客机包含应用程序以及运行该应用程序所需的任何应用程序(例如系统二进制程序和库)。它还具有自己的整个虚拟化硬件堆栈,包括虚拟化网络适配器,存储和CPU - 这意味着它也有自己的成熟的客户操作系统。从内部,客机作为自己的单位,拥有自己的专属资源。从外面,我们知道这是一个VM - 共享主机提供的资源。

如上所述,客机可以在托管管理程序或裸机管理程序上运行。他们之间有一些重要的区别。
首先,托管虚拟化管理程序在主机的操作系统上运行。例如,运行OSX的计算机可以在该OS之上安装VM(例如VirtualBox或VMware Workstation 8)。 VM不能直接访问硬件,所以它必须经过主机操作系统(在我们的例子中是Mac的OSX)。
托管管理程序的好处是底层硬件不那么重要。主机的操作系统负责硬件驱动程序而不是管理程序本身,因此被认为具有更多的“硬件兼容性”。另一方面,硬件和管理程序之间的这个附加层会产生更多的资源开销,从而降低虚拟机的性能。
裸机管理程序环境通过在主机硬件上安装和运行来解决性能问题。因为它直接与底层硬件接口,所以不需要主机操作系统来运行。在这种情况下,作为操作系统安装在主机服务器上的第一件事就是管理程序。与托管虚拟机管理程序不同,裸机管理程序具有自己的设备驱动程序,并直接与每个组件进行交互,用于任何I / O,处理或特定于操作系统的任务。这导致更好的性能,可扩展性和稳定性。这里的折衷是硬件兼容性受到限制,因为管理程序只能在其中内置许多设备驱动程序。
所有这些谈论虚拟机管理程序之后,您可能会想知道为什么我们需要在VM和主机之间的这个额外的“虚拟机管理程序”层。
那么,由于虚拟机具有自己的虚拟操作系统,虚拟机管理程序在为虚拟机提供一个管理和执行客户机操作系统的平台方面发挥重要作用。它允许主机计算机在作为其上的客户端运行的虚拟机之间共享其资源。

VM

如图所示,虚拟机将虚拟硬件,内核(即OS)和每个新虚拟机的用户空间进行打包。

容器

与提供硬件虚拟化的虚拟机不同,容器通过抽象“用户空间”来提供操作系统级的虚拟化。 当我们解开容器术语时,你会看到我的意思。
出于所有目的和目的,容器看起来像一个虚拟机。 例如,它们具有用于处理的私有空间,可以以root身份执行命令,具有专用网络接口和IP地址,允许自定义路由和iptable规则,可以挂载文件系统等。

容器和虚拟机之间的一个很大的区别是容器与其他容器共享主机系统的内核。

container

该图显示了容器仅包含用户空间,而不是像VM那样的内核或虚拟硬件。 每个容器都拥有自己的隔离用户空间,允许多个容器在单个主机上运行。 我们可以看到,所有的操作系统级架构正在容器间共享。 从头创建的唯一部分是bin和libs。 这就是容器如此轻便。

Docker从哪里入手?

Docker是一个基于Linux容器的开源项目。 它使用Linux内核功能(如命名空间和控制组)来在操作系统之上创建容器。
集装箱距离不远; Google多年来一直在使用自己的集装箱技术。 其他Linux容器技术包括已经存在多年的Solaris Zones,BSD监狱和LXC。

是什么原因让Docker变得如此受欢迎呢?

  1. 易于使用:Docker使开发人员,系统管理员,架构师和其他人更容易利用容器来快速构建和测试便携式应用程序。它允许任何人在他们的笔记本电脑上打包应用程序,而这些应用程序又可以在任何公共云,私有云甚至裸机上运行。咒语是:“建立一次,在任何地方运行”。
  2. 速度:Docker容器非常轻便和快速。由于容器只是在内核上运行的沙盒环境,因此它们占用的资源较少。与可能需要更长时间的VM相比,您可以在几秒钟内创建和运行Docker容器,因为每次都需要启动完整的虚拟操作系统。
  3. Docker Hub:Docker用户也受益于Docker Hub日益丰富的生态系统,您可以将其视为“Docker镜像的应用商店”。Docker Hub拥有成千上万的社区创建的公共图片,可随时获得用来。搜索满足您需求的镜像非常容易,随时可以下拉和使用,无需修改。
  4. 模块化和可扩展性:Docker可以轻松地将应用程序的功能分解成单个容器。例如,您的Postgres数据库可能会在一个容器中运行,并且您的Redis服务器在另一个容器中运行,而Node.js应用程序位于另一容器中。使用Docker,将这些容器链接到一起创建应用程序变得更加容易,以便将来可以轻松地自动扩展或更新组件。

基本的Docker概念

现在我们已经有了很大的发展空间,我们先看一下Docker的基本部分:

docker

Docker引擎

Docker引擎是Docker运行的层。 它是一个轻量级的运行时和工具,用于管理容器,镜像,构建等。 它在Linux系统上本机运行,由以下组成:

  1. 在主机中运行的Docker守护进程。
  2. 一个Docker客户端,然后与Docker守护进程通信以执行命令。
  3. 一个用于远程与Docker守护进行交互的REST API。

Docker客户端

Docker客户端是您作为Docker的最终用户进行通信的对象。 认为它是Docker的UI。 例如,当你做

1
docker build iampeekay/someImage .

您正在与Docker客户端通信,Docker客户端会将您的指令传达给Docker守护进程。

Docker守护进程

Docker守护程序是实际执行发送到Docker Client的命令,如构建,运行和分发容器。 Docker守护程序在主机上运行,但作为用户,您不会直接与守护进程通信。 Docker客户端也可以在主机上运行,但不需要。 它可以在不同的机器上运行,并与主机上运行的Docker守护程序进行通信。

Dockerfile

Dockerfile是您编写构建Docker镜像的说明的地方。 这些说明可以是:

  • RUN apt-get y install some-package:安装一个软件包;
  • EXPOSE 8000: 对外开放端口;
  • ENV ANT_HOME /usr/local/apache-ant 传递一个环境变量;
    等等。
    一旦设置了Dockerfile,就可以使用docker build命令来构建一个镜像。 以下是Docker文件的示例:
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# Start with ubuntu 14.04
FROM ubuntu:14.04
MAINTAINER preethi kasireddy iam.preethi.k@gmail.com
# For SSH access and port redirection
ENV ROOTPASSWORD sample
# Turn off prompts during installations
ENV DEBIAN_FRONTEND noninteractive
RUN echo "debconf shared/accepted-oracle-license-v1-1 select true" | debconf-set-selections
RUN echo "debconf shared/accepted-oracle-license-v1-1 seen true" | debconf-set-selections
# Update packages
RUN apt-get -y update
# Install system tools / libraries
RUN apt-get -y install python3-software-properties \
software-properties-common \
bzip2 \
ssh \
net-tools \
vim \
curl \
expect \
git \
nano \
wget \
build-essential \
dialog \
make \
build-essential \
checkinstall \
bridge-utils \
virt-viewer \
python-pip \
python-setuptools \
python-dev
# Install Node, npm
RUN curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
RUN apt-get install -y nodejs
# Add oracle-jdk7 to repositories
RUN add-apt-repository ppa:webupd8team/java
# Make sure the package repository is up to date
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
# Update apt
RUN apt-get -y update
# Install oracle-jdk7
RUN apt-get -y install oracle-java7-installer
# Export JAVA_HOME variable
ENV JAVA_HOME /usr/lib/jvm/java-7-oracle
# Run sshd
RUN apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo "root:$ROOTPASSWORD" | chpasswd
RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config
# SSH login fix. Otherwise user is kicked off after login
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
# Expose Node.js app port
EXPOSE 8000
# Create tap-to-android app directory
RUN mkdir -p /usr/src/my-app
WORKDIR /usr/src/my-app
# Install app dependencies
COPY . /usr/src/my-app
RUN npm install
# Add entrypoint
ADD entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["npm", "start"]

Docker镜像

镜像是从您的Dockerfile中写入的一组说明构建的只读模板。 镜像定义了您想要的打包应用程序及其依赖关系,看起来像启动时要运行的进程。

Docker镜像使用Dockerfile构建。 Dockerfile中的每个指令都为镜像添加了一个新的“镜像层”,其中镜像层表示镜像文件系统的一部分,它们添加到或替换镜像层下面的镜像层。 层是Docker轻巧而强大结构的关键。 Docker使用Union File System来实现:

联合文件系统(Union File Systems)

Docker使用Union File Systems来构建镜像。 您可以将Union File System视为可堆叠文件系统,这意味着单独文件系统(称为分支)的文件和目录可以透明地叠加以形成单个文件系统。

在重叠的分支中具有相同路径的目录的内容被视为单个合并目录,这避免了需要创建每个层的单独副本。 相反,它们都可以被赋予相同资源的指针; 当某些层需要修改时,它会创建一个副本并修改一个本地副本,保留原来的不变。 这就是文件系统如何可以可写,而不实际允许写入。 (换句话说,是一个“写时复制”系统。)

分层系统提供两个主要优点:

  1. 无复制:每次使用镜像创建和运行新容器时,镜像层有助于避免复制一组完整的文件,从而实现Docker容器的快速便宜。
  2. 层隔离:进行更改更快 - 当您更改镜像时,Docker只会将更新传播到已更改的层。

卷(Volumes)

卷是容器的“数据”部分,在容器创建时初始化。 卷允许您持久存储并共享容器的数据。 数据卷与默认的Union File System分开,并且作为主机文件系统上的普通目录和文件存在。 因此,即使您销毁,更新或重建容器,数据卷将保持不变。 当您要更新卷时,您可以直接对其进行更改。 (另外,数据量可以在多个容器之间共享和重复使用,这是非常整齐的。)

Docker容器

如上所述,Docker容器将应用程序的软件包装到与应用程序需要运行的所有内容的不可见框中。 这包括操作系统,应用程序代码,运行时,系统工具,系统库等。Docker容器是由Docker镜像构建的。 由于镜像是只读的,Docker在镜像的只读文件系统上添加了一个读写文件系统来创建一个容器。

docker

此外,然后创建容器,Docker创建一个网络接口,以便容器可以与本地主机通信,将可用的IP地址附加到容器,并在定义镜像时执行您指定运行应用程序的进程。
成功创建容器后,可以在任何环境中运行它,而无需进行更改。

Docker支柱

总是让我好奇的一件事是容器如何实际实现,特别是因为容器周围没有任何抽象的基础设施边界。 经过很多阅读,这一切都很有意义,所以这里是我尝试向你解释的! :

术语“容器”实际上只是一个抽象的概念来描述几个不同的特征如何协同工作来可视化“容器”。 让我们快速过一过这些知识点:

1 命名空间(Namespaces)

命名空间为容器提供了自己对底层Linux系统的视图,限制了容器可以看到和访问的内容。运行容器时,Docker创建特定容器将使用的命名空间。

Docker使用的内核中有几种不同类型的命名空间,例如:

  • NET:提供具有自己的系统网络堆栈视图(例如其自己的网络设备,IP地址,IP路由表,/ proc / net目录,端口号等)的容器。
  • PID:PID代表进程ID。如果您在命令行中运行过ps aux以检查系统上正在运行哪些进程,那么您将看到一个名为“PID”的列。 PID命名空间给容器提供他们可以查看和交互的进程的自己的范围视图,包括一个独立的init(PID 1),它是“所有进程的祖先”。
  • MNT:给系统自己的“mounts”视图的容器。因此,不同安装名称空间中的进程对文件系统层次结构具有不同的视图。
  • UTS:UTS代表UNIX分时系统。它允许进程识别系统标识符(即主机名,域名等)。 UTS允许容器具有与其他容器和主机系统无关的自己的主机名和NIS域名。
  • IPC:IPC代表InterProcess Communication。 IPC命名空间负责在每个容器之间运行的进程之间隔离IPC资源。
  • USER:此命名空间用于隔离每个容器中的用户。与主机系统相比,它允许容器具有不同的uid(用户ID)和gid(组ID)范围的视图。因此,进程的uid和gid在用户命名空间内外可能会有所不同,这也允许进程在容器之外拥有无特权用户,而不会牺牲容器内的root权限。

Docker将这些命名空间一起使用,以便隔离并开始创建容器。

2 控制组(Control groups)

控制组(也称为cgroups)是一个Linux内核功能,可以隔离,优先排列和记录一组进程的资源使用情况(CPU,内存,磁盘I / O,网络等)。在这个意义上,一个cgroup可以确保Docker容器只能使用他们需要的资源,如果需要,可以设置容器*可以使用什么资源的限制。 Cgroups还确保单个容器不会耗尽其中一个资源并将整个系统关闭。

Docker的未来:Docker和VM将共存

Docker肯定会获得很大的收益,但我不相信它将成为虚拟机的真正威胁。容器将继续获得成功,但有许多使用虚拟机仍然更适合的用例。
例如,如果您需要在多个服务器上运行多个应用程序,则使用虚拟机可能是有意义的。另一方面,如果您需要运行多个副本的单个应用程序,Docker提供了一些引人注目的优势。
此外,Docker允许您将应用程序分解成更多功能分立的部件,从而创建分离的关注点,这也意味着越来越多的部件要管理,这可能会变得笨重。
Docker容器的安全性也是一个令人关切的问题,因为容器共享相同的内核,容器间的隔离更薄。完整的VM只能向主机管理程序发出超级呼叫,Docker容器可以将系统调用到主机内核,从而创建更大的攻击面。当安全性特别重要时,开发人员可能会选择通过抽象硬件隔离的虚拟机,使得彼此之间的干扰更加困难。