# 一、Docker
的四大组成对象
在
Docker
体系里,有四个对象 (Object
) 是我们不得不进行介绍的,因为几乎所有Docker
以及周边生态的功能,都是围绕着它们所展开的。它们分别是:镜像 (Image
)、容器 (Container
)、网络 (Network
)、数据卷 (Volume
)...
# 1.1 镜像
所谓镜像,可以理解为一个只读的文件包,其中包含了虚拟环境运行最原始文件系统的内容
# 1.2 容器
容器就是用来隔离虚拟环境的基础设施,而在
Docker
里,它也被引申为隔离出来的虚拟环境。
- 如果把镜像理解为编程中的类,那么容器就可以理解为类的实例。镜像内存放的是不可变化的东西,当以它们为基础的容器启动后,容器内也就成为了一个“活”的空间
Docker
的容器应该有三项内容组成
- 一个
Docker
镜像 - 一个程序运行环境
- 一个指令集合
# 1.3 网络
在
Docker
中,实现了强大的网络功能,我们不但能够十分轻松的对每个容器的网络进行配置,还能在容器间建立虚拟网络,将数个容器包裹其中,同时与其他网络环境隔离
Docker
能够在容器中营造独立的域名解析环境,这使得我们可以在不修改代码和配置的前提下直接迁移容器,Docker
会为我们完成新环境的网络适配。对于这个功能,我们甚至能够在不同的物理服务器间实现,让处在两台物理机上的两个 Docker 所提供的容器,加入到同一个虚拟网络中,形成完全屏蔽硬件的效果...
# 1.4 数据卷
- 除了网络之外,文件也是重要的进行数据交互的资源。在以往的虚拟机中,我们通常直接采用虚拟机的文件系统作为应用数据等文件的存储位置。然而这种方式其实并非完全安全的,当虚拟机或者容器出现问题导致文件系统无法使用时,虽然我们可以很快的通过镜像重置文件系统使得应用快速恢复运行,但是之前存放的数据也就消失了。
- 为了保证数据的独立性,我们通常会单独挂载一个文件系统来存放数据。这种操作在虚拟机中是繁琐的,因为我们不但要搞定挂载在不同宿主机中实现的方法,还要考虑挂载文件系统兼容性,虚拟操作系统配置等问题。值得庆幸的是,这些在 Docker 里都已经为我们轻松的实现了,我们只需要简单的一两个命令或参数,就能完成文件系统目录的挂载。
- 能够这么简单的实现挂载,主要还是得益于
Docker
底层的 Union File System 技术。在UnionFS
的加持下,除了能够从宿主操作系统中挂载目录外,还能够建立独立的目录持久存放数据,或者在容器间共享。 - 在
Docker
中,通过这几种方式进行数据共享或持久化的文件或目录,我们都称为数据卷 (Volume
)...
# 二、搭建运行Docker环境
# 2.1 Docker Engine 的版本
对于 Docker Engine 来说,其主要分为两个系列
- 社区版 (
CE
, Community Edition ) - 企业版 (
EE
, Enterprise Edition )
- 社区版 (
Docker Engine CE
) 主要提供了Docker
中的容器管理等基础功能,主要针对开发者和小型团队进行开发和试验。而企业版 (Docker Engine EE
) 则在社区版的基础上增加了诸如容器管理、镜像管理、插件、安全等额外服务与功能,为容器的稳定运行提供了支持,适合于中大型项目的线上运行... - 社区版和企业版的另一区别就是免费与收费了。对于我们开发者来说,社区版已经提供了
Docker
所有核心的功能,足够满足我们在开发、测试中的需求,所以我们直接选择使用社区版进行开发即可。在这本小册中,所有的内容也是围绕着社区版的Docker Engine
展开的... - 从另外一个角度,
Docker Engine
的迭代版本又会分为稳定版 (Stable release
) 和预览版 (Edge release
)。不论是稳定版还是预览版,它们都会以发布时的年月来命名版本号,例如如 17 年 3 月的版本,版本号就是 17.03...
Docker Engine
的稳定版固定为每三个月更新一次,而预览版则每月都会更新。在预览版中可以及时掌握到最新的功能特性,不过这对于我们仅是使用Docker
的开发者来说,意义并不是特别重大的,所以我还是更推荐安装更有保障的稳定版本。- 在主要版本之外,
Docker
官方也以解决Bug
为主要目的,不定期发布次要版本。次要版本的版本号由主要版本和发布序号组成,如:17.03.2
就是对17.03
版本的第二次修正...
# 2.2 Docker 的环境依赖
- 由于
Docker
的容器隔离依赖于Linux
内核中的相关支持,所以使用Docker
首先需要确保安装机器的Linux kernel
中包含Docker
所需要使用的特性。以目前 Docker 官方主要维护的版本为例,我们需要使用基于Linux kernel 3.10
以上版本的Linux
系统来安装Docker.
.. - 也许
Linux kernel
的版本还不够直观,下面的表格就直接展示了Docker
对主流几款Linux
系统版本的要求
操作系统 | 支持的系统版本 |
---|---|
CentOS | CentOS 7 |
Debian | Debian Wheezy 7.7 (LTS) |
Debian | Jessie 8 (LTS) |
Debian | Stretch 9 |
Debian | Buster 10 |
Fedora | Fedora 26 、Fedora 27 |
Ubuntu | Ubuntu Trusty 14.04 (LTS) |
Ubuntu | Xenial 16.04 (LTS) |
Ubuntu | Artful 17.10... |
# 2.3 在 Linux 系统中安装 Docker
因为
Docker
本身就基于Linux
的核心能力,同时目前主流的Linux
系统中所拥有的软件包管理程序,已经可以很轻松的帮助我们处理各种依赖问题,所以在Linux
中安装Docker
并非什么难事
CentOS
$ sudo yum install yum-utils device-mapper-persistent-data lvm2 $ $ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo $ sudo yum install docker-ce $ $ sudo systemctl enable docker $ sudo systemctl start docker...
@程序员poetry: 代码已经复制到剪贴板
Debian
$ sudo apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common $ $ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - $ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" $ sudo apt-get update $ sudo apt-get install docker-ce $ $ sudo systemctl enable docker $ sudo systemctl start docker...
@程序员poetry: 代码已经复制到剪贴板
Fedora
$ sudo dnf -y install dnf-plugins-core $ $ sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo $ sudo dnf install docker-ce $ $ sudo systemctl enable docker $ sudo systemctl start docker...
@程序员poetry: 代码已经复制到剪贴板
Ubuntu
$ sudo apt-get install apt-transport-https ca-certificates curl software-properties-common $ $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - $ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" $ sudo apt-get update $ sudo apt-get install docker-ce $ $ sudo systemctl enable docker $ sudo systemctl start docker...
@程序员poetry: 代码已经复制到剪贴板
# 2.4 上手使用
在安装
Docker
完成之后,我们需要先启动docker daemon
使其能够为我们提供Docker
服务,这样我们才能正常使用Docker
在我们通过软件包的形式安装 Docker Engine
时,安装包已经为我们在 Linux
系统中注册了一个 Docker
服务,所以我们不需要直接启动 docker daemon
对应的 dockerd
这个程序,而是直接启动 Docker
服务即可。启动的 Docker
服务的命令其实我已经包含在了前面谈到的安装命令中,也就是...
sudo systemctl start docker
@程序员poetry: 代码已经复制到剪贴板
当然,为了实现
Docker
服务开机自启动,我们还可以运行这个命令
$ sudo systemctl enable docker
@程序员poetry: 代码已经复制到剪贴板
docker version
在
Docker
服务启动之后,我们先来尝试一个最简单的查看Docker
版本的命令:docker version
$ sudo docker version Client: Version: 18.06.1-ce API version: 1.38 Go version: go1.10.3 Git commit: e68fc7a Built: Tue Aug 21 17:23:03 2018 OS/Arch: linux/amd64 Experimental: false Server: Engine: Version: 18.06.1-ce API version: 1.38 (minimum version 1.12) Go version: go1.10.3 Git commit: e68fc7a Built: Tue Aug 21 17:25:29 2018 OS/Arch: linux/amd64 Experimental: false...
@程序员poetry: 代码已经复制到剪贴板
这个命令能够显示
Docker C/S
结构中的服务端 (docker daemon
) 和客户端 (docker CLI
) 相关的版本信息。在默认情况下,docker CLI
连接的是本机运行的docker daemon
,由于docker daemon
和docker CLI
通过RESTful
接口进行了解耦,所以我们也能修改配置用于操作其他机器上运行的docker daemon
...
docker info
如果想要了解
Docker Engine
更多相关的信息,我们还可以通过docker info
这个命令
$ sudo docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 18.06.0-ce Storage Driver: overlay2 Backing Filesystem: extfs Supports d_type: true Native Overlay Diff: true Logging Driver: json-file Cgroup Driver: cgroupfs ## ...... Live Restore Enabled: false...
@程序员poetry: 代码已经复制到剪贴板
配置国内镜像源
一个由 Docker 官方提供的国内镜像源 registry.docker-cn.com
那么有了地址,我们要如何将其配置到 Docker 中呢?
在 Linux 环境下,我们可以通过修改
/etc/docker/daemon.json
( 如果文件不存在,你可以直接创建它 ) 这个 Docker 服务的配置文件达到效果
{ "registry-mirrors": [ "https://registry.docker-cn.com" ] }
@程序员poetry: 代码已经复制到剪贴板
在修改之后,别忘了重新启动 docker daemon
来让配置生效
$ sudo systemctl restart docker
@程序员poetry: 代码已经复制到剪贴板
要验证我们配置的镜像源是否生效,我们可以通过 docker info
来查阅当前注册的镜像源列表
$ sudo docker info ## ...... Registry Mirrors: https://registry.docker-cn.com/ ## ......
@程序员poetry: 代码已经复制到剪贴板
# 三、在 Windows 和 Mac 中使用 Docker
# 3.1 Docker Desktop
对于 Windows 系统来说,安装 Docker for Windows 需要符合以下条件
- 必须使用
Windows 10 Pro
( 专业版 ) - 必须使用
64 bit
版本的Windows
对于 macOS 系统来说,安装 Docker for Mac 需要符合以下条件
Mac
硬件必须为2010
年以后的型号- 必须使用
macOS El Capitan 10.11
及以后的版本
另外,虚拟机软件
VirtualBox
与Docker Desktop
兼容性不佳,建议在安装Docker for Windows
和Docker for Mac
之前先卸载VirtualBox
在确认系统能够支持 Docker Desktop
之后,我们就从 Docker
官方网站下载这两个软件的安装程序,这里直接附上 Docker Store
的下载链接,供大家直接下载
# 3.2 启动 Docker
- 像
Linux
中一样,我们要在Windows
和macOS
中使用Docker
前,我们需要先将Docker
服务启动起来。在这两个系统中,我们需要启动的就是刚才我们安装的Docker for Windows
和Docker for Mac
了... - 启动两个软件的方式很简单,我们只需要通过操作系统的快捷访问功能查找到
Docker for Windows
或Docker for Mac
并启动即可 - 打开软件之后,我们会在
Windows
的任务栏或者macOS
的状态栏中看到Docker
的大鲸鱼图标
Docker Desktop
为我们在Windows
和macOS
中使用Docker
提供了与Linux
中几乎一致的方法,我们只需要打开Windows
中的PowerShell
获得macOS
中的 Terminal,亦或者Git Bash
、Cmder
、iTerm
等控制台类软件,输入docker
命令即可...
使用 docker version
能够看到 Docker
客户端的信息,我们可以在这里发现程序运行的平台
λ docker version Client: ## ...... OS/Arch: windows/amd64 ## ......
@程序员poetry: 代码已经复制到剪贴板
# 3.3 Docker Desktop 的实现原理
我们知道
Docker
的核心功能,也就是容器实现,是基于Linux
内核中Namespaces
、CGroups
等功能的。那么大体上可以说,Docker
是依赖于Linux
而存在的。那么问题来了,Docker Desktop
是如何实现让我们在Windows
和macOS
中如此顺畅的使用 Docker 的呢?...
- 其实
Docker Desktop
的实现逻辑很简单:既然Windows
和macOS
中没有Docker
能够利用的Linux
环境,那么我们生造一个Linux
环境就行啦!Docker for Windows
和Docker for Mac
正是这么实现的... - 由于虚拟化在云计算时代的广泛使用,
Windows
和MacOS
也将虚拟化引入到了系统本身的实现中,这其中就包含了之前我们所提到的通过Hypervisor
实现虚拟化的功能。在Windows
中,我们可以通过Hyper-V
实现虚拟化,而在macOS
中,我们可以通过 HyperKit 实现虚拟化... Docker for Windows
和Docker for Mac
这里利用了这两个操作系统提供的功能来搭建一个虚拟Linux
系统,并在其之上安装和运行docker daemon
。
除了搭建
Linux
系统并运行docker daemon
之外,Docker Desktop
系列最突出的一项功能就是我们能够直接通过PowerShell
、Terminal
这类的控制台软件在Windows
和macOS
中直接操作虚拟 Linux 系统中运行的 docker daemon...
# 3.4 主机文件挂载
- 控制能够直接在主机操作系统中进行,给我们使用 Docker Desktop 系列软件提供了极大的方便。除此之外,文件的挂载也是 Docker Desktop 所提供的大幅简化我们工作效率且简化使用的功能之一。
- 之前我们谈到了,Docker 容器中能够通过数据卷的方式挂载宿主操作系统中的文件或目录,宿主操作系统在 Windows 和 macOS 环境下的 Docker Desktop 中,指的是虚拟的 Linux 系统。
- 当然,如果只能从虚拟的 Linux 系统中进行挂载,显然不足以达到我们的期望,因为最方便的方式必然是直接从 Windows 和 macOS 里挂载文件了。
- 要实现我们所期望的效果,也就是 Docker 容器直接挂载主机系统的目录,我们可以先将目录挂载到虚拟 Linux 系统上,再利用 Docker 挂载到容器之中。这个过程被集成在了 Docker Desktop 系列软件中,我们不需要人工进行任何操作,整个过程已经实现了自动化...
Docker Desktop
对Windows
和macOS
到虚拟Linux
系统,再到Docker
容器中的挂载进行了实现,我们只需要直接选择能够被挂载的主机目录 ( 这个过程更多也是为了安全所考虑 ),剩下的过程全部由 Docker Desktop 代替我们完成。...
# 四、使用容器:镜像与容器
# 4.1 Docker 镜像
- 如果进行形象的表述,我们可以将
Docker
镜像理解为包含应用程序以及其相关依赖的一个基础文件系统,在Docker
容器启动的过程中,它以只读的方式被用于创建容器的运行环境。 - 从另一个角度看,在之前的小节里我们讲到了,
Docker
镜像其实是由基于UnionFS
文件系统的一组镜像层依次挂载而得,而每个镜像层包含的其实是对上一镜像层的修改,这些修改其实是发生在容器运行的过程中的。所以,我们也可以反过来理解,镜像是对容器运行环境进行持久化存储的结果...
# 4.2 查看镜像
- 镜像是由
Docker
进行管理的,所以它们的存储位置和存储方式等我们并不需要过多的关心,我们只需要利用Docker
所提供的一些接口或命令对它们进行控制即可。 - 如果要查看当前连接的
docker daemon
中存放和管理了哪些镜像,我们可以使用docker images
这个命令 (Linux
、macOS
还是Windows
上都是一致的 )
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE php 7-fpm f214b5c48a25 9 days ago 368MB redis 3.2 2fef532eadb3 11 days ago 76MB redis 4.0 e1a73233e3be 11 days ago 83.4MB cogset/cron latest c01d5ac6fc8a 15 months ago 125MB...
@程序员poetry: 代码已经复制到剪贴板
在
docker images
命令的结果中,我们可以看到镜像的ID
(IMAGE ID
)、构建时间 (CREATED
)、占用空间 (SIZE
) 等数据
这里需要注意一点,我们发现在结果中镜像 ID
的长度只有 12
个字符,这和我们之前说的 64
个字符貌似不一致。其实为了避免屏幕的空间都被这些看似“乱码”的镜像 ID
所挤占,所以 Docker
只显示了镜像 ID
的前 12
个字符,大部分情况下,它们已经能够让我们在单一主机中识别出不同的镜像了...
# 4.3 镜像命名
- 镜像层的
ID
既可以识别每个镜像层,也可以用来直接识别镜像 ( 因为根据最上层镜像能够找出所有依赖的下层镜像,所以最上层进行的镜像层 ID 就能表示镜像的ID
),但是使用这种无意义的超长哈希码显然是违背人性的,所以这里我们还要介绍镜像的命名,通过镜像名我们能够更容易的识别镜像... - 在
docker images
命令打印出的内容中,我们还能看到两个与镜像命名有关的数据:REPOSITORY
和TAG
,这两者其实就组成了docker
对镜像的命名规则
准确的来说,镜像的命名我们可以分成三个部分:username、repository 和 tag
username
: 主要用于识别上传镜像的不同用户,与 GitHub 中的用户空间类似。repository
:主要用于识别进行的内容,形成对镜像的表意描述。tag
:主要用户表示镜像的版本,方便区分进行内容的不同细节
对于
username
来说,在上面我们展示的docker images
结果中,有的镜像有username
这个部分,而有的镜像是没有的。没有username
这个部分的镜像,表示镜像是由Docker
官方所维护和提供的,所以就不单独标记用户了...
# 4.4 容器的生命周期
容器运行的状态流转图
图中展示了几种常见对
Docker
容器的操作命令,以及执行它们之后容器运行状态的变化。这里我们撇开命令,着重看看容器的几个核心状态,也就是图中色块表示的:Created
、Running
、Paused
、Stopped
、Deleted
在这几种状态中,Running
是最为关键的状态,在这种状态中的容器,就是真正正在运行的容器了
# 4.5 主进程
- 如果单纯去看容器的生命周期会有一些难理解的地方,而
Docker
中对容器生命周期的定义其实并不是独立存在的。 - 在
Docker
的设计中,容器的生命周期其实与容器中PID
为1
这个进程有着密切的关系。更确切的说,它们其实是共患难,同生死的兄弟。容器的启动,本质上可以理解为这个进程的启动,而容器的停止也就意味着这个进程的停止,反过来理解亦然。 - 当我们启动容器时,
Docker
其实会按照镜像中的定义,启动对应的程序,并将这个程序的主进程作为容器的主进程 ( 也就是PID
为1
的进程 )。而当我们控制容器停止时,Docker
会向主进程发送结束信号,通知程序退出。 - 而当容器中的主进程主动关闭时 ( 正常结束或出错停止 ),也会让容器随之停止。
- 通过之前提到的几个方面来看,
Docker
不仅是从设计上推崇轻量化的容器,也是许多机制上是以此为原则去实现的。所以,我们最佳的Docker
实践方法是遵循着它的逻辑,逐渐习惯这种容器即应用,应用即容器的虚拟化方式。虽然在Docker
中我们也能够实现在同一个容器中运行多个不同类型的程序,但这么做的话,Docker
就无法跟踪不同应用的生命周期,有可能造成应用的非正常关闭,进而影响系统、数据的稳定性...
# 五、使用容器:从镜像仓库获得镜像
# 5.1 镜像仓库
如果说我们把镜像的结构用
Git
项目的结构做类比,那么镜像仓库就可以看似GitLab
、GitHub
等的托管平台,只不过Docker
的镜像仓库托管的不是代码项目,而是镜像
当然,存储镜像并不是镜像仓库最值得炫耀的功能,其最大的作用是实现了 Docker
镜像的分发。借助镜像仓库,我们得到了一个镜像的中转站,我们可以将开发环境上所使用的镜像推送至镜像仓库,并在测试或生产环境上拉取到它们,而这个过程仅需要几个命令,甚至自动化完成...
# 5.2 获取镜像
虽然有很多种方式将镜像引入到
Docker
之中,但我们最为常用的获取现有镜像的方式还是直接从镜像仓库中拉取,因为这种方式简单、快速、有保障
要拉取镜像,我们可以使用 docker pull
命令,命令的参数就是我们之前所提到的镜像仓库名。
$ sudo docker pull ubuntu Using default tag: latest latest: Pulling from library/ubuntu 124c757242f8: Downloading [===============================================> ] 30.19MB/31.76MB 9d866f8bde2a: Download complete fa3f2f277e67: Download complete 398d32b153e8: Download complete afde35469481: Download complete...
@程序员poetry: 代码已经复制到剪贴板
- 当我们运行这个命令后,
Docker
就会开始从镜像仓库中拉取我们所指定的镜像了,在控制台中,我们可以看到镜像拉取的进度。下载进度会分为几行,其实每一行代表的就是一个镜像层。Docker
首先会拉取镜像所基于的所有镜像层,之后再单独拉取每一个镜像层并组合成这个镜像。当然,如果在本地已经存在相同的镜像层 ( 共享于其他的镜像 ),那么Docker
就直接略过这个镜像层的拉取而直接采用本地的内容。 - 上面是一个拉取官方镜像并且没有给出镜像标签的例子,大家注意到,当我们没有提供镜像标签时,
Docker
会默认使用latest
这个标签...
我们也能够使用完整的镜像命名来拉取镜像
<