首页 > 云计算 > > 【Docker学习笔记01】Docker的安装与使用
在《晏子春秋》有这么的描述:
“橘生淮南则为橘,生于淮北则为枳,叶徒相似,其实味不同。所以然者何?水土异也。”
大意是说,一样的橘子树,生长在淮南,结出来的果子为甘甜的橘,而生长在淮北,结出来的果子却是苦涩的枳。其原因并不复杂,就是因为它们生长的“水土”不一样。

这里的“水土”就是是指环境。类似地,软件也有自己的运行环境。
一个很常见的情况是这样的:在自己的机器上,程序跑得好好的,而在他人的机器上就是复现不出来(或复现出来非常费劲)。
这多半是因为,二者运行的环境不一样所致。一些开发人员常常会讲:"它在我的机器上是可以运行的"(It works on my machine)。
言下之意,其实是说在其他机器上,我就不能保证了。
在大自然中,我们很难做到“淮南为橘,淮北亦为橘”,因为环境很难迁移。
但是在软件的世界,我们却可以做到“水土随着橘树跑”。也就是说,不光是软件可以打包发行,而且软件所运行的环境,也可以一起打包发行。
这,就要利用到本节要讲到的Docker技术。
用官方的话来说[1],Docker 是一个基于Go语言的开源应用容器引擎。它诞生于2013年年初,最初的开发者为dotCloud公司。
后来,Docker声名鹊起,该产品的名称居然『功高盖主』,名气大过公司名称。于是,dotCloud公司也就「因势利导」,索性把公司名称——dotCloud,直接改为Docker。
使用Docker是非常方便的。它就像一个集装箱货轮,可以让开发者把他们的应用程序及所依赖的软件包,集体打包到一个可移植的容器之中,然后将它们发布(ship)到流行的Linux、Mac、Windows操作系统(Operating System,简称OS)之中,如图1-2所示。

使用Docker容器,少不了利用沙箱(sandbox)机制。沙箱可以让每个容器内运行一个应用或服务,不同的容器之间相互隔离。当然,在必要时,容器之间可以通过网络相互通信。
读者朋友可以把Docker容器理解为一种轻量级操作系统。
这样看来,Docker就是一种类似于虚拟机(Virtual Machine,简称VM)的运行机制。的确,二者确有类似之处。比如,它们都是对各种计算资源做虚拟化,从而让有限的计算资源得到更加充分的利用,同时它们都可以为应用程序提供运行环境。
但Docker与虚拟机还是有很大不同的。
虚拟机,通过中间软件层将一台或多台独立的机器虚拟运行在物理硬件之上(如图1-3-(a)所示)。
这个中间软件层被称为Hypervisor,它也可以看作是虚拟环境中的『元(meta)』操作系统(可视为操作系统的操作系统),用以协调访问服务器上的所有物理设备和虚拟机,因此它也叫虚拟机监视器(Virtual Machine Monitor)。

