Docker 是什么
Docker 本质其实是 LXC 的增强版. 它本身不是容器, 并不实现虚拟化, 虚拟化仍然是利用 linux 内核的容器技术(namespace cgroup) 实现. Docker 是容器的易用工具, 它把容器的创建, 启动, 停止, 分发变得更加简单. Docker 在早期的版本其核心就是LXC 的二次封装发行版.
Docker和LXC的区别是什么, LXC创建容器是借助LXC的template去创建, 而 Docker 发明了一个镜像技术, 把完整打包好的一个容器作为一个镜像放到仓库里, 然后当需要创建容器时, Docker 调用 LXC 的工具 lxc-create, 但不再通过 lxc 的模板去安装, 而是连接到镜像服务器上下载匹配的镜像文件, 而后基于镜像启动容器(就比如MYSQL官方把mysql程序写好打包存放到yum源的仓库, 然后我们yum install就可以安装mysql).
所以, Docker 极大的简化了容器的使用难度. 以后我们创建启动容器, 只需要一个命令,docker-run,docker-stop就可以启动停止一个容器了.
Docker 作为容器技术的一个实现, 或者说让容器技术普及开来的最成功的实现.
Docker 是基于 Go 语言实现的一个开源项目, 它的主要目标是“Build, Ship and Run Any APP, Anywhere”, 即通过对组件的封装、分发、部署、运行等生命周期的管理,使得用户的应用及其运行环境能够做到“一次封装,到处运行”
所以换句话来说, 我们普通的程序不能"一次封装, 到处运行", 比如我们写好的java/cpp程序, 想要在centos或者ubuntu下运行, 会先发现我们缺失java/cpp运行环境, 就要先安装jdk/cpp标准库, 然后才能运行程序. 所以这"一次封装"不能到处运行.
而镜像可以把OS相关依赖,运行环境依赖的库,应用程序文件及其配置文件全部封装起来.
而docker是通过build->ship->run三步走来实现一次封装,到处运行, 实现软件的封装, 分发, 部署和运行:
Docker 的引擎迭代
- Docker 早期是基于LXC 容器管理引擎实现。
- 当后来成熟之后, Docker为了打磨自己生态, 自建了一个容器引擎叫 libcontainer, 具备LXC的能力。
- 后来谷歌和Docker大战,以及 CNCF 的介入, Docker 公司将 Libcontainer 捐出, 并改名为了 runC,成为工业化标准的容器引擎。
目前所使用的新版 Docker,所使用的容器引擎就是 RunC。
Docker 和虚拟机的区别
图1:
图2:
图3:
| 传统虚拟机 | Docker 容器 | 原因 | |
|---|---|---|---|
| 磁盘占用 | 几个 GB 到几十个 GB左右 | 几十 MB 到几百 MB 左右 | 如图1,因为虚拟机保留了完整的Guest OS内核,磁盘占用高;Docker 共享了宿主机的OS内核 |
| CPU,内存占用 | 资源占用大,资源利用率低。 | 资源占用低,资源利用率高 | 如图1 :虚拟机1.资源占用角度,每个虚拟机都需要运行完整的 Guest OS,会额外占用较多 CPU、内存等资源,2.虚拟化开销角度,虚拟机通过 Hypervisor 虚拟出完整的硬件环境,存在一定虚拟化开销。这些资源并没有直接用于我们的业务程序,属于额外开销,所以相比于OS占用和虚拟化开销,我们的应用可能只占用了少部分资源;Docker1.资源占用角度,容器本身就是一个进程,不吃内存资源,它与宿主机共享 Linux 内核,不需要为每个容器运行独立 OS 内核,只需运行应用及其用户态依赖。所以创建一个容器时,不需要和虚拟机一样重新加载一个操作系统内核。从而避免引寻、加载操作系统内核返回时耗时耗资源的过程,当新建一个虚拟机时,虚拟机软件需要加载 Guest OS,返回新建过程是分钟级别的,而新建一个docker 容器只需要几秒钟;2.虚拟化开销角度,容器比虚拟机有更少的抽象层,它不经过Hypervisor虚拟化层,可以直接和宿主OS交互,使用实际物理机的硬件资源,因此虚拟化开销低,所以容器额外 CPU、内存开销较小,资源利用率通常更高。 |
| 启动速度 | (从开机到运行项目)几分钟 | 几秒 | 虚拟机启动要启动一个OS内核,而容器只是启动一个进程 |
| 安装管理 | 需要专门的运维技术 | 安装、管理方便 | 传统虚拟机项目后面需要运维团队支持维护;Docker通过Docker相关命令就能启动停止 |
| 应用部署 | 手动部署,速度慢 | 体系化部署,可以自动化,速度快 | 如图2:首先部署方面,虚拟机部署可以直接把一个包含应用程序和系统运行环境的镜像打包部署,镜像体积大,速度慢;也可以从下到上先安装OS,运行环境,再部署应用,过程繁琐速度慢;其次升级方面,如果应用升级了,jar从1.0升级到2.0, 应用程序也升级了,那需要手动去把新版本替换掉,需要手动搬运的过程。而Docker部署,如果应用是1.0, 就把1.0的镜像推送到仓库,如果是2.0也推送上去,到时候部署的时候需要哪个就拉哪个,而这些过程都可以通过docker命令实现,把这些命令串起来就可以进行自动化部署,速度快。 |
| 隔离性 | 系统级别 | 进程级别 | 虚拟机是系统级别隔离,隔离等级比较低,因为它把内核进行隔离,安全性比较高;Docker是进程级别隔离,隔离级别高,因为它没有隔离内核,安全性低。 |
| 封装程度 | 打包整个操作系统 | 打包项目代码和依赖信息 | Docker打包粒度比较低,更精准 |
Docker 和 JVM 虚拟化的区别?
Docker 版本
Docker 发展过程中衍生了以下版本,目前我们使用的版本是社区版docker-ce
- lxc:上文中提到, lxc 是最早的 linux 容器技术, 早期版本的 docker 直接使用 lxc 来实现容器的底层功能。虽然使用者相对较少,但 lxc 项目仍在持续开发演进中。但是如果docker一直依赖lxc作底层, docker的扩展性和生态构建比较困难, 所以自研了libcontainer.
- libcontainer: docker 从 0.9 版本开始自行开发了 libcontainer 模块来作为 lxc 的替代品实现容器底层特性,并在 1.10 版本彻底去除了 lxc。在 1.11 版本拆分出 runc 后,libcontainer 也随之成为了 runc 的核心功能模块, runc 后续变成了容器标准。自此,第一次容器大战Docker胜利。
- moby: 第二次容器大战,Google的K8s打败Docker的Swarm,Docker把社区版Docker engine引流到企业版,而社区版改名为moby。moby 是 docker 公司发起的开源项目,其中最主要的部分就是同名组件 moby,事实上这个 moby 就是 dockerd 目前使用的开源项目名称, docker 项目中的 engine(dockerd)仓库现在就是从 moby 仓库 fork 而来的,使用 containerd 作为运行时标准。 官网:https://mobyproject.org/
- docker-ce: docker 的开源版本, CE 指 Community Edition。 docker-ce 中的组件来自于 moby、 containerd 等其他项目。 https://www.docker.com/pricing/
- docker-ee: docker 的收费版本, EE 指 Enterprise Edition。其基础组件来源和
docker-ce 是一样的,但附加了一些其他的组件和功能。
Docker 架构
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程 API 来管理和创建 Docker 容器。Docker 容器通过 Docker 镜像来创建。
- Docker 仓库(Registry)
Docker 仓库用来保存镜像,就类似yum、apt仓库。 Docker Hub 供了庞大的镜像集合供使用。 - Docker daemon
Docker daemon 是服务器组件,是 Docker 最核心的后台进程,我们也把它称为守护进程。 - Docker 客户端(Client)
Docker 客户端通过命令行或者其他工具使用 Docker API 与 Docker 的守护进程通信。 - Docker 主机(Host)
一个实际的物理服务器或者虚拟服务器, 用于执行 Docker 守护进程和容器。 - Docker 镜像(Images)
Docker 镜像是用于创建 Docker 容器的模板。 - Docker 容器(Container)
容器是独立运行的一个或一组应用。
一个生活化的例子:
有一天我(client)去华为旗舰店(Docker Host)买手机(client运行docker build一个image), 进门之后, 一个客服(docker deamon)来问我需要什么? 我说我要买华为手机, 客服说这个手机目前的展柜(Host里没有现成的镜像)里没有, 我去帮你到仓库(Registry)找一找(docker pull), 找到之后客服放回展柜, 我说你帮我开机验验机(docker run), 客服就把手机开机(此时image被运行起来成为一个container).
Docker 生态
新时代软件诉求在这里插入图片描述
新时代软件诉求
随着云计算、互联网和大规模分布式系统的发展,软件运行环境发生了几个明显变化:
- 数据量极速增长
物联网、云计算、边缘计算等技术的发展,使需要处理的数据规模不断增大。
根据 IDC 分布的《数据时代 2025》预测,全球数据量将从 2018 年的 33ZB 增至 2025年的 175ZB,增长超过 5 倍;中国平均增速快于全球 3%,预计到 2025 年将增至48.6ZB,占全球数据圈的比例由 23.4%提升至 27.8%。其中,中国企业级数据量将从2015 年占中国数据量的 49%增长到 2025 年的 69%。
数据规模增大意味着:物理上, 需要更多服务器;软件上, 需要在大量服务器上部署软件, 软件部署和运维的规模越来越大。
因此,过去“手工登录服务器、手动安装软件”的方式逐渐无法满足需求, 体现在下面:
- 计算资源规模快速增长
企业的数据中心可能拥有成百上千甚至更多服务器。
腾讯云全球服务器数量 100w+,数据量 EB+; 2020 年阿里云:在全国已建成 5 大超级数据中心,阿里云在全球 22个地域部署了上百个数据中心,服务器的总规模数已经接近 200 万台。
于是出现一个新问题:如何快速、统一地把软件部署到大量服务器上?
如果每台服务器都手动执行:
yuminstall...wget...make... 修改配置文件 启动服务不仅效率低,而且很容易出现不同服务器环境不一致的问题。
- 软件需求爆发式增长
研发模式从瀑布开发演变为敏捷开发,原来 3 个月上一次新功能,现在两周一次,而开发过程中我们也经常遇到需要修改需求,然后变更再发布的情况。
软件上线有问题需要快速回滚,对软件有着极强的版本管理和回滚诉求。
因此软件系统产生了几个重要诉求:
- 快速发布, 快速部署;
- 版本管理, 出现问题时快速回滚。
- 软件需要方便地共享
软件开发完成之后,不仅要在开发者自己的机器上运行,还需要交付给:测试人员, 运维人员, 其他开发者, 服务器集群。
传统方式往往是:
代码 + 安装文档 + 依赖列表 + 配置文件接收者仍然需要自己重新搭建环境, 搭建环境期间就可能遇到许多各种各样的问题.
因此希望能够:将软件本身 + 运行软件所需要的环境一起交付。
- 软件运行环境越来越复杂
不同项目依赖的环境不同:
项目 A → Java 17 项目 B → Python 3.12 项目 C → GCC + C++ 项目 D → Node.js甚至同一种语言也可能存在版本冲突:
项目 A → Python 3.8 项目 B → Python 3.12如果全部直接安装在宿主机上:
服务器 ├── Java ├── Python ├── GCC ├── Node.js ├── 各种动态库 └── 各种配置容易产生各种奇怪的问题, 比如依赖冲突, 版本冲突, 环境不一致等,运维复杂。
这就是一个经典问题:“在我电脑上明明可以运行,为什么到了服务器就不能运行?”
Docker 要解决什么问题
云时代需要我们针对这些诉求有一套针对的解决方案
针对上面的问题, 可以把新时代的软件诉求概括成四个问题:
1. 我们要处理海量的数据,如何处理呢? 2. 软件需求上, 针对开发的需求需要频繁的变更上线,如何才能将修改的代码快速的分发到几百或者 几千台服务器呢?以及如何共享软件? 3. 软件怎么进行版本管理和快速回滚? 4. 不同的开发环境怎么搭建呢?- 我们要处理海量的数据,如何处理呢?
硬件上, 购买大量的服务器, 来具备数据处理的能力; 软件上, 研发对应软件来处理数据.
- 软件如何分发到不同的服务器? 如何共享软件?
搞一个中心仓库,让各个服务器去下载软件包安装, Docker Hub 是一个公共镜像仓库, 这样同一条 docker pull 命令下载的都是同样的软件:
开发者 │ │ docker push ↓ 镜像仓库 │ │ docker pull ├─────────────┐ ↓ ↓ 服务器 A 服务器 B ↓ ↓ 运行容器 运行容器类比Linux也有软件仓库:
Linux 软件 ↓ yum / apt 仓库 Docker 镜像 ↓ Docker Hub- 软件怎么进行版本管理和快速回滚?
Docker 设计了镜像, 将 docker 需要的所有信息设计一套软件格式, 把所有的依赖搞进去, 并打上版本标签, 想用哪个版本就部署哪个版本,想更换版本就关闭镜像A,启动镜像B。
镜像可以通过 Tag 管理版本:
myapp:v1.0 myapp:v1.1 myapp:v2.0软件升级:
v1.0 ↓ v2.0如果 v2.0 出现问题,可以重新使用 v1.0, 实现快速回滚。
- 不同的开发环境怎么搭建呢?
docker 设计了镜像来应对, 将
应用程序 + 运行所需的用户态环境 + 依赖库 + 配置按照统一格式打包成Docker Image, 这其中就包含了相应的开发环境, 比如:
myapp:v1.0 ├── 应用程序 ├── Python / Java / C++ runtime ├── 动态库 ├── 配置文件 └── 其他依赖总结一下:
新时代 ↓ 服务器越来越多 ↓ 软件版本越来越多 ↓ 部署越来越频繁 ↓ 环境越来越复杂 ↓ 传统手工部署成本过高 ↓ 需要标准化软件交付方式 ↓ Docker Image ↓ 应用 + 依赖 + 用户态环境统一打包 ↓ 但镜像需要在机器之间传递 ↓ Image Registry ↓ Docker Hub / Harbor ↓ 大量服务器统一拉取镜像 ↓ 快速部署、升级、回滚