每天5分钟玩转Docker容器技术(二)

每天5分钟玩转Docker容器技术(一)

容器核心知识

本篇通过 Docker 讨论容器的核心知识。

概述

容器核心知识主要回答有关容器 What、Why 和 How 三个问题。其中 How 是重点,将从架构、镜像、容器、网络和存储几个方面进行讲解。

What - 什么是容器?

容器是一种轻量级、可移植、自包含的软件打包技术,使应用程序可以在几乎任何地方以相同的方式运行。开发人员在自己笔记本上创建并测试好的容器,无需任何修改就能够在生产系统的虚拟机、物理服务器或公有云主机上运行。

容器与虚拟机

谈到容器,就不得不将它与虚拟机进行对比,因为两者都是为应用提供封装和隔离。

容器由两部分组成:

  1. 应用程序本身
  2. 依赖:比如应用程序需要的库或其他软件

容器在 Host 操作系统的用户空间中运行,与操作系统的其他进程隔离。这一点显著区别于的虚拟机。

传统的虚拟化技术,比如 VMWare, KVM, Xen,目标是创建完整的虚拟机。为了运行应用,除了部署应用本身及其依赖(通常几十 MB),还得安装整个操作系统(几十 GB)。

下图展示了二者的区别。

如图所示,由于所有的容器共享同一个 Host OS,这使得容器在体积上要比虚拟机小很多。另外,启动容器不需要启动整个操作系统,所以容器部署和启动速度更快,开销更小,也更容易迁移。

Why - 为什么需要容器?

为什么需要容器?容器到底解决的是什么问题?
简要的答案是:容器使软件具备了超强的可移植能力

容器解决的问题

我们来看看今天的软件开发面临着怎样的挑战?

如今的系统在架构上较十年前已经变得非常复杂了。以前几乎所有的应用都采用三层架构(Presentation/Application/Data),系统部署到有限的几台物理服务器上(Web Server/Application Server/Database Server)。

而今天,开发人员通常使用多种服务(比如 MQ,Cache,DB)构建和组装应用,而且应用很可能会部署到不同的环境,比如虚拟服务器,私有云和公有云。

一方面应用包含多种服务,这些服务有自己所依赖的库和软件包;另一方面存在多种部署环境,服务在运行时可能需要动态迁移到不同的环境中。这就产生了一个问题:

如何让每种服务能够在所有的部署环境中顺利运行?

于是我们得到了下面这个矩阵:

各种服务和环境通过排列组合产生了一个大矩阵。开发人员在编写代码时需要考虑不同的运行环境,运维人员则需要为不同的服务和平台配置环境。对他们双方来说,这都是一项困难而艰巨的任务。

如何解决这个问题呢?

聪明的技术人员从传统的运输行业找到了答案。

几十年前,运输业面临着类似的问题。

每一次运输,货主与承运方都会担心因货物类型的不同而导致损失,比如几个铁桶错误地压在了一堆香蕉上。另一方面,运输过程中需要使用不同的交通工具也让整个过程痛苦不堪:货物先装上车运到码头,卸货,然后装上船,到岸后又卸下船,再装上火车,到达目的地,最后卸货。一半以上的时间花费在装、卸货上,而且搬上搬下还容易损坏货物。

这同样也是一个 NxM 的矩阵。

幸运的是,集装箱的发明解决这个难题。

任何货物,无论钢琴还是保时捷,都被放到各自的集装箱中。集装箱在整个运输过程中都是密封的,只有到达最终目的地才被打开。标准集装箱可以被高效地装卸、重叠和长途运输。现代化的起重机可以自动在卡车、轮船和火车之间移动集装箱。集装箱被誉为运输业与世界贸易最重要的发明。

Docker 将集装箱思想运用到软件打包上,为代码提供了一个基于容器的标准化运输系统。Docker 可以将任何应用及其依赖打包成一个轻量级、可移植、自包含的容器。容器可以运行在几乎所有的操作系统上。

其实,“集装箱” 和 “容器” 对应的英文单词都是 “Container”。
“容器” 是国内约定俗成的叫法,可能是因为容器比集装箱更抽象,更适合软件领域的原故吧。

我个人认为:在老外的思维中,“Container” 只用到了集装箱这一个意思,Docker 的 Logo 不就是一堆集装箱吗?


Docker 的特性

我们可以看看集装箱思想是如何与 Docker 各种特性相对应的。

容器的优势

对于开发人员 - Build Once, Run Anywhere

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

对于运维人员 - Configure Once, Run Anything

只需要配置好标准的 runtime 环境,服务器就可以运行任何容器。这使得运维人员的工作变得更高效,一致和可重复。容器消除了开发、测试、生产环境的不一致性。

How - 容器是如何工作的?

接下来学习容器核心知识的最主要部分。

我们首先会介绍 Docker 的架构,然后分章节详细讨论 Docker 的镜像、容器、网络和存储。

Docker 架构详解

Docker 的核心组件包括:

  1. Docker 客户端 - Client
  2. Docker 服务器 - Docker daemon
  3. Docker 镜像 - Image
  4. Registry
  5. Docker 容器 - Container

Docker 架构如下图所示:

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

Docker 客户端

最常用的 Docker 客户端是 docker 命令。通过 docker 我们可以方便地在 Host 上构建和运行容器。

docker 支持很多操作(子命令),后面会逐步用到。

除了 docker 命令行工具,用户也可以通过 REST API 与服务器通信。

Docker 服务器

Docker daemon 是服务器组件,以 Linux 后台服务的方式运行。

Docker daemon 运行在 Docker host 上,负责创建、运行、监控容器,构建、存储镜像。

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