使用虚拟机,可以在一种操作系统(Host,宿主)里面运行另一种操作系统(Guest,客户),比如在 Mac 系统里面运行 Windows 系统。
对于在虚拟机中运行的应用程序而言,它们对此毫无感知,因为虚拟机看起来和真实系统完全一样。
而对于宿主系统来说,虚拟机就是一个普通文件,打开或关闭它,和运行普通程序相差无二,如果不需要了就直接删除,对宿主系统的其他应用程序毫无影响。
实际上,Docker的容器技术也是虚拟化技术的一种形式而已。
虚拟化的核心是对计算资源的抽象。
其目标在于,将同一个主机上同时运行多个系统和应用,从而提高系统资源的利用率,从而让成本降低、管理更加方便,并提供一定的容错容灾。
不同于虚拟机的是,Docker能够直接运行在操作系统内核之上。
因此容器的虚拟化,也被称为操作系统虚拟化。这种虚拟化最大的不同在于,它不再需要额外的Supervisor中间层支持。
奥卡姆剃刀定律(Occam's Razor)告诉我们,“如无必要,勿增实体”。
摆脱了Supervisor中间层这一实体,Docker更加轻盈。
但“有一利,必有一弊”。
由于Docker的容器依赖于操作系统的特性,因此它就不能“任性”地虚拟,只能虚拟与底层操作系统(如Linux)相同或相似的操作系统。
Docker的容器技术依赖于Linux内核的两个技术特性:Namespace(名称空间)和Cgoups(即Control goups,控制组),所以Docker上只能运行Linux相关的系统,而不能运行Windows操作系统,这也是Docker相比于虚拟机技术的一个显著不同之处,也是它在系统部署灵活性上的劣势所在。
当然,Docker技术也是有其它独到的优势的,如果没有差异化的优势,Docker自然也不会在2013年诞生以后,如雨后春笋般得到广泛关注和应用。
从图1-3-(b)可以看出,容器仅仅包含应用和它所依赖的库即可,这样的话,资源占用就大大减少。
对比而言,诸如VMvare之类的虚拟机,消耗资源大。这是因为,虚拟机需要模拟所有硬件的行为(如图1-3-(a)所示),因此对CPU、内存的损耗非常之大。
哪怕虚拟机中的应用程序,真正使用的内存只有几MB,虚拟机依然需要几百 MB 的内存才能运行起来。
除此之外,虚拟机还有其他不足之处。比如,系统操作的冗余步骤多。
这是因为,虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登录。
再比如,虚拟机启动很慢。启动一个正常操作系统需要多久,那么启动虚拟机就需多久,甚至更久。可能会出现这样的情况,要等上若干分钟,应用程序运行的环境才得以创建。这对于无缝对接的云服务而言,无疑都是无法忍受的耗时之痛。
因此,对于同样配置的服务器,轻量级的Docker要比重量级的虚拟机能提供更多、响应速度更快的服务能力。
在本质上,Docker 是对Linux容器(Linux Container, 简称LXC)技术的二次封装,它提供简单易用的容器使用接口,是目前最流行的 Linux 容器解决方案。其目前主要用途主要有几类情况[3]。
使用Docker容器开发、然后在同一环境测试和部署,环境始终如一,这样就加快了整个软件开发流程的进程,降低了开发和生成环境的环境迁移难度。
Docker的构想就是“Build,Ship and Run Any App, Anywhere”,即通过对应用程序的封装、分发、部署和运行等整个生命周期进行有效管理,达到应用组件的“一次封装,到处运行”的目的。
由于Docker 容器可以低延迟地随开随关,非常适合云服务的动态扩容和缩容。此外,对于服务器迁移工作(如从亚马逊云迁移到阿里云),使用Docker,我们只需要在新的云服务器上启动需要新的容器即可,无论新旧服务器是否使用了同一类型的平台,这无疑节省了大量的部署和调试开销。

此外,借助Docker,可以辅助构建多用户的“平台即服务”(Platform as a Service,简称PasS)基础设施和“软件即服务”(Software-as-a-Service,简称SasS)的应用程序
同一服务的不同版本,可能服务于不同场景的用户,并非所有用户都愿意迁移到最新版本的服务,因为每一次版本服务的升级,都可能是整个生态系统的升级,而非简单地一蹴而就 。比如说,虽然Java 12都出来了,但Java 6的用户还大有人在。
如果使用Docker,就非常容易创建不同版本的生产环境来运行不同的服务。此外,通过多个容器,一台机器可以跑多个服务,因此在一台机器上,就可以模拟出微服务架构。
由于Docker的轻量化,开发者可以很容易利用Docker在本地搭建测试环境,用以测试应用程序在不同应用环境下的适用性,甚至可以搭建集群测试环境。对于学习者,也很容易搭建某个环境(如TensorFlow)来学习某个应用的使用。
Docker本身是一个开源的商业产品。它有两个发布版本:社区版(Community Edition,简称CE)和企业版(Enterprise Edition,简称EE)。企业版中包含了一些收费服务,对于普通的个人开发者而言,一般用不到。下面的Docker安装介绍都针对社区版而言的。此外,由于本书并非专门介绍Docker的图书,所以仅仅对CentOS 7下的安装给予简易介绍,其它操作系统下的安装,大同小异,具体操作可以参阅官方文档[2]。
需要说明的是,目前Docker只能运行在64位平台之上,而且要求Linux内核不低于3.10,通常内核版本越高,性能越稳定。我们可以通过““uname -a”命令来检测Linux内核的详细信息:
$ uname -a
Linux centos-7 3.10.0-957.10.1.el7.x86_64 #1 SMP Mon Mar 18 15:06:45 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
或者使用“cat /proc/version”来显示Linux信息:
$ cat /proc/version
Linux version 3.10.0-957.10.1.el7.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) ) #1 SMP Mon Mar 18 15:06:45 UTC 2019
在红帽公司的RHEL和同源的CentOS 6.5以上版本,及Ubuntu 14.04以上版本的Linux操作系统,其软件源中都默认带有Docker容器。
为了顺利安装Docker EE,我们推荐使用CentOS 7(可在CentOS官网下载得到[3])。对于CentOS 7而言,可以额通过yum安装器来安装。
在终端窗口输入如下命令:
$ sudo yum check-update
在终端窗口输入如下命令:
$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2
这里的『-y』,表示『yes』表示同意安装。
在终端窗口输入如下命令:
$ sudo yum-config-manager --add-repo \
https://download.docker.com/linux/centos/docker-ce.repo说明:上面的『\』表示续行符,如果你的终端一行写的下,这个反斜杠『\』可以不加。
鉴于国内网络问题,建议使用国内源,官方源请在注释中查看。
执行下面的命令添加 yum 软件源:
$ sudo yum-config-manager --add-repo \
https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo
Docker CE 是稳定版 的软件库
更新 yum 软件源缓存,并安装 docker-ce:
$ sudo yum makecache fast
$ sudo yum install docker-ce
在安装过程中,需要手动输入『y』,表示同意安装。
另外一种安装Docker方式是,利用Docker官方提供的shell脚本来安装Docker的最新版本,目前shell脚本支持的操作系统,除了CentOS之外,还包括Ubuntu、Debian、Fedora、OpenSUSE等常见的Linux发行版。该脚本会自动检测系统信息并进行必要的配置,是一种智能安装方式。
$ curl -fsSL https://get.docker.com/ |sh
这里简单解释一下这条命令,curl命令可视为一个利用URL规则在命令行下工作的文件传输工具,它支持包括HTTP、HTTPS、FTP等众多协议。上述命令的含义如下所示:
我们可以直接在浏览器地址栏输入https://get.docker.com/,就可以发现这个URL对应的是一个shell脚本,部分脚本如图1-4所示。

