Docker与容器
写在前面
从19年初开始使用Docker到现在差不到两年了,感觉对于Docker的认识还停留在使用的阶段,各个知识点串联度不够,没有形成一个体系,因此打算抽出一些时间来系统学习Docker,以便在后续使用的时候更加得心应手。本篇首先学习Docker是什么,之后会剖析Docker和相关容器技术,以及它在DevOps等运用场景所带来的巨大便利,最后学习Docker在整个虚拟化领域中的技术定位。
虚拟技术
我们知道在主机时代比拼的是单个服务器的性能,如CPU主频和内存的强弱,而在云时代,最为看中的则是凭借虚拟化技术所构建的集群处理能力。从传统来看,虚拟化既可以通过硬件模拟来实现,也可以通过操作系统软件来实现。而容器技术则更优雅,它充分利用了操作系统本身已有的机制和特性,可以实现远超虚拟机的轻量级虚拟化。因此有人甚至将其称为“新一代的虚拟化”技术,并将基于容器打造的云平台亲切的称为“容器云”。
什么是Docker
Docker是基于Go语言开发的开源容器项目,它诞生于2013年。现在主流的操作系统包括Linux的各大发行版、macOS、Windows等都已经支持Docker。
Docker的构想是要实现“Build,Ship and Run Any App,Anywhere”,即通过对应用的封装(Packing)、分发(Distribution)、部署(Deployment)、运行(Runtime)生命周期进行管理,达到应用组件级别的“一次封装,到处运行”。请注意这里的应用组件可以是一个Web应用,一个编译环境,也可以是一套数据库平台服务,甚至是一个操作系统或集群。
基于Linux平台的多项开源技术,Docker提供了高效、敏捷和轻量级的容器方案,并支持部署到本地环境和多种主流云平台。可以说Docker首次为应用的开发、运行和部署提供了“一站式”的实用解决方案。
Linux容器技术
Linux容器技术,即Linux Containers(LXC)技术,IBM DeveloperWorks网站关于容器技术的描述十分准确:“容器有效的将单个操作系统管理的资源划分到孤立的组中,以更好的在孤立的组之间平衡有冲突的资源使用需求。与虚拟化相比,这样既不需要指令级模拟,也不需要即时编译。容器可以在核心CPU本地运行指令,而不需要任何专门的解释机制。此外,也避免了准虚拟化和系统调用替换中的负载性。”
LXC经历了长期的演化,并集成到主流的Linux内核中,进而成为Linux系统轻量级容器技术的实施标准。
从Linux容器到Docker
在LXC的基础上,Docker进一步优化了容器的使用体验,使其变得大众化。首先,Docker提供了各种容器管理工具(如分发、版本、移植等),让用户无需关注底层的操作,更加简单明了的管理和使用容器:其次,Docker通过引入分层文件系统构建和高效的镜像机制,降低了迁移难度,极大的改善了用户体验,这使得用户操作Docker容器就像操作应用自身一样简单。
通俗的讲,我们可以将Docker容器理解为一种轻量级的沙盒(sandbox)。每个容器内运行着一个应用,不同的容器相互隔离,容器之间也可以通过网络互相通信。容器的创建和停止十分迅速,几乎跟创建和终止原生应用一致;此外容器自身对于系统资源的额外需求也十分有限,远远低于传统的虚拟机。在很多时候,直接将容器当做应用本身也没有任何问题。
使用Docker的原因
Docker容器虚拟化的好处
Docker项目的发起人,Docker公司的CTO认为Docker在正确的地点、正确的时间顺应了正确的趋势—如何正确地构建应用。
我们知道在云时代,开发者创建的应用必须要能很方便的在网络上传播,也就是说应用必须脱离底层物理硬件的限制;同时还必须是“任何时间任何地点”都可以获取到的。因此我们需要一种新型的创建分布式应用程序的方式,快速分发和部署,而这些正是Docker所能提供的最大优势。
举个简单的例子,假设用户视图采用最常见的LAMP(Linux+Apache+Mysql+PHP)组合来构建网站。那么按照传统的做法,首先需要安装Apache、Mysql+PHP以及它们各自所依赖的环境;之后分别对它们进行配置(包括创建合适的用户、配置参数等);经过大量的操作后,还需要进行功能测试,看看是否工作正常;如果不正常还需要进行调式追踪,这就意味着需要花费更多的时间和可能出现一些不可控的风险。如果应用数目变多,那么事情则会变得更加难以处理。
上面倒还好说,假如一旦需要服务器迁移,如从亚马逊迁移到其他云,那么往往需要对每个应用都进行重新部署和调式。这些都是重复性的“体力活”,极大的降低了用户的工作效率。究其根源,是这些应用直接运行在底层操作系统上,无法保证同一份应用在不同的环境中行为一致。
鉴于此种情况,Docker提供了一种更为“聪明”的方式,通过容器来打包应用、解耦应用和运行平台。这也就意味着迁移的时候,只需在新的服务器上启动需要的容器即可,根本不用去理会服务器是否是同一平台等问题。毫无疑问这极大的简化了部署的流程,降低了部署过程中出现问题的风险,最重要的是节省了大量的时间。
Docker在开发和运维中的优势
由于笔者做过运维开发,因此对于Docker在开发和运维中的优势是最能切身感受到的。对于运维开发人员来说,最希望的是一次创建或者配置某个环境后可以做到在任意时间,任意地点让应用能正常运行,而Docker做到了这一点,因此深受广大运维人员的青睐。
总结起来有以下几个方面的优势:
(1)更快速的交付和部署。使用Docker,开发人员可以使用镜像来快速构建一套标准的开发环境;开发完成后,测试和运维人员可以直接使用完全相同的环境来部署代码。这样只要是开发测试过的代码,就可以确保在生产环境中无缝运行。Docker可以快速创建和删除容器,实现快速迭代、节约开发、测试、部署的大量时间,且整个过程全程可见,使团队更容易理解应用的创建和工作过程。
(2)更高效的资源利用。运行Docker容器不需要额外的虚拟化管理程序( Virtual Machine Manager,VMM,以及Hypervisor)的支持,Docker是内核级的虚拟化,可以实现更高的性能,同时对资源的额外需求很低。与传统虚拟机方式相比, Docker的性能要提高1~2个数量级。
(3)更轻松的迁移和扩展。Docker容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等,同时支持主流的操作系统发行版本。这种兼容性让用户可以在不同平台之间轻松地迁移应用。
(4)更简单的更新管理。使用Dockerfile,只需要小小的配置修改,就能替代以往大量的更新工作。所有修改都以增量的方式被分发和更新,从而实现自动化且高效的容器管理。
Docker与虚拟机比较
Docker作为一种轻量级的虚拟方式,经常用来和传统的虚拟机方式进行对比,这是一个老生常谈的话题,笔者就选择其中的4点进行介绍:
(1)Docker容器很快,启动和停止可以在秒级实现,而传统的虚拟机则少至几分钟,长则半个小时;
(2)Docker容器对于系统资源的需求很少,一台主机可以同时运行上千个Docker容器,据说在IBM服务器上已经实现了同时运行一万个容器实例的可能;但是一台主机最多只能运行8个传统的虚拟机(这已经是性能不错的主机了,笔者电脑似乎运行3个就到头了);
(3)Docker借鉴了Git的设计理念来方便用户获取、分发和更新应用镜像,存储复用,增量更新;
(4)Docker通过Dockerfile支持灵活的自动化创建和部署机制,以提高工作效率,并标准化流程。
关于Docker和传统虚拟机技术的对比,可以看如下表格:
特性 | 容器 | 虚拟机 |
---|---|---|
启动速度 | 秒级 | 分钟级 |
性能 | 接近原生 | 较差 |
内存代价 | 较小 | 较多 |
硬盘使用 | MB级别 | GB级别 |
运行密度 | 单机支持上千个容器 | 一般几十个 |
隔离性 | 安全隔离 | 完全隔离 |
迁移性 | 优秀 | 一般 |
Docker容器除了运行其中的应用外,基本不消耗额外的系统资源,这样在保证应用性能的同时可以减少系统的开销。
传统的虚拟机如果想运行N个不同的应用,就需要启动N个虚拟机,因为开发者需要为每个虚拟机单独分配独占的内存、磁盘等资源;Docker只需启动N个隔离的容器,然后将应用放进容器即可,这样使得应用可以获得接近于原生的运行性能。
这里需要着重提一下Docker的隔离性,我们知道传统的虚拟机方式采用的是相对封闭的隔离,即操作系统层面是不互通的;而Docker则利用了Linux系统上的多种防护技术实现了严格的隔离,且可以整合众多安全工具。
Docker虚拟机也称轻量级虚拟机,原因在于这些容器中所运行的Linux系统并不是一个独立完整的Linux系统,这些容器其实是共用了一个Linux内核,也就是Docker系统为它们提供的Linux内核。
Docker与虚拟化
虚拟化技术在各个行业都有,在计算机领域一般是指计算虚拟化或者服务虚拟化。这里援引维基百科的解释:
- 虚拟化:在计算机技术中,虚拟化是一种资源管理技术,是将计算机
也就是说,虚拟化的核心是对资源进行抽象,其目的是为了在同一个主机上同时运行多个系统或者应用,进而提高系统资源的利用率,同时能降低成本,便于管理和带来容错容灾功能。
一般来说,虚拟化技术分为硬件虚拟化和软件虚拟化,其中基于硬件的虚拟化技术不多,如网卡中的单根多IO虚拟化,这种比较深奥,笔者觉得目前还没有到学习它的必要;而基于软件的虚拟化技术则是日常使用最多的,它根据软件虚拟化对象所在的层次,可以划分为应用虚拟化和平台虚拟化(通常所说的虚拟机技术指的就是这个)。
应用虚拟化一般是一些模拟设备或者诸如Wine这样的软件;平台虚拟化可以细分为几个子类:(1)完全虚拟化。虚拟机模拟完整的底层硬件环境和特权指令的执行过程,客户端操作系统无须进行修改。如VMware Workstation,VirtualBox,QEMU等。(2)硬件辅助虚拟化。利用硬件(主要是CPU)辅助支持(目前x86体系结构上可用的硬件辅助虚拟化技术包括Intel-VT和AMD-V)处理敏感指令来实现完全虚拟化的功能,客户端操作系统无效修改,如VMware Workstation,Xen和KVM。(3)部分虚拟化。只针对部分硬件资源进行虚拟化,这样使得客户端操作系统需要进行修改。(4)超虚拟化。部分硬件接口以软件的形式提供为客户机操作系统,客户操作系统需要进行修改,如早期的Xen。(5)操作系统级虚拟化。内核通过创建多个虚拟的操作系统实例(内核和库)来隔离不同的进程。前面介绍的容器技术就属于这个范畴。
也就是说Docker以及其他的容器技术都属于操作系统级虚拟化这个范畴,操作系统虚拟化最大的特点就是不需要额外的supervisor支持。Docker虚拟化方式之所以有如此多的优势,这与操作系统虚拟化技术自身的设计和实现是分不开的:
从上图可以看到,左侧的传统的虚拟化方式是在硬件层面实现虚拟化,需要有额外的虚拟机管理应用和虚拟机操作系统层。而右侧的Docker容器则是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统,因此占用资源非常少,属于轻量级。
Docker核心概念
前面都是一些比较基础的知识,学习它对于Docker的设计思想会有更深的理解。接下来开始学习Docker中的三大核心概念:镜像、容器和仓库,只有理解了这三个概念才能对Docker容器的整个生命周期有一个较为清晰的认识。
Docker镜像
Docker镜像(Image)类似于虚拟机镜像,如常见的iso格式的系统镜像,你可以将其理解为一个可读的模板,但是这又和iso镜像不同的是,通常Docker镜像中是比较纯净的,一个镜像可以包含一个基本的操作系统,里面仅仅是安装了Nginx应用程序,此时可以称其为一个Nginx镜像。
镜像是创建Docker容器的基础。通过版本管理和增量的文件系统,Docker提供了一套十分简单的机制来创建和更新现有镜像,用户甚至可以从网上下载已经做好的镜像,并直接使用。
Docker容器
Docker容器(Container)类似于一个轻量级的沙箱,Docker利用容器来运行和隔离应用。
容器是是从镜像创建的应用运行实例,镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等,容器与容器之间是彼此隔离,互不可见的。
其实你完全可以将容器当做一个简易版的Linux系统环境(包括root用户权限、进程空间、用户空间和网络空间)以及运行在其中的应用程序打包而成的盒子。
请注意,镜像本身是只读的,容器从镜像启动的时候,会在镜像的最上层创建一个可写层。
Docker仓库
Docker仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像。
但是有的时候我们会将Docker仓库和仓库注册服务器(Register)搞混淆,其实这两者是有区别的。仓库注册服务器是存放仓库的地方,其上面往往包括多个仓库。每一个仓库都集中存放某一类镜像,因此往往会包括多个镜像文件,通过使用不同的tag标签来进行区分。
如下图所示的注册服务器,它存放着Ubuntu和CentOS的仓库,而这两个仓库又各自存放着不同版本的操作系统:
根据所储存镜像的公开分享与否,Docker仓库可以分为公开仓库(public)和私有仓库(Private)两种形式。目前最大的公开仓库是官方提供的Docker Hub,里面存放着大量的镜像,国内的很多云服务商也提供了仓库的本地源,以便于我们在国内稳定下载。当然了,Docker也支持用户在本地网络内创建一个只能自己访问的私有仓库,这样用户不希望公开分享自己镜像的时候就可以这样操作。
当用户创建了自己的镜像后,可以使用push命令将其上传到指定的公有仓库或者是私有仓库,这样当用户在下次使用的时候就可以直接从仓库上将之前的镜像给pull下拉,这一点与Git非常相似,因为Docker利用仓库管理镜像的设计理念借鉴了Git代码仓库的管理理念。
安装Docker引擎
Docker引擎是使用Docker容器的核心组件,它可以运行在主流的操作系统上,这里以CentOS安装Docker引擎为例进行介绍。首先点击 这里,访问Docker的官网来查看获取Docker的方式以及Docker支持的平台类型:
目前Docker支持Docker引擎、Docker Hub和Docker Cloud等多种服务。其中Docker引擎包括支持在桌面系统或云平台安装Docker,以及为企业提供简单安全弹性的容器集群的编排和管理;Docker Hub则是官方提供的云托管服务,可以提供公有或者私有的镜像仓库;Docker Cloud是官方提供的容器云服务,可以完成容器的部署与管理,可以完整地支持容器化项目,同时还提供了CI、CD功能。Docker引擎目前分为社区版(CE)和企业版(EE),社区版包括大部分的核心功能,因此正常情况下使用社区版即可。
第一步,使用uname -a
命令来查看当前CentOS的内核版本,Docker官方强调至少3.8以上,建议使用3.10及以上内核:
1 | [root@localhost ~]# uname -a |
第二步,使用yum update
命令来升级yum源,请注意切勿在生产环境中升级yum源,因为更新命令会对软件包和内核升级。
第三步,安装需要的软件包,主要是yum-utils、device-mapper-persistent-data和lvm2包,其中yum-utils提供yum-config-manager功能,另外两个是devicemapper驱动依赖的,即便于后续支持devicemapper类型:
1 | [root@localhost ~]# yum install -y yum-utils device-mapper-persistent-data lvm2 |
第四步,设置yum源,操作如下:
1 | [root@localhost ~]# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo |
第五步,查看所有仓库中所有的docker版本,并选择特定版本安装:
1 | [root@localhost ~]# yum list docker-ce --showduplicates | sort -r |
第六步,安装Docker,由于repo中默认只开启stable仓库,因此使用yum install docker-ce
命令安装的Docker版本是最新版本,即
1 | [root@localhost ~]# yum install docker-ce |
第七步,启动Docker和添加为开启自启动:
1 | [root@localhost ~]# systemctl start docker |
第八步,检查安装是否成功(注意client和service这两部分都有,则说明安装成功),可以使用docker version
命令来进行验证,如下所示:
请注意第七步和第八步的顺序不能颠倒,否则使用docker version
命令时,只会显示Client的信息:
这样我们就完成了docker-ce-19.03.13版本的安装,可以看到Docker的安装还是较为简单的。
配置镜像下载地址
由于docker是在国外,因此镜像下载速度可能不太快,建议使用国内仓库,如DaoCloud。点击这里 DaoCloud,选择加速器:
然后将下面的代码直接放在shell环境中执行:
1 | curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://f1361db2.m.daocloud.io |
执行结果如下所示:
配置Docker服务
一般来说使用Docker的用户不一定都是root用户,因此为了避免每次使用Docker命令时都需要切换到root用户,可以将当前用户加入安装自动创建的docker用户组,使用的命令如下:
1 | sudo usermod -aG docker USER_NAME |
其中的USER_NAME就是当前用户名称,然后退出并重新登录即可。
当开发者在前面设置了docker开机自启动服务时,使用ps -aux|grep docker
命令来查看docker是否正在运行时,结果如下所示:
1 | [envythink@localhost ~]$ ps -aux|grep docker |
你会发现docker服务启动时,实际上调用了dockerd命令,它支持多种启动参数,所以开发者完全可以直接通过执行dockerd命令来启动docker服务,可以使用下面的命令来启动docker服务,开启Debug模式,并监听在本地的2376端口:
1 | [envythink@localhost ~]$ dockerd -D -H tcp://127.0.0.1:2376 |
当然这些选项是可以写在/etc/docker/daemon.json
文件中的,然后由dockerd服务启动时读取:
1 | { |
对于CentOS和RedHat系统来说,服务通过systemd来管理,配置文件路径为/etc/systemd/system/docker.service.d/docker.conf
,当配置发生更新后,需要通过systemctl命令老管理Docker服务,使用的命令如下:
1 | [envythink@localhost ~]$ systemctl daemon-reload |
如果服务不能正常工作,可以查看Docker服务的日志文件来确定问题,可以使用journalctl -u docker.service
命令来进行查看。同时每次重启Docker服务后,可以通过使用docker info
命令来查看Docker信息,以确保服务已经正常运行。
小结
本篇学习了虚拟技术、Docker、容器技术、Docker的安装与三个核心概念:镜像、容器、仓库,在后面我们会围绕这三个核心概念来学习Docker的各种操作命令。