1.编辑配置文件 /etc/systemd/system/multi-user.target.wants/docker.service,在环境变量 ExecStart 后面添加 -H tcp://0.0.0.0,允许来自任意 IP 的客户端连接。

如果使用的是其他操作系统,配置文件的位置可能会不一样。

2.重启 Docker daemon。

3.服务器 IP 为 192.168.56.102,客户端在命令行里加上 -H 参数,即可与远程服务器通信。

info 子命令用于查看 Docker 服务器的信息。

Docker 镜像

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

例如某个镜像可能包含一个 Ubuntu 操作系统、一个 Apache HTTP Server 以及用户开发的 Web 应用。

镜像有多种生成方法:

  1. 可以从无到有开始创建镜像
  2. 也可以下载并使用别人创建好的现成的镜像
  3. 还可以在现有镜像上创建新的镜像

我们可以将镜像的内容和创建步骤描述在一个文本文件中,这个文件被称作 Dockerfile,通过执行 docker build <docker-file> 命令可以构建出 Docker 镜像,后面我们会讨论。

Docker 容器

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

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

Registry

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

Docker Hub(hub.docker.com/) 是默认的 Registry,由 Docker 公司维护,上面有数以万计的镜像,用户可以自由下载和使用。

出于对速度或安全的考虑,用户也可以创建自己的私有 Registry。后面我们会学习如何搭建私有 Registry。

docker pull 命令可以从 Registry 下载镜像。 docker run 命令则是先下载镜像(如果本地没有),然后再启动容器。

Docker 组件如何协作?

一个完整的例子

还记得我们运行的第一个容器吗?现在通过它来体会一下 Docker 各个组件是如何协作的。

容器启动过程如下:

  1. Docker 客户端执行 docker run 命令。
  2. Docker daemon 发现本地没有 httpd 镜像。
  3. daemon 从 Docker Hub 下载镜像。
  4. 下载完成,镜像 httpd 被保存到本地。
  5. Docker daemon 启动容器。

docker images 可以查看到 httpd 已经下载到本地。

docker ps 或者 docker container ls 显示容器正在运行。

小结

Docker 借鉴了集装箱的概念。标准集装箱将货物运往世界各地,Docker 将这个模型运用到自己的设计哲学中,唯一不同的是:集装箱运输货物,而 Docker 运输软件。

每个容器都有一个软件镜像,相当于集装箱中的货物。容器可以被创建、启动、关闭和销毁。和集装箱一样,Docker 在执行这些操作时,并不关心容器里到底装的什么,它不管里面是 Web Server,还是 Database。

用户不需要关心容器最终会在哪里运行,因为哪里都可以运行。

开发人员可以在笔记本上构建镜像并上传到 Registry,然后 QA 人员将镜像下载到物理或虚拟机做测试,最终容器会部署到生产环境。

使用 Docker 以及容器技术,我们可以快速构建一个应用服务器、一个消息中间件、一个数据库、一个持续集成环境。因为 Docker Hub 上有我们能想到的几乎所有的镜像。

不知大家是否意识到,潘多拉盒子已经被打开。容器不但降低了我们学习新技术的门槛,更提高了效率。

如果你是一个运维人员,想研究负载均衡软件 HAProxy,只需要执行 docker run haproxy,无需繁琐的手工安装和配置既可以直接进入实战。

如果你是一个开发人员,想学习怎么用 django 开发 Python Web 应用,执行 docker run django,在容器里随便折腾吧,不用担心会搞乱 Host 的环境。

不夸张的说:容器大大提升了 IT 人员的幸福指数。

最小的镜像

镜像是 Docker 容器的基石,容器是镜像的运行实例,有了镜像才能启动容器。

本章内容安排如下:

  1. 首先通过研究几个典型的镜像,分析镜像的内部结构。
  2. 然后学习如何构建自己的镜像。
  3. 最后介绍怎样管理和分发镜像。

镜像的内部结构

为什么我们要讨论镜像的内部结构?

如果只是使用镜像,当然不需要了解,直接通过 docker 命令下载和运行就可以了。

但如果我们想创建自己的镜像,或者想理解 Docker 为什么是轻量级的,就非常有必要学习这部分知识了。

我们从一个最小的镜像开始吧。

hello-world - 最小的镜像

hello-world 是 Docker 官方提供的一个镜像,通常用来验证 Docker 是否安装成功。

我们先通过 docker pull 从 Docker Hub 下载它。

docker images 命令查看镜像的信息。

hello-world 镜像竟然还不到 2KB!

通过 docker run 运行。

其实我们更关心 hello-world 镜像包含哪些内容。

Dockerfile 是镜像的描述文件,定义了如何构建 Docker 镜像。Dockerfile 的语法简洁且可读性强,后面我们会专门讨论如何编写 Dockerfile。

hello-world 的 Dockerfile 内容如下:

只有短短三条指令。

  1. FROM scratch
    此镜像是从白手起家,从 0 开始构建。
  2. COPY hello /
    将文件“hello”复制到镜像的根目录。
  3. CMD ["/hello"]
    容器启动时,执行 /hello

镜像 hello-world 中就只有一个可执行文件 “hello”,其功能就是打印出 “Hello from Docker ......” 等信息。

/hello 就是文件系统的全部内容,连最基本的 /bin,/usr, /lib, /dev 都没有。

hello-world 虽然是一个完整的镜像,但它并没有什么实际用途。通常来说,我们希望镜像能提供一个基本的操作系统环境,用户可以根据需要安装和配置软件。

这样的镜像我们称作 base 镜像。

作者:cloudman6

原文

更多技术干货敬请关注云栖社区知乎机构号:阿里云云栖社区 - 知乎

编辑于 2017-12-27

文章被以下专栏收录