上面命令的功能就是“curl -fsSL https://get.docker.com/ |sh”把上述Docker官网的安装脚本下载下来,并通过管道命令(“|”),定向输出给脚本解释器“sh”去执行。利用这种方式,你还可以执行下载的Python脚本。
实际上,上面命令可分解为如下两步更为清晰的步骤来执行[4]:
$ curl -fsSL https://get.docker.com/ -o get-docker.sh #01
$ sudo sh get-docker.sh #02
其中,在第01行命令中,“-o”参数表示的是把从官网下载的脚本,并输出(output)另存为一个名为“get-docker.sh”的文件。
第02行命令的功能是,用shell脚本解释器“sh”,去执行这个脚本文件(get-docker.sh)。
当然,我们还可以在https://github.com/moby/moby/releases找到Docker的所有发行版本及源代码,这个工作就留给读者朋友自行折腾。
在Docker安装完毕之后,需要验证它的成功性。这是我们在终端输入“docker version”命令[5]:
$ docker version
Client:
Version: 18.09.5
API version: 1.39
Go version: go1.10.8
Git commit: e8ff056
Built: Thu Apr 11 04:43:34 2019
OS/Arch: linux/amd64
Experimental: false
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
这时,我们看到Docker的客户端(Client)成功显示相应的信息,而在服务器(Server)端提示Docker的守护进程(daemon)没有开启。这是因为Docker 是一种服务器/客户端架构。命令行在运行docker命令时,还需要本机有 Docker 服务。如果这项服务没有启动,可以用下面的命令启动。
$ sudo service docker start
在Redhat或CentOS系统中,这个命令会被重定向为如下命令:
$ sudo systemctl start docker.service
所以,在Redhat或CentOS系统中,我们可以直接利用后者,但前者明显简单易忆。在Docker的服务进程开启之后,我们再次在终端输入“docker version”命令,结果显示如下:
$ docker version
Client:
Version: 18.09.5
API version: 1.39
Go version: go1.10.8
Git commit: e8ff056
Built: Thu Apr 11 04:43:34 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.0-beta4
API version: 1.40 (minimum version 1.12)
Go version: go1.12.4
Git commit: e4666ebe81
Built: Tue May 14 12:45:12 2019
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.2.5
GitCommit: bb71b10fd8f58240ca47fbb579b9d1028eea7c84
runc:
Version: 1.0.0-rc6+dev
GitCommit: 2b18fe1d885ee5083ef9f0838fee39b62d653e30
docker-init:
Version: 0.18.0
GitCommit: fec3683
从上面的运行结果可以看出,Docker的客户端(client)和服务端(server)均已正常启动。事实上,我们还可以利用“docker info”命令来显示Docker相关的信息。部分结果如下所示:
$ docker info
Containers: 14
Running: 0
Paused: 0
Stopped: 14
Images: 4
Server Version: 18.06.1-ce
...
...
seccomp
Profile: default
Kernel Version: 3.10.0-862.14.4.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 40
Total Memory: 62.65GiB
Name: centos-7
ID: J5WZ:7NO4:IME5:Y3H6:2YGG:VBBZ:BHGW:G26A:C6AB:NM3C:A5HW:YM75
Docker Root Dir: /var/lib/docker
...
当然,我们还可以运行一个最简化版的Docker镜像,来测试Docker是否安装成功:
$ sudo docker run hello-world
这条命令会尝试加载一个名叫“hello-world”的测试镜像,,并启动一个容器,如果Docker安装成功,会输出如下欢迎信息和说明信息。
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
d1725b59e92d: Already exists
Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
首先我们注意到,在输入“sudo docker run hello-world”命令时,会显示下面这句提示:
Unable to find image 'hello-world:latest' locally
这表明一开始“hello-world”这个镜像文件在本地并不存在。于是,Docker会自动从Docker Hub官方镜像库中把这个同名的镜像文件下拉(pull)到本地。这个时候宿主操作系统需要具备联网条件。
需要说明的是,Docker在运行一个容器时,首先会检查本地是否存在指定名称的镜像。如果有,则直接运行。如果没有,才会去Docker Hub上拉取同名的镜像,这个过程有点像远程下载。
从上面的提示信息可以看出,为了输出上述问候信息:“Hello from Docker!”,Docker至少做了四个方面的工作:
(1)Docker的客户端成功连接了Docker守护进程;
(2)Docker守护进程从Docker 的镜像仓库Hub中将"hello-world"的镜像下拉(pull)到本地;
(3)Docker守护进程为下载镜像创建了一个新的容器,并生成上述问候信息“Hello from Docker!”;
(4)Docker守护进程将服务器端产生的信息,回传到客户端,并显示在终端屏幕上。
如果上述提示信息的成功输出,表明了一个Docker的工作流程是正常的,也就说明了我们的Docker已经成功安装。
输出上面这段提示信息以后,该容器容器就会自动终止,我们可以利用“sudo docker ps -a ”来查询该容器的状态(后面会讲到ps命令的使用,这里不再展开)。
(未完待续)
[1] https://www.docker.com/
[2] https://docs.docker.com/
[3] https://www.centos.org/download/
[4] 请注意,这里的“#01”或“#02”,是为了描述方便而人为添加的注释行号,在实际命令中,是没有这些符号的。
[5] 这里需要说明的是,如果我们在文字描述Docker的特性时,“Docker”的“D”通常大写,但作为命令时,“docker”中的字符“d”在Linux/Mac操作系统中必须小写,因为Linux/Mac系统区分大小写。
本文作者:张玉宏,著有《深度学习之美:AI时代的数据处理与最佳实践》(电子工业出版社,2018年7月出版)。
如果文章对你有点用,不妨抬手点个赞,你的点赞,我更新的动力:)
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31537575/viewspace-3155309/ ,如需转载,请注明出处,否则将追究法律责任。