引言
软件开发最大的麻烦事之一就是环境配置,操作系统设置,各种库和组件的安装。只有它们都正确,软件才能运行。如果从一种操作系统里面运行另一种操作系统,通常我们采取的策略就是引入虚拟机,比如在 Windows 系统里面运行 Linux 系统。这种方式有个很大的缺点就是资源占用多、冗余步骤多、启动慢。目前最流行的 Linux 容器解决方案之一就是Docker,它最大优点就是轻量、资源占用少、启动快。
docker的好处是:应用隔离,资源独立
虚拟机与Linux容器
虚拟机(virtual machine)就是带环境安装的一种解决方案。它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。
虽然用户可以通过虚拟机还原软件的原始环境。但是,这个方案有几个缺点。
(1)资源占用多
虚拟机会独占一部分内存和硬盘空间。它运行的时候,其他程序就不能使用这些资源了。哪怕虚拟机里面的应用程序,真正使用的内存只有 1MB,虚拟机依然需要几百 MB 的内存才能运行。
(2)冗余步骤多
虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登录。
(3)启动慢
启动操作系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。
Linux 容器
由于虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。
Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。
由于容器是进程级别的,相比虚拟机有很多优势。
(1)启动快
容器里面的应用,直接就是底层系统的一个进程,而不是虚拟机内部的进程。所以,启动容器相当于启动本机的一个进程,而不是启动一个操作系统,速度就快很多。
(2)资源占用少
容器只占用需要的资源,不占用那些没有用到的资源;虚拟机由于是完整的操作系统,不可避免要占用所有资源。另外,多个容器可以共享资源,虚拟机都是独享资源。
(3)体积小
容器只要包含用到的组件即可,而虚拟机是整个操作系统的打包,所以容器文件比虚拟机文件要小很多。
总之,容器有点像轻量级的虚拟机,能够提供虚拟化的环境,但是成本开销小得多。
Docker是基于Go语言实现的云开源项目,诞生于2013年初,最初发起者是dotCloud公司,其目标是“Build, Ship and Run Any App, Anywhere”,主要概念包括镜像、容器、仓库。Docker引擎的技术是Linux容器(Linux Containers, LXC)技术。容器有效地将由单个操作系统的资源划分到孤立的组中,以便更好地在孤立的组之间平衡有冲突的资源使用需求。
什么是Docker
Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。
有了 Docker,就不用担心环境问题。总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
通俗解释Docker
Docker的思想来自于集装箱,集装箱解决了什么问题?在一艘大船上,把货物规整的摆放起来。并且各种各样的货物被集装箱标准化了,集装箱和集装箱之间不会互相影响。docker就是类似的理念。现在都流行云计算了,云计算就好比大货轮。docker就是集装箱。
- 不同的应用程序可能会有不同的应用环境,比如.net开发的网站和php开发的网站依赖的软件就不一样,如果把他们依赖的软件都安装在一个服务器上就要调试很久,而且很麻烦,还会造成一些冲突。这个时候你就要隔离.net开发的网站和php开发的网站。常规来讲,我们可以在服务器上创建不同的虚拟机在不同的虚拟机上放置不同的应用,但是虚拟机开销比较高。docker可以实现虚拟机隔离应用环境的功能,并且开销比虚拟机小,小就意味着省钱了。
- 开发软件的时候用的是Ubuntu,但是运维管理的都是centos,运维在把你的软件从开发环境转移到生产环境的时候就会遇到一些Ubuntu转centos的问题,比如:有个特殊版本的数据库,只有Ubuntu支持,centos不支持,在转移的过程当中运维就得想办法解决这样的问题。这时候要是有docker你就可以把开发环境直接封装转移给运维,运维直接部署你给他的docker就可以了。而且部署速度快。
- 在服务器负载方面,如果你单独开一个虚拟机,那么虚拟机会占用空闲内存的,docker部署的话,这些内存就会利用起来。总之docker就是集装箱原理。
Docker的用途
Docker 的主要用途,目前有三大类。
(1)提供一次性的环境。本地测试的软件、持续集成的时候提供单元测试和构建的环境。
(2)提供弹性的云服务。因为 Docker 容器可以随开随关,很适合动态扩容和缩容。
(3)组建微服务架构。一台机器可以跑多个服务,在本机可以模拟出微服务架构。应用场景
- Web 应用的自动化打包和发布。
- 自动化测试和持续集成、发布。
- 在服务型环境中部署和调整数据库或其他的后台应用。
- 从头编译或者扩展现有的OpenShift或Cloud Foundry平台来搭建自己的PaaS环境。
Docker 能干什么?
简化配置:这是Docker公司宣传的Docker的主要使用场景。虚拟机的最大好处是能在你的硬件设施上运行各种配置不一样的平台(软件、系统),Docker在降低额外开销的情况下提供了同样的功能。它能让你将运行环境和配置放在代码中然后部署,同一个Docker的配置可以在不同的环境中使用,这样就降低了硬件要求和应用环境之间耦合度。
代码流水线管理:前一个场景对于管理代码的流水线起到了很大的帮助。代码从开发者的机器到最终在生产环境上的部署,需要经过很多的中间环境。而每一个中间环境都有自己微小的差别,Docker给应用提供了一个从开发到上线均一致的环境,让代码的流水线变得简单不少。
提高开发效率:这就带来了一些额外的好处:Docker能提升开发者的开发效率。如果你想看一个详细一点的例子,可以参考Aater在DevOpsDays Austin 2014 大会或者是DockerCon上的演讲。 不同的开发环境中,我们都想把两件事做好。一是我们想让开发环境尽量贴近生产环境,二是我们想快速搭建开发环境。
理想状态中,要达到第一个目标,我们需要将每一个服务都跑在独立的虚拟机中以便监控生产环境中服务的运行状态。然而,我们却不想每次都需要网络连接,每次重新编译的时候远程连接上去特别麻烦。这就是Docker做的特别好的地方,开发环境的机器通常内存比较小,之前使用虚拟的时候,我们经常需要为开发环境的机器加内存,而现在Docker可以轻易的让几十个服务在Docker中跑起来。
隔离应用: 有很多种原因会让你选择在一个机器上运行不同的应用,比如之前提到的提高开发效率的场景等。我们经常需要考虑两点,一是因为要降低成本而进行服务器整合,二是将一个整体式的应用拆分成松耦合的单个服务(译者注:微服务架构)。如果你想了解为什么松耦合的应用这么重要,请参考Steve Yege的这篇论文,文中将Google和亚马逊做了比较。
整合服务器:正如通过虚拟机来整合多个应用,Docker隔离应用的能力使得Docker可以整合多个服务器以降低成本。由于没有多个操作系统的内存占用,以及能在多个实例之间共享没有使用的内存,Docker可以比虚拟机提供更好的服务器整合解决方案。
调试能力:Docker提供了很多的工具,这些工具不一定只是针对容器,但是却适用于容器。它们提供了很多的功能,包括可以为容器设置检查点、设置版本和查看两个容器之间的差别,这些特性可以帮助调试Bug。你可以在《Docker拯救世界》的文章中找到这一点的例证。
多租户: 另外一个Docker有意思的使用场景是在多租户的应用中,它可以避免关键应用的重写。我们一个特别的关于这个场景的例子是为IoT(译者注:物联网)的应用开发一个快速、易用的多租户环境。这种多租户的基本代码非常复杂,很难处理,重新规划这样一个应用不但消耗时间,也浪费金钱。使用Docker,可以为每一个租户的应用层的多个实例创建隔离的环境,这不仅简单而且成本低廉,当然这一切得益于Docker环境的启动速度和其高效的diff命令。
快速部署: 在虚拟机之前,引入新的硬件资源需要消耗几天的时间。虚拟化技术(Virtualization)将这个时间缩短到了分钟级别。而Docker通过为进程仅仅创建一个容器而无需启动一个操作系统,再次将这个过程缩短到了秒级。这正是Google和Facebook都看重的特性。你可以在数据中心创建销毁资源而无需担心重新启动带来的开销。通常数据中心的资源利用率只有30%,通过使用Docker并进行有效的资源分配可以提高资源的利用率。
Docker的三个概念
镜像(Image):类似于虚拟机中的镜像。任何应用程序运行都需要环境,而镜像就是用来提供这种运行环境的。例如一个Ubuntu镜像就是一个包含Ubuntu操作系统环境的模板,同理在该镜像上装上Apache软件,就可以称为Apache镜像。
容器(Container):类似于一个轻量级的沙盒,可以将其看作一个极简的Linux系统环境(包括root权限、进程空间、用户空间和网络空间等),以及运行在其中的应用程序。Docker引擎利用容器来运行、隔离各个应用。容器是镜像创建的应用实例,可以创建、启动、停止、删除容器,各个容器之间是是相互隔离的,互不影响。注意:镜像本身是只读的,容器从镜像启动时,Docker在镜像的上层创建一个可写层,镜像本身不变。
仓库(Repository):类似于代码仓库,这里是镜像仓库,是Docker用来集中存放镜像文件的地方。注意与注册服务器(Registry)的区别:注册服务器是存放仓库的地方,一般会有多个仓库;而仓库是存放镜像的地方,一般每个仓库存放一类镜像,每个镜像利用tag进行区分,比如Ubuntu仓库存放有多个版本(12.04、14.04等)的Ubuntu镜像。
image文件
Docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。(image和容器可以看作类似类与实例的关系)
image 是二进制文件。实际开发中,一个 image 文件往往通过继承另一个 image 文件,加上一些个性化设置而生成。举例来说,你可以在 Ubuntu 的 image 基础上,往里面加入 Apache 服务器,形成你的 image。
image 文件是通用的,一台机器的 image 文件拷贝到另一台机器,照样可以使用。一般来说,为了节省时间,我们应该尽量使用别人制作好的 image 文件,而不是自己制作。即使要定制,也应该基于别人的 image 文件进行加工,而不是从零开始制作。
为了方便共享,image 文件制作完成后,可以上传到网上的仓库。Docker 的官方仓库 Docker Hub 是最重要、最常用的 image 仓库。此外,出售自己制作的 image 文件也是可以的。
Dockerfile文件
学会使用 image 文件以后,接下来的问题就是,如何可以生成 image 文件?如果你要推广自己的软件,势必要自己制作 image 文件。
这就需要用到 Dockerfile 文件。它是一个文本文件,用来配置 image。Docker 根据 该文件生成二进制的 image 文件。
注意这里docker文件是没有后缀的
容器文件
image 文件生成的容器实例,本身也是一个文件,称为容器文件。也就是说,一旦容器生成,就会同时存在两个文件: image 文件和容器文件。而且关闭容器并不会删除容器文件,只是容器停止运行而已。
终止运行的容器文件,依然会占据硬盘空间,可以使用docker container rm命令删除。
运行上面的命令之后,再使用docker container ls —all命令,就会发现被删除的容器文件已经消失了。
仓库(Repository)
类似于代码仓库,是Docker存放镜像的场所,而Registry注册服务器是存放仓库的地方,其上放着很多仓库,每个仓库集中存放某一类镜像的多个文件,可以通过tag标签来区分。目前最大的公有仓库是Docker Hub,而国内是Docker Pool。Docker Pub:本地用户目录.dockercfg中存储登录信息,在仓库中存在centos这类由Docker公司创建、验证、支持的根镜像,也有类似xionger/centos这类由个人提供的镜像,可以通过-s N来查看高星镜像。此外,Docker Hub还可以通过设置追踪类似GitHub的网站,然后根据其更行,自动执行创建。创建和使用私有仓库:可以通过官方提供的registry镜像来简单搭建一套本地私有仓库环境。
Docker安装Python项目(来自参考文献2)
场景描述:我们使用一个简单的python项目,本项目是中文分词的算法。如何实现Docker安装部署。
第一步: Win10下创建目录文本
选择在D盘下创建docker目录,分别新建三个文件:Dockerfile,app.py,equirements.txt
Dockerfile(没有后缀):一个文本文件,包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。创建镜像必须文件。1
2
3
4
5
6
7
8
9
10
11
12
13
14# 基于镜像基础
FROM python:3.7
# 设置代码文件夹工作目录 /app
WORKDIR /app
# 复制当前代码文件到容器中 /app
ADD . /app
# 安装所需的包
RUN pip install -r requirements.txt
# Run app.py when the container launches
CMD ["python", "app.py"]
app.py:python项目的源代码,这里测试的单个python文件,如果是一个完整项目,可以将整个文件夹拷贝到这里。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# coding:utf8
"""
DESC: Python数据预处理之第一个分词程序范例
Author:伏草惟存
Prompt: code in Python3 env
"""
import jieba
str = "道路千万条,安全第一条;行车不规范,亲人两行泪。"
print("原句: \n" + str)
seg_list = jieba.cut(str)
print("分词: \n" + " / ".join(seg_list))equirements.txt :所需要的插件,以python为例,其获取方法是cmd命令,进入到【D:\docker】目录,执行命令:pip freeze > requirements.txt
第二步:生成镜像。本文采用的windows环境。docker build -t friendlyhello .命令中最后的点不要忘记,这里表示当前目录
第三步:查看镜像是否生成
第四步:运行镜像程序,这里可以看到分词效果
Docker架构
Docker Engine与 Docker machine
Docker Engine也就是”Docker”,Docker我们一般理解的,都是C/S模型,用户通过docker client向docker daemon发送REST 请求。
Docker Engine包括这么几个部分:
- Docker Daemon — docker 的守护进程,属于C/S中的server
- Docker REST API — docker daemon向外暴露的REST 接口
- Docker CLI — docker向外暴露的命令行接口(Command Line API)
因此,客户端访问服务端的方式有两种
- 一种是使用命令行工具,比如docker run, docker ps….
- 另一种就是直接通过调用REST API,比如发送一个curl http请求
Docker Machine
官方解释:
Docker Machine是一个工具,用来在虚拟主机上安装Docker Engine,并使用 docker-machine命令来管理这些虚拟主机
无论是Mac,Windows或是Linux,你都可以在其上安装Docker Machine,使用docker-machine命令来创建和管理大量的Docker hosts。它会自动创建主机,在主机上安装Docker Engine,然后配置docker client。每个被管理的主机(“machine”)都是一个Docker 主机和一个配置过的client的组合。
官方给的Docker Machine的具体使用场景:
您可以使用Docker Machine来: 在Mac或Windows上安装并运行Docker 配置和管理多个远程Docker主机 提供Swarm群集
Docker Machine 与 Docker Engine概念上的区别
- Docker Engine: 主要用来接收和处理docker命令请求的
- Docker Machine:则主要用来管理 docker化的host (安装了Docker Engine的主机)
通常,你都是在本地安装Docker Machine。Docker Machine包含自己的命令行客户端:docker-machine以及Docker Engine的客户端:docker。你可以使用Machine在一个或多个虚拟机上安装Docker Engine。这些虚拟机可以是在本地,也可以是远程的。这些虚拟化的主机可以被认为是“machine”
Docker命令行与守护进程如何交互?
Docker并非单体应用,它由多个组件构成。这篇博客将介绍Docker守护进程(daemon)与Docker命令行(CLI)。事实上,当我们在谈论安装或使用Docker时,所指的其实就是Docker守护进程与命令行。
解释一下上图中的元素:
- Docker守护进程(docker daemon)是运行在你的操作系统上的一个服务。目前,它只能运行在Linux上,因为它依赖于一些Linux内核特性(比如Cgroup与Namespace)。 但是,也有一些特殊的办法让Docker运行在MacOS与Windows上(运行在Linux虚拟机中)。
- Docker守护进程提供了REST API。许多工具(Docker命令行,Docker Compose等)都可以通过REST API与Docker守护进程进行交互,例如创建容器,构建镜像等。
- Docker命令行(docker CLI)是与Docker守护进程进行交互的主要工具。
Docker是C/S架构
Docker是Client/Server架构。其中Docker守护进程是服务端,Docker命令行是众多客户端之一。事实上,还有很多第三方的Docker客户端。
对于各种流行的编程语言,它们都有对应的Docker客户端。感兴趣的话,你也可以开发一个,使用REST API与Docker守护进程进行交互就好了。
通过客户端,你可以管理Docker的各种元素,包括镜像、容器、网络以及数据卷。对于Docker感兴趣的话,你不妨看看 Dive Into Docker course。
Docker命令行与守护进程如何交互?
从左至右理解上图:
- 最左侧是Docker客户端,即Docker命令行。我们可以运行各种Docker命令,比如构建镜像(docker build),下载镜像(docker pull),运行容器(docker run)。Docker命令行可以安装在各种操作系统上,例如Windows,MacOS或者Linux服务器。
- 中间是Docker主机,Docker守护进程运行在上面。Docker命令行可以轻松地连接远程的Docker主机(给定IP和端口即可)。而在MacOS与Windows上”运行”Docker时,Docker守护进程事实上运行在Linux虚拟机中。这里关键点在于,Docker守护进程和命令行可以运行在不同的主机上。
- 最右侧是Docker仓库,它也是Docker生态系统中的一份子。它是我们下载、上传、存储以及分享Docker镜像的地方。Docker仓库的细节与本文无关,因此不再赘述。