第一次接手一台要上线的服务器,最容易卡住的不是写业务代码,而是「环境」两个字。本地跑得好好的服务,传到测试机上就报缺库;测试机调通了,生产机的系统版本又不一样。我自己那会儿最狼狈的一次,是照着一份环境搭建文档一条条敲命令,敲到第 30 行发现前面某个包装错了版本,只能推倒重来。
Docker 就是来终结这种循环的。它把「环境」本身变成一个可以打包、可以传输、可以版本化的东西。
这篇是我系统过一遍 Docker 基础时整理的笔记,内容源自掘金小册的 Docker 资料,我按自己实际用到的场景重新归了类。它不是命令速查表,而是按「你在什么时候会用到这一组命令」来组织的:先搞清楚镜像和容器是什么关系,再按容器生命周期、网络、数据卷、Dockerfile、Compose 的顺序往下走。读完你应该能独立把一套 MySQL + Redis + 应用的开发环境用 Compose 跑起来,并且知道出问题时该往哪儿看。
在本篇文章中,我们将从浅入深,和大家一起学习以下知识:
- Docker 的四大对象(镜像、容器、网络、数据卷)分别在解决什么问题
- 各系统下安装 Docker Engine 的方式,以及 Docker Desktop 在 Windows/macOS 上到底做了什么
- 镜像的命名规则、拉取、搜索、查看与删除,以及 ID 前缀匹配这个省事的小机制
- 容器的五种状态与主进程机制,create / start / run / exec / attach 各自的适用场景
- 容器网络的三个核心概念,容器互联、端口暴露与端口映射的区别
- 三种挂载方式(Bind Mount、Volume、Tmpfs Mount)怎么选
- 镜像的提交、命名、导出导入与批量迁移
- Dockerfile 的结构与常见指令,构建缓存、命令合并、ENTRYPOINT 与 CMD 的配合
- 怎么读懂 Docker Hub 上的镜像标签,Alpine 镜像的取舍
- Docker Compose 的配置写法与常用配置项,以及一个完整的 PHP 项目目录结构
- 用 Docker Swarm 搭 Overlay 网络,让跨主机的容器互相看见
本文写于 2018 年,命令与写法保留当时的原貌。文中凡是已经过时的部分(比如
docker-compose带横线的命令、compose 文件的version字段、MAINTAINER指令、国内镜像加速地址),我都另起一小段标注了现在的情况,具体细节以官方文档为准。
# 一、Docker 的四大组成对象
先说结论,Docker 后面所有的命令,docker images、docker run、docker network create、docker volume,本质都是在操作这四个对象中的某一个。把这四个对象的关系理顺,命令是什么形状你基本能猜个八九不离十,反过来如果一上来就背命令,很快就会记混。
在
Docker体系里,有四个对象 (Object) 是我们不得不进行介绍的,因为几乎所有Docker以及周边生态的功能,都是围绕着它们所展开的。它们分别是:镜像 (Image)、容器 (Container)、网络 (Network)、数据卷 (Volume)…
这一节不涉及任何命令,纯粹是建立心智模型。你可以把它当成后面十五章的地图。
# 1.1 镜像
所谓镜像,可以理解为一个只读的文件包,其中包含了虚拟环境运行最原始文件系统的内容
只读这两个字是关键。镜像一旦构建出来就不会再变,所有对它的「修改」都不是原地改,而是往上叠一层。这个设计后面会反复出现,构建缓存、镜像层共享、docker commit,讲的都是同一件事。
# 1.2 容器
容器就是用来隔离虚拟环境的基础设施,而在
Docker里,它也被引申为隔离出来的虚拟环境。
- 如果把镜像理解为编程中的类,那么容器就可以理解为类的实例。镜像内存放的是不可变化的东西,当以它们为基础的容器启动后,容器内也就成为了一个「活」的空间
Docker 的容器应该有三项内容组成
- 一个
Docker镜像 - 一个程序运行环境
- 一个指令集合
类和实例这个类比很好用,但有一点要拎清楚:同一个镜像可以起出几十个容器,它们共享底下那几层只读镜像层,各自只多出一层可读写的沙盒。所以起十个 nginx 容器并不会占十份 nginx 镜像的磁盘。这个我一开始也想岔过,以为容器多了硬盘就会被吃光。
# 1.3 网络
在
Docker中,实现了强大的网络功能,我们不但能够十分轻松的对每个容器的网络进行配置,还能在容器间建立虚拟网络,将数个容器包裹其中,同时与其他网络环境隔离
Docker能够在容器中营造独立的域名解析环境,这使得我们可以在不修改代码和配置的前提下直接迁移容器,Docker会为我们完成新环境的网络适配。对于这个功能,我们甚至能够在不同的物理服务器间实现,让处在两台物理机上的两个 Docker 所提供的容器,加入到同一个虚拟网络中,形成完全屏蔽硬件的效果…
# 1.4 数据卷
- 除了网络之外,文件也是重要的进行数据交互的资源。在以往的虚拟机中,我们通常直接采用虚拟机的文件系统作为应用数据等文件的存储位置。然而这种方式其实并非完全安全的,当虚拟机或者容器出现问题导致文件系统无法使用时,虽然我们可以很快的通过镜像重置文件系统使得应用快速恢复运行,但是之前存放的数据也就消失了。
- 为了保证数据的独立性,我们通常会单独挂载一个文件系统来存放数据。这种操作在虚拟机中是繁琐的,因为我们不但要搞定挂载在不同宿主机中实现的方法,还要考虑挂载文件系统兼容性,虚拟操作系统配置等问题。值得庆幸的是,这些在 Docker 里都已经为我们轻松的实现了,我们只需要简单的一两个命令或参数,就能完成文件系统目录的挂载。
- 能够这么简单的实现挂载,主要还是得益于
Docker底层的 Union File System 技术。在UnionFS的加持下,除了能够从宿主操作系统中挂载目录外,还能够建立独立的目录持久存放数据,或者在容器间共享。 - 在
Docker中,通过这几种方式进行数据共享或持久化的文件或目录,我们都称为数据卷 (Volume)…
四个对象串起来看是这样的:镜像决定了容器里有什么,容器决定了程序怎么跑,网络决定了容器能和谁说话,数据卷决定了哪些东西在容器被删掉之后还能留下来。后面每一章其实都在展开其中一个。
# 二、搭建运行Docker环境
这一组内容什么时候用得上?拿到一台新的云主机、换新电脑、或者给团队新同事配开发机的时候。装 Docker 本身不难,难的是装完发现内核版本不够、或者拉镜像卡在 0%,这两个坑下面都会讲到。
# 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版本的第二次修正…
现状补充:Docker Engine CE / EE 这套命名后来调整过,社区版就叫 Docker Engine,商业形态归到 Docker Desktop 的订阅里;年月式版本号(17.03 这种)也换成了普通的语义化版本。安装命令的包名 docker-ce 在主流发行版仓库里目前还在用,但要不要装、装哪个包,还是以你所用发行版对应的官方安装文档为准。下面这几段安装命令保留 2018 年的原样,看的时候留意一下就行。
# 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并非什么难事
下面四组命令做的是同一件事,只是包管理器不同:先装上添加第三方仓库所需要的工具,再把 Docker 官方仓库地址加进去,然后装 docker-ce,最后把服务设成开机自启并立刻启动。你只需要照着自己机器的发行版挑一组执行。
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...
Debian